Loading core/java/com/android/internal/app/PlatLogoActivity.java +271 −221 Original line number Diff line number Diff line /* * Copyright (C) 2018 The Android Open Source Project * Copyright (C) 2020 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. Loading @@ -17,29 +17,32 @@ package com.android.internal.app; import android.animation.ObjectAnimator; import android.animation.TimeAnimator; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActionBar; import android.app.Activity; import android.content.ActivityNotFoundException; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.res.ColorStateList; import android.graphics.Bitmap; import android.graphics.BitmapShader; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ColorFilter; import android.graphics.Matrix; import android.graphics.LinearGradient; import android.graphics.Paint; import android.graphics.Path; import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.Shader; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.provider.Settings; import android.util.AttributeSet; import android.util.Log; import android.view.HapticFeedbackConstants; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.animation.PathInterpolator; import android.widget.FrameLayout; import android.widget.ImageView; import com.android.internal.R; Loading @@ -50,23 +53,12 @@ import org.json.JSONObject; * @hide */ public class PlatLogoActivity extends Activity { ImageView mZeroView, mOneView; BackslashDrawable mBackslash; int mClicks; private static final boolean WRITE_SETTINGS = true; static final Paint sPaint = new Paint(); static { sPaint.setStyle(Paint.Style.STROKE); sPaint.setStrokeWidth(4f); sPaint.setStrokeCap(Paint.Cap.SQUARE); } BigDialView mDialView; @Override protected void onPause() { if (mBackslash != null) { mBackslash.stopAnimating(); } mClicks = 0; super.onPause(); } Loading @@ -80,101 +72,33 @@ public class PlatLogoActivity extends Activity { getWindow().setNavigationBarColor(0); getWindow().setStatusBarColor(0); getActionBar().hide(); setContentView(R.layout.platlogo_layout); mBackslash = new BackslashDrawable((int) (50 * dp)); mOneView = findViewById(R.id.one); mOneView.setImageDrawable(new OneDrawable()); mZeroView = findViewById(R.id.zero); mZeroView.setImageDrawable(new ZeroDrawable()); final ViewGroup root = (ViewGroup) mOneView.getParent(); root.setClipChildren(false); root.setBackground(mBackslash); root.getBackground().setAlpha(0x20); View.OnTouchListener tl = new View.OnTouchListener() { float mOffsetX, mOffsetY; long mClickTime; ObjectAnimator mRotAnim; @Override public boolean onTouch(View v, MotionEvent event) { measureTouchPressure(event); switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: v.animate().scaleX(1.1f).scaleY(1.1f); v.getParent().bringChildToFront(v); mOffsetX = event.getRawX() - v.getX(); mOffsetY = event.getRawY() - v.getY(); long now = System.currentTimeMillis(); if (now - mClickTime < 350) { mRotAnim = ObjectAnimator.ofFloat(v, View.ROTATION, v.getRotation(), v.getRotation() + 3600); mRotAnim.setDuration(10000); mRotAnim.start(); mClickTime = 0; } else { mClickTime = now; } break; case MotionEvent.ACTION_MOVE: v.setX(event.getRawX() - mOffsetX); v.setY(event.getRawY() - mOffsetY); v.performHapticFeedback(HapticFeedbackConstants.TEXT_HANDLE_MOVE); break; case MotionEvent.ACTION_UP: v.performClick(); // fall through case MotionEvent.ACTION_CANCEL: v.animate().scaleX(1f).scaleY(1f); if (mRotAnim != null) mRotAnim.cancel(); testOverlap(); break; } return true; } }; final ActionBar ab = getActionBar(); if (ab != null) ab.hide(); findViewById(R.id.one).setOnTouchListener(tl); findViewById(R.id.zero).setOnTouchListener(tl); findViewById(R.id.text).setOnTouchListener(tl); mDialView = new BigDialView(this, null); if (Settings.System.getLong(getContentResolver(), "egg_mode" /* Settings.System.EGG_MODE */, 0) == 0) { mDialView.setUnlockTries(3); } private void testOverlap() { final float width = mZeroView.getWidth(); final float targetX = mZeroView.getX() + width * .2f; final float targetY = mZeroView.getY() + width * .3f; if (Math.hypot(targetX - mOneView.getX(), targetY - mOneView.getY()) < width * .2f && Math.abs(mOneView.getRotation() % 360 - 315) < 15) { mOneView.animate().x(mZeroView.getX() + width * .2f); mOneView.animate().y(mZeroView.getY() + width * .3f); mOneView.setRotation(mOneView.getRotation() % 360); mOneView.animate().rotation(315); mOneView.performHapticFeedback(HapticFeedbackConstants.CONFIRM); mBackslash.startAnimating(); mClicks++; if (mClicks >= 7) { launchNextStage(); } } else { mBackslash.stopAnimating(); } final FrameLayout layout = new FrameLayout(this); layout.setBackgroundColor(0xFFFF0000); layout.addView(mDialView, FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT); setContentView(layout); } private void launchNextStage() { private void launchNextStage(boolean locked) { final ContentResolver cr = getContentResolver(); if (Settings.System.getLong(cr, "egg_mode" /* Settings.System.EGG_MODE */, 0) == 0) { // For posterity: the moment this user unlocked the easter egg try { if (WRITE_SETTINGS) { Settings.System.putLong(cr, "egg_mode", // Settings.System.EGG_MODE, System.currentTimeMillis()); locked ? 0 : System.currentTimeMillis()); } } catch (RuntimeException e) { Log.e("com.android.internal.app.PlatLogoActivity", "Can't write settings", e); } Loading @@ -187,7 +111,7 @@ public class PlatLogoActivity extends Activity { } catch (ActivityNotFoundException ex) { Log.e("com.android.internal.app.PlatLogoActivity", "No more eggs."); } finish(); //finish(); // no longer finish upon unlock; it's fun to frob the dial } static final String TOUCH_STATS = "touch.stats"; Loading Loading @@ -223,7 +147,10 @@ public class PlatLogoActivity extends Activity { if (mPressureMax >= 0) { touchData.put("min", mPressureMin); touchData.put("max", mPressureMax); Settings.System.putString(getContentResolver(), TOUCH_STATS, touchData.toString()); if (WRITE_SETTINGS) { Settings.System.putString(getContentResolver(), TOUCH_STATS, touchData.toString()); } } } catch (Exception e) { Log.e("com.android.internal.app.PlatLogoActivity", "Can't write touch settings", e); Loading @@ -242,149 +169,272 @@ public class PlatLogoActivity extends Activity { super.onStop(); } static class ZeroDrawable extends Drawable { int mTintColor; class BigDialView extends ImageView { private static final int COLOR_GREEN = 0xff3ddc84; private static final int COLOR_BLUE = 0xff4285f4; private static final int COLOR_NAVY = 0xff073042; private static final int COLOR_ORANGE = 0xfff86734; private static final int COLOR_CHARTREUSE = 0xffeff7cf; private static final int COLOR_LIGHTBLUE = 0xffd7effe; @Override public void draw(Canvas canvas) { sPaint.setColor(mTintColor | 0xFF000000); private static final int STEPS = 11; private static final float VALUE_CHANGE_MAX = 1f / STEPS; canvas.save(); canvas.scale(canvas.getWidth() / 24f, canvas.getHeight() / 24f); private BigDialDrawable mDialDrawable; private boolean mWasLocked; canvas.drawCircle(12f, 12f, 10f, sPaint); canvas.restore(); BigDialView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(); } @Override public void setAlpha(int alpha) { } BigDialView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } BigDialView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); init(); } private void init() { mDialDrawable = new BigDialDrawable(); setImageDrawable(mDialDrawable); } @Override public void setColorFilter(ColorFilter colorFilter) { } public void onDraw(Canvas c) { super.onDraw(c); } double toPositiveDegrees(double rad) { return (Math.toDegrees(rad) + 360 - 90) % 360; } @Override public void setTintList(ColorStateList tint) { mTintColor = tint.getDefaultColor(); public boolean onTouchEvent(MotionEvent ev) { switch (ev.getActionMasked()) { case MotionEvent.ACTION_DOWN: mWasLocked = mDialDrawable.isLocked(); // pass through case MotionEvent.ACTION_MOVE: float x = ev.getX(); float y = ev.getY(); float cx = (getLeft() + getRight()) / 2f; float cy = (getTop() + getBottom()) / 2f; float angle = (float) toPositiveDegrees(Math.atan2(x - cx, y - cy)); final int oldLevel = mDialDrawable.getUserLevel(); mDialDrawable.touchAngle(angle); final int newLevel = mDialDrawable.getUserLevel(); if (oldLevel != newLevel) { performHapticFeedback(newLevel == STEPS ? HapticFeedbackConstants.CONFIRM : HapticFeedbackConstants.CLOCK_TICK); } return true; case MotionEvent.ACTION_UP: if (mWasLocked && !mDialDrawable.isLocked()) { launchNextStage(false); } return true; } return false; } @Override public int getOpacity() { return PixelFormat.TRANSLUCENT; public boolean performClick() { if (mDialDrawable.getUserLevel() < STEPS - 1) { mDialDrawable.setUserLevel(mDialDrawable.getUserLevel() + 1); performHapticFeedback(HapticFeedbackConstants.CLOCK_TICK); } return true; } static class OneDrawable extends Drawable { int mTintColor; void setUnlockTries(int tries) { mDialDrawable.setUnlockTries(tries); } @Override public void draw(Canvas canvas) { sPaint.setColor(mTintColor | 0xFF000000); private class BigDialDrawable extends Drawable { public final int STEPS = 10; private int mUnlockTries = 0; final Paint mPaint = new Paint(); final Drawable mEleven; private boolean mNightMode; private float mValue = 0f; float mElevenAnim = 0f; ObjectAnimator mElevenShowAnimator = ObjectAnimator.ofFloat(this, "elevenAnim", 0f, 1f).setDuration(300); ObjectAnimator mElevenHideAnimator = ObjectAnimator.ofFloat(this, "elevenAnim", 1f, 0f).setDuration(500); canvas.save(); canvas.scale(canvas.getWidth() / 24f, canvas.getHeight() / 24f); BigDialDrawable() { mNightMode = getContext().getResources().getConfiguration().isNightModeActive(); mEleven = getContext().getDrawable(R.drawable.ic_number11); mElevenShowAnimator.setInterpolator(new PathInterpolator(0.4f, 0f, 0.2f, 1f)); mElevenHideAnimator.setInterpolator(new PathInterpolator(0.8f, 0.2f, 0.6f, 1f)); } final Path p = new Path(); p.moveTo(12f, 21.83f); p.rLineTo(0f, -19.67f); p.rLineTo(-5f, 0f); canvas.drawPath(p, sPaint); canvas.restore(); public void setUnlockTries(int count) { if (mUnlockTries != count) { mUnlockTries = count; setValue(getValue()); invalidateSelf(); } } @Override public void setAlpha(int alpha) { } boolean isLocked() { return mUnlockTries > 0; } @Override public void setColorFilter(ColorFilter colorFilter) { } public void setValue(float v) { // until the dial is "unlocked", you can't turn it all the way to 11 final float max = isLocked() ? 1f - 1f / STEPS : 1f; mValue = v < 0f ? 0f : v > max ? max : v; invalidateSelf(); } @Override public void setTintList(ColorStateList tint) { mTintColor = tint.getDefaultColor(); public float getValue() { return mValue; } @Override public int getOpacity() { return PixelFormat.TRANSLUCENT; public int getUserLevel() { return Math.round(getValue() * STEPS - 0.25f); } public void setUserLevel(int i) { setValue(getValue() + ((float) i) / STEPS); } private static class BackslashDrawable extends Drawable implements TimeAnimator.TimeListener { Bitmap mTile; Paint mPaint = new Paint(); BitmapShader mShader; TimeAnimator mAnimator = new TimeAnimator(); Matrix mMatrix = new Matrix(); public float getElevenAnim() { return mElevenAnim; } public void draw(Canvas canvas) { canvas.drawPaint(mPaint); public void setElevenAnim(float f) { if (mElevenAnim != f) { mElevenAnim = f; invalidateSelf(); } } BackslashDrawable(int width) { mTile = Bitmap.createBitmap(width, width, Bitmap.Config.ALPHA_8); mAnimator.setTimeListener(this); @Override public void draw(@NonNull Canvas canvas) { final Rect bounds = getBounds(); final int w = bounds.width(); final int h = bounds.height(); final float w2 = w / 2f; final float h2 = h / 2f; final float radius = w / 4f; final Canvas tileCanvas = new Canvas(mTile); final float w = tileCanvas.getWidth(); final float h = tileCanvas.getHeight(); canvas.drawColor(mNightMode ? COLOR_NAVY : COLOR_LIGHTBLUE); final Path path = new Path(); path.moveTo(0, 0); path.lineTo(w / 2, 0); path.lineTo(w, h / 2); path.lineTo(w, h); path.close(); canvas.save(); canvas.rotate(45, w2, h2); canvas.clipRect(w2, h2 - radius, Math.min(w, h), h2 + radius); final int gradientColor = mNightMode ? 0x60000020 : (0x10FFFFFF & COLOR_NAVY); mPaint.setShader( new LinearGradient(w2, h2, Math.min(w, h), h2, gradientColor, 0x00FFFFFF & gradientColor, Shader.TileMode.CLAMP)); mPaint.setColor(Color.BLACK); canvas.drawPaint(mPaint); mPaint.setShader(null); canvas.restore(); mPaint.setStyle(Paint.Style.FILL); mPaint.setColor(COLOR_GREEN); path.moveTo(0, h / 2); path.lineTo(w / 2, h); path.lineTo(0, h); path.close(); canvas.drawCircle(w2, h2, radius, mPaint); final Paint slashPaint = new Paint(); slashPaint.setAntiAlias(true); slashPaint.setStyle(Paint.Style.FILL); slashPaint.setColor(0xFF000000); tileCanvas.drawPath(path, slashPaint); mPaint.setColor(mNightMode ? COLOR_LIGHTBLUE : COLOR_NAVY); final float cx = w * 0.85f; for (int i = 0; i < STEPS; i++) { final float f = (float) i / STEPS; canvas.save(); final float angle = valueToAngle(f); canvas.rotate(-angle, w2, h2); canvas.drawCircle(cx, h2, (i <= getUserLevel()) ? 20 : 5, mPaint); canvas.restore(); } //mPaint.setColor(0xFF0000FF); mShader = new BitmapShader(mTile, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT); mPaint.setShader(mShader); if (mElevenAnim > 0f) { final int color = COLOR_ORANGE; final int size2 = (int) ((0.5 + 0.5f * mElevenAnim) * w / 14); final float cx11 = cx + size2 / 4f; mEleven.setBounds((int) cx11 - size2, (int) h2 - size2, (int) cx11 + size2, (int) h2 + size2); final int alpha = 0xFFFFFF | ((int) clamp(0xFF * 2 * mElevenAnim, 0, 0xFF) << 24); mEleven.setTint(alpha & color); mEleven.draw(canvas); } public void startAnimating() { if (!mAnimator.isStarted()) { mAnimator.start(); // don't want to use the rounded value here since the quantization will be visible final float angle = valueToAngle(mValue); // it's easier to draw at far-right and rotate backwards canvas.rotate(-angle, w2, h2); mPaint.setColor(Color.WHITE); final float dimple = w2 / 12f; canvas.drawCircle(w - radius - dimple * 2, h2, dimple, mPaint); } float clamp(float x, float a, float b) { return x < a ? a : x > b ? b : x; } public void stopAnimating() { if (mAnimator.isStarted()) { mAnimator.cancel(); float angleToValue(float a) { return 1f - clamp(a / (360 - 45), 0f, 1f); } // rotation: min is at 4:30, max is at 3:00 float valueToAngle(float v) { return (1f - v) * (360 - 45); } @Override public void setAlpha(int alpha) { mPaint.setAlpha(alpha); public void touchAngle(float a) { final int oldUserLevel = getUserLevel(); final float newValue = angleToValue(a); // this is how we prevent the knob from snapping from max back to min, or from // jumping around wherever the user presses. The new value must be pretty close // to the // previous one. if (Math.abs(newValue - getValue()) < VALUE_CHANGE_MAX) { setValue(newValue); if (isLocked() && oldUserLevel != STEPS - 1 && getUserLevel() == STEPS - 1) { mUnlockTries--; } if (!isLocked()) { if (getUserLevel() == STEPS && mElevenAnim != 1f && !mElevenShowAnimator.isRunning()) { mElevenHideAnimator.cancel(); mElevenShowAnimator.start(); } else if (getUserLevel() != STEPS && mElevenAnim == 1f && !mElevenHideAnimator.isRunning()) { mElevenShowAnimator.cancel(); mElevenHideAnimator.start(); } } } } @Override public void setColorFilter(ColorFilter colorFilter) { mPaint.setColorFilter(colorFilter); public void setAlpha(int i) { } @Override public int getOpacity() { return PixelFormat.TRANSLUCENT; public void setColorFilter(@Nullable ColorFilter colorFilter) { } @Override public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) { if (mShader != null) { mMatrix.postTranslate(deltaTime / 4f, 0); mShader.setLocalMatrix(mMatrix); invalidateSelf(); public int getOpacity() { return PixelFormat.TRANSLUCENT; } } } } core/res/res/drawable-nodpi/ic_number11.xml 0 → 100644 +12 −0 Original line number Diff line number Diff line <vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24"> <path android:pathData="M5.14,5H1.59a0.88,0.88 0,0 1,-0.88 -0.89V0.88A0.87,0.87 0,0 1,1.59 0H9.36a0.87,0.87 0,0 1,0.88 0.88V23.12a0.88,0.88 0,0 1,-0.88 0.88H6a0.88,0.88 0,0 1,-0.88 -0.88Z" android:fillColor="#000000"/> <path android:pathData="M18.19,5H14.64a0.89,0.89 0,0 1,-0.88 -0.89V0.88A0.88,0.88 0,0 1,14.64 0h7.78a0.87,0.87 0,0 1,0.87 0.88V23.12a0.88,0.88 0,0 1,-0.87 0.88H19.08a0.89,0.89 0,0 1,-0.89 -0.88Z" android:fillColor="#000000"/> </vector> core/res/res/values/symbols.xml +1 −0 Original line number Diff line number Diff line Loading @@ -3830,6 +3830,7 @@ <java-symbol type="drawable" name="android_logotype" /> <java-symbol type="layout" name="platlogo_layout" /> <java-symbol type="drawable" name="ic_number11" /> <java-symbol type="integer" name="config_notificationWarnRemoteViewSizeBytes" /> <java-symbol type="integer" name="config_notificationStripRemoteViewSizeBytes" /> Loading Loading
core/java/com/android/internal/app/PlatLogoActivity.java +271 −221 Original line number Diff line number Diff line /* * Copyright (C) 2018 The Android Open Source Project * Copyright (C) 2020 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. Loading @@ -17,29 +17,32 @@ package com.android.internal.app; import android.animation.ObjectAnimator; import android.animation.TimeAnimator; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActionBar; import android.app.Activity; import android.content.ActivityNotFoundException; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.res.ColorStateList; import android.graphics.Bitmap; import android.graphics.BitmapShader; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ColorFilter; import android.graphics.Matrix; import android.graphics.LinearGradient; import android.graphics.Paint; import android.graphics.Path; import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.Shader; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.provider.Settings; import android.util.AttributeSet; import android.util.Log; import android.view.HapticFeedbackConstants; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.animation.PathInterpolator; import android.widget.FrameLayout; import android.widget.ImageView; import com.android.internal.R; Loading @@ -50,23 +53,12 @@ import org.json.JSONObject; * @hide */ public class PlatLogoActivity extends Activity { ImageView mZeroView, mOneView; BackslashDrawable mBackslash; int mClicks; private static final boolean WRITE_SETTINGS = true; static final Paint sPaint = new Paint(); static { sPaint.setStyle(Paint.Style.STROKE); sPaint.setStrokeWidth(4f); sPaint.setStrokeCap(Paint.Cap.SQUARE); } BigDialView mDialView; @Override protected void onPause() { if (mBackslash != null) { mBackslash.stopAnimating(); } mClicks = 0; super.onPause(); } Loading @@ -80,101 +72,33 @@ public class PlatLogoActivity extends Activity { getWindow().setNavigationBarColor(0); getWindow().setStatusBarColor(0); getActionBar().hide(); setContentView(R.layout.platlogo_layout); mBackslash = new BackslashDrawable((int) (50 * dp)); mOneView = findViewById(R.id.one); mOneView.setImageDrawable(new OneDrawable()); mZeroView = findViewById(R.id.zero); mZeroView.setImageDrawable(new ZeroDrawable()); final ViewGroup root = (ViewGroup) mOneView.getParent(); root.setClipChildren(false); root.setBackground(mBackslash); root.getBackground().setAlpha(0x20); View.OnTouchListener tl = new View.OnTouchListener() { float mOffsetX, mOffsetY; long mClickTime; ObjectAnimator mRotAnim; @Override public boolean onTouch(View v, MotionEvent event) { measureTouchPressure(event); switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: v.animate().scaleX(1.1f).scaleY(1.1f); v.getParent().bringChildToFront(v); mOffsetX = event.getRawX() - v.getX(); mOffsetY = event.getRawY() - v.getY(); long now = System.currentTimeMillis(); if (now - mClickTime < 350) { mRotAnim = ObjectAnimator.ofFloat(v, View.ROTATION, v.getRotation(), v.getRotation() + 3600); mRotAnim.setDuration(10000); mRotAnim.start(); mClickTime = 0; } else { mClickTime = now; } break; case MotionEvent.ACTION_MOVE: v.setX(event.getRawX() - mOffsetX); v.setY(event.getRawY() - mOffsetY); v.performHapticFeedback(HapticFeedbackConstants.TEXT_HANDLE_MOVE); break; case MotionEvent.ACTION_UP: v.performClick(); // fall through case MotionEvent.ACTION_CANCEL: v.animate().scaleX(1f).scaleY(1f); if (mRotAnim != null) mRotAnim.cancel(); testOverlap(); break; } return true; } }; final ActionBar ab = getActionBar(); if (ab != null) ab.hide(); findViewById(R.id.one).setOnTouchListener(tl); findViewById(R.id.zero).setOnTouchListener(tl); findViewById(R.id.text).setOnTouchListener(tl); mDialView = new BigDialView(this, null); if (Settings.System.getLong(getContentResolver(), "egg_mode" /* Settings.System.EGG_MODE */, 0) == 0) { mDialView.setUnlockTries(3); } private void testOverlap() { final float width = mZeroView.getWidth(); final float targetX = mZeroView.getX() + width * .2f; final float targetY = mZeroView.getY() + width * .3f; if (Math.hypot(targetX - mOneView.getX(), targetY - mOneView.getY()) < width * .2f && Math.abs(mOneView.getRotation() % 360 - 315) < 15) { mOneView.animate().x(mZeroView.getX() + width * .2f); mOneView.animate().y(mZeroView.getY() + width * .3f); mOneView.setRotation(mOneView.getRotation() % 360); mOneView.animate().rotation(315); mOneView.performHapticFeedback(HapticFeedbackConstants.CONFIRM); mBackslash.startAnimating(); mClicks++; if (mClicks >= 7) { launchNextStage(); } } else { mBackslash.stopAnimating(); } final FrameLayout layout = new FrameLayout(this); layout.setBackgroundColor(0xFFFF0000); layout.addView(mDialView, FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT); setContentView(layout); } private void launchNextStage() { private void launchNextStage(boolean locked) { final ContentResolver cr = getContentResolver(); if (Settings.System.getLong(cr, "egg_mode" /* Settings.System.EGG_MODE */, 0) == 0) { // For posterity: the moment this user unlocked the easter egg try { if (WRITE_SETTINGS) { Settings.System.putLong(cr, "egg_mode", // Settings.System.EGG_MODE, System.currentTimeMillis()); locked ? 0 : System.currentTimeMillis()); } } catch (RuntimeException e) { Log.e("com.android.internal.app.PlatLogoActivity", "Can't write settings", e); } Loading @@ -187,7 +111,7 @@ public class PlatLogoActivity extends Activity { } catch (ActivityNotFoundException ex) { Log.e("com.android.internal.app.PlatLogoActivity", "No more eggs."); } finish(); //finish(); // no longer finish upon unlock; it's fun to frob the dial } static final String TOUCH_STATS = "touch.stats"; Loading Loading @@ -223,7 +147,10 @@ public class PlatLogoActivity extends Activity { if (mPressureMax >= 0) { touchData.put("min", mPressureMin); touchData.put("max", mPressureMax); Settings.System.putString(getContentResolver(), TOUCH_STATS, touchData.toString()); if (WRITE_SETTINGS) { Settings.System.putString(getContentResolver(), TOUCH_STATS, touchData.toString()); } } } catch (Exception e) { Log.e("com.android.internal.app.PlatLogoActivity", "Can't write touch settings", e); Loading @@ -242,149 +169,272 @@ public class PlatLogoActivity extends Activity { super.onStop(); } static class ZeroDrawable extends Drawable { int mTintColor; class BigDialView extends ImageView { private static final int COLOR_GREEN = 0xff3ddc84; private static final int COLOR_BLUE = 0xff4285f4; private static final int COLOR_NAVY = 0xff073042; private static final int COLOR_ORANGE = 0xfff86734; private static final int COLOR_CHARTREUSE = 0xffeff7cf; private static final int COLOR_LIGHTBLUE = 0xffd7effe; @Override public void draw(Canvas canvas) { sPaint.setColor(mTintColor | 0xFF000000); private static final int STEPS = 11; private static final float VALUE_CHANGE_MAX = 1f / STEPS; canvas.save(); canvas.scale(canvas.getWidth() / 24f, canvas.getHeight() / 24f); private BigDialDrawable mDialDrawable; private boolean mWasLocked; canvas.drawCircle(12f, 12f, 10f, sPaint); canvas.restore(); BigDialView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(); } @Override public void setAlpha(int alpha) { } BigDialView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } BigDialView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); init(); } private void init() { mDialDrawable = new BigDialDrawable(); setImageDrawable(mDialDrawable); } @Override public void setColorFilter(ColorFilter colorFilter) { } public void onDraw(Canvas c) { super.onDraw(c); } double toPositiveDegrees(double rad) { return (Math.toDegrees(rad) + 360 - 90) % 360; } @Override public void setTintList(ColorStateList tint) { mTintColor = tint.getDefaultColor(); public boolean onTouchEvent(MotionEvent ev) { switch (ev.getActionMasked()) { case MotionEvent.ACTION_DOWN: mWasLocked = mDialDrawable.isLocked(); // pass through case MotionEvent.ACTION_MOVE: float x = ev.getX(); float y = ev.getY(); float cx = (getLeft() + getRight()) / 2f; float cy = (getTop() + getBottom()) / 2f; float angle = (float) toPositiveDegrees(Math.atan2(x - cx, y - cy)); final int oldLevel = mDialDrawable.getUserLevel(); mDialDrawable.touchAngle(angle); final int newLevel = mDialDrawable.getUserLevel(); if (oldLevel != newLevel) { performHapticFeedback(newLevel == STEPS ? HapticFeedbackConstants.CONFIRM : HapticFeedbackConstants.CLOCK_TICK); } return true; case MotionEvent.ACTION_UP: if (mWasLocked && !mDialDrawable.isLocked()) { launchNextStage(false); } return true; } return false; } @Override public int getOpacity() { return PixelFormat.TRANSLUCENT; public boolean performClick() { if (mDialDrawable.getUserLevel() < STEPS - 1) { mDialDrawable.setUserLevel(mDialDrawable.getUserLevel() + 1); performHapticFeedback(HapticFeedbackConstants.CLOCK_TICK); } return true; } static class OneDrawable extends Drawable { int mTintColor; void setUnlockTries(int tries) { mDialDrawable.setUnlockTries(tries); } @Override public void draw(Canvas canvas) { sPaint.setColor(mTintColor | 0xFF000000); private class BigDialDrawable extends Drawable { public final int STEPS = 10; private int mUnlockTries = 0; final Paint mPaint = new Paint(); final Drawable mEleven; private boolean mNightMode; private float mValue = 0f; float mElevenAnim = 0f; ObjectAnimator mElevenShowAnimator = ObjectAnimator.ofFloat(this, "elevenAnim", 0f, 1f).setDuration(300); ObjectAnimator mElevenHideAnimator = ObjectAnimator.ofFloat(this, "elevenAnim", 1f, 0f).setDuration(500); canvas.save(); canvas.scale(canvas.getWidth() / 24f, canvas.getHeight() / 24f); BigDialDrawable() { mNightMode = getContext().getResources().getConfiguration().isNightModeActive(); mEleven = getContext().getDrawable(R.drawable.ic_number11); mElevenShowAnimator.setInterpolator(new PathInterpolator(0.4f, 0f, 0.2f, 1f)); mElevenHideAnimator.setInterpolator(new PathInterpolator(0.8f, 0.2f, 0.6f, 1f)); } final Path p = new Path(); p.moveTo(12f, 21.83f); p.rLineTo(0f, -19.67f); p.rLineTo(-5f, 0f); canvas.drawPath(p, sPaint); canvas.restore(); public void setUnlockTries(int count) { if (mUnlockTries != count) { mUnlockTries = count; setValue(getValue()); invalidateSelf(); } } @Override public void setAlpha(int alpha) { } boolean isLocked() { return mUnlockTries > 0; } @Override public void setColorFilter(ColorFilter colorFilter) { } public void setValue(float v) { // until the dial is "unlocked", you can't turn it all the way to 11 final float max = isLocked() ? 1f - 1f / STEPS : 1f; mValue = v < 0f ? 0f : v > max ? max : v; invalidateSelf(); } @Override public void setTintList(ColorStateList tint) { mTintColor = tint.getDefaultColor(); public float getValue() { return mValue; } @Override public int getOpacity() { return PixelFormat.TRANSLUCENT; public int getUserLevel() { return Math.round(getValue() * STEPS - 0.25f); } public void setUserLevel(int i) { setValue(getValue() + ((float) i) / STEPS); } private static class BackslashDrawable extends Drawable implements TimeAnimator.TimeListener { Bitmap mTile; Paint mPaint = new Paint(); BitmapShader mShader; TimeAnimator mAnimator = new TimeAnimator(); Matrix mMatrix = new Matrix(); public float getElevenAnim() { return mElevenAnim; } public void draw(Canvas canvas) { canvas.drawPaint(mPaint); public void setElevenAnim(float f) { if (mElevenAnim != f) { mElevenAnim = f; invalidateSelf(); } } BackslashDrawable(int width) { mTile = Bitmap.createBitmap(width, width, Bitmap.Config.ALPHA_8); mAnimator.setTimeListener(this); @Override public void draw(@NonNull Canvas canvas) { final Rect bounds = getBounds(); final int w = bounds.width(); final int h = bounds.height(); final float w2 = w / 2f; final float h2 = h / 2f; final float radius = w / 4f; final Canvas tileCanvas = new Canvas(mTile); final float w = tileCanvas.getWidth(); final float h = tileCanvas.getHeight(); canvas.drawColor(mNightMode ? COLOR_NAVY : COLOR_LIGHTBLUE); final Path path = new Path(); path.moveTo(0, 0); path.lineTo(w / 2, 0); path.lineTo(w, h / 2); path.lineTo(w, h); path.close(); canvas.save(); canvas.rotate(45, w2, h2); canvas.clipRect(w2, h2 - radius, Math.min(w, h), h2 + radius); final int gradientColor = mNightMode ? 0x60000020 : (0x10FFFFFF & COLOR_NAVY); mPaint.setShader( new LinearGradient(w2, h2, Math.min(w, h), h2, gradientColor, 0x00FFFFFF & gradientColor, Shader.TileMode.CLAMP)); mPaint.setColor(Color.BLACK); canvas.drawPaint(mPaint); mPaint.setShader(null); canvas.restore(); mPaint.setStyle(Paint.Style.FILL); mPaint.setColor(COLOR_GREEN); path.moveTo(0, h / 2); path.lineTo(w / 2, h); path.lineTo(0, h); path.close(); canvas.drawCircle(w2, h2, radius, mPaint); final Paint slashPaint = new Paint(); slashPaint.setAntiAlias(true); slashPaint.setStyle(Paint.Style.FILL); slashPaint.setColor(0xFF000000); tileCanvas.drawPath(path, slashPaint); mPaint.setColor(mNightMode ? COLOR_LIGHTBLUE : COLOR_NAVY); final float cx = w * 0.85f; for (int i = 0; i < STEPS; i++) { final float f = (float) i / STEPS; canvas.save(); final float angle = valueToAngle(f); canvas.rotate(-angle, w2, h2); canvas.drawCircle(cx, h2, (i <= getUserLevel()) ? 20 : 5, mPaint); canvas.restore(); } //mPaint.setColor(0xFF0000FF); mShader = new BitmapShader(mTile, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT); mPaint.setShader(mShader); if (mElevenAnim > 0f) { final int color = COLOR_ORANGE; final int size2 = (int) ((0.5 + 0.5f * mElevenAnim) * w / 14); final float cx11 = cx + size2 / 4f; mEleven.setBounds((int) cx11 - size2, (int) h2 - size2, (int) cx11 + size2, (int) h2 + size2); final int alpha = 0xFFFFFF | ((int) clamp(0xFF * 2 * mElevenAnim, 0, 0xFF) << 24); mEleven.setTint(alpha & color); mEleven.draw(canvas); } public void startAnimating() { if (!mAnimator.isStarted()) { mAnimator.start(); // don't want to use the rounded value here since the quantization will be visible final float angle = valueToAngle(mValue); // it's easier to draw at far-right and rotate backwards canvas.rotate(-angle, w2, h2); mPaint.setColor(Color.WHITE); final float dimple = w2 / 12f; canvas.drawCircle(w - radius - dimple * 2, h2, dimple, mPaint); } float clamp(float x, float a, float b) { return x < a ? a : x > b ? b : x; } public void stopAnimating() { if (mAnimator.isStarted()) { mAnimator.cancel(); float angleToValue(float a) { return 1f - clamp(a / (360 - 45), 0f, 1f); } // rotation: min is at 4:30, max is at 3:00 float valueToAngle(float v) { return (1f - v) * (360 - 45); } @Override public void setAlpha(int alpha) { mPaint.setAlpha(alpha); public void touchAngle(float a) { final int oldUserLevel = getUserLevel(); final float newValue = angleToValue(a); // this is how we prevent the knob from snapping from max back to min, or from // jumping around wherever the user presses. The new value must be pretty close // to the // previous one. if (Math.abs(newValue - getValue()) < VALUE_CHANGE_MAX) { setValue(newValue); if (isLocked() && oldUserLevel != STEPS - 1 && getUserLevel() == STEPS - 1) { mUnlockTries--; } if (!isLocked()) { if (getUserLevel() == STEPS && mElevenAnim != 1f && !mElevenShowAnimator.isRunning()) { mElevenHideAnimator.cancel(); mElevenShowAnimator.start(); } else if (getUserLevel() != STEPS && mElevenAnim == 1f && !mElevenHideAnimator.isRunning()) { mElevenShowAnimator.cancel(); mElevenHideAnimator.start(); } } } } @Override public void setColorFilter(ColorFilter colorFilter) { mPaint.setColorFilter(colorFilter); public void setAlpha(int i) { } @Override public int getOpacity() { return PixelFormat.TRANSLUCENT; public void setColorFilter(@Nullable ColorFilter colorFilter) { } @Override public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) { if (mShader != null) { mMatrix.postTranslate(deltaTime / 4f, 0); mShader.setLocalMatrix(mMatrix); invalidateSelf(); public int getOpacity() { return PixelFormat.TRANSLUCENT; } } } }
core/res/res/drawable-nodpi/ic_number11.xml 0 → 100644 +12 −0 Original line number Diff line number Diff line <vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24"> <path android:pathData="M5.14,5H1.59a0.88,0.88 0,0 1,-0.88 -0.89V0.88A0.87,0.87 0,0 1,1.59 0H9.36a0.87,0.87 0,0 1,0.88 0.88V23.12a0.88,0.88 0,0 1,-0.88 0.88H6a0.88,0.88 0,0 1,-0.88 -0.88Z" android:fillColor="#000000"/> <path android:pathData="M18.19,5H14.64a0.89,0.89 0,0 1,-0.88 -0.89V0.88A0.88,0.88 0,0 1,14.64 0h7.78a0.87,0.87 0,0 1,0.87 0.88V23.12a0.88,0.88 0,0 1,-0.87 0.88H19.08a0.89,0.89 0,0 1,-0.89 -0.88Z" android:fillColor="#000000"/> </vector>
core/res/res/values/symbols.xml +1 −0 Original line number Diff line number Diff line Loading @@ -3830,6 +3830,7 @@ <java-symbol type="drawable" name="android_logotype" /> <java-symbol type="layout" name="platlogo_layout" /> <java-symbol type="drawable" name="ic_number11" /> <java-symbol type="integer" name="config_notificationWarnRemoteViewSizeBytes" /> <java-symbol type="integer" name="config_notificationStripRemoteViewSizeBytes" /> Loading