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