Loading packages/SystemUI/src/com/android/systemui/CornerHandleView.java +70 −57 Original line number Original line Diff line number Diff line Loading @@ -20,8 +20,8 @@ import android.animation.ArgbEvaluator; import android.content.Context; import android.content.Context; import android.graphics.Canvas; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Paint; import android.graphics.Path; import android.graphics.RectF; import android.graphics.RectF; import android.os.SystemProperties; import android.util.AttributeSet; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.DisplayMetrics; import android.view.ContextThemeWrapper; import android.view.ContextThemeWrapper; Loading @@ -34,17 +34,19 @@ import com.android.settingslib.Utils; * corners. * corners. */ */ public class CornerHandleView extends View { public class CornerHandleView extends View { private static final boolean ALLOW_TUNING = false; private static final float STROKE_DP_LARGE = 2f; private static final int ANGLE_DEGREES = 50; private static final float STROKE_DP_SMALL = 1.95f; private static final float STROKE_DP = 2.5f; // Radius to use if none is available. // Radius to use if none is available. private static final int FALLBACK_RADIUS_DP = 15; private static final int FALLBACK_RADIUS_DP = 15; private static final float MARGIN_DP = 8; private static final int MAX_ARC_DEGREES = 90; // Arc length along the phone's perimeter used to measure the desired angle. private static final float ARC_LENGTH_DP = 31f; private Paint mPaint; private Paint mPaint; private int mLightColor; private int mLightColor; private int mDarkColor; private int mDarkColor; private RectF mOval; private Path mPath; public CornerHandleView(Context context, AttributeSet attrs) { public CornerHandleView(Context context, AttributeSet attrs) { super(context, attrs); super(context, attrs); Loading @@ -55,18 +57,46 @@ public class CornerHandleView extends View { mPaint.setStrokeCap(Paint.Cap.ROUND); mPaint.setStrokeCap(Paint.Cap.ROUND); mPaint.setStrokeWidth(getStrokePx()); mPaint.setStrokeWidth(getStrokePx()); final int dualToneDarkTheme = Utils.getThemeAttr(mContext, final int dualToneDarkTheme = Utils.getThemeAttr(mContext, R.attr.darkIconTheme); R.attr.darkIconTheme); final int dualToneLightTheme = Utils.getThemeAttr(mContext, R.attr.lightIconTheme); final int dualToneLightTheme = Utils.getThemeAttr(mContext, R.attr.lightIconTheme); Context lightContext = new ContextThemeWrapper(mContext, dualToneLightTheme); Context lightContext = new ContextThemeWrapper(mContext, dualToneLightTheme); Context darkContext = new ContextThemeWrapper(mContext, dualToneDarkTheme); Context darkContext = new ContextThemeWrapper(mContext, dualToneDarkTheme); mLightColor = Utils.getColorAttrDefaultColor(lightContext, mLightColor = Utils.getColorAttrDefaultColor(lightContext, R.attr.singleToneColor); R.attr.singleToneColor); mDarkColor = Utils.getColorAttrDefaultColor(darkContext, R.attr.singleToneColor); mDarkColor = Utils.getColorAttrDefaultColor(darkContext, R.attr.singleToneColor); updatePath(); } updateOval(); private void updatePath() { mPath = new Path(); float marginPx = getMarginPx(); float radiusPx = getInnerRadiusPx(); float halfStrokePx = getStrokePx() / 2f; float angle = getAngle(); float startAngle = 180 + ((90 - angle) / 2); RectF circle = new RectF(marginPx + halfStrokePx, marginPx + halfStrokePx, marginPx + 2 * radiusPx - halfStrokePx, marginPx + 2 * radiusPx - halfStrokePx); if (angle >= 90f) { float innerCircumferenceDp = convertPixelToDp(radiusPx * 2 * (float) Math.PI, mContext); float arcDp = innerCircumferenceDp * getAngle() / 360f; // Add additional "arms" to the two ends of the arc. The length computation is // hand-tuned. float lineLengthPx = convertDpToPixel((ARC_LENGTH_DP - arcDp - MARGIN_DP) / 2, mContext); mPath.moveTo(marginPx + halfStrokePx, marginPx + radiusPx + lineLengthPx); mPath.lineTo(marginPx + halfStrokePx, marginPx + radiusPx); mPath.arcTo(circle, startAngle, angle); mPath.moveTo(marginPx + radiusPx, marginPx + halfStrokePx); mPath.lineTo(marginPx + radiusPx + lineLengthPx, marginPx + halfStrokePx); } else { mPath.arcTo(circle, startAngle, angle); } } } /** /** Loading @@ -83,50 +113,40 @@ public class CornerHandleView extends View { @Override @Override public void onDraw(Canvas canvas) { public void onDraw(Canvas canvas) { super.onDraw(canvas); super.onDraw(canvas); canvas.drawPath(mPath, mPaint); if (ALLOW_TUNING) { mPaint.setStrokeWidth(getStrokePx()); updateOval(); } canvas.drawArc(mOval, 180 + ((90 - getAngle()) / 2), getAngle(), false, mPaint); } } // TODO(b/133834204): Remove tweaking of corner handles private static float convertDpToPixel(float dp, Context context) { private static float convertDpToPixel(float dp, Context context) { return dp * ((float) context.getResources().getDisplayMetrics().densityDpi return dp * ((float) context.getResources().getDisplayMetrics().densityDpi / DisplayMetrics.DENSITY_DEFAULT); / DisplayMetrics.DENSITY_DEFAULT); } } private void updateOval() { private static float convertPixelToDp(float px, Context context) { mOval = new RectF(getMarginPx() - (getStrokePx() / 2.f), return px * DisplayMetrics.DENSITY_DEFAULT getMarginPx() - (getStrokePx() / 2.f), / ((float) context.getResources().getDisplayMetrics().densityDpi); getMarginPx() + (2 * (getRadiusPx()) + (getStrokePx() / 2.f)), getMarginPx() + 2 * getRadiusPx() + (getStrokePx() / 2.f)); } } private int getAngle() { private float getAngle() { if (ALLOW_TUNING) { // Measure a length of ARC_LENGTH_DP along the *screen's* perimeter, get the angle and cap return SystemProperties.getInt("CORNER_HANDLE_ANGLE_DEGREES", ANGLE_DEGREES); // it at 90. } else { float circumferenceDp = convertPixelToDp(( return ANGLE_DEGREES; getOuterRadiusPx()) * 2 * (float) Math.PI, mContext); float angleDeg = (ARC_LENGTH_DP / circumferenceDp) * 360; if (angleDeg > MAX_ARC_DEGREES) { angleDeg = MAX_ARC_DEGREES; } } return angleDeg; } } private int getMarginPx() { private float getMarginPx() { // Hand-derived function to map radiusPx to the margin amount. return convertDpToPixel(MARGIN_DP, mContext); // https://www.wolframalpha.com/input/?i=0.001402+*+x+%5E2%E2%88%920.08661+*+x%2B17.20+from+40+to+180 int radius = getRadiusPx(); int marginPx = (int) (0.001402f * radius * radius - 0.08661f * radius + 17.2f); if (ALLOW_TUNING) { return SystemProperties.getInt("CORNER_HANDLE_MARGIN_PX", marginPx); } else { return marginPx; } } private float getInnerRadiusPx() { return getOuterRadiusPx() - getMarginPx(); } } private int getRadiusPx() { private float getOuterRadiusPx() { // Attempt to get the bottom corner radius, otherwise fall back on the generic or top // Attempt to get the bottom corner radius, otherwise fall back on the generic or top // values. If none are available, use the FALLBACK_RADIUS_DP. // values. If none are available, use the FALLBACK_RADIUS_DP. int radius = getResources().getDimensionPixelSize( int radius = getResources().getDimensionPixelSize( Loading @@ -142,19 +162,12 @@ public class CornerHandleView extends View { if (radius == 0) { if (radius == 0) { radius = (int) convertDpToPixel(FALLBACK_RADIUS_DP, mContext); radius = (int) convertDpToPixel(FALLBACK_RADIUS_DP, mContext); } } if (ALLOW_TUNING) { return SystemProperties.getInt("CORNER_HANDLE_RADIUS_PX", radius); } else { return radius; return radius; } } } private int getStrokePx() { private float getStrokePx() { if (ALLOW_TUNING) { // Use a slightly smaller stroke if we need to cover the full corner angle. return SystemProperties.getInt("CORNER_HANDLE_STROKE_PX", return convertDpToPixel((getAngle() < 90) ? STROKE_DP_LARGE : STROKE_DP_SMALL, (int) convertDpToPixel(STROKE_DP, getContext())); getContext()); } else { return (int) convertDpToPixel(STROKE_DP, getContext()); } } } } } Loading
packages/SystemUI/src/com/android/systemui/CornerHandleView.java +70 −57 Original line number Original line Diff line number Diff line Loading @@ -20,8 +20,8 @@ import android.animation.ArgbEvaluator; import android.content.Context; import android.content.Context; import android.graphics.Canvas; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Paint; import android.graphics.Path; import android.graphics.RectF; import android.graphics.RectF; import android.os.SystemProperties; import android.util.AttributeSet; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.DisplayMetrics; import android.view.ContextThemeWrapper; import android.view.ContextThemeWrapper; Loading @@ -34,17 +34,19 @@ import com.android.settingslib.Utils; * corners. * corners. */ */ public class CornerHandleView extends View { public class CornerHandleView extends View { private static final boolean ALLOW_TUNING = false; private static final float STROKE_DP_LARGE = 2f; private static final int ANGLE_DEGREES = 50; private static final float STROKE_DP_SMALL = 1.95f; private static final float STROKE_DP = 2.5f; // Radius to use if none is available. // Radius to use if none is available. private static final int FALLBACK_RADIUS_DP = 15; private static final int FALLBACK_RADIUS_DP = 15; private static final float MARGIN_DP = 8; private static final int MAX_ARC_DEGREES = 90; // Arc length along the phone's perimeter used to measure the desired angle. private static final float ARC_LENGTH_DP = 31f; private Paint mPaint; private Paint mPaint; private int mLightColor; private int mLightColor; private int mDarkColor; private int mDarkColor; private RectF mOval; private Path mPath; public CornerHandleView(Context context, AttributeSet attrs) { public CornerHandleView(Context context, AttributeSet attrs) { super(context, attrs); super(context, attrs); Loading @@ -55,18 +57,46 @@ public class CornerHandleView extends View { mPaint.setStrokeCap(Paint.Cap.ROUND); mPaint.setStrokeCap(Paint.Cap.ROUND); mPaint.setStrokeWidth(getStrokePx()); mPaint.setStrokeWidth(getStrokePx()); final int dualToneDarkTheme = Utils.getThemeAttr(mContext, final int dualToneDarkTheme = Utils.getThemeAttr(mContext, R.attr.darkIconTheme); R.attr.darkIconTheme); final int dualToneLightTheme = Utils.getThemeAttr(mContext, R.attr.lightIconTheme); final int dualToneLightTheme = Utils.getThemeAttr(mContext, R.attr.lightIconTheme); Context lightContext = new ContextThemeWrapper(mContext, dualToneLightTheme); Context lightContext = new ContextThemeWrapper(mContext, dualToneLightTheme); Context darkContext = new ContextThemeWrapper(mContext, dualToneDarkTheme); Context darkContext = new ContextThemeWrapper(mContext, dualToneDarkTheme); mLightColor = Utils.getColorAttrDefaultColor(lightContext, mLightColor = Utils.getColorAttrDefaultColor(lightContext, R.attr.singleToneColor); R.attr.singleToneColor); mDarkColor = Utils.getColorAttrDefaultColor(darkContext, R.attr.singleToneColor); mDarkColor = Utils.getColorAttrDefaultColor(darkContext, R.attr.singleToneColor); updatePath(); } updateOval(); private void updatePath() { mPath = new Path(); float marginPx = getMarginPx(); float radiusPx = getInnerRadiusPx(); float halfStrokePx = getStrokePx() / 2f; float angle = getAngle(); float startAngle = 180 + ((90 - angle) / 2); RectF circle = new RectF(marginPx + halfStrokePx, marginPx + halfStrokePx, marginPx + 2 * radiusPx - halfStrokePx, marginPx + 2 * radiusPx - halfStrokePx); if (angle >= 90f) { float innerCircumferenceDp = convertPixelToDp(radiusPx * 2 * (float) Math.PI, mContext); float arcDp = innerCircumferenceDp * getAngle() / 360f; // Add additional "arms" to the two ends of the arc. The length computation is // hand-tuned. float lineLengthPx = convertDpToPixel((ARC_LENGTH_DP - arcDp - MARGIN_DP) / 2, mContext); mPath.moveTo(marginPx + halfStrokePx, marginPx + radiusPx + lineLengthPx); mPath.lineTo(marginPx + halfStrokePx, marginPx + radiusPx); mPath.arcTo(circle, startAngle, angle); mPath.moveTo(marginPx + radiusPx, marginPx + halfStrokePx); mPath.lineTo(marginPx + radiusPx + lineLengthPx, marginPx + halfStrokePx); } else { mPath.arcTo(circle, startAngle, angle); } } } /** /** Loading @@ -83,50 +113,40 @@ public class CornerHandleView extends View { @Override @Override public void onDraw(Canvas canvas) { public void onDraw(Canvas canvas) { super.onDraw(canvas); super.onDraw(canvas); canvas.drawPath(mPath, mPaint); if (ALLOW_TUNING) { mPaint.setStrokeWidth(getStrokePx()); updateOval(); } canvas.drawArc(mOval, 180 + ((90 - getAngle()) / 2), getAngle(), false, mPaint); } } // TODO(b/133834204): Remove tweaking of corner handles private static float convertDpToPixel(float dp, Context context) { private static float convertDpToPixel(float dp, Context context) { return dp * ((float) context.getResources().getDisplayMetrics().densityDpi return dp * ((float) context.getResources().getDisplayMetrics().densityDpi / DisplayMetrics.DENSITY_DEFAULT); / DisplayMetrics.DENSITY_DEFAULT); } } private void updateOval() { private static float convertPixelToDp(float px, Context context) { mOval = new RectF(getMarginPx() - (getStrokePx() / 2.f), return px * DisplayMetrics.DENSITY_DEFAULT getMarginPx() - (getStrokePx() / 2.f), / ((float) context.getResources().getDisplayMetrics().densityDpi); getMarginPx() + (2 * (getRadiusPx()) + (getStrokePx() / 2.f)), getMarginPx() + 2 * getRadiusPx() + (getStrokePx() / 2.f)); } } private int getAngle() { private float getAngle() { if (ALLOW_TUNING) { // Measure a length of ARC_LENGTH_DP along the *screen's* perimeter, get the angle and cap return SystemProperties.getInt("CORNER_HANDLE_ANGLE_DEGREES", ANGLE_DEGREES); // it at 90. } else { float circumferenceDp = convertPixelToDp(( return ANGLE_DEGREES; getOuterRadiusPx()) * 2 * (float) Math.PI, mContext); float angleDeg = (ARC_LENGTH_DP / circumferenceDp) * 360; if (angleDeg > MAX_ARC_DEGREES) { angleDeg = MAX_ARC_DEGREES; } } return angleDeg; } } private int getMarginPx() { private float getMarginPx() { // Hand-derived function to map radiusPx to the margin amount. return convertDpToPixel(MARGIN_DP, mContext); // https://www.wolframalpha.com/input/?i=0.001402+*+x+%5E2%E2%88%920.08661+*+x%2B17.20+from+40+to+180 int radius = getRadiusPx(); int marginPx = (int) (0.001402f * radius * radius - 0.08661f * radius + 17.2f); if (ALLOW_TUNING) { return SystemProperties.getInt("CORNER_HANDLE_MARGIN_PX", marginPx); } else { return marginPx; } } private float getInnerRadiusPx() { return getOuterRadiusPx() - getMarginPx(); } } private int getRadiusPx() { private float getOuterRadiusPx() { // Attempt to get the bottom corner radius, otherwise fall back on the generic or top // Attempt to get the bottom corner radius, otherwise fall back on the generic or top // values. If none are available, use the FALLBACK_RADIUS_DP. // values. If none are available, use the FALLBACK_RADIUS_DP. int radius = getResources().getDimensionPixelSize( int radius = getResources().getDimensionPixelSize( Loading @@ -142,19 +162,12 @@ public class CornerHandleView extends View { if (radius == 0) { if (radius == 0) { radius = (int) convertDpToPixel(FALLBACK_RADIUS_DP, mContext); radius = (int) convertDpToPixel(FALLBACK_RADIUS_DP, mContext); } } if (ALLOW_TUNING) { return SystemProperties.getInt("CORNER_HANDLE_RADIUS_PX", radius); } else { return radius; return radius; } } } private int getStrokePx() { private float getStrokePx() { if (ALLOW_TUNING) { // Use a slightly smaller stroke if we need to cover the full corner angle. return SystemProperties.getInt("CORNER_HANDLE_STROKE_PX", return convertDpToPixel((getAngle() < 90) ? STROKE_DP_LARGE : STROKE_DP_SMALL, (int) convertDpToPixel(STROKE_DP, getContext())); getContext()); } else { return (int) convertDpToPixel(STROKE_DP, getContext()); } } } } }