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 15 package com.android.egg.neko; 16 17 import android.app.Notification; 18 import android.app.NotificationManager; 19 import android.app.job.JobInfo; 20 import android.app.job.JobParameters; 21 import android.app.job.JobScheduler; 22 import android.app.job.JobService; 23 import android.content.BroadcastReceiver; 24 import android.content.ComponentName; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.os.Bundle; 28 29 import java.util.List; 30 import android.util.Log; 31 32 import com.android.egg.R; 33 34 import java.util.Random; 35 36 public class NekoService extends JobService { 37 38 private static final String TAG = "NekoService"; 39 40 public static int JOB_ID = 42; 41 42 public static int CAT_NOTIFICATION = 1; 43 44 public static float CAT_CAPTURE_PROB = 1.0f; // generous 45 46 public static long SECONDS = 1000; 47 public static long MINUTES = 60 * SECONDS; 48 49 public static long INTERVAL_FLEX = 5 * MINUTES; 50 51 public static float INTERVAL_JITTER_FRAC = 0.25f; 52 53 @Override onStartJob(JobParameters params)54 public boolean onStartJob(JobParameters params) { 55 Log.v(TAG, "Starting job: " + String.valueOf(params)); 56 57 NotificationManager noman = getSystemService(NotificationManager.class); 58 if (NekoLand.DEBUG_NOTIFICATIONS) { 59 final Bundle extras = new Bundle(); 60 extras.putString("android.substName", getString(R.string.notification_name)); 61 final int size = getResources() 62 .getDimensionPixelSize(android.R.dimen.notification_large_icon_width); 63 final Cat cat = Cat.create(this); 64 final Notification.Builder builder 65 = cat.buildNotification(this) 66 .setContentTitle("DEBUG") 67 .setContentText("Ran job: " + params); 68 noman.notify(1, builder.build()); 69 } 70 71 final PrefState prefs = new PrefState(this); 72 int food = prefs.getFoodState(); 73 if (food != 0) { 74 prefs.setFoodState(0); // nom 75 final Random rng = new Random(); 76 if (rng.nextFloat() <= CAT_CAPTURE_PROB) { 77 Cat cat; 78 List<Cat> cats = prefs.getCats(); 79 final int[] probs = getResources().getIntArray(R.array.food_new_cat_prob); 80 final float new_cat_prob = (float)((food < probs.length) ? probs[food] : 50) / 100f; 81 82 if (cats.size() == 0 || rng.nextFloat() <= new_cat_prob) { 83 cat = Cat.create(this); 84 prefs.addCat(cat); 85 cat.logAdd(this); 86 Log.v(TAG, "A new cat is here: " + cat.getName()); 87 } else { 88 cat = cats.get(rng.nextInt(cats.size())); 89 Log.v(TAG, "A cat has returned: " + cat.getName()); 90 } 91 92 final Notification.Builder builder = cat.buildNotification(this); 93 noman.notify(CAT_NOTIFICATION, builder.build()); 94 } 95 } 96 cancelJob(this); 97 return false; 98 } 99 100 @Override onStopJob(JobParameters jobParameters)101 public boolean onStopJob(JobParameters jobParameters) { 102 return false; 103 } 104 registerJobIfNeeded(Context context, long intervalMinutes)105 public static void registerJobIfNeeded(Context context, long intervalMinutes) { 106 JobScheduler jss = context.getSystemService(JobScheduler.class); 107 JobInfo info = jss.getPendingJob(JOB_ID); 108 if (info == null) { 109 registerJob(context, intervalMinutes); 110 } 111 } 112 registerJob(Context context, long intervalMinutes)113 public static void registerJob(Context context, long intervalMinutes) { 114 JobScheduler jss = context.getSystemService(JobScheduler.class); 115 jss.cancel(JOB_ID); 116 long interval = intervalMinutes * MINUTES; 117 long jitter = (long)(INTERVAL_JITTER_FRAC * interval); 118 interval += (long)(Math.random() * (2 * jitter)) - jitter; 119 final JobInfo jobInfo = new JobInfo.Builder(JOB_ID, 120 new ComponentName(context, NekoService.class)) 121 .setPeriodic(interval, INTERVAL_FLEX) 122 .build(); 123 124 Log.v(TAG, "A cat will visit in " + interval + "ms: " + String.valueOf(jobInfo)); 125 jss.schedule(jobInfo); 126 127 if (NekoLand.DEBUG_NOTIFICATIONS) { 128 NotificationManager noman = context.getSystemService(NotificationManager.class); 129 noman.notify(500, new Notification.Builder(context) 130 .setSmallIcon(R.drawable.stat_icon) 131 .setContentTitle(String.format("Job scheduled in %d min", (interval / MINUTES))) 132 .setContentText(String.valueOf(jobInfo)) 133 .setPriority(Notification.PRIORITY_MIN) 134 .setCategory(Notification.CATEGORY_SERVICE) 135 .setShowWhen(true) 136 .build()); 137 } 138 } 139 cancelJob(Context context)140 public static void cancelJob(Context context) { 141 JobScheduler jss = context.getSystemService(JobScheduler.class); 142 Log.v(TAG, "Canceling job"); 143 jss.cancel(JOB_ID); 144 } 145 } 146