Merge "MISSION UPDATES -- ANDROID 14 -- PLATLOGO" into udc-dev

This commit is contained in:
Daniel Sandler
2023-06-11 01:09:04 +00:00
committed by Android (Google) Code Review
2 changed files with 489 additions and 316 deletions

View File

@@ -16,42 +16,50 @@
package com.android.internal.app; package com.android.internal.app;
import static android.graphics.PixelFormat.TRANSLUCENT; import static android.os.VibrationEffect.Composition.PRIMITIVE_SPIN;
import android.animation.ObjectAnimator; import android.animation.ObjectAnimator;
import android.animation.TimeAnimator;
import android.annotation.SuppressLint;
import android.app.ActionBar; import android.app.ActionBar;
import android.app.Activity; import android.app.Activity;
import android.content.ActivityNotFoundException; import android.content.ActivityNotFoundException;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter; import android.graphics.ColorFilter;
import android.graphics.Paint; import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Rect; import android.graphics.Rect;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.os.Bundle; import android.os.Bundle;
import android.os.CombinedVibration;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.os.VibrationEffect;
import android.os.VibratorManager;
import android.provider.Settings; import android.provider.Settings;
import android.util.DisplayMetrics; import android.util.DisplayMetrics;
import android.util.Log; import android.util.Log;
import android.view.Gravity; import android.view.Gravity;
import android.view.HapticFeedbackConstants; import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View; import android.view.View;
import android.view.animation.DecelerateInterpolator; import android.view.WindowInsets;
import android.view.animation.OvershootInterpolator;
import android.widget.AnalogClock;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.ImageView; import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.internal.R; import com.android.internal.R;
import org.json.JSONObject; import org.json.JSONObject;
import java.time.Clock; import java.util.Random;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
/** /**
* @hide * @hide
@@ -59,30 +67,160 @@ import java.time.ZonedDateTime;
public class PlatLogoActivity extends Activity { public class PlatLogoActivity extends Activity {
private static final String TAG = "PlatLogoActivity"; private static final String TAG = "PlatLogoActivity";
private static final String S_EGG_UNLOCK_SETTING = "egg_mode_s"; private static final long LAUNCH_TIME = 5000L;
private static final String U_EGG_UNLOCK_SETTING = "egg_mode_u";
private static final float MIN_WARP = 1f;
private static final float MAX_WARP = 10f; // after all these years
private static final boolean FINISH_AFTER_NEXT_STAGE_LAUNCH = false;
private SettableAnalogClock mClock;
private ImageView mLogo; private ImageView mLogo;
private BubblesDrawable mBg; private Starfield mStarfield;
private FrameLayout mLayout;
private TimeAnimator mAnim;
private ObjectAnimator mWarpAnim;
private Random mRandom;
private float mDp;
private RumblePack mRumble;
private boolean mAnimationsEnabled = true;
private final View.OnTouchListener mTouchListener = new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
measureTouchPressure(event);
startWarp();
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
stopWarp();
break;
}
return true;
}
};
private final Runnable mLaunchNextStage = () -> {
stopWarp();
launchNextStage(false);
};
private final TimeAnimator.TimeListener mTimeListener = new TimeAnimator.TimeListener() {
@Override
public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) {
mStarfield.update(deltaTime);
final float warpFrac = (mStarfield.getWarp() - MIN_WARP) / (MAX_WARP - MIN_WARP);
if (mAnimationsEnabled) {
mLogo.setTranslationX(mRandom.nextFloat() * warpFrac * 5 * mDp);
mLogo.setTranslationY(mRandom.nextFloat() * warpFrac * 5 * mDp);
}
if (warpFrac > 0f) {
mRumble.rumble(warpFrac);
}
mLayout.postInvalidate();
}
};
private class RumblePack implements Handler.Callback {
private static final int MSG = 6464;
private static final int INTERVAL = 50;
private final VibratorManager mVibeMan;
private final HandlerThread mVibeThread;
private final Handler mVibeHandler;
private boolean mSpinPrimitiveSupported;
private long mLastVibe = 0;
@SuppressLint("MissingPermission")
@Override
public boolean handleMessage(Message msg) {
final float warpFrac = msg.arg1 / 100f;
if (mSpinPrimitiveSupported) {
if (msg.getWhen() > mLastVibe + INTERVAL) {
mLastVibe = msg.getWhen();
mVibeMan.vibrate(CombinedVibration.createParallel(
VibrationEffect.startComposition()
.addPrimitive(PRIMITIVE_SPIN, (float) Math.pow(warpFrac, 3.0))
.compose()
));
}
} else {
if (mRandom.nextFloat() < warpFrac) {
mLogo.performHapticFeedback(HapticFeedbackConstants.CLOCK_TICK);
}
}
return false;
}
RumblePack() {
mVibeMan = getSystemService(VibratorManager.class);
mSpinPrimitiveSupported = mVibeMan.getDefaultVibrator()
.areAllPrimitivesSupported(PRIMITIVE_SPIN);
mVibeThread = new HandlerThread("VibratorThread");
mVibeThread.start();
mVibeHandler = Handler.createAsync(mVibeThread.getLooper(), this);
}
public void destroy() {
mVibeThread.quit();
}
private void rumble(float warpFrac) {
if (!mVibeThread.isAlive()) return;
final Message msg = Message.obtain();
msg.what = MSG;
msg.arg1 = (int) (warpFrac * 100);
mVibeHandler.removeMessages(MSG);
mVibeHandler.sendMessage(msg);
}
}
@Override @Override
protected void onPause() { protected void onDestroy() {
super.onPause(); mRumble.destroy();
super.onDestroy();
} }
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
getWindow().setDecorFitsSystemWindows(false);
getWindow().setNavigationBarColor(0); getWindow().setNavigationBarColor(0);
getWindow().setStatusBarColor(0); getWindow().setStatusBarColor(0);
getWindow().getDecorView().getWindowInsetsController().hide(WindowInsets.Type.systemBars());
final ActionBar ab = getActionBar(); final ActionBar ab = getActionBar();
if (ab != null) ab.hide(); if (ab != null) ab.hide();
final FrameLayout layout = new FrameLayout(this); try {
mAnimationsEnabled = Settings.Global.getFloat(getContentResolver(),
Settings.Global.ANIMATOR_DURATION_SCALE) > 0f;
} catch (Settings.SettingNotFoundException e) {
mAnimationsEnabled = true;
}
mClock = new SettableAnalogClock(this); mRumble = new RumblePack();
mLayout = new FrameLayout(this);
mRandom = new Random();
mDp = getResources().getDisplayMetrics().density;
mStarfield = new Starfield(mRandom, mDp * 2f);
mStarfield.setVelocity(
200f * (mRandom.nextFloat() - 0.5f),
200f * (mRandom.nextFloat() - 0.5f));
mLayout.setBackground(mStarfield);
final DisplayMetrics dm = getResources().getDisplayMetrics(); final DisplayMetrics dm = getResources().getDisplayMetrics();
final float dp = dm.density; final float dp = dm.density;
@@ -90,22 +228,79 @@ public class PlatLogoActivity extends Activity {
final int widgetSize = (int) (minSide * 0.75); final int widgetSize = (int) (minSide * 0.75);
final FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(widgetSize, widgetSize); final FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(widgetSize, widgetSize);
lp.gravity = Gravity.CENTER; lp.gravity = Gravity.CENTER;
layout.addView(mClock, lp);
mLogo = new ImageView(this); mLogo = new ImageView(this);
mLogo.setVisibility(View.GONE);
mLogo.setImageResource(R.drawable.platlogo); mLogo.setImageResource(R.drawable.platlogo);
layout.addView(mLogo, lp); mLogo.setOnTouchListener(mTouchListener);
mLogo.requestFocus();
mLayout.addView(mLogo, lp);
mBg = new BubblesDrawable(); Log.v(TAG, "Hello");
mBg.setLevel(0);
mBg.avoid = widgetSize / 2;
mBg.padding = 0.5f * dp;
mBg.minR = 1 * dp;
layout.setBackground(mBg);
layout.setOnLongClickListener(mBg);
setContentView(layout); setContentView(mLayout);
}
private void startAnimating() {
mAnim = new TimeAnimator();
mAnim.setTimeListener(mTimeListener);
mAnim.start();
}
private void stopAnimating() {
mAnim.cancel();
mAnim = null;
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_SPACE) {
if (event.getRepeatCount() == 0) {
startWarp();
}
return true;
}
return false;
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_SPACE) {
stopWarp();
return true;
}
return false;
}
private void startWarp() {
stopWarp();
mWarpAnim = ObjectAnimator.ofFloat(mStarfield, "warp", MIN_WARP, MAX_WARP)
.setDuration(LAUNCH_TIME);
mWarpAnim.start();
mLogo.postDelayed(mLaunchNextStage, LAUNCH_TIME + 1000L);
}
private void stopWarp() {
if (mWarpAnim != null) {
mWarpAnim.cancel();
mWarpAnim.removeAllListeners();
mWarpAnim = null;
}
mStarfield.setWarp(1f);
mLogo.removeCallbacks(mLaunchNextStage);
}
@Override
public void onResume() {
super.onResume();
startAnimating();
}
@Override
public void onPause() {
stopWarp();
stopAnimating();
super.onPause();
} }
private boolean shouldWriteSettings() { private boolean shouldWriteSettings() {
@@ -113,38 +308,14 @@ public class PlatLogoActivity extends Activity {
} }
private void launchNextStage(boolean locked) { private void launchNextStage(boolean locked) {
mClock.animate()
.alpha(0f).scaleX(0.5f).scaleY(0.5f)
.withEndAction(() -> mClock.setVisibility(View.GONE))
.start();
mLogo.setAlpha(0f);
mLogo.setScaleX(0.5f);
mLogo.setScaleY(0.5f);
mLogo.setVisibility(View.VISIBLE);
mLogo.animate()
.alpha(1f)
.scaleX(1f)
.scaleY(1f)
.setInterpolator(new OvershootInterpolator())
.start();
mLogo.postDelayed(() -> {
final ObjectAnimator anim = ObjectAnimator.ofInt(mBg, "level", 0, 10000);
anim.setInterpolator(new DecelerateInterpolator(1f));
anim.start();
},
500
);
final ContentResolver cr = getContentResolver(); final ContentResolver cr = getContentResolver();
try { try {
if (shouldWriteSettings()) { if (shouldWriteSettings()) {
Log.v(TAG, "Saving egg unlock=" + locked); Log.v(TAG, "Saving egg locked=" + locked);
syncTouchPressure(); syncTouchPressure();
Settings.System.putLong(cr, Settings.System.putLong(cr,
S_EGG_UNLOCK_SETTING, U_EGG_UNLOCK_SETTING,
locked ? 0 : System.currentTimeMillis()); locked ? 0 : System.currentTimeMillis());
} }
} catch (RuntimeException e) { } catch (RuntimeException e) {
@@ -152,14 +323,18 @@ public class PlatLogoActivity extends Activity {
} }
try { try {
startActivity(new Intent(Intent.ACTION_MAIN) final Intent eggActivity = new Intent(Intent.ACTION_MAIN)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_CLEAR_TASK) | Intent.FLAG_ACTIVITY_CLEAR_TASK)
.addCategory("com.android.internal.category.PLATLOGO")); .addCategory("com.android.internal.category.PLATLOGO");
Log.v(TAG, "launching: " + eggActivity);
startActivity(eggActivity);
} catch (ActivityNotFoundException ex) { } catch (ActivityNotFoundException ex) {
Log.e("com.android.internal.app.PlatLogoActivity", "No more eggs."); Log.e("com.android.internal.app.PlatLogoActivity", "No more eggs.");
} }
//finish(); // no longer finish upon unlock; it's fun to frob the dial if (FINISH_AFTER_NEXT_STAGE_LAUNCH) {
finish(); // we're done here.
}
} }
static final String TOUCH_STATS = "touch.stats"; static final String TOUCH_STATS = "touch.stats";
@@ -217,266 +392,111 @@ public class PlatLogoActivity extends Activity {
super.onStop(); super.onStop();
} }
/** private static class Starfield extends Drawable {
* Subclass of AnalogClock that allows the user to flip up the glass and adjust the hands. private static final int NUM_STARS = 34; // Build.VERSION_CODES.UPSIDE_DOWN_CAKE
*/
public class SettableAnalogClock extends AnalogClock {
private int mOverrideHour = -1;
private int mOverrideMinute = -1;
private boolean mOverride = false;
public SettableAnalogClock(Context context) { private static final int NUM_PLANES = 2;
super(context); private final float[] mStars = new float[NUM_STARS * 4];
private float mVx, mVy;
private long mDt = 0;
private final Paint mStarPaint;
private final Random mRng;
private final float mSize;
private final Rect mSpace = new Rect();
private float mWarp = 1f;
private float mBuffer;
public void setWarp(float warp) {
mWarp = warp;
}
public float getWarp() {
return mWarp;
}
Starfield(Random rng, float size) {
mRng = rng;
mSize = size;
mStarPaint = new Paint();
mStarPaint.setStyle(Paint.Style.STROKE);
mStarPaint.setColor(Color.WHITE);
} }
@Override @Override
protected Instant now() { public void onBoundsChange(Rect bounds) {
final Instant realNow = super.now(); mSpace.set(bounds);
final ZoneId tz = Clock.systemDefaultZone().getZone(); mBuffer = mSize * NUM_PLANES * 2 * MAX_WARP;
final ZonedDateTime zdTime = realNow.atZone(tz); mSpace.inset(-(int) mBuffer, -(int) mBuffer);
if (mOverride) { final float w = mSpace.width();
if (mOverrideHour < 0) { final float h = mSpace.height();
mOverrideHour = zdTime.getHour(); for (int i = 0; i < NUM_STARS; i++) {
} mStars[4 * i] = mRng.nextFloat() * w;
return Clock.fixed(zdTime mStars[4 * i + 1] = mRng.nextFloat() * h;
.withHour(mOverrideHour) mStars[4 * i + 2] = mStars[4 * i];
.withMinute(mOverrideMinute) mStars[4 * i + 3] = mStars[4 * i + 1];
.withSecond(0)
.toInstant(), tz).instant();
} else {
return realNow;
} }
} }
double toPositiveDegrees(double rad) { public void setVelocity(float x, float y) {
return (Math.toDegrees(rad) + 360 - 90) % 360; mVx = x;
mVy = y;
} }
@Override @Override
public boolean onTouchEvent(MotionEvent ev) { public void draw(@NonNull Canvas canvas) {
switch (ev.getActionMasked()) { final float dtSec = mDt / 1000f;
case MotionEvent.ACTION_DOWN: final float dx = (mVx * dtSec * mWarp);
mOverride = true; final float dy = (mVy * dtSec * mWarp);
// pass through
case MotionEvent.ACTION_MOVE:
measureTouchPressure(ev);
float x = ev.getX(); final boolean inWarp = mWarp > 1f;
float y = ev.getY();
float cx = getWidth() / 2f;
float cy = getHeight() / 2f;
float angle = (float) toPositiveDegrees(Math.atan2(x - cx, y - cy));
int minutes = (75 - (int) (angle / 6)) % 60; canvas.drawColor(Color.BLACK); // 0xFF16161D);
int minuteDelta = minutes - mOverrideMinute;
if (minuteDelta != 0) {
if (Math.abs(minuteDelta) > 45 && mOverrideHour >= 0) {
int hourDelta = (minuteDelta < 0) ? 1 : -1;
mOverrideHour = (mOverrideHour + 24 + hourDelta) % 24;
}
mOverrideMinute = minutes;
if (mOverrideMinute == 0) {
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
if (getScaleX() == 1f) {
setScaleX(1.05f);
setScaleY(1.05f);
animate().scaleX(1f).scaleY(1f).setDuration(150).start();
}
} else {
performHapticFeedback(HapticFeedbackConstants.CLOCK_TICK);
}
onTimeChanged(); if (mDt > 0 && mDt < 1000) {
postInvalidate(); canvas.translate(
} -(mBuffer) + mRng.nextFloat() * (mWarp - 1f),
-(mBuffer) + mRng.nextFloat() * (mWarp - 1f)
return true; );
case MotionEvent.ACTION_UP: final float w = mSpace.width();
if (mOverrideMinute == 0 && (mOverrideHour % 12) == 1) { final float h = mSpace.height();
Log.v(TAG, "13:00"); for (int i = 0; i < NUM_STARS; i++) {
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); final int plane = (int) ((((float) i) / NUM_STARS) * NUM_PLANES) + 1;
launchNextStage(false); mStars[4 * i + 2] = (mStars[4 * i + 2] + dx * plane + w) % w;
} mStars[4 * i + 3] = (mStars[4 * i + 3] + dy * plane + h) % h;
return true; mStars[4 * i + 0] = inWarp ? mStars[4 * i + 2] - dx * mWarp * 2 * plane : -100;
} mStars[4 * i + 1] = inWarp ? mStars[4 * i + 3] - dy * mWarp * 2 * plane : -100;
return false;
}
}
private static final String[][] EMOJI_SETS = {
{"🍇", "🍈", "🍉", "🍊", "🍋", "🍌", "🍍", "🥭", "🍎", "🍏", "🍐", "🍑",
"🍒", "🍓", "🫐", "🥝"},
{"😺", "😸", "😹", "😻", "😼", "😽", "🙀", "😿", "😾"},
{"😀", "😃", "😄", "😁", "😆", "😅", "🤣", "😂", "🙂", "🙃", "🫠", "😉", "😊",
"😇", "🥰", "😍", "🤩", "😘", "😗", "☺️", "😚", "😙", "🥲", "😋", "😛", "😜",
"🤪", "😝", "🤑", "🤗", "🤭", "🫢", "🫣", "🤫", "🤔", "🫡", "🤐", "🤨", "😐",
"😑", "😶", "🫥", "😏", "😒", "🙄", "😬", "🤥", "😌", "😔", "😪", "🤤", "😴",
"😷"},
{ "🤩", "😍", "🥰", "😘", "🥳", "🥲", "🥹" },
{ "🫠" },
{"💘", "💝", "💖", "💗", "💓", "💞", "💕", "❣", "💔", "❤", "🧡", "💛",
"💚", "💙", "💜", "🤎", "🖤", "🤍"},
// {"👁", "️🫦", "👁️"}, // this one is too much
{"👽", "🛸", "✨", "🌟", "💫", "🚀", "🪐", "🌙", "⭐", "🌍"},
{"🌑", "🌒", "🌓", "🌔", "🌕", "🌖", "🌗", "🌘"},
{"🐙", "🪸", "🦑", "🦀", "🦐", "🐡", "🦞", "🐠", "🐟", "🐳", "🐋", "🐬", "🫧", "🌊",
"🦈"},
{"🙈", "🙉", "🙊", "🐵", "🐒"},
{"♈", "♉", "♊", "♋", "♌", "♍", "♎", "♏", "♐", "♑", "♒", "♓"},
{"🕛", "🕧", "🕐", "🕜", "🕑", "🕝", "🕒", "🕞", "🕓", "🕟", "🕔", "🕠", "🕕", "🕡",
"🕖", "🕢", "🕗", "🕣", "🕘", "🕤", "🕙", "🕥", "🕚", "🕦"},
{"🌺", "🌸", "💮", "🏵️", "🌼", "🌿"},
{"🐢", "✨", "🌟", "👑"}
};
static class Bubble {
public float x, y, r;
public int color;
public String text = null;
}
class BubblesDrawable extends Drawable implements View.OnLongClickListener {
private static final int MAX_BUBBS = 2000;
private final int[] mColorIds = {
android.R.color.system_accent3_400,
android.R.color.system_accent3_500,
android.R.color.system_accent3_600,
android.R.color.system_accent2_400,
android.R.color.system_accent2_500,
android.R.color.system_accent2_600,
};
private int[] mColors = new int[mColorIds.length];
private int mEmojiSet = -1;
private final Bubble[] mBubbs = new Bubble[MAX_BUBBS];
private int mNumBubbs;
private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
public float avoid = 0f;
public float padding = 0f;
public float minR = 0f;
BubblesDrawable() {
for (int i = 0; i < mColorIds.length; i++) {
mColors[i] = getColor(mColorIds[i]);
}
for (int j = 0; j < mBubbs.length; j++) {
mBubbs[j] = new Bubble();
}
}
@Override
public void draw(Canvas canvas) {
if (getLevel() == 0) return;
final float f = getLevel() / 10000f;
mPaint.setStyle(Paint.Style.FILL);
mPaint.setTextAlign(Paint.Align.CENTER);
int drawn = 0;
for (int j = 0; j < mNumBubbs; j++) {
if (mBubbs[j].color == 0 || mBubbs[j].r == 0) continue;
if (mBubbs[j].text != null) {
mPaint.setTextSize(mBubbs[j].r * 1.75f);
canvas.drawText(mBubbs[j].text, mBubbs[j].x,
mBubbs[j].y + mBubbs[j].r * f * 0.6f, mPaint);
} else {
mPaint.setColor(mBubbs[j].color);
canvas.drawCircle(mBubbs[j].x, mBubbs[j].y, mBubbs[j].r * f, mPaint);
}
drawn++;
}
}
public void chooseEmojiSet() {
mEmojiSet = (int) (Math.random() * EMOJI_SETS.length);
final String[] emojiSet = EMOJI_SETS[mEmojiSet];
for (int j = 0; j < mBubbs.length; j++) {
mBubbs[j].text = emojiSet[(int) (Math.random() * emojiSet.length)];
}
invalidateSelf();
}
@Override
protected boolean onLevelChange(int level) {
invalidateSelf();
return true;
}
@Override
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
randomize();
}
private void randomize() {
final float w = getBounds().width();
final float h = getBounds().height();
final float maxR = Math.min(w, h) / 3f;
mNumBubbs = 0;
if (avoid > 0f) {
mBubbs[mNumBubbs].x = w / 2f;
mBubbs[mNumBubbs].y = h / 2f;
mBubbs[mNumBubbs].r = avoid;
mBubbs[mNumBubbs].color = 0;
mNumBubbs++;
}
for (int j = 0; j < MAX_BUBBS; j++) {
// a simple but time-tested bubble-packing algorithm:
// 1. pick a spot
// 2. shrink the bubble until it is no longer overlapping any other bubble
// 3. if the bubble hasn't popped, keep it
int tries = 5;
while (tries-- > 0) {
float x = (float) Math.random() * w;
float y = (float) Math.random() * h;
float r = Math.min(Math.min(x, w - x), Math.min(y, h - y));
// shrink radius to fit other bubbs
for (int i = 0; i < mNumBubbs; i++) {
r = (float) Math.min(r,
Math.hypot(x - mBubbs[i].x, y - mBubbs[i].y) - mBubbs[i].r
- padding);
if (r < minR) break;
}
if (r >= minR) {
// we have found a spot for this bubble to live, let's save it and move on
r = Math.min(maxR, r);
mBubbs[mNumBubbs].x = x;
mBubbs[mNumBubbs].y = y;
mBubbs[mNumBubbs].r = r;
mBubbs[mNumBubbs].color = mColors[(int) (Math.random() * mColors.length)];
mNumBubbs++;
break;
}
} }
} }
Log.v(TAG, String.format("successfully placed %d bubbles (%d%%)", final int slice = (mStars.length / NUM_PLANES / 4) * 4;
mNumBubbs, (int) (100f * mNumBubbs / MAX_BUBBS))); for (int p = 0; p < NUM_PLANES; p++) {
mStarPaint.setStrokeWidth(mSize * (p + 1));
if (inWarp) {
canvas.drawLines(mStars, p * slice, slice, mStarPaint);
}
canvas.drawPoints(mStars, p * slice, slice, mStarPaint);
}
} }
@Override @Override
public void setAlpha(int alpha) { } public void setAlpha(int alpha) {
}
@Override @Override
public void setColorFilter(ColorFilter colorFilter) { } public void setColorFilter(@Nullable ColorFilter colorFilter) {
}
@Override @Override
public int getOpacity() { public int getOpacity() {
return TRANSLUCENT; return PixelFormat.OPAQUE;
} }
@Override public void update(long dt) {
public boolean onLongClick(View v) { mDt = dt;
if (getLevel() == 0) return false;
chooseEmojiSet();
return true;
} }
} }
} }

