1 /* 2 * Copyright 2013 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.example.android.jobscheduler; 18 19 import android.app.Activity; 20 import android.app.job.JobInfo; 21 import android.app.job.JobScheduler; 22 import android.content.ComponentName; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.os.Bundle; 26 import android.os.Handler; 27 import android.os.Message; 28 import android.os.Messenger; 29 import android.os.PersistableBundle; 30 import android.support.annotation.ColorRes; 31 import android.support.annotation.Nullable; 32 import android.text.TextUtils; 33 import android.util.Log; 34 import android.view.View; 35 import android.widget.CheckBox; 36 import android.widget.EditText; 37 import android.widget.RadioButton; 38 import android.widget.TextView; 39 import android.widget.Toast; 40 41 import com.example.android.jobscheduler.service.MyJobService; 42 43 import java.lang.ref.WeakReference; 44 import java.util.List; 45 46 47 /** 48 * Schedules and configures jobs to be executed by a {@link JobScheduler}. 49 * <p> 50 * {@link MyJobService} can send messages to this via a {@link Messenger} 51 * that is sent in the Intent that starts the Service. 52 */ 53 public class MainActivity extends Activity { 54 55 private static final String TAG = MainActivity.class.getSimpleName(); 56 57 public static final int MSG_UNCOLOR_START = 0; 58 public static final int MSG_UNCOLOR_STOP = 1; 59 public static final int MSG_COLOR_START = 2; 60 public static final int MSG_COLOR_STOP = 3; 61 62 public static final String MESSENGER_INTENT_KEY 63 = BuildConfig.APPLICATION_ID + ".MESSENGER_INTENT_KEY"; 64 public static final String WORK_DURATION_KEY = 65 BuildConfig.APPLICATION_ID + ".WORK_DURATION_KEY"; 66 67 private EditText mDelayEditText; 68 private EditText mDeadlineEditText; 69 private EditText mDurationTimeEditText; 70 private RadioButton mWiFiConnectivityRadioButton; 71 private RadioButton mAnyConnectivityRadioButton; 72 private CheckBox mRequiresChargingCheckBox; 73 private CheckBox mRequiresIdleCheckbox; 74 75 private ComponentName mServiceComponent; 76 77 private int mJobId = 0; 78 79 // Handler for incoming messages from the service. 80 private IncomingMessageHandler mHandler; 81 82 @Override onCreate(Bundle savedInstanceState)83 public void onCreate(Bundle savedInstanceState) { 84 super.onCreate(savedInstanceState); 85 setContentView(R.layout.sample_main); 86 87 // Set up UI. 88 mDelayEditText = (EditText) findViewById(R.id.delay_time); 89 mDurationTimeEditText = (EditText) findViewById(R.id.duration_time); 90 mDeadlineEditText = (EditText) findViewById(R.id.deadline_time); 91 mWiFiConnectivityRadioButton = (RadioButton) findViewById(R.id.checkbox_unmetered); 92 mAnyConnectivityRadioButton = (RadioButton) findViewById(R.id.checkbox_any); 93 mRequiresChargingCheckBox = (CheckBox) findViewById(R.id.checkbox_charging); 94 mRequiresIdleCheckbox = (CheckBox) findViewById(R.id.checkbox_idle); 95 mServiceComponent = new ComponentName(this, MyJobService.class); 96 97 mHandler = new IncomingMessageHandler(this); 98 } 99 100 @Override onStop()101 protected void onStop() { 102 // A service can be "started" and/or "bound". In this case, it's "started" by this Activity 103 // and "bound" to the JobScheduler (also called "Scheduled" by the JobScheduler). This call 104 // to stopService() won't prevent scheduled jobs to be processed. However, failing 105 // to call stopService() would keep it alive indefinitely. 106 stopService(new Intent(this, MyJobService.class)); 107 super.onStop(); 108 } 109 110 @Override onStart()111 protected void onStart() { 112 super.onStart(); 113 // Start service and provide it a way to communicate with this class. 114 Intent startServiceIntent = new Intent(this, MyJobService.class); 115 Messenger messengerIncoming = new Messenger(mHandler); 116 startServiceIntent.putExtra(MESSENGER_INTENT_KEY, messengerIncoming); 117 startService(startServiceIntent); 118 } 119 120 /** 121 * Executed when user clicks on SCHEDULE JOB. 122 */ scheduleJob(View v)123 public void scheduleJob(View v) { 124 JobInfo.Builder builder = new JobInfo.Builder(mJobId++, mServiceComponent); 125 126 String delay = mDelayEditText.getText().toString(); 127 if (!TextUtils.isEmpty(delay)) { 128 builder.setMinimumLatency(Long.valueOf(delay) * 1000); 129 } 130 String deadline = mDeadlineEditText.getText().toString(); 131 if (!TextUtils.isEmpty(deadline)) { 132 builder.setOverrideDeadline(Long.valueOf(deadline) * 1000); 133 } 134 boolean requiresUnmetered = mWiFiConnectivityRadioButton.isChecked(); 135 boolean requiresAnyConnectivity = mAnyConnectivityRadioButton.isChecked(); 136 if (requiresUnmetered) { 137 builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED); 138 } else if (requiresAnyConnectivity) { 139 builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY); 140 } 141 builder.setRequiresDeviceIdle(mRequiresIdleCheckbox.isChecked()); 142 builder.setRequiresCharging(mRequiresChargingCheckBox.isChecked()); 143 144 // Extras, work duration. 145 PersistableBundle extras = new PersistableBundle(); 146 String workDuration = mDurationTimeEditText.getText().toString(); 147 if (TextUtils.isEmpty(workDuration)) { 148 workDuration = "1"; 149 } 150 extras.putLong(WORK_DURATION_KEY, Long.valueOf(workDuration) * 1000); 151 152 builder.setExtras(extras); 153 154 // Schedule job 155 Log.d(TAG, "Scheduling job"); 156 JobScheduler tm = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE); 157 tm.schedule(builder.build()); 158 } 159 160 /** 161 * Executed when user clicks on CANCEL ALL. 162 */ cancelAllJobs(View v)163 public void cancelAllJobs(View v) { 164 JobScheduler tm = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE); 165 tm.cancelAll(); 166 Toast.makeText(MainActivity.this, R.string.all_jobs_cancelled, Toast.LENGTH_SHORT).show(); 167 } 168 169 /** 170 * Executed when user clicks on FINISH LAST TASK. 171 */ finishJob(View v)172 public void finishJob(View v) { 173 JobScheduler jobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE); 174 List<JobInfo> allPendingJobs = jobScheduler.getAllPendingJobs(); 175 if (allPendingJobs.size() > 0) { 176 // Finish the last one 177 int jobId = allPendingJobs.get(0).getId(); 178 jobScheduler.cancel(jobId); 179 Toast.makeText( 180 MainActivity.this, String.format(getString(R.string.cancelled_job), jobId), 181 Toast.LENGTH_SHORT).show(); 182 } else { 183 Toast.makeText( 184 MainActivity.this, getString(R.string.no_jobs_to_cancel), 185 Toast.LENGTH_SHORT).show(); 186 } 187 } 188 189 /** 190 * A {@link Handler} allows you to send messages associated with a thread. A {@link Messenger} 191 * uses this handler to communicate from {@link MyJobService}. It's also used to make 192 * the start and stop views blink for a short period of time. 193 */ 194 private static class IncomingMessageHandler extends Handler { 195 196 // Prevent possible leaks with a weak reference. 197 private WeakReference<MainActivity> mActivity; 198 IncomingMessageHandler(MainActivity activity)199 IncomingMessageHandler(MainActivity activity) { 200 super(/* default looper */); 201 this.mActivity = new WeakReference<>(activity); 202 } 203 204 @Override handleMessage(Message msg)205 public void handleMessage(Message msg) { 206 MainActivity mainActivity = mActivity.get(); 207 if (mainActivity == null) { 208 // Activity is no longer available, exit. 209 return; 210 } 211 View showStartView = mainActivity.findViewById(R.id.onstart_textview); 212 View showStopView = mainActivity.findViewById(R.id.onstop_textview); 213 Message m; 214 switch (msg.what) { 215 /* 216 * Receives callback from the service when a job has landed 217 * on the app. Turns on indicator and sends a message to turn it off after 218 * a second. 219 */ 220 case MSG_COLOR_START: 221 // Start received, turn on the indicator and show text. 222 showStartView.setBackgroundColor(getColor(R.color.start_received)); 223 updateParamsTextView(msg.obj, "started"); 224 225 // Send message to turn it off after a second. 226 m = Message.obtain(this, MSG_UNCOLOR_START); 227 sendMessageDelayed(m, 1000L); 228 break; 229 /* 230 * Receives callback from the service when a job that previously landed on the 231 * app must stop executing. Turns on indicator and sends a message to turn it 232 * off after two seconds. 233 */ 234 case MSG_COLOR_STOP: 235 // Stop received, turn on the indicator and show text. 236 showStopView.setBackgroundColor(getColor(R.color.stop_received)); 237 updateParamsTextView(msg.obj, "stopped"); 238 239 // Send message to turn it off after a second. 240 m = obtainMessage(MSG_UNCOLOR_STOP); 241 sendMessageDelayed(m, 2000L); 242 break; 243 case MSG_UNCOLOR_START: 244 showStartView.setBackgroundColor(getColor(R.color.none_received)); 245 updateParamsTextView(null, ""); 246 break; 247 case MSG_UNCOLOR_STOP: 248 showStopView.setBackgroundColor(getColor(R.color.none_received)); 249 updateParamsTextView(null, ""); 250 break; 251 } 252 } 253 updateParamsTextView(@ullable Object jobId, String action)254 private void updateParamsTextView(@Nullable Object jobId, String action) { 255 TextView paramsTextView = (TextView) mActivity.get().findViewById(R.id.task_params); 256 if (jobId == null) { 257 paramsTextView.setText(""); 258 return; 259 } 260 String jobIdText = String.valueOf(jobId); 261 paramsTextView.setText(String.format("Job ID %s %s", jobIdText, action)); 262 } 263 getColor(@olorRes int color)264 private int getColor(@ColorRes int color) { 265 return mActivity.get().getResources().getColor(color); 266 } 267 } 268 } 269