/* Copyright (C) 2014 Sergii Pylypenko. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.cups.android; import android.app.Activity; import android.app.Service; import android.content.Context; import android.os.Bundle; import android.os.IBinder; import android.view.MotionEvent; import android.view.KeyEvent; import android.view.Window; import android.view.WindowManager; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import android.widget.EditText; import android.text.Editable; import android.widget.Button; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.FrameLayout; import android.graphics.drawable.Drawable; import android.graphics.Color; import android.content.res.Configuration; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Intent; import android.view.View.OnKeyListener; import android.view.MenuItem; import android.view.Menu; import android.view.Gravity; import android.text.method.TextKeyListener; import java.util.LinkedList; import java.io.SequenceInputStream; import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.FileOutputStream; import java.io.File; import java.io.FileInputStream; import java.util.Set; import android.text.SpannedString; import java.io.BufferedReader; import java.io.BufferedInputStream; import java.io.InputStreamReader; import android.view.inputmethod.InputMethodManager; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.os.Handler; import android.os.Message; import android.os.SystemClock; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import android.content.pm.ActivityInfo; import android.view.Display; import android.text.InputType; import android.util.Log; import android.view.Surface; import android.app.ProgressDialog; import android.text.util.Linkify; import android.provider.Settings; import android.app.AlertDialog; import android.widget.ScrollView; import android.content.DialogInterface; import android.net.Uri; import java.util.*; public class PrintJobs { private static boolean destroyed = false; private static HashMap<android.printservice.PrintJob, String[]> trackedJobs = new HashMap<android.printservice.PrintJob, String[]>(); private static CupsPrintService context = null; private static Handler mainThread = null; synchronized public static void init(final CupsPrintService c) { if (context != null) Log.e(TAG, "Error: Already initialized with context " + context + ", new context " + c); destroyed = false; context = c; mainThread = new Handler(context.getMainLooper()); Log.d(TAG, "Creating jobs tracking thread"); new Thread(new Runnable() { public void run() { trackJobsThread(); } }).start(); } synchronized public static void destroy() { Log.d(TAG, "Destroying jobs tracking thread"); destroyed = true; } synchronized public static void trackJob(final android.printservice.PrintJob job) { trackedJobs.put(job, new String[] {job.getTag(), job.getInfo().getPrinterId().getLocalId()}); Log.d(TAG, "Started tracking job: " + job.getTag() + " printer " + job.getInfo().getPrinterId().getLocalId()); } synchronized public static void stopTrackingJob(final android.printservice.PrintJob job) { trackedJobs.remove(job); } private static String getJobStatus(final String[] jobInfo) { for (String s: jobInfo) { if (s.startsWith("Status:") && s.length() > "Status:".length() + 1) return s.substring("Status:".length() + 1); } return ""; } private static boolean isJobSucceeded(final String[] jobInfo) { for (String s: jobInfo) { if (s.equals("Alerts: job-completed-successfully") || s.equals("Alerts: processing-to-stop-point")) return true; } return false; } synchronized private static HashMap<android.printservice.PrintJob, String[]> copyTrackedJobsArray() { // I'm to lazy to find out how to synchronize class object from static context, so I'll just create another synchronized method return new HashMap<android.printservice.PrintJob, String[]>(trackedJobs); } private static void trackJobsThread() { while (!destroyed) { try { Thread.sleep(10000); } catch (Exception e) { } // Invoking commandline tools is slow, so we will create local copy of trackedJobs, and only lock it when changing it HashMap<android.printservice.PrintJob, String[]> trackedCopy = copyTrackedJobsArray(); final HashMap<String, Map<String, String[]> > activeJobs = new HashMap<String, Map<String, String[]> >(); final HashMap<String, Map<String, String[]> > completedJobs = new HashMap<String, Map<String, String[]> >(); for (final android.printservice.PrintJob job: trackedCopy.keySet()) { final String jobName = trackedJobs.get(job)[0]; final String printer = trackedJobs.get(job)[1]; if (!activeJobs.containsKey(printer)) activeJobs.put(printer, Cups.getPrintJobs(context, printer, false)); if (activeJobs.get(printer).containsKey(jobName)) { mainThread.post(new Runnable() { public void run() { String status = getJobStatus(activeJobs.get(printer).get(jobName)); Log.d(TAG, "Print job " + jobName + " status: " + status); if (status.length() > 0) job.block(status); else if (!job.isStarted()) job.start(); } }); } else { if (!completedJobs.containsKey(printer)) completedJobs.put(printer, Cups.getPrintJobs(context, printer, true)); mainThread.post(new Runnable() { public void run() { if (!completedJobs.get(printer).containsKey(jobName)) job.fail(context.getResources().getString(R.string.error_job_disappeared)); else if (isJobSucceeded(completedJobs.get(printer).get(jobName))) job.complete(); else { String status = getJobStatus(completedJobs.get(printer).get(jobName)); Log.d(TAG, "Print job " + jobName + " failed with status: " + status); job.fail(status); } } }); stopTrackingJob(job); } } } } static public final String TAG = "PrintJobs"; }