Cat.java revision 27a9fcc61823f919cee773df563b49ee11004f3b
1/* 2 * Copyright (C) 2016 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 15package com.android.egg.neko; 16 17import android.app.Notification; 18import android.app.PendingIntent; 19import android.content.ComponentName; 20import android.content.Context; 21import android.content.Intent; 22import android.content.res.Resources; 23import android.graphics.*; 24import android.graphics.drawable.Drawable; 25import android.graphics.drawable.Icon; 26import android.os.Bundle; 27 28import java.util.Random; 29import java.util.concurrent.ThreadLocalRandom; 30 31import com.android.egg.R; 32 33public class Cat extends Drawable { 34 private Random mNotSoRandom; 35 private Bitmap mBitmap; 36 private long mSeed; 37 private String mName; 38 private int mBodyColor; 39 40 private synchronized Random notSoRandom(long seed) { 41 if (mNotSoRandom == null) { 42 mNotSoRandom = new Random(); 43 mNotSoRandom.setSeed(seed); 44 } 45 return mNotSoRandom; 46 } 47 48 public static final float frandrange(Random r, float a, float b) { 49 return (b-a)*r.nextFloat() + a; 50 } 51 52 public static final Object choose(Random r, Object...l) { 53 return l[r.nextInt(l.length)]; 54 } 55 56 public static final int chooseP(Random r, int[] a) { 57 int pct = r.nextInt(1000); 58 final int stop = a.length-2; 59 int i=0; 60 while (i<stop) { 61 pct -= a[i]; 62 if (pct < 0) break; 63 i+=2; 64 } 65 return a[i+1]; 66 } 67 68 public static final int[] P_BODY_COLORS = { 69 180, 0xFF212121, // black 70 180, 0xFFFFFFFF, // white 71 140, 0xFF616161, // gray 72 140, 0xFF795548, // brown 73 100, 0xFF90A4AE, // steel 74 100, 0xFFFFF9C4, // buff 75 100, 0xFFFF8F00, // orange 76 5, 0xFF29B6F6, // blue..? 77 5, 0xFFFFCDD2, // pink!? 78 5, 0xFFCE93D8, // purple?!?!? 79 4, 0xFF43A047, // yeah, why not green 80 1, 0, // ?!?!?! 81 }; 82 83 public static final int[] P_COLLAR_COLORS = { 84 250, 0xFFFFFFFF, 85 250, 0xFF000000, 86 250, 0xFFF44336, 87 50, 0xFF1976D2, 88 50, 0xFFFDD835, 89 50, 0xFFFB8C00, 90 50, 0xFFF48FB1, 91 50, 0xFF4CAF50, 92 }; 93 94 public static final int[] P_BELLY_COLORS = { 95 750, 0, 96 250, 0xFFFFFFFF, 97 }; 98 99 public static final int[] P_DARK_SPOT_COLORS = { 100 700, 0, 101 250, 0xFF212121, 102 50, 0xFF6D4C41, 103 }; 104 105 public static final int[] P_LIGHT_SPOT_COLORS = { 106 700, 0, 107 300, 0xFFFFFFFF, 108 }; 109 110 private CatParts D; 111 112 public static void tint(int color, Drawable ... ds) { 113 for (Drawable d : ds) { 114 if (d != null) { 115 d.mutate().setTint(color); 116 } 117 } 118 } 119 120 public static boolean isDark(int color) { 121 final int r = (color & 0xFF0000) >> 16; 122 final int g = (color & 0x00FF00) >> 8; 123 final int b = color & 0x0000FF; 124 return (r + g + b) < 0x80; 125 } 126 127 public Cat(Context context, long seed) { 128 D = new CatParts(context); 129 mSeed = seed; 130 131 setName(context.getString(R.string.default_cat_name, 132 String.valueOf(mSeed).substring(0, 3))); 133 134 final Random nsr = notSoRandom(seed); 135 136 // body color 137 mBodyColor = chooseP(nsr, P_BODY_COLORS); 138 if (mBodyColor == 0) mBodyColor = Color.HSVToColor(new float[] { 139 nsr.nextFloat()*360f, frandrange(nsr,0.5f,1f), frandrange(nsr,0.5f, 1f)}); 140 141 tint(mBodyColor, D.body, D.head, D.leg1, D.leg2, D.leg3, D.leg4, D.tail, 142 D.leftEar, D.rightEar, D.foot1, D.foot2, D.foot3, D.foot4, D.tailCap); 143 tint(0x20000000, D.leg2Shadow, D.tailShadow); 144 if (isDark(mBodyColor)) { 145 tint(0xFFFFFFFF, D.leftEye, D.rightEye, D.mouth, D.nose); 146 } 147 tint(isDark(mBodyColor) ? 0xFFEF9A9A : 0x20D50000, D.leftEarInside, D.rightEarInside); 148 149 tint(chooseP(nsr, P_BELLY_COLORS), D.belly); 150 tint(chooseP(nsr, P_BELLY_COLORS), D.back); 151 final int faceColor = chooseP(nsr, P_BELLY_COLORS); 152 tint(faceColor, D.faceSpot); 153 if (!isDark(faceColor)) { 154 tint(0xFF000000, D.mouth, D.nose); 155 } 156 157 if (nsr.nextFloat() < 0.25f) { 158 tint(0xFFFFFFFF, D.foot1, D.foot2, D.foot3, D.foot4); 159 } else { 160 if (nsr.nextFloat() < 0.25f) { 161 tint(0xFFFFFFFF, D.foot1, D.foot2); 162 } else if (nsr.nextFloat() < 0.25f) { 163 tint(0xFFFFFFFF, D.foot3, D.foot4); 164 } else if (nsr.nextFloat() < 0.1f) { 165 tint(0xFFFFFFFF, (Drawable) choose(nsr, D.foot1, D.foot2, D.foot3, D.foot4)); 166 } 167 } 168 169 tint(nsr.nextFloat() < 0.333f ? 0xFFFFFFFF : mBodyColor, D.tailCap); 170 171 final int capColor = chooseP(nsr, isDark(mBodyColor) ? P_LIGHT_SPOT_COLORS : P_DARK_SPOT_COLORS); 172 tint(capColor, D.cap); 173 //tint(chooseP(nsr, isDark(bodyColor) ? P_LIGHT_SPOT_COLORS : P_DARK_SPOT_COLORS), D.nose); 174 175 final int collarColor = chooseP(nsr, P_COLLAR_COLORS); 176 tint(collarColor, D.collar); 177 tint((nsr.nextFloat() < 0.1f) ? collarColor : 0, D.bowtie); 178 } 179 180 public static Cat create(Context context) { 181 return new Cat(context, Math.abs(ThreadLocalRandom.current().nextInt())); 182 } 183 184 public Notification.Builder buildNotification(Context context) { 185 final Bundle extras = new Bundle(); 186 extras.putString("android.substName", context.getString(R.string.notification_name)); 187 final Intent intent = new Intent(Intent.ACTION_MAIN) 188 .setClass(context, NekoLand.class) 189 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 190 return new Notification.Builder(context) 191 .setSmallIcon(Icon.createWithResource(context, R.drawable.stat_icon)) 192 .setLargeIcon(createLargeIcon(context)) 193 .setColor(getBodyColor()) 194 .setPriority(Notification.PRIORITY_LOW) 195 .setContentTitle(context.getString(R.string.notification_title)) 196 .setShowWhen(true) 197 .setCategory(Notification.CATEGORY_STATUS) 198 .setContentText(getName()) 199 .setContentIntent(PendingIntent.getActivity(context, 0, intent, 0)) 200 .setAutoCancel(true) 201 .addExtras(extras); 202 } 203 204 public long getSeed() { 205 return mSeed; 206 } 207 208 @Override 209 public void draw(Canvas canvas) { 210 final int w = Math.min(canvas.getWidth(), canvas.getHeight()); 211 final int h = w; 212 213 if (mBitmap == null || mBitmap.getWidth() != w || mBitmap.getHeight() != h) { 214 mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); 215 final Canvas bitCanvas = new Canvas(mBitmap); 216 slowDraw(bitCanvas, 0, 0, w, h); 217 } 218 canvas.drawBitmap(mBitmap, 0, 0, null); 219 } 220 221 private void slowDraw(Canvas canvas, int x, int y, int w, int h) { 222 for (int i = 0; i < D.drawingOrder.length; i++) { 223 final Drawable d = D.drawingOrder[i]; 224 if (d != null) { 225 d.setBounds(x, y, x+w, y+h); 226 d.draw(canvas); 227 } 228 } 229 230 } 231 232 public Bitmap createBitmap(int w, int h) { 233 if (mBitmap != null && mBitmap.getWidth() == w && mBitmap.getHeight() == h) { 234 return mBitmap.copy(mBitmap.getConfig(), true); 235 } 236 Bitmap result = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); 237 slowDraw(new Canvas(result), 0, 0, w, h); 238 return result; 239 } 240 241 public Icon createLargeIcon(Context context) { 242 final Resources res = context.getResources(); 243 final int w = res.getDimensionPixelSize(android.R.dimen.notification_large_icon_width); 244 final int h = res.getDimensionPixelSize(android.R.dimen.notification_large_icon_height); 245 246 Bitmap result = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); 247 final Canvas canvas = new Canvas(result); 248 final Paint pt = new Paint(); 249 float[] hsv = new float[3]; 250 Color.colorToHSV(mBodyColor, hsv); 251 hsv[2] = (hsv[2]>0.5f) 252 ? (hsv[2] - 0.25f) 253 : (hsv[2] + 0.25f); 254 pt.setColor(Color.HSVToColor(hsv)); 255 float r = w/2; 256 canvas.drawCircle(r, r, r, pt); 257 int m = w/10; 258 259 slowDraw(canvas, m, m, w-m-m, h-m-m); 260 261 return Icon.createWithBitmap(result); 262 } 263 264 @Override 265 public void setAlpha(int i) { 266 267 } 268 269 @Override 270 public void setColorFilter(ColorFilter colorFilter) { 271 272 } 273 274 @Override 275 public int getOpacity() { 276 return PixelFormat.TRANSLUCENT; 277 } 278 279 public String getName() { 280 return mName; 281 } 282 283 public void setName(String name) { 284 this.mName = name; 285 } 286 287 public int getBodyColor() { 288 return mBodyColor; 289 } 290 291 public static class CatParts { 292 public Drawable leftEar; 293 public Drawable rightEar; 294 public Drawable rightEarInside; 295 public Drawable leftEarInside; 296 public Drawable head; 297 public Drawable faceSpot; 298 public Drawable cap; 299 public Drawable mouth; 300 public Drawable body; 301 public Drawable foot1; 302 public Drawable leg1; 303 public Drawable foot2; 304 public Drawable leg2; 305 public Drawable foot3; 306 public Drawable leg3; 307 public Drawable foot4; 308 public Drawable leg4; 309 public Drawable tail; 310 public Drawable leg2Shadow; 311 public Drawable tailShadow; 312 public Drawable tailCap; 313 public Drawable belly; 314 public Drawable back; 315 public Drawable rightEye; 316 public Drawable leftEye; 317 public Drawable nose; 318 public Drawable bowtie; 319 public Drawable collar; 320 public Drawable[] drawingOrder; 321 322 public CatParts(Context context) { 323 body = context.getDrawable(R.drawable.body); 324 head = context.getDrawable(R.drawable.head); 325 leg1 = context.getDrawable(R.drawable.leg1); 326 leg2 = context.getDrawable(R.drawable.leg2); 327 leg3 = context.getDrawable(R.drawable.leg3); 328 leg4 = context.getDrawable(R.drawable.leg4); 329 tail = context.getDrawable(R.drawable.tail); 330 leftEar = context.getDrawable(R.drawable.left_ear); 331 rightEar = context.getDrawable(R.drawable.right_ear); 332 rightEarInside = context.getDrawable(R.drawable.right_ear_inside); 333 leftEarInside = context.getDrawable(R.drawable.left_ear_inside); 334 faceSpot = context.getDrawable(R.drawable.face_spot); 335 cap = context.getDrawable(R.drawable.cap); 336 mouth = context.getDrawable(R.drawable.mouth); 337 foot4 = context.getDrawable(R.drawable.foot4); 338 foot3 = context.getDrawable(R.drawable.foot3); 339 foot1 = context.getDrawable(R.drawable.foot1); 340 foot2 = context.getDrawable(R.drawable.foot2); 341 leg2Shadow = context.getDrawable(R.drawable.leg2_shadow); 342 tailShadow = context.getDrawable(R.drawable.tail_shadow); 343 tailCap = context.getDrawable(R.drawable.tail_cap); 344 belly = context.getDrawable(R.drawable.belly); 345 back = context.getDrawable(R.drawable.back); 346 rightEye = context.getDrawable(R.drawable.right_eye); 347 leftEye = context.getDrawable(R.drawable.left_eye); 348 nose = context.getDrawable(R.drawable.nose); 349 collar = context.getDrawable(R.drawable.collar); 350 bowtie = context.getDrawable(R.drawable.bowtie); 351 drawingOrder = getDrawingOrder(); 352 } 353 private Drawable[] getDrawingOrder() { 354 return new Drawable[] { 355 collar, 356 leftEar, leftEarInside, rightEar, rightEarInside, 357 head, 358 faceSpot, 359 cap, 360 leftEye, rightEye, 361 nose, mouth, 362 tail, tailCap, tailShadow, 363 foot1, leg1, 364 foot2, leg2, 365 foot3, leg3, 366 foot4, leg4, 367 leg2Shadow, 368 body, belly, 369 bowtie 370 }; 371 } 372 } 373} 374