127a9fcc61823f919cee773df563b49ee11004f3bDan Sandler/* 227a9fcc61823f919cee773df563b49ee11004f3bDan Sandler * Copyright (C) 2016 The Android Open Source Project 327a9fcc61823f919cee773df563b49ee11004f3bDan Sandler * 427a9fcc61823f919cee773df563b49ee11004f3bDan Sandler * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 527a9fcc61823f919cee773df563b49ee11004f3bDan Sandler * except in compliance with the License. You may obtain a copy of the License at 627a9fcc61823f919cee773df563b49ee11004f3bDan Sandler * 727a9fcc61823f919cee773df563b49ee11004f3bDan Sandler * http://www.apache.org/licenses/LICENSE-2.0 827a9fcc61823f919cee773df563b49ee11004f3bDan Sandler * 927a9fcc61823f919cee773df563b49ee11004f3bDan Sandler * Unless required by applicable law or agreed to in writing, software distributed under the 1027a9fcc61823f919cee773df563b49ee11004f3bDan Sandler * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 1127a9fcc61823f919cee773df563b49ee11004f3bDan Sandler * KIND, either express or implied. See the License for the specific language governing 1227a9fcc61823f919cee773df563b49ee11004f3bDan Sandler * permissions and limitations under the License. 1327a9fcc61823f919cee773df563b49ee11004f3bDan Sandler */ 1427a9fcc61823f919cee773df563b49ee11004f3bDan Sandler 1527a9fcc61823f919cee773df563b49ee11004f3bDan Sandlerpackage com.android.egg.neko; 1627a9fcc61823f919cee773df563b49ee11004f3bDan Sandler 1727a9fcc61823f919cee773df563b49ee11004f3bDan Sandlerimport android.app.Notification; 1827a9fcc61823f919cee773df563b49ee11004f3bDan Sandlerimport android.app.NotificationManager; 1927a9fcc61823f919cee773df563b49ee11004f3bDan Sandlerimport android.app.job.JobInfo; 2027a9fcc61823f919cee773df563b49ee11004f3bDan Sandlerimport android.app.job.JobParameters; 2127a9fcc61823f919cee773df563b49ee11004f3bDan Sandlerimport android.app.job.JobScheduler; 2227a9fcc61823f919cee773df563b49ee11004f3bDan Sandlerimport android.app.job.JobService; 2327a9fcc61823f919cee773df563b49ee11004f3bDan Sandlerimport android.content.BroadcastReceiver; 2427a9fcc61823f919cee773df563b49ee11004f3bDan Sandlerimport android.content.ComponentName; 2527a9fcc61823f919cee773df563b49ee11004f3bDan Sandlerimport android.content.Context; 2627a9fcc61823f919cee773df563b49ee11004f3bDan Sandlerimport android.content.Intent; 2727a9fcc61823f919cee773df563b49ee11004f3bDan Sandlerimport android.os.Bundle; 2827a9fcc61823f919cee773df563b49ee11004f3bDan Sandler 2927a9fcc61823f919cee773df563b49ee11004f3bDan Sandlerimport java.util.List; 3027a9fcc61823f919cee773df563b49ee11004f3bDan Sandlerimport android.util.Log; 3127a9fcc61823f919cee773df563b49ee11004f3bDan Sandler 3227a9fcc61823f919cee773df563b49ee11004f3bDan Sandlerimport com.android.egg.R; 3327a9fcc61823f919cee773df563b49ee11004f3bDan Sandler 3427a9fcc61823f919cee773df563b49ee11004f3bDan Sandlerimport java.util.Random; 3527a9fcc61823f919cee773df563b49ee11004f3bDan Sandler 3627a9fcc61823f919cee773df563b49ee11004f3bDan Sandlerpublic class NekoService extends JobService { 3727a9fcc61823f919cee773df563b49ee11004f3bDan Sandler 3827a9fcc61823f919cee773df563b49ee11004f3bDan Sandler private static final String TAG = "NekoService"; 3927a9fcc61823f919cee773df563b49ee11004f3bDan Sandler 4027a9fcc61823f919cee773df563b49ee11004f3bDan Sandler public static int JOB_ID = 42; 4127a9fcc61823f919cee773df563b49ee11004f3bDan Sandler 4227a9fcc61823f919cee773df563b49ee11004f3bDan Sandler public static int CAT_NOTIFICATION = 1; 4327a9fcc61823f919cee773df563b49ee11004f3bDan Sandler 4427a9fcc61823f919cee773df563b49ee11004f3bDan Sandler public static float CAT_CAPTURE_PROB = 1.0f; // generous 4527a9fcc61823f919cee773df563b49ee11004f3bDan Sandler 4627a9fcc61823f919cee773df563b49ee11004f3bDan Sandler public static long SECONDS = 1000; 4727a9fcc61823f919cee773df563b49ee11004f3bDan Sandler public static long MINUTES = 60 * SECONDS; 4827a9fcc61823f919cee773df563b49ee11004f3bDan Sandler 4927a9fcc61823f919cee773df563b49ee11004f3bDan Sandler public static long INTERVAL_FLEX = 5 * MINUTES; 5027a9fcc61823f919cee773df563b49ee11004f3bDan Sandler 5127a9fcc61823f919cee773df563b49ee11004f3bDan Sandler public static float INTERVAL_JITTER_FRAC = 0.25f; 5227a9fcc61823f919cee773df563b49ee11004f3bDan Sandler 5327a9fcc61823f919cee773df563b49ee11004f3bDan Sandler @Override 5427a9fcc61823f919cee773df563b49ee11004f3bDan Sandler public boolean onStartJob(JobParameters params) { 5527a9fcc61823f919cee773df563b49ee11004f3bDan Sandler Log.v(TAG, "Starting job: " + String.valueOf(params)); 5627a9fcc61823f919cee773df563b49ee11004f3bDan Sandler 5727a9fcc61823f919cee773df563b49ee11004f3bDan Sandler NotificationManager noman = getSystemService(NotificationManager.class); 5827a9fcc61823f919cee773df563b49ee11004f3bDan Sandler if (NekoLand.DEBUG_NOTIFICATIONS) { 5927a9fcc61823f919cee773df563b49ee11004f3bDan Sandler final Bundle extras = new Bundle(); 6027a9fcc61823f919cee773df563b49ee11004f3bDan Sandler extras.putString("android.substName", getString(R.string.notification_name)); 6127a9fcc61823f919cee773df563b49ee11004f3bDan Sandler final int size = getResources() 6227a9fcc61823f919cee773df563b49ee11004f3bDan Sandler .getDimensionPixelSize(android.R.dimen.notification_large_icon_width); 6327a9fcc61823f919cee773df563b49ee11004f3bDan Sandler final Cat cat = Cat.create(this); 6427a9fcc61823f919cee773df563b49ee11004f3bDan Sandler final Notification.Builder builder 6527a9fcc61823f919cee773df563b49ee11004f3bDan Sandler = cat.buildNotification(this) 6627a9fcc61823f919cee773df563b49ee11004f3bDan Sandler .setContentTitle("DEBUG") 6727a9fcc61823f919cee773df563b49ee11004f3bDan Sandler .setContentText("Ran job: " + params); 6827a9fcc61823f919cee773df563b49ee11004f3bDan Sandler noman.notify(1, builder.build()); 6927a9fcc61823f919cee773df563b49ee11004f3bDan Sandler } 7027a9fcc61823f919cee773df563b49ee11004f3bDan Sandler 7127a9fcc61823f919cee773df563b49ee11004f3bDan Sandler final PrefState prefs = new PrefState(this); 7227a9fcc61823f919cee773df563b49ee11004f3bDan Sandler int food = prefs.getFoodState(); 7327a9fcc61823f919cee773df563b49ee11004f3bDan Sandler if (food != 0) { 7427a9fcc61823f919cee773df563b49ee11004f3bDan Sandler prefs.setFoodState(0); // nom 7527a9fcc61823f919cee773df563b49ee11004f3bDan Sandler final Random rng = new Random(); 7627a9fcc61823f919cee773df563b49ee11004f3bDan Sandler if (rng.nextFloat() <= CAT_CAPTURE_PROB) { 7727a9fcc61823f919cee773df563b49ee11004f3bDan Sandler Cat cat; 7827a9fcc61823f919cee773df563b49ee11004f3bDan Sandler List<Cat> cats = prefs.getCats(); 7927a9fcc61823f919cee773df563b49ee11004f3bDan Sandler final int[] probs = getResources().getIntArray(R.array.food_new_cat_prob); 8027a9fcc61823f919cee773df563b49ee11004f3bDan Sandler final float new_cat_prob = (float)((food < probs.length) ? probs[food] : 50) / 100f; 8127a9fcc61823f919cee773df563b49ee11004f3bDan Sandler 8227a9fcc61823f919cee773df563b49ee11004f3bDan Sandler if (cats.size() == 0 || rng.nextFloat() <= new_cat_prob) { 8327a9fcc61823f919cee773df563b49ee11004f3bDan Sandler cat = Cat.create(this); 8427a9fcc61823f919cee773df563b49ee11004f3bDan Sandler prefs.addCat(cat); 85a801d40531b71f8c75bb03bb8fec429f503e391eChris Wren cat.logAdd(this); 8627a9fcc61823f919cee773df563b49ee11004f3bDan Sandler Log.v(TAG, "A new cat is here: " + cat.getName()); 8727a9fcc61823f919cee773df563b49ee11004f3bDan Sandler } else { 8827a9fcc61823f919cee773df563b49ee11004f3bDan Sandler cat = cats.get(rng.nextInt(cats.size())); 8927a9fcc61823f919cee773df563b49ee11004f3bDan Sandler Log.v(TAG, "A cat has returned: " + cat.getName()); 9027a9fcc61823f919cee773df563b49ee11004f3bDan Sandler } 9127a9fcc61823f919cee773df563b49ee11004f3bDan Sandler 9227a9fcc61823f919cee773df563b49ee11004f3bDan Sandler final Notification.Builder builder = cat.buildNotification(this); 9327a9fcc61823f919cee773df563b49ee11004f3bDan Sandler noman.notify(CAT_NOTIFICATION, builder.build()); 9427a9fcc61823f919cee773df563b49ee11004f3bDan Sandler } 9527a9fcc61823f919cee773df563b49ee11004f3bDan Sandler } 9627a9fcc61823f919cee773df563b49ee11004f3bDan Sandler cancelJob(this); 9727a9fcc61823f919cee773df563b49ee11004f3bDan Sandler return false; 9827a9fcc61823f919cee773df563b49ee11004f3bDan Sandler } 9927a9fcc61823f919cee773df563b49ee11004f3bDan Sandler 10027a9fcc61823f919cee773df563b49ee11004f3bDan Sandler @Override 10127a9fcc61823f919cee773df563b49ee11004f3bDan Sandler public boolean onStopJob(JobParameters jobParameters) { 10227a9fcc61823f919cee773df563b49ee11004f3bDan Sandler return false; 10327a9fcc61823f919cee773df563b49ee11004f3bDan Sandler } 10427a9fcc61823f919cee773df563b49ee11004f3bDan Sandler 105946c2b89396554b2618ea5bbaff4652f75768016Jason Monk public static void registerJobIfNeeded(Context context, long intervalMinutes) { 106946c2b89396554b2618ea5bbaff4652f75768016Jason Monk JobScheduler jss = context.getSystemService(JobScheduler.class); 107946c2b89396554b2618ea5bbaff4652f75768016Jason Monk JobInfo info = jss.getPendingJob(JOB_ID); 108946c2b89396554b2618ea5bbaff4652f75768016Jason Monk if (info == null) { 109946c2b89396554b2618ea5bbaff4652f75768016Jason Monk registerJob(context, intervalMinutes); 110946c2b89396554b2618ea5bbaff4652f75768016Jason Monk } 111946c2b89396554b2618ea5bbaff4652f75768016Jason Monk } 112946c2b89396554b2618ea5bbaff4652f75768016Jason Monk 11327a9fcc61823f919cee773df563b49ee11004f3bDan Sandler public static void registerJob(Context context, long intervalMinutes) { 11427a9fcc61823f919cee773df563b49ee11004f3bDan Sandler JobScheduler jss = context.getSystemService(JobScheduler.class); 11527a9fcc61823f919cee773df563b49ee11004f3bDan Sandler jss.cancel(JOB_ID); 11627a9fcc61823f919cee773df563b49ee11004f3bDan Sandler long interval = intervalMinutes * MINUTES; 11727a9fcc61823f919cee773df563b49ee11004f3bDan Sandler long jitter = (long)(INTERVAL_JITTER_FRAC * interval); 11827a9fcc61823f919cee773df563b49ee11004f3bDan Sandler interval += (long)(Math.random() * (2 * jitter)) - jitter; 11927a9fcc61823f919cee773df563b49ee11004f3bDan Sandler final JobInfo jobInfo = new JobInfo.Builder(JOB_ID, 12027a9fcc61823f919cee773df563b49ee11004f3bDan Sandler new ComponentName(context, NekoService.class)) 12127a9fcc61823f919cee773df563b49ee11004f3bDan Sandler .setPeriodic(interval, INTERVAL_FLEX) 12227a9fcc61823f919cee773df563b49ee11004f3bDan Sandler .build(); 12327a9fcc61823f919cee773df563b49ee11004f3bDan Sandler 12427a9fcc61823f919cee773df563b49ee11004f3bDan Sandler Log.v(TAG, "A cat will visit in " + interval + "ms: " + String.valueOf(jobInfo)); 12527a9fcc61823f919cee773df563b49ee11004f3bDan Sandler jss.schedule(jobInfo); 12627a9fcc61823f919cee773df563b49ee11004f3bDan Sandler 12727a9fcc61823f919cee773df563b49ee11004f3bDan Sandler if (NekoLand.DEBUG_NOTIFICATIONS) { 12827a9fcc61823f919cee773df563b49ee11004f3bDan Sandler NotificationManager noman = context.getSystemService(NotificationManager.class); 12927a9fcc61823f919cee773df563b49ee11004f3bDan Sandler noman.notify(500, new Notification.Builder(context) 13027a9fcc61823f919cee773df563b49ee11004f3bDan Sandler .setSmallIcon(R.drawable.stat_icon) 13127a9fcc61823f919cee773df563b49ee11004f3bDan Sandler .setContentTitle(String.format("Job scheduled in %d min", (interval / MINUTES))) 13227a9fcc61823f919cee773df563b49ee11004f3bDan Sandler .setContentText(String.valueOf(jobInfo)) 13327a9fcc61823f919cee773df563b49ee11004f3bDan Sandler .setPriority(Notification.PRIORITY_MIN) 13427a9fcc61823f919cee773df563b49ee11004f3bDan Sandler .setCategory(Notification.CATEGORY_SERVICE) 13527a9fcc61823f919cee773df563b49ee11004f3bDan Sandler .setShowWhen(true) 13627a9fcc61823f919cee773df563b49ee11004f3bDan Sandler .build()); 13727a9fcc61823f919cee773df563b49ee11004f3bDan Sandler } 13827a9fcc61823f919cee773df563b49ee11004f3bDan Sandler } 13927a9fcc61823f919cee773df563b49ee11004f3bDan Sandler 14027a9fcc61823f919cee773df563b49ee11004f3bDan Sandler public static void cancelJob(Context context) { 14127a9fcc61823f919cee773df563b49ee11004f3bDan Sandler JobScheduler jss = context.getSystemService(JobScheduler.class); 14227a9fcc61823f919cee773df563b49ee11004f3bDan Sandler Log.v(TAG, "Canceling job"); 14327a9fcc61823f919cee773df563b49ee11004f3bDan Sandler jss.cancel(JOB_ID); 14427a9fcc61823f919cee773df563b49ee11004f3bDan Sandler } 14527a9fcc61823f919cee773df563b49ee11004f3bDan Sandler} 146