View File

@@ -13,33 +13,186 @@ Copyright (C) 2021 The Android Open Source Project
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<vector android:height="128dp" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="128dp" xmlns:aapt="http://schemas.android.com/aapt"
android:viewportHeight="24" android:width="512dp"
android:viewportWidth="24" android:height="512dp"
xmlns:android="http://schemas.android.com/apk/res/android"> android:viewportWidth="512"
android:viewportHeight="512">
<path
android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0">
<aapt:attr name="android:fillColor">
<gradient
android:startX="256"
android:startY="21.81"
android:endX="256"
android:endY="350.42"
android:type="linear">
<item android:offset="0" android:color="#FF073042"/>
<item android:offset="1" android:color="#FF073042"/>
</gradient>
</aapt:attr>
</path>
<group>
<clip-path
android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/>
<path <path
android:fillColor="@android:color/system_accent1_400" android:pathData="m195.27,187.64l2.25,-6.69c13.91,78.13 50.84,284.39 50.84,50.33 0,-0.97 0.72,-1.81 1.62,-1.81h12.69c0.9,0 1.62,0.83 1.62,1.8 -0.2,409.91 -69.03,-43.64 -69.03,-43.64Z"
android:pathData="M11,0.3c0.6,-0.3 1.4,-0.3 2,0l0.6,0.4c0.7,0.4 1.4,0.6 2.2,0.6l0.7,-0.1c0.7,0 1.4,0.3 1.8,0.9l0.3,0.6c0.4,0.7 1,1.2 1.7,1.5L21,4.5c0.7,0.3 1.1,0.9 1.2,1.7v0.7C22.2,7.7 22.5,8.4 23,9l0.4,0.5c0.4,0.6 0.5,1.3 0.2,2l-0.3,0.6c-0.3,0.7 -0.4,1.5 -0.3,2.3l0.1,0.7c0.1,0.7 -0.2,1.4 -0.7,1.9L22,17.5c-0.6,0.5 -1.1,1.1 -1.3,1.9L20.5,20c-0.2,0.7 -0.8,1.2 -1.5,1.4l-0.7,0.1c-0.8,0.2 -1.4,0.5 -2,1.1l-0.5,0.5c-0.5,0.5 -1.3,0.7 -2,0.5l-0.6,-0.2c-0.8,-0.2 -1.5,-0.2 -2.3,0l-0.6,0.2c-0.7,0.2 -1.5,0 -2,-0.5l-0.5,-0.5c-0.5,-0.5 -1.2,-0.9 -2,-1.1L5,21.4c-0.7,-0.2 -1.3,-0.7 -1.5,-1.4l-0.2,-0.7C3.1,18.6 2.6,18 2,17.5l-0.6,-0.4c-0.6,-0.5 -0.8,-1.2 -0.7,-1.9l0.1,-0.7c0.1,-0.8 0,-1.6 -0.3,-2.3l-0.3,-0.6c-0.3,-0.7 -0.2,-1.4 0.2,-2L1,9c0.5,-0.6 0.7,-1.4 0.8,-2.2V6.2C1.9,5.5 2.3,4.8 3,4.5l0.6,-0.3c0.7,-0.3 1.3,-0.9 1.7,-1.5l0.3,-0.6c0.4,-0.6 1.1,-1 1.8,-0.9l0.7,0.1c0.8,0 1.6,-0.2 2.2,-0.6L11,0.3z" android:fillColor="#3ddc84"/>
/> </group>
<group>
<clip-path
android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/>
<path <path
android:pathData="M6.3,6.5l3,0l0,12.2" android:pathData="m158.77,180.68l-33.17,57.45c-1.9,3.3 -0.77,7.52 2.53,9.42 3.3,1.9 7.52,0.77 9.42,-2.53l33.59,-58.17c54.27,24.33 116.34,24.33 170.61,0l33.59,58.17c1.97,3.26 6.21,4.3 9.47,2.33 3.17,-1.91 4.26,-5.99 2.47,-9.23l-33.16,-57.45c56.95,-30.97 95.91,-88.64 101.61,-156.76H57.17c5.7,68.12 44.65,125.79 101.61,156.76Z"
android:strokeWidth="2.22" android:fillColor="#fff"/>
android:strokeColor="@android:color/system_accent3_800" </group>
/> <group>
<clip-path
android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/>
<path <path
android:pathData="M12.3,6.5h4l-2,4c2.2,0.3 3.6,2.4 3.3,4.5c-0.3,1.9 -1.9,3.3 -3.8,3.3c-0.5,0 -1,-0.1 -1.4,-0.3" android:pathData="M250.26,187.66L262.17,187.66A2.13,2.13 0,0 1,264.3 189.78L264.3,217.85A2.13,2.13 0,0 1,262.17 219.98L250.26,219.98A2.13,2.13 0,0 1,248.14 217.85L248.14,189.78A2.13,2.13 0,0 1,250.26 187.66z"
android:strokeWidth="2.22" android:fillColor="#3ddc84"/>
android:strokeColor="@android:color/system_accent3_800" </group>
/> <group>
<clip-path
android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/>
<path <path
android:pathData="M6.3,6.5l3,0l0,12.2" android:pathData="M250.12,170.29L262.32,170.29A1.98,1.98 0,0 1,264.3 172.26L264.3,176.39A1.98,1.98 0,0 1,262.32 178.37L250.12,178.37A1.98,1.98 0,0 1,248.14 176.39L248.14,172.26A1.98,1.98 0,0 1,250.12 170.29z"
android:strokeWidth="0.56" android:fillColor="#3ddc84"/>
android:strokeColor="@android:color/system_neutral1_100" </group>
/> <group>
<clip-path
android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/>
<path <path
android:pathData="M12.3,6.5h4l-2,4c2.2,0.3 3.6,2.4 3.3,4.5c-0.3,1.9 -1.9,3.3 -3.8,3.3c-0.5,0 -1,-0.1 -1.4,-0.3" android:pathData="M171.92,216.82h4.04v4.04h-4.04z"
android:strokeWidth="0.56" android:fillColor="#fff"/>
android:strokeColor="@android:color/system_neutral1_100" </group>
/> <group>
<clip-path
android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/>
<path
android:pathData="M188.8,275.73h4.04v4.04h-4.04z"
android:fillColor="#fff"/>
</group>
<group>
<clip-path
android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/>
<path
android:pathData="M369.04,337.63h4.04v4.04h-4.04z"
android:fillColor="#fff"/>
</group>
<group>
<clip-path
android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/>
<path
android:pathData="M285.93,252.22h4.04v4.04h-4.04z"
android:fillColor="#fff"/>
</group>
<group>
<clip-path
android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/>
<path
android:pathData="M318.96,218.84h4.04v4.04h-4.04z"
android:fillColor="#fff"/>
</group>
<group>
<clip-path
android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/>
<path
android:pathData="M294.05,288.55h4.04v4.04h-4.04z"
android:fillColor="#fff"/>
</group>
<group>
<clip-path
android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/>
<path
android:pathData="M330.82,273.31h8.08v8.08h-8.08z"
android:fillColor="#fff"/>
</group>
<group>
<clip-path
android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/>
<path
android:pathData="M188.8,298.95h4.04v4.04h-4.04z"
android:fillColor="#fff"/>
</group>
<group>
<clip-path
android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/>
<path
android:pathData="M220.14,238.94h8.08v8.08h-8.08z"
android:fillColor="#fff"/>
</group>
<group>
<clip-path
android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/>
<path
android:pathData="M272.1,318.9h8.08v8.08h-8.08z"
android:fillColor="#fff"/>
</group>
<group>
<clip-path
android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/>
<path
android:pathData="M293.34,349.25h8.08v8.08h-8.08z"
android:fillColor="#fff"/>
</group>
<group>
<clip-path
android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/>
<path
android:pathData="M161.05,254.24h8.08v8.08h-8.08z"
android:fillColor="#fff"/>
</group>
<group>
<clip-path
android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/>
<path
android:pathData="M378.92,192h8.08v8.08h-8.08z"
android:fillColor="#fff"/>
</group>
<group>
<clip-path
android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/>
<path
android:pathData="M137.87,323.7h8.08v8.08h-8.08z"
android:fillColor="#fff"/>
</group>
<path
android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"
android:strokeWidth="56.561"
android:fillColor="#00000000"
android:strokeColor="#f86734"/>
<path
android:pathData="m256.22,126.57c-6.69,0 -12.12,5.27 -12.12,11.77v14.52c0,1.25 1.02,2.27 2.27,2.27h0c1.25,0 2.27,-1.02 2.27,-2.27v-3.91c0,-2.51 2.04,-4.55 4.55,-4.55h6.06c2.51,0 4.55,2.04 4.55,4.55v3.91c0,1.25 1.02,2.27 2.27,2.27s2.27,-1.02 2.27,-2.27v-14.52c0,-6.5 -5.43,-11.77 -12.12,-11.77Z"
android:fillColor="#3ddc84"/>
<path
android:pathData="m93.34,116.36l3.85,-4.36 29.64,9.76 -4.44,5.03 -6.23,-2.1 -7.86,8.91 2.86,5.92 -4.43,5.03 -13.39,-28.18ZM110.43,122.76l-8.86,-3.02 4.11,8.41 4.76,-5.39Z"
android:fillColor="#fff"/>
<path
android:pathData="m153.62,100.85l-21.71,-6.2 10.38,14.38 -5.21,3.76 -16.78,-23.26 4.49,-3.24 21.65,6.19 -10.35,-14.35 5.24,-3.78 16.78,23.26 -4.49,3.24Z"
android:fillColor="#fff"/>
<path
android:pathData="m161.46,63.15l8.99,-3.84c7.43,-3.18 15.96,0.12 19.09,7.44 3.13,7.32 -0.38,15.76 -7.81,18.94l-8.99,3.84 -11.28,-26.38ZM179.41,80.26c4.46,-1.91 5.96,-6.72 4.15,-10.96 -1.81,-4.24 -6.33,-6.48 -10.79,-4.57l-3.08,1.32 6.64,15.53 3.08,-1.32Z"
android:fillColor="#fff"/>
<path
android:pathData="m204.23,47.57l11.1,-2.2c5.31,-1.05 9.47,2.08 10.4,6.76 0.72,3.65 -0.76,6.37 -4.07,8.34l12.4,10.44 -7.57,1.5 -11.65,-9.76 -1.03,0.2 2.3,11.61 -6.3,1.25 -5.57,-28.14ZM216.78,56.7c1.86,-0.37 3,-1.71 2.68,-3.33 -0.34,-1.7 -1.88,-2.43 -3.74,-2.06l-4.04,0.8 1.07,5.39 4.04,-0.8Z"
android:fillColor="#fff"/>
<path
android:pathData="m244.29,55.6c0.13,-8.16 6.86,-14.72 15.06,-14.58 8.16,0.13 14.72,6.9 14.58,15.06s-6.91,14.72 -15.06,14.58c-8.2,-0.13 -14.71,-6.9 -14.58,-15.06ZM267.44,55.98c0.08,-4.64 -3.54,-8.66 -8.18,-8.74 -4.68,-0.08 -8.42,3.82 -8.5,8.47 -0.08,4.65 3.54,8.66 8.22,8.74 4.64,0.08 8.39,-3.82 8.46,-8.47Z"
android:fillColor="#fff"/>
<path
android:pathData="m294.39,44.84l6.31,1.23 -5.49,28.16 -6.31,-1.23 5.49,-28.16Z"
android:fillColor="#fff"/>
<path
android:pathData="m321.94,51.41l9.14,3.48c7.55,2.88 11.39,11.17 8.56,18.61 -2.83,7.44 -11.22,11.07 -18.77,8.19l-9.14,-3.48 10.22,-26.8ZM322.96,76.19c4.53,1.73 8.95,-0.69 10.6,-5 1.64,-4.3 -0.05,-9.06 -4.58,-10.78l-3.13,-1.19 -6.01,15.78 3.13,1.19Z"
android:fillColor="#fff"/>
<path
android:pathData="m381.41,89.24l-4.21,-3.21 3.65,-4.78 9.06,6.91 -17.4,22.81 -4.85,-3.7 13.75,-18.02Z"
android:fillColor="#fff"/>
<path
android:pathData="m397.96,126.37l-9.56,-10.26 3.61,-3.36 22.8,-1.25 3.74,4.02 -12.35,11.51 2.51,2.69 -4.08,3.8 -2.51,-2.69 -4.55,4.24 -4.16,-4.46 4.55,-4.24ZM407.83,117.17l-10.28,0.58 4.49,4.82 5.79,-5.4Z"
android:fillColor="#fff"/>
</vector> </vector>