Loading packages/SystemUI/res/values/dimens.xml +3 −0 Original line number Diff line number Diff line Loading @@ -827,4 +827,7 @@ <!-- How far to inset the rounded edges --> <dimen name="stat_sys_mobile_signal_circle_inset">0.9dp</dimen> <!-- Width of the hollow triangle for empty signal state --> <dimen name="mobile_signal_empty_strokewidth">2dp</dimen> </resources> packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java +5 −1 Original line number Diff line number Diff line Loading @@ -141,8 +141,12 @@ public class CellularTile extends QSTileImpl<SignalState> { && mDataController.isMobileDataEnabled(); state.icon = new SignalIcon(cb.mobileSignalIconId); if (cb.airplaneModeEnabled) { state.state = Tile.STATE_INACTIVE; } else { state.state = Tile.STATE_ACTIVE; } } @Override public int getMetricsCategory() { Loading packages/SystemUI/src/com/android/systemui/statusbar/phone/SignalDrawable.java +112 −6 Original line number Diff line number Diff line Loading @@ -21,13 +21,14 @@ import android.annotation.Nullable; import android.content.Context; import android.graphics.Canvas; import android.graphics.ColorFilter; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Paint.Style; import android.graphics.Path; import android.graphics.Path.Direction; import android.graphics.Path.FillType; import android.graphics.Path.Op; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.Drawable; import android.os.Handler; import android.util.LayoutDirection; Loading Loading @@ -63,6 +64,7 @@ public class SignalDrawable extends Drawable { private static final int STATE_EMPTY = 1; private static final int STATE_CUT = 2; private static final int STATE_CARRIER_CHANGE = 3; private static final int STATE_AIRPLANE = 4; private static final long DOT_DELAY = 1000; Loading Loading @@ -97,6 +99,18 @@ public class SignalDrawable extends Drawable { // How far the circle defining the corners is inset from the edges private final float mAppliedCornerInset; // The easiest way to understand this is as if we set Style.STROKE and draw the triangle, // but that is only theoretically right. Instead, draw the triangle and clip out a smaller // one inset by this amount. private final float mEmptyStrokeWidth; private static final float INV_TAN = 1f / (float) Math.tan(Math.PI / 8f); private final float mEmptyDiagInset; // == mEmptyStrokeWidth * INV_TAN //TODO: This is needed because drawing the triangle is paramterized on the rounded edges. // We get rounded corners by placing circles at arbitrary points along the legs of the triangle, // but that means we lose the notion of a triangle being strictly half of its containing square. // As a result, here's a value to tweak the insets of the cutout for the no-signal icon. private final float mCutExtraOffset; private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); private final Paint mForegroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); private final int mDarkModeBackgroundColor; Loading @@ -106,6 +120,10 @@ public class SignalDrawable extends Drawable { private final Path mFullPath = new Path(); private final Path mForegroundPath = new Path(); private final Path mXPath = new Path(); // Cut out when STATE_EMPTY private final Path mCutPath = new Path(); // Draws the slash when in airplane mode private final SlashArtist mSlash = new SlashArtist(); private final Handler mHandler; private float mOldDarkIntensity = -1; private float mNumLevels = 1; Loading @@ -126,11 +144,18 @@ public class SignalDrawable extends Drawable { mLightModeFillColor = Utils.getDefaultColor(context, R.color.light_mode_icon_color_dual_tone_fill); mIntrinsicSize = context.getResources().getDimensionPixelSize(R.dimen.signal_icon_size); // mCutPath parameters mEmptyStrokeWidth = context.getResources() .getDimensionPixelSize(R.dimen.mobile_signal_empty_strokewidth); mEmptyDiagInset = mEmptyStrokeWidth * INV_TAN; mHandler = new Handler(); setDarkIntensity(0); mAppliedCornerInset = context.getResources() .getDimensionPixelSize(R.dimen.stat_sys_mobile_signal_circle_inset); mCutExtraOffset = mAppliedCornerInset / 2f; } public void setIntrinsicSize(int size) { Loading Loading @@ -224,9 +249,10 @@ public class SignalDrawable extends Drawable { } mFullPath.reset(); mFullPath.setFillType(FillType.WINDING); final float width = getBounds().width(); final float height = getBounds().height(); final float padding = (int) (PAD * width); // Stay on pixel boundary final float padding = Math.round(PAD * width); final float cornerRadius = RADIUS_RATIO * height; // Offset from circle where the hypotenuse meets the circle final float diagOffset = DIAG_OFFSET_MULTIPLIER * cornerRadius; Loading Loading @@ -292,10 +318,29 @@ public class SignalDrawable extends Drawable { mFullPath.rLineTo(0, cut); } mPaint.setStyle(mState == STATE_EMPTY ? Style.STROKE : Style.FILL); mForegroundPaint.setStyle(mState == STATE_EMPTY ? Style.STROKE : Style.FILL); if (mState != STATE_CARRIER_CHANGE) { if (mState == STATE_EMPTY) { // Cut out a smaller triangle from the center of mFullPath mCutPath.reset(); mCutPath.setFillType(FillType.WINDING); mCutPath.moveTo(width - padding - mEmptyStrokeWidth, height - padding - mEmptyStrokeWidth); mCutPath.lineTo(width - padding - mEmptyStrokeWidth, padding + mEmptyDiagInset - mCutExtraOffset); mCutPath.lineTo(padding + mEmptyDiagInset - mCutExtraOffset, height - padding - mEmptyStrokeWidth); mCutPath.lineTo(width - padding - mEmptyStrokeWidth, height - padding - mEmptyStrokeWidth); // In empty state, draw the full path as the foreground paint mForegroundPath.set(mFullPath); mFullPath.reset(); mForegroundPath.op(mCutPath, Path.Op.DIFFERENCE); } else if (mState == STATE_AIRPLANE) { // Airplane mode is slashed, full-signal mForegroundPath.set(mFullPath); mFullPath.reset(); mSlash.draw((int) height, (int) width, canvas, mForegroundPaint); } else if (mState != STATE_CARRIER_CHANGE) { mForegroundPath.reset(); int sigWidth = Math.round(calcFit(mLevel / (mNumLevels - 1)) * (width - 2 * padding)); mForegroundPath.addRect(padding, padding, padding + sigWidth, height - padding, Loading Loading @@ -405,4 +450,65 @@ public class SignalDrawable extends Drawable { public static int getEmptyState(int numLevels) { return (STATE_EMPTY << STATE_SHIFT) | (numLevels << NUM_LEVEL_SHIFT); } public static int getAirplaneModeState(int numLevels) { return (STATE_AIRPLANE << STATE_SHIFT) | (numLevels << NUM_LEVEL_SHIFT); } private final class SlashArtist { // These values are derived in un-rotated (vertical) orientation private static final float SLASH_WIDTH = 1.8384776f; private static final float SLASH_HEIGHT = 22f; private static final float CENTER_X = 10.65f; private static final float CENTER_Y = 15.869239f; private static final float SCALE = 24f; // Bottom is derived during animation private static final float LEFT = (CENTER_X - (SLASH_WIDTH / 2)) / SCALE; private static final float TOP = (CENTER_Y - (SLASH_HEIGHT / 2)) / SCALE; private static final float RIGHT = (CENTER_X + (SLASH_WIDTH / 2)) / SCALE; private static final float BOTTOM = (CENTER_Y + (SLASH_HEIGHT / 2)) / SCALE; // Draw the slash washington-monument style; rotate to no-u-turn style private static final float ROTATION = -45f; private final Path mPath = new Path(); private final RectF mSlashRect = new RectF(); void draw(int height, int width, @NonNull Canvas canvas, Paint paint) { Matrix m = new Matrix(); updateRect( scale(LEFT, width), scale(TOP, height), scale(RIGHT, width), scale(BOTTOM, height)); mPath.reset(); // Draw the slash vertically mPath.addRect(mSlashRect, Direction.CW); m.setRotate(ROTATION, width / 2, height / 2); mPath.transform(m); canvas.drawPath(mPath, paint); // Rotate back to vertical, and draw the cut-out rect next to this one m.setRotate(-ROTATION, width / 2, height / 2); mPath.transform(m); m.setTranslate(mSlashRect.width(), 0); mPath.transform(m); mPath.addRect(mSlashRect, Direction.CW); m.setRotate(ROTATION, width / 2, height / 2); mPath.transform(m); canvas.clipOutPath(mPath); } void updateRect(float left, float top, float right, float bottom) { mSlashRect.left = left; mSlashRect.top = top; mSlashRect.right = right; mSlashRect.bottom = bottom; } private float scale(float frac, int width) { return frac * width; } } } packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java +4 −0 Original line number Diff line number Diff line Loading @@ -254,6 +254,10 @@ public class MobileSignalController extends SignalController< @Override public int getQsCurrentIconId() { if (mCurrentState.airplaneMode) { return SignalDrawable.getAirplaneModeState(getNumLevels()); } return getCurrentIconId(); } Loading Loading
packages/SystemUI/res/values/dimens.xml +3 −0 Original line number Diff line number Diff line Loading @@ -827,4 +827,7 @@ <!-- How far to inset the rounded edges --> <dimen name="stat_sys_mobile_signal_circle_inset">0.9dp</dimen> <!-- Width of the hollow triangle for empty signal state --> <dimen name="mobile_signal_empty_strokewidth">2dp</dimen> </resources>
packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java +5 −1 Original line number Diff line number Diff line Loading @@ -141,8 +141,12 @@ public class CellularTile extends QSTileImpl<SignalState> { && mDataController.isMobileDataEnabled(); state.icon = new SignalIcon(cb.mobileSignalIconId); if (cb.airplaneModeEnabled) { state.state = Tile.STATE_INACTIVE; } else { state.state = Tile.STATE_ACTIVE; } } @Override public int getMetricsCategory() { Loading
packages/SystemUI/src/com/android/systemui/statusbar/phone/SignalDrawable.java +112 −6 Original line number Diff line number Diff line Loading @@ -21,13 +21,14 @@ import android.annotation.Nullable; import android.content.Context; import android.graphics.Canvas; import android.graphics.ColorFilter; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Paint.Style; import android.graphics.Path; import android.graphics.Path.Direction; import android.graphics.Path.FillType; import android.graphics.Path.Op; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.Drawable; import android.os.Handler; import android.util.LayoutDirection; Loading Loading @@ -63,6 +64,7 @@ public class SignalDrawable extends Drawable { private static final int STATE_EMPTY = 1; private static final int STATE_CUT = 2; private static final int STATE_CARRIER_CHANGE = 3; private static final int STATE_AIRPLANE = 4; private static final long DOT_DELAY = 1000; Loading Loading @@ -97,6 +99,18 @@ public class SignalDrawable extends Drawable { // How far the circle defining the corners is inset from the edges private final float mAppliedCornerInset; // The easiest way to understand this is as if we set Style.STROKE and draw the triangle, // but that is only theoretically right. Instead, draw the triangle and clip out a smaller // one inset by this amount. private final float mEmptyStrokeWidth; private static final float INV_TAN = 1f / (float) Math.tan(Math.PI / 8f); private final float mEmptyDiagInset; // == mEmptyStrokeWidth * INV_TAN //TODO: This is needed because drawing the triangle is paramterized on the rounded edges. // We get rounded corners by placing circles at arbitrary points along the legs of the triangle, // but that means we lose the notion of a triangle being strictly half of its containing square. // As a result, here's a value to tweak the insets of the cutout for the no-signal icon. private final float mCutExtraOffset; private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); private final Paint mForegroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); private final int mDarkModeBackgroundColor; Loading @@ -106,6 +120,10 @@ public class SignalDrawable extends Drawable { private final Path mFullPath = new Path(); private final Path mForegroundPath = new Path(); private final Path mXPath = new Path(); // Cut out when STATE_EMPTY private final Path mCutPath = new Path(); // Draws the slash when in airplane mode private final SlashArtist mSlash = new SlashArtist(); private final Handler mHandler; private float mOldDarkIntensity = -1; private float mNumLevels = 1; Loading @@ -126,11 +144,18 @@ public class SignalDrawable extends Drawable { mLightModeFillColor = Utils.getDefaultColor(context, R.color.light_mode_icon_color_dual_tone_fill); mIntrinsicSize = context.getResources().getDimensionPixelSize(R.dimen.signal_icon_size); // mCutPath parameters mEmptyStrokeWidth = context.getResources() .getDimensionPixelSize(R.dimen.mobile_signal_empty_strokewidth); mEmptyDiagInset = mEmptyStrokeWidth * INV_TAN; mHandler = new Handler(); setDarkIntensity(0); mAppliedCornerInset = context.getResources() .getDimensionPixelSize(R.dimen.stat_sys_mobile_signal_circle_inset); mCutExtraOffset = mAppliedCornerInset / 2f; } public void setIntrinsicSize(int size) { Loading Loading @@ -224,9 +249,10 @@ public class SignalDrawable extends Drawable { } mFullPath.reset(); mFullPath.setFillType(FillType.WINDING); final float width = getBounds().width(); final float height = getBounds().height(); final float padding = (int) (PAD * width); // Stay on pixel boundary final float padding = Math.round(PAD * width); final float cornerRadius = RADIUS_RATIO * height; // Offset from circle where the hypotenuse meets the circle final float diagOffset = DIAG_OFFSET_MULTIPLIER * cornerRadius; Loading Loading @@ -292,10 +318,29 @@ public class SignalDrawable extends Drawable { mFullPath.rLineTo(0, cut); } mPaint.setStyle(mState == STATE_EMPTY ? Style.STROKE : Style.FILL); mForegroundPaint.setStyle(mState == STATE_EMPTY ? Style.STROKE : Style.FILL); if (mState != STATE_CARRIER_CHANGE) { if (mState == STATE_EMPTY) { // Cut out a smaller triangle from the center of mFullPath mCutPath.reset(); mCutPath.setFillType(FillType.WINDING); mCutPath.moveTo(width - padding - mEmptyStrokeWidth, height - padding - mEmptyStrokeWidth); mCutPath.lineTo(width - padding - mEmptyStrokeWidth, padding + mEmptyDiagInset - mCutExtraOffset); mCutPath.lineTo(padding + mEmptyDiagInset - mCutExtraOffset, height - padding - mEmptyStrokeWidth); mCutPath.lineTo(width - padding - mEmptyStrokeWidth, height - padding - mEmptyStrokeWidth); // In empty state, draw the full path as the foreground paint mForegroundPath.set(mFullPath); mFullPath.reset(); mForegroundPath.op(mCutPath, Path.Op.DIFFERENCE); } else if (mState == STATE_AIRPLANE) { // Airplane mode is slashed, full-signal mForegroundPath.set(mFullPath); mFullPath.reset(); mSlash.draw((int) height, (int) width, canvas, mForegroundPaint); } else if (mState != STATE_CARRIER_CHANGE) { mForegroundPath.reset(); int sigWidth = Math.round(calcFit(mLevel / (mNumLevels - 1)) * (width - 2 * padding)); mForegroundPath.addRect(padding, padding, padding + sigWidth, height - padding, Loading Loading @@ -405,4 +450,65 @@ public class SignalDrawable extends Drawable { public static int getEmptyState(int numLevels) { return (STATE_EMPTY << STATE_SHIFT) | (numLevels << NUM_LEVEL_SHIFT); } public static int getAirplaneModeState(int numLevels) { return (STATE_AIRPLANE << STATE_SHIFT) | (numLevels << NUM_LEVEL_SHIFT); } private final class SlashArtist { // These values are derived in un-rotated (vertical) orientation private static final float SLASH_WIDTH = 1.8384776f; private static final float SLASH_HEIGHT = 22f; private static final float CENTER_X = 10.65f; private static final float CENTER_Y = 15.869239f; private static final float SCALE = 24f; // Bottom is derived during animation private static final float LEFT = (CENTER_X - (SLASH_WIDTH / 2)) / SCALE; private static final float TOP = (CENTER_Y - (SLASH_HEIGHT / 2)) / SCALE; private static final float RIGHT = (CENTER_X + (SLASH_WIDTH / 2)) / SCALE; private static final float BOTTOM = (CENTER_Y + (SLASH_HEIGHT / 2)) / SCALE; // Draw the slash washington-monument style; rotate to no-u-turn style private static final float ROTATION = -45f; private final Path mPath = new Path(); private final RectF mSlashRect = new RectF(); void draw(int height, int width, @NonNull Canvas canvas, Paint paint) { Matrix m = new Matrix(); updateRect( scale(LEFT, width), scale(TOP, height), scale(RIGHT, width), scale(BOTTOM, height)); mPath.reset(); // Draw the slash vertically mPath.addRect(mSlashRect, Direction.CW); m.setRotate(ROTATION, width / 2, height / 2); mPath.transform(m); canvas.drawPath(mPath, paint); // Rotate back to vertical, and draw the cut-out rect next to this one m.setRotate(-ROTATION, width / 2, height / 2); mPath.transform(m); m.setTranslate(mSlashRect.width(), 0); mPath.transform(m); mPath.addRect(mSlashRect, Direction.CW); m.setRotate(ROTATION, width / 2, height / 2); mPath.transform(m); canvas.clipOutPath(mPath); } void updateRect(float left, float top, float right, float bottom) { mSlashRect.left = left; mSlashRect.top = top; mSlashRect.right = right; mSlashRect.bottom = bottom; } private float scale(float frac, int width) { return frac * width; } } }
packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java +4 −0 Original line number Diff line number Diff line Loading @@ -254,6 +254,10 @@ public class MobileSignalController extends SignalController< @Override public int getQsCurrentIconId() { if (mCurrentState.airplaneMode) { return SignalDrawable.getAirplaneModeState(getNumLevels()); } return getCurrentIconId(); } Loading