将android14的下拉栏进行修改,要求实现
要实现这种效果
要将形状从之前的长方形改成圆形我们需要对他找到他生成tile的地方,他是通过
diff --git a/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
index 79260897..3061ddec 100644
--- a/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
+++ b/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
@@ -122,6 +122,7 @@ public class QSFactoryImpl implements QSFactory {
@Override
public QSTileView createTileView(Context context, QSTile tile, boolean collapsedView) {
QSIconView icon = tile.createTileView(context);
- return new QSTileViewImpl(context, icon, collapsedView);
+ Log.w("xsr_spec", "No stock tile spec: " + tile.getTileSpec());
+ return new QSTileViewBak(context, icon, collapsedView);
}
}
所以我们只需要在这里修改逻辑使用自己的方法就可以改变形状,我这里搬运了android11的图标实现
diff --git a/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java b/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
new file mode 100644
index 00000000..f2db08c5
--- /dev/null
+++ b/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
@@ -0,0 +1,392 @@
+/*
+ * Copyright (C) 2017 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.tileimpl;
+
+import static com.android.systemui.qs.tileimpl.QSIconViewImpl.QS_ANIM_LENGTH;
+
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.drawable.AdaptiveIconDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.RippleDrawable;
+import android.graphics.drawable.ShapeDrawable;
+import android.graphics.drawable.shapes.PathShape;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.service.quicksettings.Tile;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.PathParser;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.Switch;
+
+import com.android.settingslib.Utils;
+import com.android.systemui.R;
+import com.android.systemui.plugins.qs.QSIconView;
+import com.android.systemui.plugins.qs.QSTile;
+import com.android.systemui.plugins.qs.QSTile.BooleanState;
+
+import android.graphics.drawable.shapes.OvalShape;
+
+public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView {
+
+ private static final String TAG = "QSTileBaseView";
+ private static final int ICON_MASK_ID = com.android.internal.R.string.config_icon_mask;
+ private final H mHandler = new H();
+ private final int[] mLocInScreen = new int[2];
+ private final FrameLayout mIconFrame;
+ protected QSIconView mIcon;
+ protected RippleDrawable mRipple;
+ private Drawable mTileBackground;
+ private String mAccessibilityClass;
+ private boolean mTileState;
+ private boolean mCollapsedView;
+ private boolean mShowRippleEffect = true;
+ private float mStrokeWidthActive;
+ private float mStrokeWidthInactive;
+
+ private final ImageView mBg;
+ private final int mColorActive;
+ private final int mColorInactive;
+ private final int mColorDisabled;
+ private int mCircleColor;
+ private int mBgSize;
+ int position ;
+
+ public QSTileBaseView(Context context, QSIconView icon) {
+ this(context, icon, false);
+ }
+
+ public QSTileBaseView(Context context, QSIconView icon, boolean collapsedView) {
+ super(context);
+ // Default to Quick Tile padding, and QSTileView will specify its own padding.
+ int padding = context.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_padding);
+ mIconFrame = new FrameLayout(context);
+ mStrokeWidthActive = context.getResources()
+ .getDimension(com.android.internal.R.dimen.config_qsTileStrokeWidthActive);
+ mStrokeWidthInactive = context.getResources()
+ .getDimension(com.android.internal.R.dimen.config_qsTileStrokeWidthInactive);
+ int size = 72 ;
+ addView(mIconFrame, 0);
+ mBg = new ImageView(getContext());
+
+ Drawable d = context.getResources().getDrawable(R.drawable.qs_tile_background_shape_custom);
+ d.setTintList(ColorStateList.valueOf(Color.TRANSPARENT));
+ int bgSize = context.getResources().getDimensionPixelSize(R.dimen.qs_tile_background_size);
+ mBg.setImageDrawable(d);
+ FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(bgSize, bgSize, Gravity.CENTER);
+ mIconFrame.addView(mBg, lp);
+ mBg.setLayoutParams(lp);
+ mIcon = icon;
+ FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT,
+ Gravity.CENTER);
+ mIconFrame.addView(mIcon, params);
+ mIconFrame.setClipChildren(false);
+ mIconFrame.setClipToPadding(false);
+
+ mTileBackground = newTileBackground();
+ if (mTileBackground instanceof RippleDrawable) {
+ setRipple((RippleDrawable) mTileBackground);
+ }
+ setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
+ setBackground(mTileBackground);
+
+ mColorActive = context.getColor(R.color.pad_pro_blue);
+ mColorDisabled = Utils.getDisabled(context,
+ Utils.getColorAttrDefaultColor(context, android.R.attr.textColorTertiary));
+ mColorInactive = Utils.getColorAttrDefaultColor(context, R.attr.offStateColor);
+ setPadding(0, 0, 0, 0);
+ setClipChildren(false);
+ setClipToPadding(false);
+ mCollapsedView = collapsedView;
+ setFocusable(true);
+ }
+
+ @Override
+ public void setPosition(int position) {
+ // 你的方法实现
+ position = position;
+ }
+
+ public View getBgCircle() {
+ return mBg;
+ }
+
+ protected Drawable newTileBackground() {
+ final int[] attrs = new int[]{android.R.attr.selectableItemBackgroundBorderless};
+ final TypedArray ta = getContext().obtainStyledAttributes(attrs);
+ final Drawable d = ta.getDrawable(0);
+ ta.recycle();
+ return d;
+ }
+
+ private void setRipple(RippleDrawable tileBackground) {
+ mRipple = tileBackground;
+ if (getWidth() != 0) {
+ updateRippleSize();
+ }
+ }
+
+ private void updateRippleSize() {
+ // center the touch feedback on the center of the icon, and dial it down a bit
+ final int cx = mIconFrame.getMeasuredWidth() / 2 + mIconFrame.getLeft();
+ final int cy = mIconFrame.getMeasuredHeight() / 2 + mIconFrame.getTop();
+ final int rad = (int) (mIcon.getHeight() * .85f);
+ mRipple.setHotspotBounds(cx - rad, cy - rad, cx + rad, cy + rad);
+ }
+
+ @Override
+ public void init(QSTile tile) {
+ init(v -> tile.click(this), v -> tile.secondaryClick(this), view -> {
+ tile.longClick(this);
+ return true;
+ });
+ }
+
+ public void init(OnClickListener click, OnClickListener secondaryClick,
+ OnLongClickListener longClick) {
+ setOnClickListener(click);
+ setOnLongClickListener(longClick);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ super.onLayout(changed, l, t, r, b);
+ if (mRipple != null) {
+ updateRippleSize();
+ }
+ }
+
+ @Override
+ public boolean hasOverlappingRendering() {
+ // Avoid layers for this layout - we don't need them.
+ return false;
+ }
+
+ /**
+ * Update the accessibility order for this view.
+ *
+ * @param previousView the view which should be before this one
+ * @return the last view in this view which is accessible
+ */
+ public View updateAccessibilityOrder(View previousView) {
+ setAccessibilityTraversalAfter(previousView.getId());
+ return this;
+ }
+
+ public void onStateChanged(QSTile.State state) {
+ mHandler.obtainMessage(H.STATE_CHANGED, state).sendToTarget();
+ }
+
+ private void updateStrokeShapeWidth(QSTile.State state) {
+ Resources resources = getContext().getResources();
+ if (!(mBg.getDrawable() instanceof ShapeDrawable)) {
+ return;
+ }
+ ShapeDrawable d = (ShapeDrawable) mBg.getDrawable();
+ d.getPaint().setStyle(Paint.Style.FILL);
+ switch (state.state) {
+ case Tile.STATE_INACTIVE:
+ if (mStrokeWidthInactive >= 0) {
+ d.getPaint().setStyle(Paint.Style.STROKE);
+ d.getPaint().setStrokeWidth(mStrokeWidthInactive);
+ }
+ break;
+ case Tile.STATE_ACTIVE:
+ if (mStrokeWidthActive >= 0) {
+ d.getPaint().setStyle(Paint.Style.STROKE);
+ d.getPaint().setStrokeWidth(mStrokeWidthActive);
+ }
+ break;
+ }
+ }
+
+ protected void handleStateChanged(QSTile.State state) {
+ updateStrokeShapeWidth(state);
+ int circleColor = getCircleColor(state.state);
+ boolean allowAnimations = animationsEnabled();
+ if (circleColor != mCircleColor) {
+ if (allowAnimations) {
+ ValueAnimator animator = ValueAnimator.ofArgb(mCircleColor, circleColor)
+ .setDuration(QS_ANIM_LENGTH);
+ animator.addUpdateListener(animation -> mBg.setImageTintList(ColorStateList.valueOf(
+ (Integer) animation.getAnimatedValue())));
+ animator.start();
+ } else {
+ QSIconViewImpl.setTint(mBg, circleColor);
+ }
+ mCircleColor = circleColor;
+ }
+
+ mShowRippleEffect = state.showRippleEffect;
+ setClickable(state.state != Tile.STATE_UNAVAILABLE);
+ setLongClickable(state.handlesLongClick);
+ mIcon.setIcon(state, allowAnimations);
+ setContentDescription(state.contentDescription);
+ final StringBuilder stateDescription = new StringBuilder();
+ switch (state.state) {
+ case Tile.STATE_UNAVAILABLE:
+ stateDescription.append(mContext.getString(R.string.tile_unavailable));
+ break;
+ case Tile.STATE_INACTIVE:
+ if (state instanceof QSTile.BooleanState) {
+ stateDescription.append(mContext.getString(R.string.switch_bar_off));
+ }
+ break;
+ case Tile.STATE_ACTIVE:
+ if (state instanceof QSTile.BooleanState) {
+ stateDescription.append(mContext.getString(R.string.switch_bar_on));
+ }
+ break;
+ default:
+ break;
+ }
+ if (!TextUtils.isEmpty(state.stateDescription)) {
+ stateDescription.append(", ");
+ stateDescription.append(state.stateDescription);
+ }
+ setStateDescription(stateDescription.toString());
+
+ mAccessibilityClass =
+ state.state == Tile.STATE_UNAVAILABLE ? null : state.expandedAccessibilityClassName;
+ if (state instanceof QSTile.BooleanState) {
+ boolean newState = ((BooleanState) state).value;
+ if (mTileState != newState) {
+ mTileState = newState;
+ }
+ }
+ }
+
+ /* The view should not be animated if it's not on screen and no part of it is visible.
+ */
+ protected boolean animationsEnabled() {
+ if (!isShown()) {
+ return false;
+ }
+ if (getAlpha() != 1f) {
+ return false;
+ }
+ getLocationOnScreen(mLocInScreen);
+ return mLocInScreen[1] >= -getHeight();
+ }
+
+ private int getCircleColor(int state) {
+ switch (state) {
+ case Tile.STATE_ACTIVE:
+ return mColorActive;
+ case Tile.STATE_INACTIVE:
+ case Tile.STATE_UNAVAILABLE:
+ return mColorDisabled;
+ default:
+ Log.e(TAG, "Invalid state " + state);
+ return 0;
+ }
+ }
+
+ @Override
+ public void setClickable(boolean clickable) {
+ super.setClickable(clickable);
+ setBackground(clickable && mShowRippleEffect ? mRipple : null);
+ }
+
+ @Override
+ public int getDetailY() {
+ return getTop() + getHeight() / 2;
+ }
+
+ public QSIconView getIcon() {
+ return mIcon;
+ }
+
+ public View getIconWithBackground() {
+ return mIconFrame;
+ }
+
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ if (!TextUtils.isEmpty(mAccessibilityClass)) {
+ event.setClassName(mAccessibilityClass);
+ }
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ // Clear selected state so it is not announce by talkback.
+ info.setSelected(false);
+ if (!TextUtils.isEmpty(mAccessibilityClass)) {
+ info.setClassName(mAccessibilityClass);
+ if (Switch.class.getName().equals(mAccessibilityClass)) {
+ String label = getResources().getString(
+ mTileState ? R.string.switch_bar_on : R.string.switch_bar_off);
+
+ android.util.Log.d("xsr_mLabel", "label = : "+label);
+ // Set the text here for tests in
+ // android.platform.test.scenario.sysui.quicksettings. Can be removed when
+ // UiObject2 has a new getStateDescription() API and tests are updated.
+ info.setText(label);
+ info.setChecked(mTileState);
+ info.setCheckable(true);
+ if (isLongClickable()) {
+ info.addAction(
+ new AccessibilityNodeInfo.AccessibilityAction(
+ AccessibilityNodeInfo.AccessibilityAction
+ .ACTION_LONG_CLICK.getId(),
+ getResources().getString(
+ R.string.accessibility_long_click_tile)));
+ }
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder(getClass().getSimpleName()).append('[');
+ sb.append("locInScreen=(" + mLocInScreen[0] + ", " + mLocInScreen[1] + ")");
+ sb.append(", iconView=" + mIcon.toString());
+ sb.append(", tileState=" + mTileState);
+ sb.append("]");
+ return sb.toString();
+ }
+
+ private class H extends Handler {
+ private static final int STATE_CHANGED = 1;
+
+ public H() {
+ super(Looper.getMainLooper());
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == STATE_CHANGED) {
+ handleStateChanged((QSTile.State) msg.obj);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/systemui/qs/tileimpl/QSTileViewBak.java b/src/com/android/systemui/qs/tileimpl/QSTileViewBak.java
new file mode 100644
index 00000000..370932a3
--- /dev/null
+++ b/src/com/android/systemui/qs/tileimpl/QSTileViewBak.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2017 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.tileimpl;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.Configuration;
+import android.service.quicksettings.Tile;
+import android.text.TextUtils;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.android.settingslib.Utils;
+import com.android.systemui.FontSizeUtils;
+import com.android.systemui.R;
+import com.android.systemui.plugins.qs.QSIconView;
+import com.android.systemui.plugins.qs.QSTile;
+
+import java.util.Objects;
+import com.android.systemui.animation.LaunchableView ;
+import com.android.systemui.animation.LaunchableViewDelegate ;
+import kotlin.Unit;
+
+/** View that represents a standard quick settings tile. **/
+public class QSTileViewBak extends QSTileBaseView implements LaunchableView {
+ private static final int MAX_LABEL_LINES = 2;
+ private static final boolean DUAL_TARGET_ALLOWED = false;
+ private View mDivider;
+ protected TextView mLabel;
+ protected TextView mSecondLine;
+ private ImageView mPadLock;
+ private int mState;
+ private ViewGroup mLabelContainer;
+ private View mExpandIndicator;
+ private View mExpandSpace;
+ private ColorStateList mColorLabelDefault;
+ private ColorStateList mColorLabelUnavailable;
+ LaunchableViewDelegate launchableViewDelegate ;
+
+ public QSTileViewBak(Context context, QSIconView icon) {
+ this(context, icon, false);
+ }
+
+ public QSTileViewBak(Context context, QSIconView icon, boolean collapsedView) {
+ super(context, icon, collapsedView);
+
+ setClipChildren(false);
+ setClipToPadding(false);
+
+ setClickable(true);
+ setId(View.generateViewId());
+ createLabel();
+ setOrientation(VERTICAL);
+ setGravity(Gravity.CENTER_HORIZONTAL | Gravity.TOP);
+ mColorLabelDefault = Utils.getColorAttr(getContext(), android.R.attr.textColorPrimary);
+ // The text color for unavailable tiles is textColorSecondary, same as secondaryLabel for
+ // contrast purposes
+ mColorLabelUnavailable = Utils.getColorAttr(getContext(),
+ android.R.attr.textColorSecondary);
+ launchableViewDelegate = new LaunchableViewDelegate(
+ this,
+ visibility -> {
+ super.setVisibility(visibility);
+ return Unit.INSTANCE;
+ });
+ }
+
+ public TextView getLabel() {
+ return mLabel;
+ }
+
+ @Override
+public void setShouldBlockVisibilityChanges(boolean block) {
+ launchableViewDelegate.setShouldBlockVisibilityChanges(block);
+}
+
+
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ FontSizeUtils.updateFontSize(mLabel, R.dimen.qs_tile_text_size);
+ FontSizeUtils.updateFontSize(mSecondLine, R.dimen.qs_tile_text_size);
+ }
+
+ @Override
+ public int getDetailY() {
+ android.util.Log.d("xsr_mLabel", "mLabelContainer.getHeight() / 2 = : "+mLabelContainer.getHeight() / 2);
+ return getTop() + mLabelContainer.getTop() + mLabelContainer.getHeight() / 2;
+ }
+
+ protected void createLabel() {
+ mLabelContainer = (ViewGroup) LayoutInflater.from(getContext())
+ .inflate(R.layout.qs_tile_label_bak, this, false);
+ android.util.Log.d("xsr_mLabel", "mLabelContainer = : "+mLabelContainer);
+ mLabelContainer.setClipChildren(false);
+ mLabelContainer.setClipToPadding(false);
+ mLabel = mLabelContainer.findViewById(R.id.tile_label_bak);
+ mPadLock = mLabelContainer.findViewById(R.id.restricted_padlock_bak);
+ mDivider = mLabelContainer.findViewById(R.id.underline_bak);
+ mExpandIndicator = mLabelContainer.findViewById(R.id.expand_indicator_bak);
+ mExpandSpace = mLabelContainer.findViewById(R.id.expand_space_bak);
+ mSecondLine = mLabelContainer.findViewById(R.id.app_label_bak);
+ addView(mLabelContainer,1);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ android.util.Log.d("xsr_mLabel", "mLabel.getLineCount() = : "+mLabel.getLineCount());
+ // Remeasure view if the primary label requires more then 2 lines or the secondary label
+ // text will be cut off.
+ if (mLabel.getLineCount() > MAX_LABEL_LINES || !TextUtils.isEmpty(mSecondLine.getText())
+ && mSecondLine.getLineHeight() > mSecondLine.getHeight()) {
+
+ android.util.Log.d("xsr_mLabel", " onMeasure");
+ mLabel.setSingleLine();
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+ }
+
+
+ @Override
+ protected void handleStateChanged(QSTile.State state) {
+ super.handleStateChanged(state);
+ if (!Objects.equals(mLabel.getText(), state.label) || mState != state.state) {
+ mLabel.setTextColor(state.state == Tile.STATE_UNAVAILABLE ? mColorLabelUnavailable
+ : mColorLabelDefault);
+ mState = state.state;
+ android.util.Log.d("xsr_mLabel", "mLabel = : "+state.label);
+ mLabel.setText(state.label);
+ FontSizeUtils.updateFontSize(mLabel, R.dimen.qs_tile_text_size);
+ FontSizeUtils.updateFontSize(mSecondLine, R.dimen.qs_tile_text_size);
+ mLabel.setVisibility(View.VISIBLE);
+ }
+ // if (!Objects.equals(mSecondLine.getText(), state.secondaryLabel)) {
+ // mSecondLine.setText(state.secondaryLabel);
+ // mSecondLine.setVisibility(TextUtils.isEmpty(state.secondaryLabel) ? View.GONE
+ // : View.VISIBLE);
+ // }
+ boolean dualTarget = DUAL_TARGET_ALLOWED && state.dualTarget;
+ mExpandIndicator.setVisibility(dualTarget ? View.VISIBLE : View.GONE);
+ mExpandSpace.setVisibility(dualTarget ? View.VISIBLE : View.GONE);
+ mLabelContainer.setContentDescription(dualTarget ? state.dualLabelContentDescription
+ : null);
+ if (dualTarget != mLabelContainer.isClickable()) {
+ mLabelContainer.setClickable(dualTarget);
+ mLabelContainer.setLongClickable(dualTarget);
+ mLabelContainer.setBackground(dualTarget ? newTileBackground() : null);
+ }
+ mLabel.setEnabled(!state.disabledByPolicy);
+ mPadLock.setVisibility(state.disabledByPolicy ? View.VISIBLE : View.GONE);
+ }
+
+ @Override
+ public void init(OnClickListener click, OnClickListener secondaryClick,
+ OnLongClickListener longClick) {
+ super.init(click, secondaryClick, longClick);
+ mLabelContainer.setOnClickListener(secondaryClick);
+ mLabelContainer.setOnLongClickListener(longClick);
+ mLabelContainer.setClickable(false);
+ mLabelContainer.setLongClickable(false);
+ }
+}
有了这两个方法后我们就可以改变形状了,但是烧录发现会闪屏,发现缺少animator,所以我们修改下这个文件
diff --git a/src/com/android/systemui/qs/QSAnimator.java b/src/com/android/systemui/qs/QSAnimator.java
index 8b451c19..439bb9e0 100644
--- a/src/com/android/systemui/qs/QSAnimator.java
+++ b/src/com/android/systemui/qs/QSAnimator.java
@@ -358,44 +358,44 @@ public class QSAnimator implements QSHost.Callback, PagedTileLayout.PageListener
);
// Label containers
- translateContent(
- quickTileView.getLabelContainer(),
- tileView.getLabelContainer(),
- view,
- xOffset,
- yOffset,
- mTmpLoc1,
- translationXBuilder,
- translationYBuilder,
- qqsTranslationYBuilder
- );
+ // translateContent(
+ // quickTileView.getLabelContainer(),
+ // tileView.getLabelContainer(),
+ // view,
+ // xOffset,
+ // yOffset,
+ // mTmpLoc1,
+ // translationXBuilder,
+ // translationYBuilder,
+ // qqsTranslationYBuilder
+ // );
// Secondary icon
- translateContent(
- quickTileView.getSecondaryIcon(),
- tileView.getSecondaryIcon(),
- view,
- xOffset,
- yOffset,
- mTmpLoc1,
- translationXBuilder,
- translationYBuilder,
- qqsTranslationYBuilder
- );
+ // translateContent(
+ // quickTileView.getSecondaryIcon(),
+ // tileView.getSecondaryIcon(),
+ // view,
+ // xOffset,
+ // yOffset,
+ // mTmpLoc1,
+ // translationXBuilder,
+ // translationYBuilder,
+ // qqsTranslationYBuilder
+ // );
// Secondary labels on tiles not in QQS have two alpha animation applied:
// * on the tile themselves
// * on TileLayout
// Therefore, we use a quadratic interpolator animator to animate the alpha
// for tiles in QQS to match.
- quadraticInterpolatorBuilder
- .addFloat(quickTileView.getSecondaryLabel(), "alpha", 0, 1);
- nonFirstPageAlphaBuilder
- .addFloat(quickTileView.getSecondaryLabel(), "alpha", 0, 0);
+ //quadraticInterpolatorBuilder
+ // .addFloat(quickTileView.getSecondaryLabel(), "alpha", 0, 1);
+ //nonFirstPageAlphaBuilder
+ // .addFloat(quickTileView.getSecondaryLabel(), "alpha", 0, 0);
mAnimatedQsViews.add(tileView);
mAllViews.add(quickTileView);
- mAllViews.add(quickTileView.getSecondaryLabel());
+ // mAllViews.add(quickTileView.getSecondaryLabel());
} else if (!isIconInAnimatedRow(count)) {
// Pretend there's a corresponding QQS tile (for the position) that we are
// expanding from.
@@ -414,8 +414,8 @@ public class QSAnimator implements QSHost.Callback, PagedTileLayout.PageListener
mOtherFirstPageTilesHeightAnimator.addView(tileView);
tileView.setClipChildren(true);
tileView.setClipToPadding(true);
- firstPageBuilder.addFloat(tileView.getSecondaryLabel(), "alpha", 0, 1);
- mAllViews.add(tileView.getSecondaryLabel());
+ // firstPageBuilder.addFloat(tileView.getSecondaryLabel(), "alpha", 0, 1);
+ // mAllViews.add(tileView.getSecondaryLabel());
}
mAllViews.add(tileView);
这里我们就修改了形状了,需要注意的是android14需要给自己写的 QSTileViewBak实现LaunchableView这个类,不然点击以太网这个tile会闪退。
之后我们就需要实现对应的tile了,这里需要新增三个tile,首页,屏幕保护和设置,直接放补丁了,都简单
diff --git a/src/com/android/systemui/qs/tiles/BackHomeTile.java b/src/com/android/systemui/qs/tiles/BackHomeTile.java
new file mode 100644
index 00000000..82321faa
--- /dev/null
+++ b/src/com/android/systemui/qs/tiles/BackHomeTile.java
@@ -0,0 +1,114 @@
+package com.android.systemui.qs.tiles;
+
+import android.content.Intent;
+import android.service.quicksettings.Tile;
+import android.widget.Switch;
+import com.android.systemui.plugins.qs.QSTile.BooleanState;
+import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.R;
+import javax.inject.Inject;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.provider.Settings.Global;
+import android.service.quicksettings.Tile;
+import android.sysprop.TelephonyProperties;
+import android.telephony.TelephonyManager;
+import android.view.View;
+import android.util.Log;
+import android.widget.Switch;
+
+import androidx.annotation.Nullable;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.systemui.R;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.plugins.qs.QSTile.BooleanState;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.QsEventLogger;
+import com.android.systemui.qs.SettingObserver;
+import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.settings.UserTracker;
+import com.android.systemui.util.settings.GlobalSettings;
+
+/** Quick settings tile: Return to Home **/
+public class BackHomeTile extends QSTileImpl {
+ public static final String TILE_SPEC = "home";
+ private final Icon mIcon = ResourceIcon.get(R.drawable.ic_home);
+ private ActivityStarter mActivityStarter;
+
+ @Inject
+ public BackHomeTile(
+ QSHost host,
+ QsEventLogger uiEventLogger,
+ @Background Looper backgroundLooper,
+ @Main Handler mainHandler,
+ FalsingManager falsingManager,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger
+
+ ) {
+ super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ statusBarStateController, activityStarter, qsLogger);
+ mActivityStarter=activityStarter;
+ }
+
+ @Override
+ public BooleanState newTileState() {
+ return new BooleanState();
+ }
+
+ // 处理点击事件
+ @Override
+ public void handleClick(@Nullable View view) {
+ // 假设mActivityStarter是ActivityStarter的实例,已经通过依赖注入提供
+ Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.addCategory(Intent.CATEGORY_HOME);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+ // 使用ActivityStarter启动主屏幕
+ mActivityStarter.startActivity(intent, false );
+ }
+
+ @Override
+ protected void handleUpdateState(BooleanState state, Object arg) {
+ state.icon = mIcon;
+ state.label = mContext.getString(R.string.quick_settings_home_label);
+ state.contentDescription = state.label;
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return 0;
+ }
+
+ @Override
+ public Intent getLongClickIntent() {
+ return null;
+ }
+
+ @Override
+ protected void handleSetListening(boolean listening) {
+ }
+
+ @Override
+ public CharSequence getTileLabel() {
+ return mContext.getString(R.string.quick_settings_home_label);
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/systemui/qs/tiles/InternetTile.java b/src/com/android/systemui/qs/tiles/InternetTile.java
index 05351a53..88ade77c 100644
--- a/src/com/android/systemui/qs/tiles/InternetTile.java
+++ b/src/com/android/systemui/qs/tiles/InternetTile.java
@@ -132,6 +132,7 @@ public class InternetTile extends QSTileImpl {
@Override
protected void handleClick(@Nullable View view) {
+ android.util.Log.d("xsr_wifi", "view: "+view);
mHandler.post(() -> mInternetDialogFactory.create(true,
mAccessPointController.canConfigMobileData(),
mAccessPointController.canConfigWifi(), view));
diff --git a/src/com/android/systemui/qs/tiles/ScreenSaverTile.java b/src/com/android/systemui/qs/tiles/ScreenSaverTile.java
new file mode 100644
index 00000000..55d7bf8f
--- /dev/null
+++ b/src/com/android/systemui/qs/tiles/ScreenSaverTile.java
@@ -0,0 +1,136 @@
+package com.android.systemui.qs.tiles;
+
+import android.content.Intent;
+import android.provider.Settings;
+import com.android.systemui.plugins.qs.QSTile.BooleanState;
+import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.R;
+import javax.inject.Inject;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.IntentFilter;
+import android.os.Handler;
+import android.os.Looper;
+import android.view.View;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.plugins.qs.QSTile.BooleanState;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.qs.QsEventLogger;
+import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.settings.UserTracker;
+import com.android.systemui.util.concurrency.ThreadFactory;
+
+import android.content.Intent;
+import android.provider.Settings;
+import android.service.quicksettings.Tile;
+import com.android.systemui.plugins.qs.QSTile.BooleanState;
+import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.R;
+import javax.inject.Inject;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.provider.Settings.Global;
+import android.service.quicksettings.Tile;
+import android.sysprop.TelephonyProperties;
+import android.telephony.TelephonyManager;
+import android.view.View;
+import android.util.Log;
+import android.widget.Switch;
+import com.android.settingslib.dream.DreamBackend;
+
+import androidx.annotation.Nullable;
+
+/** Quick settings tile: Screen Saver **/
+public class ScreenSaverTile extends QSTileImpl {
+ public static final String TILE_SPEC = "screen_saver";
+ private final Icon mIcon = ResourceIcon.get(R.drawable.ic_screen_saver); // Replace with your icon drawable
+ private ActivityStarter mActivityStarter;
+ private Handler mHandler;
+
+ @Inject
+ public ScreenSaverTile(
+ QSHost host,
+ QsEventLogger uiEventLogger,
+ @Background Looper backgroundLooper,
+ @Main Handler mainHandler,
+ FalsingManager falsingManager,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger
+ ) {
+ super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ statusBarStateController, activityStarter, qsLogger);
+ mActivityStarter = activityStarter;
+ mHandler = mainHandler;
+ }
+
+ @Override
+ public BooleanState newTileState() {
+ return new BooleanState();
+ }
+
+ // 处理点击事件
+ @Override
+ protected void handleClick(@Nullable View view) {
+ startDreaming();
+ }
+
+ @Override
+ protected void handleUpdateState(BooleanState state, Object arg) {
+ state.icon = mIcon;
+ state.label = mContext.getString(R.string.quick_settings_screen_saver_label); // Replace with your label
+ state.contentDescription = state.label;
+ }
+
+ private void startDreaming() {
+ // 获取 DreamBackend 的实例
+ DreamBackend dreamBackend = DreamBackend.getInstance(mContext);
+
+ // 检查屏幕保护程序是否可用
+ if (dreamBackend.isEnabled()) {
+ // 启动屏幕保护程序
+ dreamBackend.startDreaming();
+ }
+//else {
+// // 如果屏幕保护程序不可用,可以显示一个提示或者错误信息
+// Toast.makeText(this, "Screen saver is not enabled.", Toast.LENGTH_SHORT).show();
+// }
+}
+
+ @Override
+ public int getMetricsCategory() {
+ return 0;
+ }
+
+ @Override
+ public Intent getLongClickIntent() {
+ return null;
+ }
+
+ @Override
+ protected void handleSetListening(boolean listening) {
+ }
+
+ @Override
+ public CharSequence getTileLabel() {
+ return mContext.getString(R.string.quick_settings_screen_saver_label); // Replace with your label
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/systemui/qs/tiles/SettingsTile.java b/src/com/android/systemui/qs/tiles/SettingsTile.java
new file mode 100644
index 00000000..54d8198e
--- /dev/null
+++ b/src/com/android/systemui/qs/tiles/SettingsTile.java
@@ -0,0 +1,120 @@
+package com.android.systemui.qs.tiles;
+
+import android.content.Intent;
+import android.provider.Settings;
+import android.service.quicksettings.Tile;
+import com.android.systemui.plugins.qs.QSTile.BooleanState;
+import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.R;
+import javax.inject.Inject;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.provider.Settings.Global;
+import android.service.quicksettings.Tile;
+import android.sysprop.TelephonyProperties;
+import android.telephony.TelephonyManager;
+import android.view.View;
+import android.util.Log;
+import android.widget.Switch;
+
+import androidx.annotation.Nullable;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.systemui.R;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.plugins.qs.QSTile.BooleanState;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.QsEventLogger;
+import com.android.systemui.qs.SettingObserver;
+import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.settings.UserTracker;
+import com.android.systemui.util.settings.GlobalSettings;
+
+/** Quick settings tile: Open Settings **/
+public class SettingsTile extends QSTileImpl {
+ public static final String TILE_SPEC = "settings";
+ private final Icon mIcon = ResourceIcon.get(R.drawable.ic_settings);
+ private ActivityStarter mActivityStarter;
+ private Handler handler;
+
+ @Inject
+ public SettingsTile(
+ QSHost host,
+ QsEventLogger uiEventLogger,
+ @Background Looper backgroundLooper,
+ @Main Handler mainHandler,
+ FalsingManager falsingManager,
+ MetricsLogger metricsLogger,
+ StatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter,
+ QSLogger qsLogger
+
+ ) {
+ super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+ statusBarStateController, activityStarter, qsLogger);
+ mActivityStarter = activityStarter;
+ handler = mainHandler;
+ }
+
+ @Override
+ public BooleanState newTileState() {
+ return new BooleanState();
+ }
+
+ // 处理点击事件
+ @Override
+ protected void handleClick(@Nullable View view) {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ Intent intent = new Intent(Settings.ACTION_SOUND_SETTINGS);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mActivityStarter.startActivity(intent, /* dismissShade= */ true);
+ // 在这里执行需要在主线程上的操作
+ }
+});
+
+ }
+
+ @Override
+ protected void handleUpdateState(BooleanState state, Object arg) {
+ state.icon = mIcon;
+ state.label = mContext.getString(R.string.quick_settings_settings_label);
+ state.contentDescription = state.label;
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return 0;
+ }
+
+ @Override
+ public Intent getLongClickIntent() {
+ return null;
+ }
+
+ @Override
+ protected void handleSetListening(boolean listening) {
+ }
+
+ @Override
+ public CharSequence getTileLabel() {
+ return mContext.getString(R.string.quick_settings_settings_label);
+ }
+}
\ No newline at end of file
添加这些后需要注意 没有写资源文件,大家可以自行编辑,文章最后会附上全部patch
diff --git a/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt b/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt
index 92aa9866..82f462e8 100644
--- a/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt
+++ b/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt
@@ -24,6 +24,9 @@ import com.android.systemui.qs.tiles.DataSaverTile
import com.android.systemui.qs.tiles.HotspotTile
import com.android.systemui.qs.tiles.InternetTile
import com.android.systemui.qs.tiles.NfcTile
+import com.android.systemui.qs.tiles.SettingsTile
+import com.android.systemui.qs.tiles.BackHomeTile
+import com.android.systemui.qs.tiles.ScreenSaverTile
import dagger.Binds
import dagger.Module
import dagger.multibindings.IntoMap
@@ -68,6 +71,21 @@ interface ConnectivityModule {
@StringKey(DataSaverTile.TILE_SPEC)
fun bindDataSaverTile(dataSaverTile: DataSaverTile): QSTileImpl<*>
+ @Binds
+ @IntoMap
+ @StringKey(BackHomeTile.TILE_SPEC)
+ fun bindBackHomeTile(backHomeTile: BackHomeTile): QSTileImpl<*>
+
+ @Binds
+ @IntoMap
+ @StringKey(SettingsTile.TILE_SPEC)
+ fun bindSettingsTile(settingsTile: SettingsTile): QSTileImpl<*>
+
+ @Binds
+ @IntoMap
+ @StringKey(ScreenSaverTile.TILE_SPEC)
+ fun bindScreenSaverTile(screenSaverTile: ScreenSaverTile): QSTileImpl<*>
+
/** Inject NfcTile into tileMap in QSModule */
@Binds @IntoMap @StringKey(NfcTile.TILE_SPEC) fun bindNfcTile(nfcTile: NfcTile): QSTileImpl<*>
}
此外还需要注意要在config.xml里修改不然没办法显示
diff --git a/res/values/config.xml b/res/values/config.xml
index d608f7bf..7c284ede 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -78,12 +78,11 @@
- internet,bt,flashlight,dnd,alarm,airplane,controls,wallet,rotation,battery,cast,screenrecord,mictoggle,cameratoggle,custom(com.android.permissioncontroller/.permission.service.SafetyCenterQsTileService),custom(com.google.android.gms/.nearby.sharing.SharingTileService),qr_code_scanner
+ home,screen_saver,mictoggle,cameratoggle,internet,bt,settings
- com.android.permissioncontroller.permission.service.v33.SafetyCenterQsTileService
@@ -91,7 +90,7 @@
- internet,bt,flashlight,dnd,alarm,airplane,controls,wallet,rotation,battery,cast,screenrecord,mictoggle,cameratoggle,location,hotspot,inversion,saver,dark,work,night,reverse,reduce_brightness,qr_code_scanner,onehanded,color_correction,dream,font_scaling
+ home,settings,screen_saver,internet,bt,flashlight,dnd,alarm,airplane,controls,wallet,rotation,battery,cast,screenrecord,mictoggle,cameratoggle,location,hotspot,inversion,saver,dark,work,night,reverse,reduce_brightness,qr_code_scanner,onehanded,color_correction,dream,font_scaling
但是我修改这个后他依然没有办法正确显示,之后我就
diff --git a/src/com/android/systemui/qs/QSTileHost.java b/src/com/android/systemui/qs/QSTileHost.java
index 57bb203b..cd80ba7a 100644
--- a/src/com/android/systemui/qs/QSTileHost.java
+++ b/src/com/android/systemui/qs/QSTileHost.java
@@ -287,6 +287,11 @@ public class QSTileHost implements QSHost, Tunable, PluginListener, P
if (!TILES_SETTING.equals(key)) {
return;
}
+
+ if(newValue == null){
+
+ newValue = mContext.getResources().getString(R.string.quick_settings_tiles_default);
+ }
// Do not process tiles if the flag is enabled.
if (mFeatureFlags.isEnabled(Flags.QS_PIPELINE_NEW_HOST)) {
return;
这样就可以显示自己想要的了,只需要修改默认值就可以啦,但是烧录玩发现经常多一个,就是系统会自己添加,我们只需要
diff --git a/src/com/android/systemui/statusbar/phone/AutoTileManager.java b/src/com/android/systemui/statusbar/phone/AutoTileManager.java
index f6d53b3b..1c3a7fa5 100644
--- a/src/com/android/systemui/statusbar/phone/AutoTileManager.java
+++ b/src/com/android/systemui/statusbar/phone/AutoTileManager.java
@@ -361,11 +361,11 @@ public class AutoTileManager implements UserAwareController {
}
private void initSafetyTile() {
- if (mSafetySpec == null || mAutoTracker.isAdded(mSafetySpec)) {
+ // if (mSafetySpec == null || mAutoTracker.isAdded(mSafetySpec)) {
return;
- }
- mHost.addTile(CustomTile.getComponentFromSpec(mSafetySpec), true);
- mAutoTracker.setTileAdded(mSafetySpec);
+ // }
+ // mHost.addTile(CustomTile.getComponentFromSpec(mSafetySpec), true);
+ //mAutoTracker.setTileAdded(mSafetySpec);
}
@VisibleForTesting
但是其实给config里面的safety_quick_settings_tile_class改成null就可以,为了保险我改了两处
到了这里tile的修改就基本结束了,
之后我们就要修改布局了
我们要实现那个效果,首先要将page现在在第一行,再将亮度调节和音量调节显示
diff --git a/src/com/android/systemui/qs/QSPanel.java b/src/com/android/systemui/qs/QSPanel.java
index b936c41d..fe411012 100644
--- a/src/com/android/systemui/qs/QSPanel.java
+++ b/src/com/android/systemui/qs/QSPanel.java
@@ -49,6 +49,12 @@ import com.android.systemui.tuner.TunerService.Tunable;
import java.util.ArrayList;
import java.util.List;
+import androidx.constraintlayout.widget.ConstraintSet;
+import android.graphics.drawable.Drawable;
+import androidx.appcompat.content.res.AppCompatResources;
+import androidx.constraintlayout.widget.ConstraintLayout;
+import com.android.systemui.settings.volume.VolumeSliderView;
+
/** View that represents the quick settings tile panel (when expanded/pulled down). **/
public class QSPanel extends LinearLayout implements Tunable {
@@ -104,6 +110,7 @@ public class QSPanel extends LinearLayout implements Tunable {
private ViewGroup mMediaHostView;
private boolean mShouldMoveMediaOnExpansion = true;
private QSLogger mQsLogger;
+ View volumeSliderView ;
/**
* Specifies if we can collapse to QQS in current state. In split shade that should be always
* false. It influences available accessibility actions.
@@ -128,7 +135,10 @@ public class QSPanel extends LinearLayout implements Tunable {
void initialize(QSLogger qsLogger) {
mQsLogger = qsLogger;
mTileLayout = getOrCreateTileLayout();
+ mTileLayout.setCowsAndColumns(7,1);
+ volumeSliderView = LayoutInflater.from(mContext).inflate(
+ R.layout.quick_settings_volume_dialog, this, false);
if (mUsingMediaPlayer) {
mHorizontalLinearLayout = new RemeasuringLinearLayout(mContext);
mHorizontalLinearLayout.setOrientation(LinearLayout.HORIZONTAL);
@@ -182,7 +192,7 @@ public class QSPanel extends LinearLayout implements Tunable {
removeView(mBrightnessView);
mMovableContentStartIndex--;
}
- addView(view, 0);
+ // addView(view, 0);
mBrightnessView = view;
setBrightnessViewMargin();
@@ -398,7 +408,7 @@ public class QSPanel extends LinearLayout implements Tunable {
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mFooter = findViewById(R.id.qs_footer);
+ // mFooter = findViewById(R.id.qs_footer);
}
private void updateHorizontalLinearLayoutMargins() {
@@ -430,18 +440,18 @@ public class QSPanel extends LinearLayout implements Tunable {
return true;
}
- private void switchAllContentToParent(ViewGroup parent, QSTileLayout newLayout) {
- int index = parent == this ? mMovableContentStartIndex : 0;
+ private void switchAllContentToParent(ViewGroup parent, QSTileLayout newLayout , int index) {
+ // int index = parent == this ? mMovableContentStartIndex : 0;
// Let's first move the tileLayout to the new parent, since that should come first.
switchToParent((View) newLayout, parent, index);
- index++;
+ // index++;
- if (mFooter != null) {
- // Then the footer with the settings
- switchToParent(mFooter, parent, index);
- index++;
- }
+ // if (mFooter != null) {
+ // // Then the footer with the settings
+ // switchToParent(mFooter, parent, index);
+ // index++;
+ // }
}
private void switchToParent(View child, ViewGroup parent, int index) {
@@ -607,7 +617,65 @@ public class QSPanel extends LinearLayout implements Tunable {
Log.d(getDumpableTag(), "setUsingHorizontalLayout: " + horizontal + ", " + force);
mUsingHorizontalLayout = horizontal;
ViewGroup newParent = horizontal ? mHorizontalContentContainer : this;
- switchAllContentToParent(newParent, mTileLayout);
+if(mBrightnessView != null){
+ Log.w("xsr_edit","mBrightnessView 22222222222222222222222 ="+mBrightnessView);
+
+ //switchToParent(mBrightnessView,newParent,2);
+ConstraintLayout constraintLayout = new ConstraintLayout(mContext);
+constraintLayout.setLayoutParams(new ConstraintLayout.LayoutParams(
+ ConstraintLayout.LayoutParams.MATCH_PARENT, 84));
+
+
+
+// 为mBrightnessView设置参数
+
+// 添加mBrightnessView到ConstraintLayout
+//yconstraintLayout.addView(mBrightnessView);
+ ViewGroup currentParent = (ViewGroup) mBrightnessView.getParent();
+ if (currentParent != constraintLayout) {
+ if (currentParent != null) {
+ currentParent.removeView(mBrightnessView);
+ }
+ }
+ ViewGroup currentParents = (ViewGroup) volumeSliderView.getParent();
+ if (currentParents != constraintLayout) {
+ if (currentParents != null) {
+ currentParents.removeView(volumeSliderView);
+ }
+ }
+
+ // // 将mBrightnessView添加到LinearLayout
+
+
+ constraintLayout.addView(mBrightnessView,0);
+ constraintLayout.addView(volumeSliderView ,1);
+
+ ConstraintSet constraintSet = new ConstraintSet();
+ constraintSet.clone(constraintLayout) ;
+ constraintSet.connect(mBrightnessView.getId(), ConstraintSet.START,
+ ConstraintSet.PARENT_ID, ConstraintSet.START, 0); // 替换 yourStartMargin 为实际值
+ constraintSet.connect(mBrightnessView.getId(), ConstraintSet.END,
+ volumeSliderView.getId(), ConstraintSet.START, 24) ;
+ constraintSet.connect(volumeSliderView.getId(), ConstraintSet.START,
+ mBrightnessView.getId(), ConstraintSet.END, 24);
+ constraintSet.connect(volumeSliderView.getId(), ConstraintSet.END,
+ ConstraintSet.PARENT_ID, ConstraintSet.END, 0); // 替换 yourEndMargin 为实际
+ constraintSet.applyTo(constraintLayout) ;
+
+
+
+ // 调用switchToParent方法,传入新的LinearLayout作为父布局
+ switchAllContentToParent(newParent, mTileLayout,0);
+ switchToParent(constraintLayout, newParent, 1);
+
+
+ }
+ else {
+ Log.w("xsr_edit","else");
+ // switchAllContentToParent(newParent,mRegularTileLayout,1);
+ switchAllContentToParent(newParent, mTileLayout,0);
+ }
+ // switchAllContentToParent(newParent, mTileLayout);
reAttachMediaHost(mediaHostView, horizontal);
if (needsDynamicRowsAndColumns()) {
mTileLayout.setMinRows(horizontal ? 2 : 1);
@@ -684,6 +752,8 @@ public class QSPanel extends LinearLayout implements Tunable {
/** */
int getOffsetTop(QSPanelControllerBase.TileRecord tile);
+ void setCowsAndColumns(int rows, int columns) ;
+
/** */
boolean updateResources();
diff --git a/src/com/android/systemui/settings/volume/VolumeSliderView.java b/src/com/android/systemui/settings/volume/VolumeSliderView.java
new file mode 100644
index 00000000..18805b54
--- /dev/null
+++ b/src/com/android/systemui/settings/volume/VolumeSliderView.java
@@ -0,0 +1,151 @@
+package com.android.systemui.settings.volume;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.SeekBar;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.systemui.R;
+
+import android.media.AudioManager;
+
+/**
+ * {@code FrameLayout} used to show and manipulate a {@link SeekBar} for volume control.
+ */
+public class VolumeSliderView extends FrameLayout implements VolumeChangeObserver.VolumeChangeListener {
+
+ @NonNull
+ private SeekBar mSeekBar;
+ private DispatchTouchEventListener mListener;
+ private AudioManager mAudioManager;
+ private VolumeChangeObserver mVolumeChangeObserver;
+
+ public VolumeSliderView(Context context) {
+ this(context, null);
+
+ }
+
+ public VolumeSliderView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ initAudioManager(context);
+ mVolumeChangeObserver = new VolumeChangeObserver(context);
+ mVolumeChangeObserver.setVolumeChangeListener(this);
+ }
+
+ private void initAudioManager(Context context) {
+ mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+
+}
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mSeekBar = findViewById(R.id.v_slider);
+ mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ // Handle volume change
+ if (fromUser) {
+ int volume = progress;
+ // 确保音量值在有效范围内
+ volume = Math.max(0, Math.min(volume, mSeekBar.getMax()));
+ // 设置音量
+ setVolume(volume);
+ }
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ // Handle start tracking touch
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ // Handle stop tracking touch
+ }
+ });
+ mVolumeChangeObserver.registerReceiver();
+ setValue(mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC));
+ }
+
+ /**
+ * Attaches a listener to relay touch events.
+ * @param listener use {@code null} to remove listener
+ */
+ public void setOnDispatchTouchEventListener(DispatchTouchEventListener listener) {
+ mListener = listener;
+ }
+
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent ev) {
+ if (mListener != null) {
+ mListener.onDispatchTouchEvent(ev);
+ }
+ return super.dispatchTouchEvent(ev);
+ }
+
+ private void setVolume(int volume) {
+ if (mAudioManager != null) {
+ int maxVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
+ android.util.Log.d("xsr_volume", "maxVolume: "+maxVolume);
+ int normalizedVolume = (int) (volume * (float) maxVolume / mSeekBar.getMax());
+ mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, normalizedVolume, 0);
+ }
+}
+
+ /**
+ * Sets the maximum value of the {@link SeekBar}.
+ * @param max
+ */
+ public void setMax(int max) {
+ mSeekBar.setMax(max);
+ }
+
+ /**
+ * Sets the current value of the {@link SeekBar}.
+ * @param value
+ */
+ public void setValue(int value) {
+ android.util.Log.d("xsr_volume", "onVolumeChanged: "+value);
+ int maxVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
+ android.util.Log.d("xsr_volume", "maxVolume: "+maxVolume);
+ android.util.Log.d("xsr_volume", "mSeekBar.getMax(): "+mSeekBar.getMax());
+ mSeekBar.setProgress((int)(value /(float) maxVolume * mSeekBar.getMax()));
+ }
+
+ /**
+ * @return the current value of the {@link SeekBar}
+ */
+ public int getValue() {
+ return mSeekBar.getProgress();
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ // Additional layout logic if necessary
+ }
+
+ /**
+ * Interface to attach a listener for {@link View#dispatchTouchEvent}.
+ */
+ @FunctionalInterface
+ interface DispatchTouchEventListener {
+ boolean onDispatchTouchEvent(MotionEvent ev);
+ }
+
+
+ @Override
+ public void onVolumeChanged(int volume) {
+ //系统媒体音量改变时的回调
+ // android.util.Log.d("xsr_volume", "onVolumeChanged: "+volume);
+ // int maxVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
+ setValue(volume );
+ }
+}
\ No newline at end of file
现在就可以控制音量了,但是发现其他地方调节后这里的无法同步,我们新增一个
diff --git a/src/com/android/systemui/settings/volume/VolumeChangeObserver.java b/src/com/android/systemui/settings/volume/VolumeChangeObserver.java
new file mode 100644
index 00000000..fda60d01
--- /dev/null
+++ b/src/com/android/systemui/settings/volume/VolumeChangeObserver.java
@@ -0,0 +1,117 @@
+package com.android.systemui.settings.volume;
+
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.media.AudioManager;
+
+/**
+ * This class is used to observe changes in the system's media volume.
+ */
+public class VolumeChangeObserver {
+
+ private static final String VOLUME_CHANGED_ACTION = AudioManager.VOLUME_CHANGED_ACTION;
+ private static final String EXTRA_VOLUME_STREAM_TYPE = AudioManager.EXTRA_VOLUME_STREAM_TYPE;
+ private static final String EXTRA_VOLUME_STREAM_VALUE = AudioManager.EXTRA_VOLUME_STREAM_VALUE;
+
+ public interface VolumeChangeListener {
+ /**
+ * Called when the system media volume changes.
+ *
+ * @param volume The new volume level.
+ */
+ void onVolumeChanged(int volume);
+ }
+
+ private VolumeChangeListener mVolumeChangeListener;
+ private VolumeBroadcastReceiver mVolumeBroadcastReceiver;
+ private Context mContext;
+ private AudioManager mAudioManager;
+ private boolean mRegistered = false;
+
+ public VolumeChangeObserver(Context context) {
+ mContext = context.getApplicationContext(); // Use application context to avoid leaks
+ mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+ }
+
+ /**
+ * Gets the current media volume.
+ *
+ * @return The current media volume, or -1 if the AudioManager is null.
+ */
+ public int getCurrentMusicVolume() {
+ return mAudioManager != null ? mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC) : -1;
+ }
+
+ /**
+ * Gets the maximum media volume.
+ *
+ * @return The maximum media volume, or a default value if the AudioManager is null.
+ */
+ public int getMaxMusicVolume() {
+ return mAudioManager != null ? mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC) : 15;
+ }
+
+ /**
+ * Registers the volume broadcast receiver.
+ */
+ public void registerReceiver() {
+ if (!mRegistered) {
+ mVolumeBroadcastReceiver = new VolumeBroadcastReceiver(this);
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(VOLUME_CHANGED_ACTION);
+ mContext.registerReceiver(mVolumeBroadcastReceiver, filter);
+ mRegistered = true;
+ }
+ }
+
+ public VolumeChangeListener getVolumeChangeListener() {
+ return mVolumeChangeListener;
+ }
+
+ public void setVolumeChangeListener(VolumeChangeListener volumeChangeListener) {
+ this.mVolumeChangeListener = volumeChangeListener;
+ }
+
+ /**
+ * Unregisters the volume broadcast receiver.
+ */
+ public void unregisterReceiver() {
+ if (mRegistered) {
+ try {
+ mContext.unregisterReceiver(mVolumeBroadcastReceiver);
+ mVolumeBroadcastReceiver = null;
+ mRegistered = false;
+ } catch (IllegalArgumentException e) {
+ // Handle the case where the receiver is not registered.
+ }
+ }
+ }
+
+ /**
+ * Sets the volume change listener.
+ *
+ * @param volumeChangeListener The listener to be notified of volume changes.
+ */
+ private static class VolumeBroadcastReceiver extends BroadcastReceiver {
+ private final VolumeChangeObserver mVolumeChangeObserver;
+
+ public VolumeBroadcastReceiver(VolumeChangeObserver volumeChangeObserver) {
+ mVolumeChangeObserver = volumeChangeObserver;
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (VOLUME_CHANGED_ACTION.equals(intent.getAction())
+ && intent.hasExtra(EXTRA_VOLUME_STREAM_TYPE)
+ && intent.getIntExtra(EXTRA_VOLUME_STREAM_TYPE, -1) == AudioManager.STREAM_MUSIC) {
+ int volume = intent.getIntExtra(EXTRA_VOLUME_STREAM_VALUE, -1);
+ if (volume >= 0) {
+ mVolumeChangeObserver.getVolumeChangeListener().onVolumeChanged(volume);
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
这样就可以实现基本了