Jason Monk | 48edc0c | 2017-04-10 15:01:27 -0400 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2017 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file |
| 5 | * except in compliance with the License. You may obtain a copy of the License at |
| 6 | * |
| 7 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | * |
| 9 | * Unless required by applicable law or agreed to in writing, software distributed under the |
| 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| 11 | * KIND, either express or implied. See the License for the specific language governing |
| 12 | * permissions and limitations under the License. |
| 13 | */ |
| 14 | |
Evan Laird | 06e9fd8 | 2018-02-10 09:36:55 -0800 | [diff] [blame] | 15 | package com.android.settingslib.graph; |
Jason Monk | 48edc0c | 2017-04-10 15:01:27 -0400 | [diff] [blame] | 16 | |
| 17 | import android.animation.ArgbEvaluator; |
| 18 | import android.annotation.IntRange; |
| 19 | import android.annotation.NonNull; |
| 20 | import android.annotation.Nullable; |
| 21 | import android.content.Context; |
Amin Shaikh | 0ad7e51 | 2019-04-29 07:36:34 -0400 | [diff] [blame] | 22 | import android.content.res.ColorStateList; |
Jason Monk | 48edc0c | 2017-04-10 15:01:27 -0400 | [diff] [blame] | 23 | import android.graphics.Canvas; |
| 24 | import android.graphics.ColorFilter; |
Amin Shaikh | 408d2d3 | 2019-06-04 10:28:02 -0400 | [diff] [blame] | 25 | import android.graphics.Matrix; |
Jason Monk | 48edc0c | 2017-04-10 15:01:27 -0400 | [diff] [blame] | 26 | import android.graphics.Paint; |
Jason Monk | 48edc0c | 2017-04-10 15:01:27 -0400 | [diff] [blame] | 27 | import android.graphics.Path; |
| 28 | import android.graphics.Path.Direction; |
| 29 | import android.graphics.Path.FillType; |
Amin Shaikh | 0ad7e51 | 2019-04-29 07:36:34 -0400 | [diff] [blame] | 30 | import android.graphics.PorterDuff; |
| 31 | import android.graphics.PorterDuffXfermode; |
Jason Monk | 48edc0c | 2017-04-10 15:01:27 -0400 | [diff] [blame] | 32 | import android.graphics.Rect; |
Amin Shaikh | 0ad7e51 | 2019-04-29 07:36:34 -0400 | [diff] [blame] | 33 | import android.graphics.drawable.DrawableWrapper; |
Jason Monk | 48edc0c | 2017-04-10 15:01:27 -0400 | [diff] [blame] | 34 | import android.os.Handler; |
Amin Shaikh | 0ad7e51 | 2019-04-29 07:36:34 -0400 | [diff] [blame] | 35 | import android.telephony.SignalStrength; |
Jason Monk | 54b610f | 2017-05-25 16:11:52 -0400 | [diff] [blame] | 36 | import android.util.LayoutDirection; |
Amin Shaikh | 408d2d3 | 2019-06-04 10:28:02 -0400 | [diff] [blame] | 37 | import android.util.PathParser; |
Jason Monk | 48edc0c | 2017-04-10 15:01:27 -0400 | [diff] [blame] | 38 | |
| 39 | import com.android.settingslib.R; |
| 40 | import com.android.settingslib.Utils; |
| 41 | |
Amin Shaikh | 0ad7e51 | 2019-04-29 07:36:34 -0400 | [diff] [blame] | 42 | /** |
| 43 | * Drawable displaying a mobile cell signal indicator. |
| 44 | */ |
| 45 | public class SignalDrawable extends DrawableWrapper { |
Jason Monk | 48edc0c | 2017-04-10 15:01:27 -0400 | [diff] [blame] | 46 | |
| 47 | private static final String TAG = "SignalDrawable"; |
| 48 | |
| 49 | private static final int NUM_DOTS = 3; |
| 50 | |
| 51 | private static final float VIEWPORT = 24f; |
| 52 | private static final float PAD = 2f / VIEWPORT; |
Jason Monk | 48edc0c | 2017-04-10 15:01:27 -0400 | [diff] [blame] | 53 | |
| 54 | private static final float DOT_SIZE = 3f / VIEWPORT; |
Amin Shaikh | 0ad7e51 | 2019-04-29 07:36:34 -0400 | [diff] [blame] | 55 | private static final float DOT_PADDING = 1.5f / VIEWPORT; |
Jason Monk | 48edc0c | 2017-04-10 15:01:27 -0400 | [diff] [blame] | 56 | |
| 57 | // All of these are masks to push all of the drawable state into one int for easy callbacks |
| 58 | // and flow through sysui. |
| 59 | private static final int LEVEL_MASK = 0xff; |
| 60 | private static final int NUM_LEVEL_SHIFT = 8; |
| 61 | private static final int NUM_LEVEL_MASK = 0xff << NUM_LEVEL_SHIFT; |
Jason Monk | 7e6c83c | 2017-04-26 14:35:24 -0400 | [diff] [blame] | 62 | private static final int STATE_SHIFT = 16; |
| 63 | private static final int STATE_MASK = 0xff << STATE_SHIFT; |
Jason Monk | 7e6c83c | 2017-04-26 14:35:24 -0400 | [diff] [blame] | 64 | private static final int STATE_CUT = 2; |
| 65 | private static final int STATE_CARRIER_CHANGE = 3; |
Jason Monk | 48edc0c | 2017-04-10 15:01:27 -0400 | [diff] [blame] | 66 | |
| 67 | private static final long DOT_DELAY = 1000; |
| 68 | |
Jason Monk | 48edc0c | 2017-04-10 15:01:27 -0400 | [diff] [blame] | 69 | private final Paint mForegroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); |
Amin Shaikh | 0ad7e51 | 2019-04-29 07:36:34 -0400 | [diff] [blame] | 70 | private final Paint mTransparentPaint = new Paint(Paint.ANTI_ALIAS_FLAG); |
Jason Monk | 48edc0c | 2017-04-10 15:01:27 -0400 | [diff] [blame] | 71 | private final int mDarkModeFillColor; |
Jason Monk | 48edc0c | 2017-04-10 15:01:27 -0400 | [diff] [blame] | 72 | private final int mLightModeFillColor; |
Amin Shaikh | 0ad7e51 | 2019-04-29 07:36:34 -0400 | [diff] [blame] | 73 | private final Path mCutoutPath = new Path(); |
Jason Monk | 48edc0c | 2017-04-10 15:01:27 -0400 | [diff] [blame] | 74 | private final Path mForegroundPath = new Path(); |
| 75 | private final Path mXPath = new Path(); |
Amin Shaikh | 408d2d3 | 2019-06-04 10:28:02 -0400 | [diff] [blame] | 76 | private final Matrix mXScaleMatrix = new Matrix(); |
| 77 | private final Path mScaledXPath = new Path(); |
Jason Monk | 48edc0c | 2017-04-10 15:01:27 -0400 | [diff] [blame] | 78 | private final Handler mHandler; |
Amin Shaikh | 408d2d3 | 2019-06-04 10:28:02 -0400 | [diff] [blame] | 79 | private final float mCutoutWidthFraction; |
| 80 | private final float mCutoutHeightFraction; |
Amin Shaikh | 0ad7e51 | 2019-04-29 07:36:34 -0400 | [diff] [blame] | 81 | private float mDarkIntensity = -1; |
| 82 | private final int mIntrinsicSize; |
Jason Monk | 48edc0c | 2017-04-10 15:01:27 -0400 | [diff] [blame] | 83 | private boolean mAnimating; |
| 84 | private int mCurrentDot; |
| 85 | |
| 86 | public SignalDrawable(Context context) { |
Amin Shaikh | 0ad7e51 | 2019-04-29 07:36:34 -0400 | [diff] [blame] | 87 | super(context.getDrawable(com.android.internal.R.drawable.ic_signal_cellular)); |
Amin Shaikh | 408d2d3 | 2019-06-04 10:28:02 -0400 | [diff] [blame] | 88 | final String xPathString = context.getString( |
| 89 | com.android.internal.R.string.config_signalXPath); |
| 90 | mXPath.set(PathParser.createPathFromPathData(xPathString)); |
| 91 | updateScaledXPath(); |
| 92 | mCutoutWidthFraction = context.getResources().getFloat( |
| 93 | com.android.internal.R.dimen.config_signalCutoutWidthFraction); |
| 94 | mCutoutHeightFraction = context.getResources().getFloat( |
| 95 | com.android.internal.R.dimen.config_signalCutoutHeightFraction); |
Amin Shaikh | 0ad7e51 | 2019-04-29 07:36:34 -0400 | [diff] [blame] | 96 | mDarkModeFillColor = Utils.getColorStateListDefaultColor(context, |
| 97 | R.color.dark_mode_icon_color_single_tone); |
| 98 | mLightModeFillColor = Utils.getColorStateListDefaultColor(context, |
| 99 | R.color.light_mode_icon_color_single_tone); |
Jason Monk | 48edc0c | 2017-04-10 15:01:27 -0400 | [diff] [blame] | 100 | mIntrinsicSize = context.getResources().getDimensionPixelSize(R.dimen.signal_icon_size); |
Amin Shaikh | 0ad7e51 | 2019-04-29 07:36:34 -0400 | [diff] [blame] | 101 | mTransparentPaint.setColor(context.getColor(android.R.color.transparent)); |
| 102 | mTransparentPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); |
Jason Monk | 48edc0c | 2017-04-10 15:01:27 -0400 | [diff] [blame] | 103 | mHandler = new Handler(); |
| 104 | setDarkIntensity(0); |
Jason Monk | 01df36f | 2017-06-07 13:02:47 -0400 | [diff] [blame] | 105 | } |
| 106 | |
Amin Shaikh | 408d2d3 | 2019-06-04 10:28:02 -0400 | [diff] [blame] | 107 | private void updateScaledXPath() { |
| 108 | if (getBounds().isEmpty()) { |
| 109 | mXScaleMatrix.setScale(1f, 1f); |
| 110 | } else { |
| 111 | mXScaleMatrix.setScale(getBounds().width() / VIEWPORT, getBounds().height() / VIEWPORT); |
| 112 | } |
| 113 | mXPath.transform(mXScaleMatrix, mScaledXPath); |
| 114 | } |
| 115 | |
Jason Monk | 48edc0c | 2017-04-10 15:01:27 -0400 | [diff] [blame] | 116 | @Override |
| 117 | public int getIntrinsicWidth() { |
| 118 | return mIntrinsicSize; |
| 119 | } |
| 120 | |
| 121 | @Override |
| 122 | public int getIntrinsicHeight() { |
| 123 | return mIntrinsicSize; |
| 124 | } |
| 125 | |
Jason Monk | 48edc0c | 2017-04-10 15:01:27 -0400 | [diff] [blame] | 126 | private void updateAnimation() { |
Amin Shaikh | 0ad7e51 | 2019-04-29 07:36:34 -0400 | [diff] [blame] | 127 | boolean shouldAnimate = isInState(STATE_CARRIER_CHANGE) && isVisible(); |
Jason Monk | 48edc0c | 2017-04-10 15:01:27 -0400 | [diff] [blame] | 128 | if (shouldAnimate == mAnimating) return; |
| 129 | mAnimating = shouldAnimate; |
| 130 | if (shouldAnimate) { |
| 131 | mChangeDot.run(); |
| 132 | } else { |
| 133 | mHandler.removeCallbacks(mChangeDot); |
| 134 | } |
| 135 | } |
| 136 | |
| 137 | @Override |
Amin Shaikh | 0ad7e51 | 2019-04-29 07:36:34 -0400 | [diff] [blame] | 138 | protected boolean onLevelChange(int packedState) { |
| 139 | super.onLevelChange(unpackLevel(packedState)); |
| 140 | updateAnimation(); |
| 141 | setTintList(ColorStateList.valueOf(mForegroundPaint.getColor())); |
Amin Shaikh | b7e99e1 | 2019-06-12 10:31:46 -0400 | [diff] [blame] | 142 | invalidateSelf(); |
Jason Monk | 48edc0c | 2017-04-10 15:01:27 -0400 | [diff] [blame] | 143 | return true; |
| 144 | } |
| 145 | |
Amin Shaikh | 0ad7e51 | 2019-04-29 07:36:34 -0400 | [diff] [blame] | 146 | private int unpackLevel(int packedState) { |
| 147 | int numBins = (packedState & NUM_LEVEL_MASK) >> NUM_LEVEL_SHIFT; |
| 148 | int levelOffset = numBins == (SignalStrength.NUM_SIGNAL_STRENGTH_BINS + 1) ? 10 : 0; |
| 149 | int level = (packedState & LEVEL_MASK); |
| 150 | return level + levelOffset; |
Evan Laird | 5e43f2d | 2017-07-13 11:09:39 -0400 | [diff] [blame] | 151 | } |
| 152 | |
Jason Monk | 48edc0c | 2017-04-10 15:01:27 -0400 | [diff] [blame] | 153 | public void setDarkIntensity(float darkIntensity) { |
Amin Shaikh | 0ad7e51 | 2019-04-29 07:36:34 -0400 | [diff] [blame] | 154 | if (darkIntensity == mDarkIntensity) { |
Jason Monk | 48edc0c | 2017-04-10 15:01:27 -0400 | [diff] [blame] | 155 | return; |
| 156 | } |
Amin Shaikh | 0ad7e51 | 2019-04-29 07:36:34 -0400 | [diff] [blame] | 157 | setTintList(ColorStateList.valueOf(getFillColor(darkIntensity))); |
| 158 | } |
| 159 | |
| 160 | @Override |
| 161 | public void setTintList(ColorStateList tint) { |
| 162 | super.setTintList(tint); |
| 163 | int colorForeground = mForegroundPaint.getColor(); |
| 164 | mForegroundPaint.setColor(tint.getDefaultColor()); |
| 165 | if (colorForeground != mForegroundPaint.getColor()) invalidateSelf(); |
Jason Monk | 48edc0c | 2017-04-10 15:01:27 -0400 | [diff] [blame] | 166 | } |
| 167 | |
| 168 | private int getFillColor(float darkIntensity) { |
| 169 | return getColorForDarkIntensity( |
| 170 | darkIntensity, mLightModeFillColor, mDarkModeFillColor); |
| 171 | } |
| 172 | |
Jason Monk | 48edc0c | 2017-04-10 15:01:27 -0400 | [diff] [blame] | 173 | private int getColorForDarkIntensity(float darkIntensity, int lightColor, int darkColor) { |
| 174 | return (int) ArgbEvaluator.getInstance().evaluate(darkIntensity, lightColor, darkColor); |
| 175 | } |
| 176 | |
| 177 | @Override |
| 178 | protected void onBoundsChange(Rect bounds) { |
| 179 | super.onBoundsChange(bounds); |
Amin Shaikh | 408d2d3 | 2019-06-04 10:28:02 -0400 | [diff] [blame] | 180 | updateScaledXPath(); |
Jason Monk | 48edc0c | 2017-04-10 15:01:27 -0400 | [diff] [blame] | 181 | invalidateSelf(); |
| 182 | } |
| 183 | |
| 184 | @Override |
| 185 | public void draw(@NonNull Canvas canvas) { |
Amin Shaikh | 0ad7e51 | 2019-04-29 07:36:34 -0400 | [diff] [blame] | 186 | canvas.saveLayer(null, null); |
Evan Laird | ae5b700 | 2017-07-06 11:41:21 -0400 | [diff] [blame] | 187 | final float width = getBounds().width(); |
| 188 | final float height = getBounds().height(); |
| 189 | |
Jason Monk | 54b610f | 2017-05-25 16:11:52 -0400 | [diff] [blame] | 190 | boolean isRtl = getLayoutDirection() == LayoutDirection.RTL; |
| 191 | if (isRtl) { |
| 192 | canvas.save(); |
| 193 | // Mirror the drawable |
Evan Laird | ae5b700 | 2017-07-06 11:41:21 -0400 | [diff] [blame] | 194 | canvas.translate(width, 0); |
Jason Monk | 54b610f | 2017-05-25 16:11:52 -0400 | [diff] [blame] | 195 | canvas.scale(-1.0f, 1.0f); |
| 196 | } |
Amin Shaikh | 0ad7e51 | 2019-04-29 07:36:34 -0400 | [diff] [blame] | 197 | super.draw(canvas); |
| 198 | mCutoutPath.reset(); |
| 199 | mCutoutPath.setFillType(FillType.WINDING); |
Evan Laird | ea77e6e | 2017-06-21 18:24:36 -0400 | [diff] [blame] | 200 | |
Evan Laird | ea77e6e | 2017-06-21 18:24:36 -0400 | [diff] [blame] | 201 | final float padding = Math.round(PAD * width); |
Evan Laird | ada8432 | 2017-06-01 14:47:27 -0400 | [diff] [blame] | 202 | |
Amin Shaikh | 0ad7e51 | 2019-04-29 07:36:34 -0400 | [diff] [blame] | 203 | if (isInState(STATE_CARRIER_CHANGE)) { |
Jason Monk | 48edc0c | 2017-04-10 15:01:27 -0400 | [diff] [blame] | 204 | float dotSize = (DOT_SIZE * height); |
| 205 | float dotPadding = (DOT_PADDING * height); |
Amin Shaikh | 0ad7e51 | 2019-04-29 07:36:34 -0400 | [diff] [blame] | 206 | float dotSpacing = dotPadding + dotSize; |
Jason Monk | 48edc0c | 2017-04-10 15:01:27 -0400 | [diff] [blame] | 207 | float x = width - padding - dotSize; |
| 208 | float y = height - padding - dotSize; |
| 209 | mForegroundPath.reset(); |
Amin Shaikh | 0ad7e51 | 2019-04-29 07:36:34 -0400 | [diff] [blame] | 210 | drawDotAndPadding(x, y, dotPadding, dotSize, 2); |
| 211 | drawDotAndPadding(x - dotSpacing, y, dotPadding, dotSize, 1); |
| 212 | drawDotAndPadding(x - dotSpacing * 2, y, dotPadding, dotSize, 0); |
| 213 | canvas.drawPath(mCutoutPath, mTransparentPaint); |
| 214 | canvas.drawPath(mForegroundPath, mForegroundPaint); |
| 215 | } else if (isInState(STATE_CUT)) { |
Amin Shaikh | 408d2d3 | 2019-06-04 10:28:02 -0400 | [diff] [blame] | 216 | float cutX = (mCutoutWidthFraction * width / VIEWPORT); |
| 217 | float cutY = (mCutoutHeightFraction * height / VIEWPORT); |
| 218 | mCutoutPath.moveTo(width, height); |
| 219 | mCutoutPath.rLineTo(-cutX, 0); |
| 220 | mCutoutPath.rLineTo(0, -cutY); |
| 221 | mCutoutPath.rLineTo(cutX, 0); |
| 222 | mCutoutPath.rLineTo(0, cutY); |
Amin Shaikh | 0ad7e51 | 2019-04-29 07:36:34 -0400 | [diff] [blame] | 223 | canvas.drawPath(mCutoutPath, mTransparentPaint); |
Amin Shaikh | 408d2d3 | 2019-06-04 10:28:02 -0400 | [diff] [blame] | 224 | canvas.drawPath(mScaledXPath, mForegroundPaint); |
Jason Monk | 48edc0c | 2017-04-10 15:01:27 -0400 | [diff] [blame] | 225 | } |
Jason Monk | 54b610f | 2017-05-25 16:11:52 -0400 | [diff] [blame] | 226 | if (isRtl) { |
| 227 | canvas.restore(); |
| 228 | } |
Amin Shaikh | 0ad7e51 | 2019-04-29 07:36:34 -0400 | [diff] [blame] | 229 | canvas.restore(); |
Jason Monk | 48edc0c | 2017-04-10 15:01:27 -0400 | [diff] [blame] | 230 | } |
| 231 | |
Amin Shaikh | 0ad7e51 | 2019-04-29 07:36:34 -0400 | [diff] [blame] | 232 | private void drawDotAndPadding(float x, float y, |
| 233 | float dotPadding, float dotSize, int i) { |
| 234 | if (i == mCurrentDot) { |
| 235 | // Draw dot |
| 236 | mForegroundPath.addRect(x, y, x + dotSize, y + dotSize, Direction.CW); |
| 237 | // Draw dot padding |
| 238 | mCutoutPath.addRect(x - dotPadding, y - dotPadding, x + dotSize + dotPadding, |
| 239 | y + dotSize + dotPadding, Direction.CW); |
Jason Monk | 48edc0c | 2017-04-10 15:01:27 -0400 | [diff] [blame] | 240 | } |
Jason Monk | 48edc0c | 2017-04-10 15:01:27 -0400 | [diff] [blame] | 241 | } |
| 242 | |
| 243 | @Override |
| 244 | public void setAlpha(@IntRange(from = 0, to = 255) int alpha) { |
Amin Shaikh | 0ad7e51 | 2019-04-29 07:36:34 -0400 | [diff] [blame] | 245 | super.setAlpha(alpha); |
Jason Monk | 48edc0c | 2017-04-10 15:01:27 -0400 | [diff] [blame] | 246 | mForegroundPaint.setAlpha(alpha); |
| 247 | } |
| 248 | |
| 249 | @Override |
| 250 | public void setColorFilter(@Nullable ColorFilter colorFilter) { |
Amin Shaikh | 0ad7e51 | 2019-04-29 07:36:34 -0400 | [diff] [blame] | 251 | super.setColorFilter(colorFilter); |
Jason Monk | 48edc0c | 2017-04-10 15:01:27 -0400 | [diff] [blame] | 252 | mForegroundPaint.setColorFilter(colorFilter); |
| 253 | } |
| 254 | |
| 255 | @Override |
Jason Monk | 48edc0c | 2017-04-10 15:01:27 -0400 | [diff] [blame] | 256 | public boolean setVisible(boolean visible, boolean restart) { |
Amin Shaikh | 0ad7e51 | 2019-04-29 07:36:34 -0400 | [diff] [blame] | 257 | boolean changed = super.setVisible(visible, restart); |
Jason Monk | 48edc0c | 2017-04-10 15:01:27 -0400 | [diff] [blame] | 258 | updateAnimation(); |
Amin Shaikh | 0ad7e51 | 2019-04-29 07:36:34 -0400 | [diff] [blame] | 259 | return changed; |
Jason Monk | 48edc0c | 2017-04-10 15:01:27 -0400 | [diff] [blame] | 260 | } |
| 261 | |
| 262 | private final Runnable mChangeDot = new Runnable() { |
| 263 | @Override |
| 264 | public void run() { |
| 265 | if (++mCurrentDot == NUM_DOTS) { |
| 266 | mCurrentDot = 0; |
| 267 | } |
| 268 | invalidateSelf(); |
| 269 | mHandler.postDelayed(mChangeDot, DOT_DELAY); |
| 270 | } |
| 271 | }; |
| 272 | |
Amin Shaikh | 0ad7e51 | 2019-04-29 07:36:34 -0400 | [diff] [blame] | 273 | /** |
| 274 | * Returns whether this drawable is in the specified state. |
| 275 | * |
| 276 | * @param state must be one of {@link #STATE_CARRIER_CHANGE} or {@link #STATE_CUT} |
| 277 | */ |
| 278 | private boolean isInState(int state) { |
| 279 | return getState(getLevel()) == state; |
Jason Monk | 48edc0c | 2017-04-10 15:01:27 -0400 | [diff] [blame] | 280 | } |
| 281 | |
| 282 | public static int getState(int fullState) { |
| 283 | return (fullState & STATE_MASK) >> STATE_SHIFT; |
| 284 | } |
| 285 | |
Jason Monk | 48edc0c | 2017-04-10 15:01:27 -0400 | [diff] [blame] | 286 | public static int getState(int level, int numLevels, boolean cutOut) { |
| 287 | return ((cutOut ? STATE_CUT : 0) << STATE_SHIFT) |
| 288 | | (numLevels << NUM_LEVEL_SHIFT) |
| 289 | | level; |
| 290 | } |
| 291 | |
Amin Shaikh | 0ad7e51 | 2019-04-29 07:36:34 -0400 | [diff] [blame] | 292 | /** Returns the state representing empty mobile signal with the given number of levels. */ |
| 293 | public static int getEmptyState(int numLevels) { |
Amin Shaikh | 4f91308 | 2019-05-06 21:06:36 +0000 | [diff] [blame] | 294 | return getState(0, numLevels, true); |
Amin Shaikh | 0ad7e51 | 2019-04-29 07:36:34 -0400 | [diff] [blame] | 295 | } |
| 296 | |
| 297 | /** Returns the state representing carrier change with the given number of levels. */ |
Jason Monk | 48edc0c | 2017-04-10 15:01:27 -0400 | [diff] [blame] | 298 | public static int getCarrierChangeState(int numLevels) { |
| 299 | return (STATE_CARRIER_CHANGE << STATE_SHIFT) | (numLevels << NUM_LEVEL_SHIFT); |
| 300 | } |
Jason Monk | 48edc0c | 2017-04-10 15:01:27 -0400 | [diff] [blame] | 301 | } |