blob: e3d8d05e7a802c72d1064690cb160b26b641970b [file] [log] [blame]
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -07001/*
2 * Copyright (C) 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
17package com.android.printspooler;
18
19import android.app.Activity;
Svetoslav Ganov14db9652013-08-06 14:40:46 -070020import android.app.Dialog;
Svetoslav269403b2013-08-14 17:31:04 -070021import android.app.LoaderManager;
Svetoslavb4fda132013-10-25 18:57:43 -070022import android.content.ActivityNotFoundException;
Svetoslav7bfbbcb2013-10-10 13:36:23 -070023import android.content.ComponentName;
Svetoslav597945f2013-07-17 18:37:36 -070024import android.content.Context;
Svetoslav269403b2013-08-14 17:31:04 -070025import android.content.Intent;
26import android.content.Loader;
Svetoslav7bfbbcb2013-10-10 13:36:23 -070027import android.content.ServiceConnection;
Svetoslav597945f2013-07-17 18:37:36 -070028import android.content.pm.PackageInfo;
Svetoslav597945f2013-07-17 18:37:36 -070029import android.content.pm.PackageManager.NameNotFoundException;
Svetoslavb4fda132013-10-25 18:57:43 -070030import android.content.pm.ResolveInfo;
31import android.content.pm.ServiceInfo;
Svetoslav66160bb2013-08-12 22:36:50 -070032import android.database.DataSetObserver;
Svetoslav597945f2013-07-17 18:37:36 -070033import android.graphics.Rect;
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -070034import android.graphics.drawable.Drawable;
Svetoslav269403b2013-08-14 17:31:04 -070035import android.net.Uri;
36import android.os.AsyncTask;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070037import android.os.Bundle;
38import android.os.Handler;
39import android.os.IBinder;
Svetoslav Ganova0027152013-06-25 14:59:53 -070040import android.os.IBinder.DeathRecipient;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070041import android.os.Looper;
42import android.os.Message;
43import android.os.RemoteException;
Svetoslav Ganov85b1f882013-07-24 17:00:06 -070044import android.print.ILayoutResultCallback;
Svetoslav Ganova0027152013-06-25 14:59:53 -070045import android.print.IPrintDocumentAdapter;
Svetoslav Ganov858a1852013-10-17 22:20:40 -070046import android.print.IPrintDocumentAdapterObserver;
Svetoslav Ganov85b1f882013-07-24 17:00:06 -070047import android.print.IWriteResultCallback;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070048import android.print.PageRange;
49import android.print.PrintAttributes;
Svetoslavcc65b0c2013-09-10 21:08:32 -070050import android.print.PrintAttributes.Margins;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070051import android.print.PrintAttributes.MediaSize;
Svetoslav269403b2013-08-14 17:31:04 -070052import android.print.PrintAttributes.Resolution;
Svetoslav Ganov85b1f882013-07-24 17:00:06 -070053import android.print.PrintDocumentAdapter;
Svetoslav Ganova0027152013-06-25 14:59:53 -070054import android.print.PrintDocumentInfo;
Svetoslav2fbd2a72013-09-16 17:53:51 -070055import android.print.PrintJobId;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070056import android.print.PrintJobInfo;
Svetoslav269403b2013-08-14 17:31:04 -070057import android.print.PrintManager;
Svetoslav Ganov798bed62013-08-11 12:29:39 -070058import android.print.PrinterCapabilitiesInfo;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070059import android.print.PrinterId;
60import android.print.PrinterInfo;
Svetoslavb4fda132013-10-25 18:57:43 -070061import android.printservice.PrintService;
62import android.printservice.PrintServiceInfo;
Svetoslav7bfbbcb2013-10-10 13:36:23 -070063import android.provider.DocumentsContract;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070064import android.text.Editable;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070065import android.text.TextUtils;
Svetoslav Ganov85b1f882013-07-24 17:00:06 -070066import android.text.TextUtils.SimpleStringSplitter;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070067import android.text.TextWatcher;
Svetoslav4e4874b2013-10-01 17:53:17 -070068import android.util.ArrayMap;
Svetoslav597945f2013-07-17 18:37:36 -070069import android.util.AttributeSet;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070070import android.util.Log;
Svetoslav3aa2e2b2013-10-10 16:46:06 -070071import android.view.Gravity;
Svetoslav Ganov85b1f882013-07-24 17:00:06 -070072import android.view.KeyEvent;
73import android.view.MotionEvent;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070074import android.view.View;
Svetoslav Ganov32c5eb32013-08-06 23:49:25 -070075import android.view.View.MeasureSpec;
Svetoslav Ganov860f8a62013-09-14 00:59:03 -070076import android.view.View.OnAttachStateChangeListener;
Svetoslav597945f2013-07-17 18:37:36 -070077import android.view.View.OnClickListener;
Svetoslav89ed9fc2013-10-11 17:00:45 -070078import android.view.View.OnFocusChangeListener;
Svetoslav Ganov695c7fa2013-08-07 19:29:42 -070079import android.view.ViewConfiguration;
Svetoslav597945f2013-07-17 18:37:36 -070080import android.view.ViewGroup;
Svetoslav7bfbbcb2013-10-10 13:36:23 -070081import android.view.ViewGroup.LayoutParams;
Svetoslav Ganov860f8a62013-09-14 00:59:03 -070082import android.view.ViewPropertyAnimator;
Svetoslav597945f2013-07-17 18:37:36 -070083import android.view.inputmethod.InputMethodManager;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070084import android.widget.AdapterView;
85import android.widget.AdapterView.OnItemSelectedListener;
86import android.widget.ArrayAdapter;
Svetoslav66160bb2013-08-12 22:36:50 -070087import android.widget.BaseAdapter;
Svetoslav597945f2013-07-17 18:37:36 -070088import android.widget.Button;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070089import android.widget.EditText;
Svetoslav3aa2e2b2013-10-10 16:46:06 -070090import android.widget.FrameLayout;
Svetoslavc335eb42013-09-26 15:55:47 -070091import android.widget.ImageView;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070092import android.widget.Spinner;
Svetoslav597945f2013-07-17 18:37:36 -070093import android.widget.TextView;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -070094
Svetoslav Ganov7be27ac2013-09-30 09:04:50 -070095import com.android.printspooler.MediaSizeUtils.MediaSizeComparator;
96
Svetoslav7bfbbcb2013-10-10 13:36:23 -070097import libcore.io.IoUtils;
98
Svetoslav269403b2013-08-14 17:31:04 -070099import java.io.File;
100import java.io.FileInputStream;
101import java.io.FileNotFoundException;
102import java.io.IOException;
103import java.io.InputStream;
104import java.io.OutputStream;
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700105import java.lang.ref.WeakReference;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700106import java.util.ArrayList;
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700107import java.util.Arrays;
Svetoslav Ganov7be27ac2013-09-30 09:04:50 -0700108import java.util.Collections;
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700109import java.util.Comparator;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700110import java.util.List;
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700111import java.util.concurrent.atomic.AtomicInteger;
Svetoslav597945f2013-07-17 18:37:36 -0700112import java.util.regex.Matcher;
113import java.util.regex.Pattern;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700114
115/**
116 * Activity for configuring a print job.
117 */
118public class PrintJobConfigActivity extends Activity {
119
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700120 private static final String LOG_TAG = "PrintJobConfigActivity";
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700121
Svetoslav5559c362013-09-20 11:58:55 -0700122 private static final boolean DEBUG = false;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700123
Svetoslav269403b2013-08-14 17:31:04 -0700124 public static final String INTENT_EXTRA_PRINTER_ID = "INTENT_EXTRA_PRINTER_ID";
125
126 private static final int LOADER_ID_PRINTERS_LOADER = 1;
127
Svetoslav773f54d2013-09-03 14:01:43 -0700128 private static final int ORIENTATION_PORTRAIT = 0;
129 private static final int ORIENTATION_LANDSCAPE = 1;
130
Svetoslav269403b2013-08-14 17:31:04 -0700131 private static final int DEST_ADAPTER_MAX_ITEM_COUNT = 9;
132
Svetoslav269403b2013-08-14 17:31:04 -0700133 private static final int DEST_ADAPTER_ITEM_ID_SAVE_AS_PDF = Integer.MAX_VALUE;
134 private static final int DEST_ADAPTER_ITEM_ID_ALL_PRINTERS = Integer.MAX_VALUE - 1;
Svetoslav269403b2013-08-14 17:31:04 -0700135
136 private static final int ACTIVITY_REQUEST_CREATE_FILE = 1;
137 private static final int ACTIVITY_REQUEST_SELECT_PRINTER = 2;
Svetoslavb4fda132013-10-25 18:57:43 -0700138 private static final int ACTIVITY_POPULATE_ADVANCED_PRINT_OPTIONS = 3;
Svetoslav269403b2013-08-14 17:31:04 -0700139
140 private static final int CONTROLLER_STATE_FINISHED = 1;
141 private static final int CONTROLLER_STATE_FAILED = 2;
142 private static final int CONTROLLER_STATE_CANCELLED = 3;
143 private static final int CONTROLLER_STATE_INITIALIZED = 4;
144 private static final int CONTROLLER_STATE_STARTED = 5;
145 private static final int CONTROLLER_STATE_LAYOUT_STARTED = 6;
146 private static final int CONTROLLER_STATE_LAYOUT_COMPLETED = 7;
147 private static final int CONTROLLER_STATE_WRITE_STARTED = 8;
148 private static final int CONTROLLER_STATE_WRITE_COMPLETED = 9;
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700149
150 private static final int EDITOR_STATE_INITIALIZED = 1;
151 private static final int EDITOR_STATE_CONFIRMED_PRINT = 2;
Svetoslava36285f2013-09-05 11:27:45 -0700152 private static final int EDITOR_STATE_CANCELLED = 3;
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700153
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700154 private static final int MIN_COPIES = 1;
Svetoslav269403b2013-08-14 17:31:04 -0700155 private static final String MIN_COPIES_STRING = String.valueOf(MIN_COPIES);
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700156
Svetoslavf0c48a72013-09-18 14:09:47 -0700157 private static final Pattern PATTERN_DIGITS = Pattern.compile("[\\d]+");
Svetoslav597945f2013-07-17 18:37:36 -0700158
159 private static final Pattern PATTERN_ESCAPE_SPECIAL_CHARS = Pattern.compile(
160 "(?=[]\\[+&|!(){}^\"~*?:\\\\])");
161
162 private static final Pattern PATTERN_PAGE_RANGE = Pattern.compile(
Svetoslav Ganovab051ba2013-09-26 00:34:54 -0700163 "[\\s]*[0-9]*[\\s]*[\\-]?[\\s]*[0-9]*[\\s]*?(([,])"
164 + "[\\s]*[0-9]*[\\s]*[\\-]?[\\s]*[0-9]*[\\s]*|[\\s]*)+");
Svetoslav597945f2013-07-17 18:37:36 -0700165
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700166 public static final PageRange[] ALL_PAGES_ARRAY = new PageRange[] {PageRange.ALL_PAGES};
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700167
Svetoslav651dd4e2013-09-12 14:37:47 -0700168 private final PrintAttributes mOldPrintAttributes = new PrintAttributes.Builder().build();
169 private final PrintAttributes mCurrPrintAttributes = new PrintAttributes.Builder().build();
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700170
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700171 private final DeathRecipient mDeathRecipient = new DeathRecipient() {
172 @Override
173 public void binderDied() {
174 finish();
175 }
176 };
177
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700178 private Editor mEditor;
179 private Document mDocument;
180 private PrintController mController;
Svetoslav66160bb2013-08-12 22:36:50 -0700181
Svetoslav2fbd2a72013-09-16 17:53:51 -0700182 private PrintJobId mPrintJobId;
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700183
184 private IBinder mIPrintDocumentAdapter;
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -0700185
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700186 private Dialog mGeneratingPrintJobDialog;
187
Svetoslav7bfbbcb2013-10-10 13:36:23 -0700188 private PrintSpoolerProvider mSpoolerProvider;
189
190 private String mCallingPackageName;
191
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -0700192 @Override
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700193 protected void onCreate(Bundle bundle) {
194 super.onCreate(bundle);
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700195
Svetoslav Ganov56ddf1f2013-10-05 19:55:49 -0700196 setTitle(R.string.print_dialog);
197
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700198 Bundle extras = getIntent().getExtras();
199
Svetoslav7bfbbcb2013-10-10 13:36:23 -0700200 PrintJobInfo printJob = extras.getParcelable(PrintManager.EXTRA_PRINT_JOB);
Svetoslav2fbd2a72013-09-16 17:53:51 -0700201 if (printJob == null) {
202 throw new IllegalArgumentException("printJob cannot be null");
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700203 }
204
Svetoslav2fbd2a72013-09-16 17:53:51 -0700205 mPrintJobId = printJob.getId();
Svetoslav7bfbbcb2013-10-10 13:36:23 -0700206 mIPrintDocumentAdapter = extras.getBinder(PrintManager.EXTRA_PRINT_DOCUMENT_ADAPTER);
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700207 if (mIPrintDocumentAdapter == null) {
208 throw new IllegalArgumentException("PrintDocumentAdapter cannot be null");
209 }
210
Svetoslav Ganov858a1852013-10-17 22:20:40 -0700211 try {
212 IPrintDocumentAdapter.Stub.asInterface(mIPrintDocumentAdapter)
213 .setObserver(new PrintDocumentAdapterObserver(this));
214 } catch (RemoteException re) {
215 finish();
216 return;
217 }
218
Svetoslav2fbd2a72013-09-16 17:53:51 -0700219 PrintAttributes attributes = printJob.getAttributes();
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700220 if (attributes != null) {
221 mCurrPrintAttributes.copyFrom(attributes);
222 }
223
Svetoslav7bfbbcb2013-10-10 13:36:23 -0700224 mCallingPackageName = extras.getString(DocumentsContract.EXTRA_PACKAGE_NAME);
Svetoslav269403b2013-08-14 17:31:04 -0700225
Svetoslav7bfbbcb2013-10-10 13:36:23 -0700226 setContentView(R.layout.print_job_config_activity_container);
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700227
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700228 try {
229 mIPrintDocumentAdapter.linkToDeath(mDeathRecipient, 0);
230 } catch (RemoteException re) {
231 finish();
232 return;
233 }
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700234
Svetoslav7bfbbcb2013-10-10 13:36:23 -0700235 mDocument = new Document();
236 mEditor = new Editor();
237
238 mSpoolerProvider = new PrintSpoolerProvider(this,
239 new Runnable() {
240 @Override
241 public void run() {
242 // We got the spooler so unleash the UI.
243 mController = new PrintController(new RemotePrintDocumentAdapter(
244 IPrintDocumentAdapter.Stub.asInterface(mIPrintDocumentAdapter),
245 mSpoolerProvider.getSpooler().generateFileForPrintJob(mPrintJobId)));
246 mController.initialize();
247
248 mEditor.initialize();
249 mEditor.postCreate();
250 }
251 });
Svetoslav66160bb2013-08-12 22:36:50 -0700252 }
253
254 @Override
Svetoslav Ganov44720af2013-08-20 16:32:53 -0700255 public void onResume() {
256 super.onResume();
Svetoslav7bfbbcb2013-10-10 13:36:23 -0700257 if (mSpoolerProvider.getSpooler() != null) {
258 mEditor.refreshCurrentPrinter();
259 }
Svetoslav Ganov44720af2013-08-20 16:32:53 -0700260 }
261
262 @Override
Svetoslavb4fda132013-10-25 18:57:43 -0700263 public void onPause() {
264 if (isFinishing()) {
265 if (mController != null && mController.hasStarted()) {
266 mController.finish();
267 }
268 if (mEditor != null && mEditor.isPrintConfirmed()
269 && mController != null && mController.isFinished()) {
270 mSpoolerProvider.getSpooler().setPrintJobState(mPrintJobId,
271 PrintJobInfo.STATE_QUEUED, null);
272 } else {
273 mSpoolerProvider.getSpooler().setPrintJobState(mPrintJobId,
274 PrintJobInfo.STATE_CANCELED, null);
275 }
276 if (mGeneratingPrintJobDialog != null) {
277 mGeneratingPrintJobDialog.dismiss();
278 mGeneratingPrintJobDialog = null;
279 }
280 mIPrintDocumentAdapter.unlinkToDeath(mDeathRecipient, 0);
281 mSpoolerProvider.destroy();
282 }
283 super.onPause();
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700284 }
285
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700286 public boolean onTouchEvent(MotionEvent event) {
Svetoslav Ganov858a1852013-10-17 22:20:40 -0700287 if (mController != null && mEditor != null &&
288 !mEditor.isPrintConfirmed() && mEditor.shouldCloseOnTouch(event)) {
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700289 if (!mController.isWorking()) {
290 PrintJobConfigActivity.this.finish();
291 }
292 mEditor.cancel();
293 return true;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700294 }
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700295 return super.onTouchEvent(event);
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700296 }
297
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700298 public boolean onKeyDown(int keyCode, KeyEvent event) {
299 if (keyCode == KeyEvent.KEYCODE_BACK) {
300 event.startTracking();
301 }
302 return super.onKeyDown(keyCode, event);
303 }
304
305 public boolean onKeyUp(int keyCode, KeyEvent event) {
Svetoslav Ganov858a1852013-10-17 22:20:40 -0700306 if (mController != null && mEditor != null) {
307 if (keyCode == KeyEvent.KEYCODE_BACK) {
308 if (mEditor.isShwoingGeneratingPrintJobUi()) {
309 return true;
310 }
311 if (event.isTracking() && !event.isCanceled()) {
312 if (!mController.isWorking()) {
313 PrintJobConfigActivity.this.finish();
314 }
315 }
316 mEditor.cancel();
Svetoslav Ganov32c5eb32013-08-06 23:49:25 -0700317 return true;
318 }
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700319 }
320 return super.onKeyUp(keyCode, event);
321 }
322
323 private boolean printAttributesChanged() {
324 return !mOldPrintAttributes.equals(mCurrPrintAttributes);
325 }
326
327 private class PrintController {
328 private final AtomicInteger mRequestCounter = new AtomicInteger();
329
330 private final RemotePrintDocumentAdapter mRemotePrintAdapter;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700331
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700332 private final Bundle mMetadata;
333
334 private final ControllerHandler mHandler;
335
336 private final LayoutResultCallback mLayoutResultCallback;
337
338 private final WriteResultCallback mWriteResultCallback;
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -0700339
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700340 private int mControllerState = CONTROLLER_STATE_INITIALIZED;
341
Svetoslav Ganov44720af2013-08-20 16:32:53 -0700342 private boolean mHasStarted;
343
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700344 private PageRange[] mRequestedPages;
345
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700346 public PrintController(RemotePrintDocumentAdapter adapter) {
347 mRemotePrintAdapter = adapter;
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700348 mMetadata = new Bundle();
349 mHandler = new ControllerHandler(getMainLooper());
350 mLayoutResultCallback = new LayoutResultCallback(mHandler);
351 mWriteResultCallback = new WriteResultCallback(mHandler);
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -0700352 }
353
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700354 public void initialize() {
Svetoslav Ganov44720af2013-08-20 16:32:53 -0700355 mHasStarted = false;
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700356 mControllerState = CONTROLLER_STATE_INITIALIZED;
357 }
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -0700358
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700359 public void cancel() {
Svetoslavd270cb92013-10-31 14:27:16 -0700360 if (isWorking()) {
361 mRemotePrintAdapter.cancel();
362 }
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700363 mControllerState = CONTROLLER_STATE_CANCELLED;
364 }
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -0700365
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700366 public boolean isCancelled() {
367 return (mControllerState == CONTROLLER_STATE_CANCELLED);
368 }
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -0700369
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700370 public boolean isFinished() {
371 return (mControllerState == CONTROLLER_STATE_FINISHED);
372 }
373
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700374 public boolean hasStarted() {
Svetoslav Ganov44720af2013-08-20 16:32:53 -0700375 return mHasStarted;
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700376 }
377
378 public boolean hasPerformedLayout() {
379 return mControllerState >= CONTROLLER_STATE_LAYOUT_COMPLETED;
380 }
381
Svetoslav Ganovcaff3882013-10-09 10:53:26 -0700382 public boolean isPerformingLayout() {
383 return mControllerState == CONTROLLER_STATE_LAYOUT_STARTED;
384 }
385
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700386 public boolean isWorking() {
387 return mControllerState == CONTROLLER_STATE_LAYOUT_STARTED
388 || mControllerState == CONTROLLER_STATE_WRITE_STARTED;
389 }
390
391 public void start() {
392 mControllerState = CONTROLLER_STATE_STARTED;
Svetoslav Ganov44720af2013-08-20 16:32:53 -0700393 mHasStarted = true;
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700394 mRemotePrintAdapter.start();
395 }
396
397 public void update() {
Svetoslav Ganov44720af2013-08-20 16:32:53 -0700398 if (!mController.hasStarted()) {
399 mController.start();
400 }
Svetoslav Ganovcaff3882013-10-09 10:53:26 -0700401
402 // If the print attributes are the same and we are performing
403 // a layout, then we have to wait for it to completed which will
404 // trigger writing of the necessary pages.
405 final boolean printAttributesChanged = printAttributesChanged();
406 if (!printAttributesChanged && isPerformingLayout()) {
407 return;
408 }
409
Svetoslav5559c362013-09-20 11:58:55 -0700410 // If print is confirmed we always do a layout since the previous
411 // ones were for preview and this one is for printing.
Svetoslav Ganovcaff3882013-10-09 10:53:26 -0700412 if (!printAttributesChanged && !mEditor.isPrintConfirmed()) {
Svetoslavcc65b0c2013-09-10 21:08:32 -0700413 if (mDocument.info == null) {
414 // We are waiting for the result of a layout, so do nothing.
415 return;
416 }
Svetoslav773f54d2013-09-03 14:01:43 -0700417 // If the attributes didn't change and we have done a layout, then
418 // we do not do a layout but may have to ask the app to write some
419 // pages. Hence, pretend layout completed and nothing changed, so
420 // we handle writing as usual.
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700421 handleOnLayoutFinished(mDocument.info, false, mRequestCounter.get());
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700422 } else {
Svetoslav7bfbbcb2013-10-10 13:36:23 -0700423 mSpoolerProvider.getSpooler().setPrintJobAttributesNoPersistence(
Svetoslav269403b2013-08-14 17:31:04 -0700424 mPrintJobId, mCurrPrintAttributes);
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700425
Svetoslav6811f4e2013-09-18 15:58:28 -0700426 mMetadata.putBoolean(PrintDocumentAdapter.EXTRA_PRINT_PREVIEW,
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700427 !mEditor.isPrintConfirmed());
428
429 mControllerState = CONTROLLER_STATE_LAYOUT_STARTED;
430
431 mRemotePrintAdapter.layout(mOldPrintAttributes, mCurrPrintAttributes,
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700432 mLayoutResultCallback, mMetadata, mRequestCounter.incrementAndGet());
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700433
434 mOldPrintAttributes.copyFrom(mCurrPrintAttributes);
435 }
436 }
437
438 public void finish() {
439 mControllerState = CONTROLLER_STATE_FINISHED;
440 mRemotePrintAdapter.finish();
441 }
442
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700443 private void handleOnLayoutFinished(PrintDocumentInfo info,
444 boolean layoutChanged, int sequence) {
445 if (mRequestCounter.get() != sequence) {
446 return;
447 }
448
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700449 if (isCancelled()) {
Svetoslav Ganov44720af2013-08-20 16:32:53 -0700450 mEditor.updateUi();
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700451 if (mEditor.isDone()) {
452 PrintJobConfigActivity.this.finish();
453 }
454 return;
455 }
456
Svetoslav2c1b1772013-12-20 17:10:25 -0800457 final int oldControllerState = mControllerState;
458
459 if (mControllerState == CONTROLLER_STATE_LAYOUT_STARTED) {
460 mControllerState = CONTROLLER_STATE_LAYOUT_COMPLETED;
461 }
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700462
Svetoslav5559c362013-09-20 11:58:55 -0700463 // For layout purposes we care only whether the type or the page
464 // count changed. We still do not have the size since we did not
465 // call write. We use "layoutChanged" set by the application to
466 // know whether something else changed about the document.
467 final boolean infoChanged = !equalsIgnoreSize(info, mDocument.info);
Svetoslav66160bb2013-08-12 22:36:50 -0700468 // If the info changed, we update the document and the print job.
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700469 if (infoChanged) {
470 mDocument.info = info;
Svetoslav Ganovaec14172013-08-27 00:30:54 -0700471 // Set the info.
Svetoslav7bfbbcb2013-10-10 13:36:23 -0700472 mSpoolerProvider.getSpooler().setPrintJobPrintDocumentInfoNoPersistence(
Svetoslav66160bb2013-08-12 22:36:50 -0700473 mPrintJobId, info);
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700474 }
475
476 // If the document info or the layout changed, then
477 // drop the pages since we have to fetch them again.
478 if (infoChanged || layoutChanged) {
479 mDocument.pages = null;
Svetoslav7bfbbcb2013-10-10 13:36:23 -0700480 mSpoolerProvider.getSpooler().setPrintJobPagesNoPersistence(
Svetoslav Ganovaec14172013-08-27 00:30:54 -0700481 mPrintJobId, null);
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700482 }
483
Svetoslav2c1b1772013-12-20 17:10:25 -0800484 PageRange[] oldRequestedPages = mRequestedPages;
485
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700486 // No pages means that the user selected an invalid range while we
487 // were doing a layout or the layout returned a document info for
488 // which the selected range is invalid. In such a case we do not
489 // write anything and wait for the user to fix the range which will
490 // trigger an update.
491 mRequestedPages = mEditor.getRequestedPages();
Svetoslavc3484022013-09-16 19:03:39 -0700492 if (mRequestedPages == null || mRequestedPages.length == 0) {
Svetoslav Ganov44720af2013-08-20 16:32:53 -0700493 mEditor.updateUi();
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700494 if (mEditor.isDone()) {
495 PrintJobConfigActivity.this.finish();
496 }
497 return;
Svetoslavc3484022013-09-16 19:03:39 -0700498 } else {
499 // If print is not confirmed we just ask for the first of the
500 // selected pages to emulate a behavior that shows preview
501 // increasing the chances that apps will implement the APIs
502 // correctly.
503 if (!mEditor.isPrintConfirmed()) {
504 if (ALL_PAGES_ARRAY.equals(mRequestedPages)) {
505 mRequestedPages = new PageRange[] {new PageRange(0, 0)};
506 } else {
507 final int firstPage = mRequestedPages[0].getStart();
508 mRequestedPages = new PageRange[] {new PageRange(firstPage, firstPage)};
509 }
510 }
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700511 }
512
Svetoslav2c1b1772013-12-20 17:10:25 -0800513 // If the info and the layout did not change...
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700514 if (!infoChanged && !layoutChanged
Svetoslav2c1b1772013-12-20 17:10:25 -0800515 // and we have the requested pages ...
516 && (PageRangeUtils.contains(mDocument.pages, mRequestedPages))
517 // ...or the requested pages are being written...
518 || (oldControllerState == CONTROLLER_STATE_WRITE_STARTED
519 && oldRequestedPages != null
520 && PageRangeUtils.contains(oldRequestedPages, mRequestedPages))) {
Svetoslava4f64092013-09-20 18:57:21 -0700521 // Nothing interesting changed and we have all requested pages.
522 // Then update the print jobs's pages as we will not do a write
523 // and we usually update the pages in the write complete callback.
Svetoslav2c1b1772013-12-20 17:10:25 -0800524 if (mDocument.pages != null) {
525 // Update the print job's pages given we have them.
526 updatePrintJobPages(mDocument.pages, mRequestedPages);
527 }
Svetoslavebec4682013-10-08 17:17:08 -0700528 mEditor.updateUi();
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700529 if (mEditor.isDone()) {
Svetoslava36285f2013-09-05 11:27:45 -0700530 requestCreatePdfFileOrFinish();
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700531 }
532 return;
533 }
534
Svetoslav Ganov44720af2013-08-20 16:32:53 -0700535 mEditor.updateUi();
536
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700537 // Request a write of the pages of interest.
538 mControllerState = CONTROLLER_STATE_WRITE_STARTED;
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700539 mRemotePrintAdapter.write(mRequestedPages, mWriteResultCallback,
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700540 mRequestCounter.incrementAndGet());
541 }
542
Svetoslav Ganovcaff3882013-10-09 10:53:26 -0700543 private void handleOnLayoutFailed(final CharSequence error, int sequence) {
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700544 if (mRequestCounter.get() != sequence) {
545 return;
546 }
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700547 mControllerState = CONTROLLER_STATE_FAILED;
Svetoslav Ganovcaff3882013-10-09 10:53:26 -0700548 mEditor.showUi(Editor.UI_ERROR, new Runnable() {
549 @Override
550 public void run() {
551 if (!TextUtils.isEmpty(error)) {
552 TextView messageView = (TextView) findViewById(R.id.message);
553 messageView.setText(error);
554 }
555 }
556 });
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700557 }
558
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700559 private void handleOnWriteFinished(PageRange[] pages, int sequence) {
560 if (mRequestCounter.get() != sequence) {
561 return;
562 }
563
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700564 if (isCancelled()) {
565 if (mEditor.isDone()) {
566 PrintJobConfigActivity.this.finish();
567 }
568 return;
569 }
570
571 mControllerState = CONTROLLER_STATE_WRITE_COMPLETED;
572
Svetoslav Ganovd26d4892013-08-28 14:37:54 -0700573 // Update the document size.
Svetoslav7bfbbcb2013-10-10 13:36:23 -0700574 File file = mSpoolerProvider.getSpooler()
Svetoslav Ganovd26d4892013-08-28 14:37:54 -0700575 .generateFileForPrintJob(mPrintJobId);
576 mDocument.info.setDataSize(file.length());
577
Svetoslav5559c362013-09-20 11:58:55 -0700578 // Update the print job with the updated info.
Svetoslav7bfbbcb2013-10-10 13:36:23 -0700579 mSpoolerProvider.getSpooler().setPrintJobPrintDocumentInfoNoPersistence(
Svetoslav5559c362013-09-20 11:58:55 -0700580 mPrintJobId, mDocument.info);
581
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700582 // Update which pages we have fetched.
583 mDocument.pages = PageRangeUtils.normalize(pages);
584
585 if (DEBUG) {
586 Log.i(LOG_TAG, "Requested: " + Arrays.toString(mRequestedPages)
587 + " and got: " + Arrays.toString(mDocument.pages));
588 }
589
Svetoslava4f64092013-09-20 18:57:21 -0700590 updatePrintJobPages(mDocument.pages, mRequestedPages);
591
592 if (mEditor.isDone()) {
593 requestCreatePdfFileOrFinish();
594 }
595 }
596
597 private void updatePrintJobPages(PageRange[] writtenPages, PageRange[] requestedPages) {
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700598 // Adjust the print job pages based on what was requested and written.
599 // The cases are ordered in the most expected to the least expected.
Svetoslava4f64092013-09-20 18:57:21 -0700600 if (Arrays.equals(writtenPages, requestedPages)) {
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700601 // We got a document with exactly the pages we wanted. Hence,
602 // the printer has to print all pages in the data.
Svetoslav7bfbbcb2013-10-10 13:36:23 -0700603 mSpoolerProvider.getSpooler().setPrintJobPagesNoPersistence(mPrintJobId,
Svetoslav66160bb2013-08-12 22:36:50 -0700604 ALL_PAGES_ARRAY);
Svetoslava4f64092013-09-20 18:57:21 -0700605 } else if (Arrays.equals(writtenPages, ALL_PAGES_ARRAY)) {
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700606 // We requested specific pages but got all of them. Hence,
607 // the printer has to print only the requested pages.
Svetoslav7bfbbcb2013-10-10 13:36:23 -0700608 mSpoolerProvider.getSpooler().setPrintJobPagesNoPersistence(mPrintJobId,
Svetoslava4f64092013-09-20 18:57:21 -0700609 requestedPages);
610 } else if (PageRangeUtils.contains(writtenPages, requestedPages)) {
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700611 // We requested specific pages and got more but not all pages.
612 // Hence, we have to offset appropriately the printed pages to
Svetoslava4f64092013-09-20 18:57:21 -0700613 // be based off the start of the written ones instead of zero.
614 // The written pages are always non-null and not empty.
615 final int offset = -writtenPages[0].getStart();
616 PageRange[] offsetPages = Arrays.copyOf(requestedPages, requestedPages.length);
617 PageRangeUtils.offset(offsetPages, offset);
Svetoslav7bfbbcb2013-10-10 13:36:23 -0700618 mSpoolerProvider.getSpooler().setPrintJobPagesNoPersistence(mPrintJobId,
Svetoslav66160bb2013-08-12 22:36:50 -0700619 offsetPages);
Svetoslava4f64092013-09-20 18:57:21 -0700620 } else if (Arrays.equals(requestedPages, ALL_PAGES_ARRAY)
621 && writtenPages.length == 1 && writtenPages[0].getStart() == 0
622 && writtenPages[0].getEnd() == mDocument.info.getPageCount() - 1) {
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700623 // We requested all pages via the special constant and got all
624 // of them as an explicit enumeration. Hence, the printer has
625 // to print only the requested pages.
Svetoslav7bfbbcb2013-10-10 13:36:23 -0700626 mSpoolerProvider.getSpooler().setPrintJobPagesNoPersistence(mPrintJobId,
Svetoslava4f64092013-09-20 18:57:21 -0700627 writtenPages);
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700628 } else {
629 // We did not get the pages we requested, then the application
630 // misbehaves, so we fail quickly.
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700631 mControllerState = CONTROLLER_STATE_FAILED;
Svetoslav2c1b1772013-12-20 17:10:25 -0800632 Log.e(LOG_TAG, "Received invalid pages from the app: requested="
633 + Arrays.toString(requestedPages) + " written="
634 + Arrays.toString(writtenPages));
Svetoslavb4fda132013-10-25 18:57:43 -0700635 mEditor.showUi(Editor.UI_ERROR, null);
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700636 }
Svetoslava36285f2013-09-05 11:27:45 -0700637 }
638
639 private void requestCreatePdfFileOrFinish() {
640 if (mEditor.isPrintingToPdf()) {
Svetoslava36285f2013-09-05 11:27:45 -0700641 Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
642 intent.setType("application/pdf");
Svetoslav Ganov4d4c66d2013-10-24 18:04:39 -0700643 intent.putExtra(Intent.EXTRA_TITLE, mDocument.info.getName());
Svetoslav7bfbbcb2013-10-10 13:36:23 -0700644 intent.putExtra(DocumentsContract.EXTRA_PACKAGE_NAME, mCallingPackageName);
Svetoslava36285f2013-09-05 11:27:45 -0700645 startActivityForResult(intent, ACTIVITY_REQUEST_CREATE_FILE);
646 } else {
647 PrintJobConfigActivity.this.finish();
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700648 }
649 }
650
Svetoslav Ganovcaff3882013-10-09 10:53:26 -0700651 private void handleOnWriteFailed(final CharSequence error, int sequence) {
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700652 if (mRequestCounter.get() != sequence) {
653 return;
654 }
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700655 mControllerState = CONTROLLER_STATE_FAILED;
Svetoslav Ganovcaff3882013-10-09 10:53:26 -0700656 mEditor.showUi(Editor.UI_ERROR, new Runnable() {
657 @Override
658 public void run() {
659 if (!TextUtils.isEmpty(error)) {
660 TextView messageView = (TextView) findViewById(R.id.message);
661 messageView.setText(error);
662 }
663 }
664 });
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700665 }
666
Svetoslav5559c362013-09-20 11:58:55 -0700667 private boolean equalsIgnoreSize(PrintDocumentInfo lhs, PrintDocumentInfo rhs) {
668 if (lhs == rhs) {
669 return true;
670 }
671 if (lhs == null) {
672 if (rhs != null) {
673 return false;
674 }
675 } else {
676 if (rhs == null) {
677 return false;
678 }
679 if (lhs.getContentType() != rhs.getContentType()
680 || lhs.getPageCount() != rhs.getPageCount()) {
681 return false;
682 }
683 }
684 return true;
685 }
686
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700687 private final class ControllerHandler extends Handler {
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700688 public static final int MSG_ON_LAYOUT_FINISHED = 1;
689 public static final int MSG_ON_LAYOUT_FAILED = 2;
690 public static final int MSG_ON_WRITE_FINISHED = 3;
691 public static final int MSG_ON_WRITE_FAILED = 4;
692
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700693 public ControllerHandler(Looper looper) {
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700694 super(looper, null, false);
695 }
696
697 @Override
698 public void handleMessage(Message message) {
699 switch (message.what) {
700 case MSG_ON_LAYOUT_FINISHED: {
701 PrintDocumentInfo info = (PrintDocumentInfo) message.obj;
702 final boolean changed = (message.arg1 == 1);
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700703 final int sequence = message.arg2;
704 handleOnLayoutFinished(info, changed, sequence);
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700705 } break;
706
707 case MSG_ON_LAYOUT_FAILED: {
708 CharSequence error = (CharSequence) message.obj;
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700709 final int sequence = message.arg1;
710 handleOnLayoutFailed(error, sequence);
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700711 } break;
712
713 case MSG_ON_WRITE_FINISHED: {
714 PageRange[] pages = (PageRange[]) message.obj;
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700715 final int sequence = message.arg1;
716 handleOnWriteFinished(pages, sequence);
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700717 } break;
718
719 case MSG_ON_WRITE_FAILED: {
720 CharSequence error = (CharSequence) message.obj;
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700721 final int sequence = message.arg1;
722 handleOnWriteFailed(error, sequence);
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700723 } break;
724 }
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -0700725 }
726 }
727 }
728
Svetoslav Ganov14db9652013-08-06 14:40:46 -0700729 private static final class LayoutResultCallback extends ILayoutResultCallback.Stub {
730 private final WeakReference<PrintController.ControllerHandler> mWeakHandler;
731
732 public LayoutResultCallback(PrintController.ControllerHandler handler) {
733 mWeakHandler = new WeakReference<PrintController.ControllerHandler>(handler);
734 }
735
736 @Override
737 public void onLayoutFinished(PrintDocumentInfo info, boolean changed, int sequence) {
738 Handler handler = mWeakHandler.get();
739 if (handler != null) {
740 handler.obtainMessage(PrintController.ControllerHandler.MSG_ON_LAYOUT_FINISHED,
741 changed ? 1 : 0, sequence, info).sendToTarget();
742 }
743 }
744
745 @Override
746 public void onLayoutFailed(CharSequence error, int sequence) {
747 Handler handler = mWeakHandler.get();
748 if (handler != null) {
749 handler.obtainMessage(PrintController.ControllerHandler.MSG_ON_LAYOUT_FAILED,
750 sequence, 0, error).sendToTarget();
751 }
752 }
753 }
754
755 private static final class WriteResultCallback extends IWriteResultCallback.Stub {
756 private final WeakReference<PrintController.ControllerHandler> mWeakHandler;
757
758 public WriteResultCallback(PrintController.ControllerHandler handler) {
759 mWeakHandler = new WeakReference<PrintController.ControllerHandler>(handler);
760 }
761
762 @Override
763 public void onWriteFinished(PageRange[] pages, int sequence) {
764 Handler handler = mWeakHandler.get();
765 if (handler != null) {
766 handler.obtainMessage(PrintController.ControllerHandler.MSG_ON_WRITE_FINISHED,
767 sequence, 0, pages).sendToTarget();
768 }
769 }
770
771 @Override
772 public void onWriteFailed(CharSequence error, int sequence) {
773 Handler handler = mWeakHandler.get();
774 if (handler != null) {
775 handler.obtainMessage(PrintController.ControllerHandler.MSG_ON_WRITE_FAILED,
776 sequence, 0, error).sendToTarget();
777 }
778 }
779 }
780
Svetoslav269403b2013-08-14 17:31:04 -0700781 @Override
782 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
783 switch (requestCode) {
784 case ACTIVITY_REQUEST_CREATE_FILE: {
785 if (data != null) {
786 Uri uri = data.getData();
787 writePrintJobDataAndFinish(uri);
788 } else {
789 mEditor.showUi(Editor.UI_EDITING_PRINT_JOB,
790 new Runnable() {
791 @Override
792 public void run() {
793 mEditor.initialize();
794 mEditor.bindUi();
Svetoslav Ganov7ff610e2013-10-12 17:23:59 -0700795 mEditor.reselectCurrentPrinter();
Svetoslav269403b2013-08-14 17:31:04 -0700796 mEditor.updateUi();
797 }
798 });
799 }
800 } break;
801
802 case ACTIVITY_REQUEST_SELECT_PRINTER: {
803 if (resultCode == RESULT_OK) {
804 PrinterId printerId = (PrinterId) data.getParcelableExtra(
805 INTENT_EXTRA_PRINTER_ID);
Svetoslav4e4874b2013-10-01 17:53:17 -0700806 if (printerId != null) {
807 mEditor.ensurePrinterSelected(printerId);
808 break;
809 }
Svetoslav269403b2013-08-14 17:31:04 -0700810 }
Svetoslav4e4874b2013-10-01 17:53:17 -0700811 mEditor.ensureCurrentPrinterSelected();
Svetoslav269403b2013-08-14 17:31:04 -0700812 } break;
Svetoslavb4fda132013-10-25 18:57:43 -0700813
814 case ACTIVITY_POPULATE_ADVANCED_PRINT_OPTIONS: {
815 if (resultCode == RESULT_OK) {
816 PrintJobInfo printJobInfo = (PrintJobInfo) data.getParcelableExtra(
817 PrintService.EXTRA_PRINT_JOB_INFO);
818 if (printJobInfo != null) {
819 mEditor.updateFromAdvancedOptions(printJobInfo);
820 break;
821 }
822 }
823 mEditor.cancel();
824 PrintJobConfigActivity.this.finish();
825 } break;
Svetoslav269403b2013-08-14 17:31:04 -0700826 }
827 }
828
829 private void writePrintJobDataAndFinish(final Uri uri) {
830 new AsyncTask<Void, Void, Void>() {
831 @Override
832 protected Void doInBackground(Void... params) {
833 InputStream in = null;
834 OutputStream out = null;
835 try {
Svetoslav7bfbbcb2013-10-10 13:36:23 -0700836 PrintJobInfo printJob = mSpoolerProvider.getSpooler()
Svetoslav269403b2013-08-14 17:31:04 -0700837 .getPrintJobInfo(mPrintJobId, PrintManager.APP_ID_ANY);
838 if (printJob == null) {
839 return null;
840 }
Svetoslav7bfbbcb2013-10-10 13:36:23 -0700841 File file = mSpoolerProvider.getSpooler()
Svetoslav269403b2013-08-14 17:31:04 -0700842 .generateFileForPrintJob(mPrintJobId);
843 in = new FileInputStream(file);
844 out = getContentResolver().openOutputStream(uri);
845 final byte[] buffer = new byte[8192];
846 while (true) {
847 final int readByteCount = in.read(buffer);
848 if (readByteCount < 0) {
849 break;
850 }
851 out.write(buffer, 0, readByteCount);
852 }
853 } catch (FileNotFoundException fnfe) {
854 Log.e(LOG_TAG, "Error writing print job data!", fnfe);
855 } catch (IOException ioe) {
856 Log.e(LOG_TAG, "Error writing print job data!", ioe);
857 } finally {
858 IoUtils.closeQuietly(in);
859 IoUtils.closeQuietly(out);
860 }
861 return null;
862 }
863
864 @Override
865 public void onPostExecute(Void result) {
866 mEditor.cancel();
867 PrintJobConfigActivity.this.finish();
868 }
869 }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
870 }
871
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700872 private final class Editor {
Svetoslav269403b2013-08-14 17:31:04 -0700873 private static final int UI_NONE = 0;
874 private static final int UI_EDITING_PRINT_JOB = 1;
875 private static final int UI_GENERATING_PRINT_JOB = 2;
Svetoslav Ganovcaff3882013-10-09 10:53:26 -0700876 private static final int UI_ERROR = 3;
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -0700877
Svetoslav269403b2013-08-14 17:31:04 -0700878 private EditText mCopiesEditText;
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -0700879
Svetoslav Ganov44720af2013-08-20 16:32:53 -0700880 private TextView mRangeOptionsTitle;
881 private TextView mPageRangeTitle;
882 private EditText mPageRangeEditText;
Svetoslav269403b2013-08-14 17:31:04 -0700883
884 private Spinner mDestinationSpinner;
Svetoslav7bfbbcb2013-10-10 13:36:23 -0700885 private DestinationAdapter mDestinationSpinnerAdapter;
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -0700886
Svetoslav269403b2013-08-14 17:31:04 -0700887 private Spinner mMediaSizeSpinner;
Svetoslav7bfbbcb2013-10-10 13:36:23 -0700888 private ArrayAdapter<SpinnerItem<MediaSize>> mMediaSizeSpinnerAdapter;
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -0700889
Svetoslav269403b2013-08-14 17:31:04 -0700890 private Spinner mColorModeSpinner;
Svetoslav7bfbbcb2013-10-10 13:36:23 -0700891 private ArrayAdapter<SpinnerItem<Integer>> mColorModeSpinnerAdapter;
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -0700892
Svetoslav269403b2013-08-14 17:31:04 -0700893 private Spinner mOrientationSpinner;
Svetoslav7bfbbcb2013-10-10 13:36:23 -0700894 private ArrayAdapter<SpinnerItem<Integer>> mOrientationSpinnerAdapter;
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -0700895
Svetoslav269403b2013-08-14 17:31:04 -0700896 private Spinner mRangeOptionsSpinner;
Svetoslav7bfbbcb2013-10-10 13:36:23 -0700897 private ArrayAdapter<SpinnerItem<Integer>> mRangeOptionsSpinnerAdapter;
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -0700898
Svetoslav7bfbbcb2013-10-10 13:36:23 -0700899 private SimpleStringSplitter mStringCommaSplitter =
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700900 new SimpleStringSplitter(',');
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -0700901
Svetoslav269403b2013-08-14 17:31:04 -0700902 private View mContentContainer;
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700903
Svetoslavb4fda132013-10-25 18:57:43 -0700904 private View mAdvancedPrintOptionsContainer;
905
906 private Button mAdvancedOptionsButton;
907
Svetoslav269403b2013-08-14 17:31:04 -0700908 private Button mPrintButton;
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -0700909
Svetoslav4e4874b2013-10-01 17:53:17 -0700910 private PrinterId mNextPrinterId;
911
Svetoslav Ganov44720af2013-08-20 16:32:53 -0700912 private PrinterInfo mCurrentPrinter;
913
Svetoslav7bfbbcb2013-10-10 13:36:23 -0700914 private MediaSizeComparator mMediaSizeComparator;
Svetoslav Ganov7be27ac2013-09-30 09:04:50 -0700915
Svetoslav89ed9fc2013-10-11 17:00:45 -0700916 private final OnFocusChangeListener mFocusListener = new OnFocusChangeListener() {
917 @Override
918 public void onFocusChange(View view, boolean hasFocus) {
919 EditText editText = (EditText) view;
920 if (!TextUtils.isEmpty(editText.getText())) {
921 editText.setSelection(editText.getText().length());
922 }
923 }
924 };
925
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -0700926 private final OnItemSelectedListener mOnItemSelectedListener =
927 new AdapterView.OnItemSelectedListener() {
928 @Override
929 public void onItemSelected(AdapterView<?> spinner, View view, int position, long id) {
930 if (spinner == mDestinationSpinner) {
Svetoslav Ganov8c433762013-08-02 14:22:19 -0700931 if (mIgnoreNextDestinationChange) {
932 mIgnoreNextDestinationChange = false;
933 return;
934 }
Svetoslav Ganov44720af2013-08-20 16:32:53 -0700935
Svetoslav7bfbbcb2013-10-10 13:36:23 -0700936 if (position == AdapterView.INVALID_POSITION) {
937 updateUi();
938 return;
939 }
940
Svetoslav269403b2013-08-14 17:31:04 -0700941 if (id == DEST_ADAPTER_ITEM_ID_ALL_PRINTERS) {
Svetoslav Ganov44720af2013-08-20 16:32:53 -0700942 startSelectPrinterActivity();
Svetoslav269403b2013-08-14 17:31:04 -0700943 return;
944 }
Svetoslav Ganov44720af2013-08-20 16:32:53 -0700945
Svetoslav Ganov6be4c7642013-09-26 21:12:47 -0700946 mCapabilitiesTimeout.remove();
Svetoslav Ganov44720af2013-08-20 16:32:53 -0700947
Svetoslavcc65b0c2013-09-10 21:08:32 -0700948 mCurrentPrinter = (PrinterInfo) mDestinationSpinnerAdapter
Svetoslav66160bb2013-08-12 22:36:50 -0700949 .getItem(position);
Svetoslav Ganov44720af2013-08-20 16:32:53 -0700950
Svetoslav7bfbbcb2013-10-10 13:36:23 -0700951 mSpoolerProvider.getSpooler().setPrintJobPrinterNoPersistence(
Svetoslavcc65b0c2013-09-10 21:08:32 -0700952 mPrintJobId, mCurrentPrinter);
Svetoslav Ganov44720af2013-08-20 16:32:53 -0700953
Svetoslavcc65b0c2013-09-10 21:08:32 -0700954 if (mCurrentPrinter.getStatus() == PrinterInfo.STATUS_UNAVAILABLE) {
Svetoslav1c664b62013-11-04 12:30:08 -0800955 mCapabilitiesTimeout.post();
Svetoslavcc65b0c2013-09-10 21:08:32 -0700956 updateUi();
957 return;
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -0700958 }
Svetoslav269403b2013-08-14 17:31:04 -0700959
Svetoslavcc65b0c2013-09-10 21:08:32 -0700960 PrinterCapabilitiesInfo capabilities = mCurrentPrinter.getCapabilities();
961 if (capabilities == null) {
Svetoslav Ganov6be4c7642013-09-26 21:12:47 -0700962 mCapabilitiesTimeout.post();
Svetoslavcc65b0c2013-09-10 21:08:32 -0700963 updateUi();
964 refreshCurrentPrinter();
965 } else {
966 updatePrintAttributes(capabilities);
967 updateUi();
968 mController.update();
Svetoslav Ganov307a0d42013-09-23 15:51:03 -0700969 refreshCurrentPrinter();
Svetoslavcc65b0c2013-09-10 21:08:32 -0700970 }
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -0700971 } else if (spinner == mMediaSizeSpinner) {
Svetoslavb4fda132013-10-25 18:57:43 -0700972 if (mIgnoreNextMediaSizeChange) {
973 mIgnoreNextMediaSizeChange = false;
974 return;
975 }
Svetoslavcc65b0c2013-09-10 21:08:32 -0700976 if (mOldMediaSizeSelectionIndex
977 == mMediaSizeSpinner.getSelectedItemPosition()) {
978 mOldMediaSizeSelectionIndex = AdapterView.INVALID_POSITION;
Svetoslav Ganov8c433762013-08-02 14:22:19 -0700979 return;
980 }
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -0700981 SpinnerItem<MediaSize> mediaItem = mMediaSizeSpinnerAdapter.getItem(position);
Svetoslav Ganov7be27ac2013-09-30 09:04:50 -0700982 if (mOrientationSpinner.getSelectedItemPosition() == 0) {
983 mCurrPrintAttributes.setMediaSize(mediaItem.value.asPortrait());
984 } else {
985 mCurrPrintAttributes.setMediaSize(mediaItem.value.asLandscape());
986 }
Svetoslav Ganov85b1f882013-07-24 17:00:06 -0700987 if (!hasErrors()) {
988 mController.update();
989 }
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -0700990 } else if (spinner == mColorModeSpinner) {
Svetoslavb4fda132013-10-25 18:57:43 -0700991 if (mIgnoreNextColorChange) {
992 mIgnoreNextColorChange = false;
993 return;
994 }
Svetoslavcc65b0c2013-09-10 21:08:32 -0700995 if (mOldColorModeSelectionIndex
996 == mColorModeSpinner.getSelectedItemPosition()) {
997 mOldColorModeSelectionIndex = AdapterView.INVALID_POSITION;
Svetoslav Ganov8c433762013-08-02 14:22:19 -0700998 return;
999 }
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07001000 SpinnerItem<Integer> colorModeItem =
1001 mColorModeSpinnerAdapter.getItem(position);
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07001002 mCurrPrintAttributes.setColorMode(colorModeItem.value);
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07001003 if (!hasErrors()) {
1004 mController.update();
1005 }
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07001006 } else if (spinner == mOrientationSpinner) {
Svetoslav Ganov8c433762013-08-02 14:22:19 -07001007 if (mIgnoreNextOrientationChange) {
1008 mIgnoreNextOrientationChange = false;
1009 return;
1010 }
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07001011 SpinnerItem<Integer> orientationItem =
1012 mOrientationSpinnerAdapter.getItem(position);
Svetoslavcc65b0c2013-09-10 21:08:32 -07001013 setCurrentPrintAttributesOrientation(orientationItem.value);
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07001014 if (!hasErrors()) {
1015 mController.update();
1016 }
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07001017 } else if (spinner == mRangeOptionsSpinner) {
Svetoslav Ganov8c433762013-08-02 14:22:19 -07001018 if (mIgnoreNextRangeOptionChange) {
1019 mIgnoreNextRangeOptionChange = false;
1020 return;
1021 }
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07001022 updateUi();
1023 if (!hasErrors()) {
1024 mController.update();
1025 }
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07001026 }
1027 }
1028
1029 @Override
1030 public void onNothingSelected(AdapterView<?> parent) {
1031 /* do nothing*/
1032 }
1033 };
1034
Svetoslavcc65b0c2013-09-10 21:08:32 -07001035 private void setCurrentPrintAttributesOrientation(int orientation) {
1036 MediaSize mediaSize = mCurrPrintAttributes.getMediaSize();
1037 if (orientation == ORIENTATION_PORTRAIT) {
1038 if (!mediaSize.isPortrait()) {
1039 // Rotate the media size.
1040 mCurrPrintAttributes.setMediaSize(mediaSize.asPortrait());
1041
1042 // Rotate the resolution.
1043 Resolution oldResolution = mCurrPrintAttributes.getResolution();
1044 Resolution newResolution = new Resolution(
1045 oldResolution.getId(),
Svetoslav651dd4e2013-09-12 14:37:47 -07001046 oldResolution.getLabel(),
Svetoslavcc65b0c2013-09-10 21:08:32 -07001047 oldResolution.getVerticalDpi(),
1048 oldResolution.getHorizontalDpi());
1049 mCurrPrintAttributes.setResolution(newResolution);
1050
1051 // Rotate the physical margins.
Svetoslav651dd4e2013-09-12 14:37:47 -07001052 Margins oldMinMargins = mCurrPrintAttributes.getMinMargins();
1053 Margins newMinMargins = new Margins(
1054 oldMinMargins.getBottomMils(),
1055 oldMinMargins.getLeftMils(),
1056 oldMinMargins.getTopMils(),
1057 oldMinMargins.getRightMils());
1058 mCurrPrintAttributes.setMinMargins(newMinMargins);
Svetoslavcc65b0c2013-09-10 21:08:32 -07001059 }
1060 } else {
1061 if (mediaSize.isPortrait()) {
1062 // Rotate the media size.
1063 mCurrPrintAttributes.setMediaSize(mediaSize.asLandscape());
1064
1065 // Rotate the resolution.
1066 Resolution oldResolution = mCurrPrintAttributes.getResolution();
1067 Resolution newResolution = new Resolution(
1068 oldResolution.getId(),
Svetoslav651dd4e2013-09-12 14:37:47 -07001069 oldResolution.getLabel(),
Svetoslavcc65b0c2013-09-10 21:08:32 -07001070 oldResolution.getVerticalDpi(),
1071 oldResolution.getHorizontalDpi());
1072 mCurrPrintAttributes.setResolution(newResolution);
1073
1074 // Rotate the physical margins.
Svetoslav651dd4e2013-09-12 14:37:47 -07001075 Margins oldMinMargins = mCurrPrintAttributes.getMinMargins();
Svetoslavcc65b0c2013-09-10 21:08:32 -07001076 Margins newMargins = new Margins(
Svetoslav651dd4e2013-09-12 14:37:47 -07001077 oldMinMargins.getTopMils(),
1078 oldMinMargins.getRightMils(),
1079 oldMinMargins.getBottomMils(),
1080 oldMinMargins.getLeftMils());
1081 mCurrPrintAttributes.setMinMargins(newMargins);
Svetoslavcc65b0c2013-09-10 21:08:32 -07001082 }
1083 }
1084 }
1085
1086 private void updatePrintAttributes(PrinterCapabilitiesInfo capabilities) {
Svetoslav651dd4e2013-09-12 14:37:47 -07001087 PrintAttributes defaults = capabilities.getDefaults();
Svetoslavcc65b0c2013-09-10 21:08:32 -07001088
Svetoslav Ganov7be27ac2013-09-30 09:04:50 -07001089 // Sort the media sizes based on the current locale.
1090 List<MediaSize> sortedMediaSizes = new ArrayList<MediaSize>(
1091 capabilities.getMediaSizes());
1092 Collections.sort(sortedMediaSizes, mMediaSizeComparator);
1093
Svetoslavcc65b0c2013-09-10 21:08:32 -07001094 // Media size.
1095 MediaSize currMediaSize = mCurrPrintAttributes.getMediaSize();
1096 if (currMediaSize == null) {
1097 mCurrPrintAttributes.setMediaSize(defaults.getMediaSize());
1098 } else {
1099 MediaSize currMediaSizePortrait = currMediaSize.asPortrait();
Svetoslav Ganov7be27ac2013-09-30 09:04:50 -07001100 final int mediaSizeCount = sortedMediaSizes.size();
Svetoslavcc65b0c2013-09-10 21:08:32 -07001101 for (int i = 0; i < mediaSizeCount; i++) {
Svetoslav Ganov7be27ac2013-09-30 09:04:50 -07001102 MediaSize mediaSize = sortedMediaSizes.get(i);
Svetoslavcc65b0c2013-09-10 21:08:32 -07001103 if (currMediaSizePortrait.equals(mediaSize.asPortrait())) {
Svetoslav Ganov7be27ac2013-09-30 09:04:50 -07001104 mCurrPrintAttributes.setMediaSize(currMediaSize);
Svetoslavcc65b0c2013-09-10 21:08:32 -07001105 break;
1106 }
1107 }
1108 }
1109
1110 // Color mode.
1111 final int colorMode = mCurrPrintAttributes.getColorMode();
1112 if ((capabilities.getColorModes() & colorMode) == 0) {
1113 mCurrPrintAttributes.setColorMode(colorMode);
1114 }
1115
1116 // Resolution
1117 Resolution resolution = mCurrPrintAttributes.getResolution();
1118 if (resolution == null || !capabilities.getResolutions().contains(resolution)) {
1119 mCurrPrintAttributes.setResolution(defaults.getResolution());
1120 }
1121
1122 // Margins.
Svetoslav651dd4e2013-09-12 14:37:47 -07001123 Margins margins = mCurrPrintAttributes.getMinMargins();
Svetoslavcc65b0c2013-09-10 21:08:32 -07001124 if (margins == null) {
Svetoslav651dd4e2013-09-12 14:37:47 -07001125 mCurrPrintAttributes.setMinMargins(defaults.getMinMargins());
Svetoslavcc65b0c2013-09-10 21:08:32 -07001126 } else {
1127 Margins minMargins = capabilities.getMinMargins();
1128 if (margins.getLeftMils() < minMargins.getLeftMils()
1129 || margins.getTopMils() < minMargins.getTopMils()
1130 || margins.getRightMils() > minMargins.getRightMils()
1131 || margins.getBottomMils() > minMargins.getBottomMils()) {
Svetoslav651dd4e2013-09-12 14:37:47 -07001132 mCurrPrintAttributes.setMinMargins(defaults.getMinMargins());
Svetoslavcc65b0c2013-09-10 21:08:32 -07001133 }
1134 }
1135 }
1136
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07001137 private final TextWatcher mCopiesTextWatcher = new TextWatcher() {
1138 @Override
1139 public void onTextChanged(CharSequence s, int start, int before, int count) {
1140 /* do nothing */
1141 }
1142
1143 @Override
1144 public void beforeTextChanged(CharSequence s, int start, int count, int after) {
1145 /* do nothing */
1146 }
1147
1148 @Override
1149 public void afterTextChanged(Editable editable) {
Svetoslav Ganov8c433762013-08-02 14:22:19 -07001150 if (mIgnoreNextCopiesChange) {
1151 mIgnoreNextCopiesChange = false;
1152 return;
1153 }
1154
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07001155 final boolean hadErrors = hasErrors();
1156
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07001157 if (editable.length() == 0) {
1158 mCopiesEditText.setError("");
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07001159 updateUi();
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07001160 return;
1161 }
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07001162
Svetoslav Ganov44720af2013-08-20 16:32:53 -07001163 int copies = 0;
1164 try {
1165 copies = Integer.parseInt(editable.toString());
1166 } catch (NumberFormatException nfe) {
1167 /* ignore */
1168 }
1169
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07001170 if (copies < MIN_COPIES) {
1171 mCopiesEditText.setError("");
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07001172 updateUi();
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07001173 return;
1174 }
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07001175
1176 mCopiesEditText.setError(null);
Svetoslav7bfbbcb2013-10-10 13:36:23 -07001177 mSpoolerProvider.getSpooler().setPrintJobCopiesNoPersistence(
Svetoslav269403b2013-08-14 17:31:04 -07001178 mPrintJobId, copies);
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07001179 updateUi();
1180
1181 if (hadErrors && !hasErrors() && printAttributesChanged()) {
1182 mController.update();
1183 }
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07001184 }
1185 };
1186
1187 private final TextWatcher mRangeTextWatcher = new TextWatcher() {
1188 @Override
1189 public void onTextChanged(CharSequence s, int start, int before, int count) {
1190 /* do nothing */
1191 }
1192
1193 @Override
1194 public void beforeTextChanged(CharSequence s, int start, int count, int after) {
1195 /* do nothing */
1196 }
1197
1198 @Override
1199 public void afterTextChanged(Editable editable) {
Svetoslav Ganov8c433762013-08-02 14:22:19 -07001200 if (mIgnoreNextRangeChange) {
1201 mIgnoreNextRangeChange = false;
1202 return;
1203 }
1204
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07001205 final boolean hadErrors = hasErrors();
1206
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07001207 String text = editable.toString();
1208
1209 if (TextUtils.isEmpty(text)) {
Svetoslav Ganov44720af2013-08-20 16:32:53 -07001210 mPageRangeEditText.setError("");
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07001211 updateUi();
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07001212 return;
1213 }
1214
1215 String escapedText = PATTERN_ESCAPE_SPECIAL_CHARS.matcher(text).replaceAll("////");
1216 if (!PATTERN_PAGE_RANGE.matcher(escapedText).matches()) {
Svetoslav Ganov44720af2013-08-20 16:32:53 -07001217 mPageRangeEditText.setError("");
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07001218 updateUi();
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07001219 return;
1220 }
1221
Svetoslav Ganovab051ba2013-09-26 00:34:54 -07001222 // The range
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07001223 Matcher matcher = PATTERN_DIGITS.matcher(text);
1224 while (matcher.find()) {
Svetoslav Ganovab051ba2013-09-26 00:34:54 -07001225 String numericString = text.substring(matcher.start(), matcher.end()).trim();
1226 if (TextUtils.isEmpty(numericString)) {
1227 continue;
1228 }
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07001229 final int pageIndex = Integer.parseInt(numericString);
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07001230 if (pageIndex < 1 || pageIndex > mDocument.info.getPageCount()) {
Svetoslav Ganov44720af2013-08-20 16:32:53 -07001231 mPageRangeEditText.setError("");
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07001232 updateUi();
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07001233 return;
1234 }
1235 }
1236
Svetoslav Ganovb0e68fd2013-09-21 12:01:54 -07001237 // We intentionally do not catch the case of the from page being
1238 // greater than the to page. When computing the requested pages
1239 // we just swap them if necessary.
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07001240
Svetoslavb4fda132013-10-25 18:57:43 -07001241 // Keep the print job up to date with the selected pages if we
1242 // know how many pages are there in the document.
1243 PageRange[] requestedPages = getRequestedPages();
1244 if (requestedPages != null && requestedPages.length > 0
1245 && requestedPages[requestedPages.length - 1].getEnd()
1246 < mDocument.info.getPageCount()) {
1247 mSpoolerProvider.getSpooler().setPrintJobPagesNoPersistence(
1248 mPrintJobId, requestedPages);
1249 }
1250
Svetoslav Ganov44720af2013-08-20 16:32:53 -07001251 mPageRangeEditText.setError(null);
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07001252 mPrintButton.setEnabled(true);
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07001253 updateUi();
1254
1255 if (hadErrors && !hasErrors() && printAttributesChanged()) {
1256 updateUi();
1257 }
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07001258 }
1259 };
1260
Svetoslav Ganov6be4c7642013-09-26 21:12:47 -07001261 private final WaitForPrinterCapabilitiesTimeout mCapabilitiesTimeout =
1262 new WaitForPrinterCapabilitiesTimeout();
1263
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07001264 private int mEditorState;
1265
Svetoslav Ganov8c433762013-08-02 14:22:19 -07001266 private boolean mIgnoreNextDestinationChange;
Svetoslavcc65b0c2013-09-10 21:08:32 -07001267 private int mOldMediaSizeSelectionIndex;
1268 private int mOldColorModeSelectionIndex;
Svetoslav Ganov8c433762013-08-02 14:22:19 -07001269 private boolean mIgnoreNextOrientationChange;
1270 private boolean mIgnoreNextRangeOptionChange;
1271 private boolean mIgnoreNextCopiesChange;
1272 private boolean mIgnoreNextRangeChange;
Svetoslavb4fda132013-10-25 18:57:43 -07001273 private boolean mIgnoreNextMediaSizeChange;
1274 private boolean mIgnoreNextColorChange;
Svetoslav Ganov8c433762013-08-02 14:22:19 -07001275
Svetoslav269403b2013-08-14 17:31:04 -07001276 private int mCurrentUi = UI_NONE;
1277
Svetoslav Ganov44720af2013-08-20 16:32:53 -07001278 private boolean mFavoritePrinterSelected;
1279
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07001280 public Editor() {
Svetoslav7bfbbcb2013-10-10 13:36:23 -07001281 showUi(UI_EDITING_PRINT_JOB, null);
1282 }
1283
1284 public void postCreate() {
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07001285 // Destination.
Svetoslav Ganov7be27ac2013-09-30 09:04:50 -07001286 mMediaSizeComparator = new MediaSizeComparator(PrintJobConfigActivity.this);
Svetoslav269403b2013-08-14 17:31:04 -07001287 mDestinationSpinnerAdapter = new DestinationAdapter();
Svetoslav66160bb2013-08-12 22:36:50 -07001288 mDestinationSpinnerAdapter.registerDataSetObserver(new DataSetObserver() {
1289 @Override
1290 public void onChanged() {
Svetoslav Ganov9186d0c2013-09-03 00:11:58 -07001291 // Initially, we have only safe to PDF as a printer but after some
Svetoslav Ganov44720af2013-08-20 16:32:53 -07001292 // printers are loaded we want to select the user's favorite one
1293 // which is the first.
Svetoslav2c1b1772013-12-20 17:10:25 -08001294 if (!mFavoritePrinterSelected && mDestinationSpinnerAdapter.getCount() > 1) {
Svetoslav Ganov44720af2013-08-20 16:32:53 -07001295 mFavoritePrinterSelected = true;
1296 mDestinationSpinner.setSelection(0);
Svetoslav Ganov69b91e02013-10-07 22:17:30 -07001297 // Workaround again the weird spinner behavior to notify for selection
1298 // change on the next layout pass as the current printer is used below.
1299 mCurrentPrinter = (PrinterInfo) mDestinationSpinnerAdapter.getItem(0);
Svetoslav269403b2013-08-14 17:31:04 -07001300 }
1301
Svetoslav4e4874b2013-10-01 17:53:17 -07001302 // If there is a next printer to select and we succeed selecting
1303 // it - done. Let the selection handling code make everything right.
1304 if (mNextPrinterId != null && selectPrinter(mNextPrinterId)) {
1305 mNextPrinterId = null;
1306 return;
1307 }
1308
Svetoslav Ganov44720af2013-08-20 16:32:53 -07001309 // If the current printer properties changed, we update the UI.
1310 if (mCurrentPrinter != null) {
1311 final int printerCount = mDestinationSpinnerAdapter.getCount();
1312 for (int i = 0; i < printerCount; i++) {
1313 Object item = mDestinationSpinnerAdapter.getItem(i);
1314 // Some items are not printers
1315 if (item instanceof PrinterInfo) {
1316 PrinterInfo printer = (PrinterInfo) item;
1317 if (!printer.getId().equals(mCurrentPrinter.getId())) {
1318 continue;
1319 }
1320
Svetoslav9d843e02013-10-14 13:15:31 -07001321 // If nothing changed - done.
1322 if (mCurrentPrinter.equals(printer)) {
1323 return;
1324 }
1325
Svetoslav Ganov9186d0c2013-09-03 00:11:58 -07001326 // If the current printer became available and has no
1327 // capabilities, we refresh it.
1328 if (mCurrentPrinter.getStatus() == PrinterInfo.STATUS_UNAVAILABLE
1329 && printer.getStatus() != PrinterInfo.STATUS_UNAVAILABLE
Svetoslav9d843e02013-10-14 13:15:31 -07001330 && printer.getCapabilities() == null) {
1331 if (!mCapabilitiesTimeout.isPosted()) {
1332 mCapabilitiesTimeout.post();
1333 }
1334 mCurrentPrinter.copyFrom(printer);
Svetoslav Ganov9186d0c2013-09-03 00:11:58 -07001335 refreshCurrentPrinter();
1336 return;
1337 }
1338
Svetoslav02dded12013-10-11 13:56:08 -07001339 // If the current printer became unavailable or its
1340 // capabilities go away, we update the UI and add a
1341 // timeout to declare the printer as unavailable.
1342 if ((mCurrentPrinter.getStatus() != PrinterInfo.STATUS_UNAVAILABLE
1343 && printer.getStatus() == PrinterInfo.STATUS_UNAVAILABLE)
1344 || (mCurrentPrinter.getCapabilities() != null
1345 && printer.getCapabilities() == null)) {
1346 if (!mCapabilitiesTimeout.isPosted()) {
1347 mCapabilitiesTimeout.post();
Svetoslav02dded12013-10-11 13:56:08 -07001348 }
Svetoslav9d843e02013-10-14 13:15:31 -07001349 mCurrentPrinter.copyFrom(printer);
1350 updateUi();
1351 return;
Svetoslav02dded12013-10-11 13:56:08 -07001352 }
1353
Svetoslavcc65b0c2013-09-10 21:08:32 -07001354 // We just refreshed the current printer.
1355 if (printer.getCapabilities() != null
Svetoslav Ganov6be4c7642013-09-26 21:12:47 -07001356 && mCapabilitiesTimeout.isPosted()) {
1357 mCapabilitiesTimeout.remove();
Svetoslavcc65b0c2013-09-10 21:08:32 -07001358 updatePrintAttributes(printer.getCapabilities());
1359 updateUi();
1360 mController.update();
1361 }
1362
Svetoslav Ganov44720af2013-08-20 16:32:53 -07001363 // Update the UI if capabilities changed.
1364 boolean capabilitiesChanged = false;
1365
1366 if (mCurrentPrinter.getCapabilities() == null) {
1367 if (printer.getCapabilities() != null) {
Svetoslav2c1b1772013-12-20 17:10:25 -08001368 updatePrintAttributes(printer.getCapabilities());
Svetoslav Ganov44720af2013-08-20 16:32:53 -07001369 capabilitiesChanged = true;
1370 }
1371 } else if (!mCurrentPrinter.getCapabilities().equals(
1372 printer.getCapabilities())) {
1373 capabilitiesChanged = true;
1374 }
1375
Svetoslavcc65b0c2013-09-10 21:08:32 -07001376 // Update the UI if the status changed.
1377 final boolean statusChanged = mCurrentPrinter.getStatus()
1378 != printer.getStatus();
Svetoslav Ganov44720af2013-08-20 16:32:53 -07001379
Svetoslavcc65b0c2013-09-10 21:08:32 -07001380 // Update the printer with the latest info.
1381 if (!mCurrentPrinter.equals(printer)) {
1382 mCurrentPrinter.copyFrom(printer);
1383 }
1384
1385 if (capabilitiesChanged || statusChanged) {
1386 // If something changed during update...
Svetoslav Ganov24c686b2013-10-05 18:52:06 -07001387 if (updateUi() || !mController.hasPerformedLayout()) {
Svetoslav Ganov44720af2013-08-20 16:32:53 -07001388 // Update the document.
1389 mController.update();
1390 }
1391 }
1392
1393 break;
1394 }
Svetoslav66160bb2013-08-12 22:36:50 -07001395 }
1396 }
Svetoslav66160bb2013-08-12 22:36:50 -07001397 }
1398
1399 @Override
1400 public void onInvalidated() {
Svetoslavcc65b0c2013-09-10 21:08:32 -07001401 /* do nothing - we always have one fake PDF printer */
Svetoslav66160bb2013-08-12 22:36:50 -07001402 }
1403 });
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07001404
1405 // Media size.
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07001406 mMediaSizeSpinnerAdapter = new ArrayAdapter<SpinnerItem<MediaSize>>(
1407 PrintJobConfigActivity.this,
1408 R.layout.spinner_dropdown_item, R.id.title);
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07001409
1410 // Color mode.
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07001411 mColorModeSpinnerAdapter = new ArrayAdapter<SpinnerItem<Integer>>(
1412 PrintJobConfigActivity.this,
1413 R.layout.spinner_dropdown_item, R.id.title);
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07001414
1415 // Orientation
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07001416 mOrientationSpinnerAdapter = new ArrayAdapter<SpinnerItem<Integer>>(
1417 PrintJobConfigActivity.this,
1418 R.layout.spinner_dropdown_item, R.id.title);
Svetoslav773f54d2013-09-03 14:01:43 -07001419 String[] orientationLabels = getResources().getStringArray(
1420 R.array.orientation_labels);
1421 mOrientationSpinnerAdapter.add(new SpinnerItem<Integer>(
1422 ORIENTATION_PORTRAIT, orientationLabels[0]));
1423 mOrientationSpinnerAdapter.add(new SpinnerItem<Integer>(
1424 ORIENTATION_LANDSCAPE, orientationLabels[1]));
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07001425
1426 // Range options
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07001427 mRangeOptionsSpinnerAdapter = new ArrayAdapter<SpinnerItem<Integer>>(
1428 PrintJobConfigActivity.this,
1429 R.layout.spinner_dropdown_item, R.id.title);
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07001430 final int[] rangeOptionsValues = getResources().getIntArray(
1431 R.array.page_options_values);
1432 String[] rangeOptionsLabels = getResources().getStringArray(
1433 R.array.page_options_labels);
1434 final int rangeOptionsCount = rangeOptionsLabels.length;
1435 for (int i = 0; i < rangeOptionsCount; i++) {
1436 mRangeOptionsSpinnerAdapter.add(new SpinnerItem<Integer>(
1437 rangeOptionsValues[i], rangeOptionsLabels[i]));
1438 }
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07001439
Svetoslav269403b2013-08-14 17:31:04 -07001440 showUi(UI_EDITING_PRINT_JOB, null);
1441 bindUi();
Svetoslav Ganov8c433762013-08-02 14:22:19 -07001442 updateUi();
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07001443 }
1444
Svetoslav Ganovcaff3882013-10-09 10:53:26 -07001445 public void reselectCurrentPrinter() {
1446 if (mCurrentPrinter != null) {
1447 // TODO: While the data did not change and we set the adapter
1448 // to a newly inflated spinner, the latter does not show the
1449 // current item unless we poke the adapter. This requires more
1450 // investigation. Maybe an optimization in AdapterView does not
1451 // call into the adapter if the view is not visible which is the
1452 // case when we set the adapter.
1453 mDestinationSpinnerAdapter.notifyDataSetChanged();
1454 final int position = mDestinationSpinnerAdapter.getPrinterIndex(
1455 mCurrentPrinter.getId());
1456 mDestinationSpinner.setSelection(position);
1457 }
1458 }
1459
Svetoslav Ganov44720af2013-08-20 16:32:53 -07001460 public void refreshCurrentPrinter() {
1461 PrinterInfo printer = (PrinterInfo) mDestinationSpinner.getSelectedItem();
1462 if (printer != null) {
1463 FusedPrintersProvider printersLoader = (FusedPrintersProvider)
1464 (Loader<?>) getLoaderManager().getLoader(
1465 LOADER_ID_PRINTERS_LOADER);
1466 if (printersLoader != null) {
Svetoslav Ganovd26d4892013-08-28 14:37:54 -07001467 printersLoader.setTrackedPrinter(printer.getId());
Svetoslav269403b2013-08-14 17:31:04 -07001468 }
1469 }
1470 }
1471
Svetoslav Ganov9186d0c2013-09-03 00:11:58 -07001472 public void addCurrentPrinterToHistory() {
1473 PrinterInfo printer = (PrinterInfo) mDestinationSpinner.getSelectedItem();
Svetoslava36285f2013-09-05 11:27:45 -07001474 PrinterId fakePdfPritnerId = mDestinationSpinnerAdapter.mFakePdfPrinter.getId();
1475 if (printer != null && !printer.getId().equals(fakePdfPritnerId)) {
Svetoslav Ganov9186d0c2013-09-03 00:11:58 -07001476 FusedPrintersProvider printersLoader = (FusedPrintersProvider)
1477 (Loader<?>) getLoaderManager().getLoader(
1478 LOADER_ID_PRINTERS_LOADER);
1479 if (printersLoader != null) {
1480 printersLoader.addHistoricalPrinter(printer);
1481 }
1482 }
1483 }
1484
Svetoslavb4fda132013-10-25 18:57:43 -07001485 public void updateFromAdvancedOptions(PrintJobInfo printJobInfo) {
1486 boolean updateContent = false;
1487
1488 // Copies.
1489 mCopiesEditText.setText(String.valueOf(printJobInfo.getCopies()));
1490
1491 // Media size and orientation
1492 PrintAttributes attributes = printJobInfo.getAttributes();
1493 if (!mCurrPrintAttributes.getMediaSize().equals(attributes.getMediaSize())) {
1494 final int mediaSizeCount = mMediaSizeSpinnerAdapter.getCount();
1495 for (int i = 0; i < mediaSizeCount; i++) {
1496 MediaSize mediaSize = mMediaSizeSpinnerAdapter.getItem(i).value;
1497 if (mediaSize.asPortrait().equals(attributes.getMediaSize().asPortrait())) {
1498 updateContent = true;
1499 mCurrPrintAttributes.setMediaSize(attributes.getMediaSize());
1500 mMediaSizeSpinner.setSelection(i);
1501 mIgnoreNextMediaSizeChange = true;
1502 if (attributes.getMediaSize().isPortrait()) {
1503 mOrientationSpinner.setSelection(0);
1504 mIgnoreNextOrientationChange = true;
1505 } else {
1506 mOrientationSpinner.setSelection(1);
1507 mIgnoreNextOrientationChange = true;
1508 }
1509 break;
1510 }
1511 }
1512 }
1513
1514 // Color mode.
1515 final int colorMode = attributes.getColorMode();
1516 if (mCurrPrintAttributes.getColorMode() != colorMode) {
1517 if (colorMode == PrintAttributes.COLOR_MODE_MONOCHROME) {
1518 updateContent = true;
1519 mColorModeSpinner.setSelection(0);
1520 mIgnoreNextColorChange = true;
1521 mCurrPrintAttributes.setColorMode(attributes.getColorMode());
1522 } else if (colorMode == PrintAttributes.COLOR_MODE_COLOR) {
1523 updateContent = true;
1524 mColorModeSpinner.setSelection(1);
1525 mIgnoreNextColorChange = true;
1526 mCurrPrintAttributes.setColorMode(attributes.getColorMode());
1527 }
1528 }
1529
1530 // Range.
1531 PageRange[] pageRanges = printJobInfo.getPages();
1532 if (pageRanges != null && pageRanges.length > 0) {
1533 pageRanges = PageRangeUtils.normalize(pageRanges);
1534 final int pageRangeCount = pageRanges.length;
1535 if (pageRangeCount == 1 && pageRanges[0] == PageRange.ALL_PAGES) {
1536 mRangeOptionsSpinner.setSelection(0);
1537 } else {
1538 final int pageCount = mDocument.info.getPageCount();
1539 if (pageRanges[0].getStart() >= 0
1540 && pageRanges[pageRanges.length - 1].getEnd() < pageCount) {
1541 mRangeOptionsSpinner.setSelection(1);
1542 StringBuilder builder = new StringBuilder();
1543 for (int i = 0; i < pageRangeCount; i++) {
1544 if (builder.length() > 0) {
1545 builder.append(',');
1546 }
1547 PageRange pageRange = pageRanges[i];
Svetoslav Ganov1a85d9f2013-11-15 21:42:12 -08001548 final int shownStartPage = pageRange.getStart() + 1;
1549 final int shownEndPage = pageRange.getEnd() + 1;
1550 builder.append(shownStartPage);
1551 if (shownStartPage != shownEndPage) {
1552 builder.append('-');
1553 builder.append(shownEndPage);
1554 }
Svetoslavb4fda132013-10-25 18:57:43 -07001555 }
1556 mPageRangeEditText.setText(builder.toString());
1557 }
1558 }
1559 }
1560
1561 // Update the advanced options.
1562 mSpoolerProvider.getSpooler().setPrintJobAdvancedOptionsNoPersistence(
1563 mPrintJobId, printJobInfo.getAdvancedOptions());
1564
1565 // Update the content if needed.
1566 if (updateContent) {
1567 mController.update();
1568 }
1569 }
1570
Svetoslav4e4874b2013-10-01 17:53:17 -07001571 public void ensurePrinterSelected(PrinterId printerId) {
1572 // If the printer is not present maybe the loader is not
1573 // updated yet. In this case make a note and as soon as
1574 // the printer appears will will select it.
1575 if (!selectPrinter(printerId)) {
1576 mNextPrinterId = printerId;
1577 }
1578 }
1579
1580 public boolean selectPrinter(PrinterId printerId) {
1581 mDestinationSpinnerAdapter.ensurePrinterInVisibleAdapterPosition(printerId);
Svetoslav Ganov44720af2013-08-20 16:32:53 -07001582 final int position = mDestinationSpinnerAdapter.getPrinterIndex(printerId);
Svetoslav4e4874b2013-10-01 17:53:17 -07001583 if (position != AdapterView.INVALID_POSITION
1584 && position != mDestinationSpinner.getSelectedItemPosition()) {
1585 Object item = mDestinationSpinnerAdapter.getItem(position);
1586 mCurrentPrinter = (PrinterInfo) item;
1587 mDestinationSpinner.setSelection(position);
1588 return true;
1589 }
1590 return false;
1591 }
1592
1593 public void ensureCurrentPrinterSelected() {
1594 if (mCurrentPrinter != null) {
1595 selectPrinter(mCurrentPrinter.getId());
1596 }
Svetoslav Ganov44720af2013-08-20 16:32:53 -07001597 }
1598
Svetoslav269403b2013-08-14 17:31:04 -07001599 public boolean isPrintingToPdf() {
1600 return mDestinationSpinner.getSelectedItem()
1601 == mDestinationSpinnerAdapter.mFakePdfPrinter;
1602 }
1603
Svetoslav Ganov695c7fa2013-08-07 19:29:42 -07001604 public boolean shouldCloseOnTouch(MotionEvent event) {
1605 if (event.getAction() != MotionEvent.ACTION_DOWN) {
1606 return false;
1607 }
1608
1609 final int[] locationInWindow = new int[2];
1610 mContentContainer.getLocationInWindow(locationInWindow);
1611
1612 final int windowTouchSlop = ViewConfiguration.get(PrintJobConfigActivity.this)
1613 .getScaledWindowTouchSlop();
1614 final int eventX = (int) event.getX();
1615 final int eventY = (int) event.getY();
1616 final int lenientWindowLeft = locationInWindow[0] - windowTouchSlop;
1617 final int lenientWindowRight = lenientWindowLeft + mContentContainer.getWidth()
1618 + windowTouchSlop;
1619 final int lenientWindowTop = locationInWindow[1] - windowTouchSlop;
1620 final int lenientWindowBottom = lenientWindowTop + mContentContainer.getHeight()
1621 + windowTouchSlop;
1622
1623 if (eventX < lenientWindowLeft || eventX > lenientWindowRight
1624 || eventY < lenientWindowTop || eventY > lenientWindowBottom) {
1625 return true;
1626 }
1627 return false;
1628 }
1629
Svetoslav Ganov32c5eb32013-08-06 23:49:25 -07001630 public boolean isShwoingGeneratingPrintJobUi() {
Svetoslav269403b2013-08-14 17:31:04 -07001631 return (mCurrentUi == UI_GENERATING_PRINT_JOB);
Svetoslav Ganov32c5eb32013-08-06 23:49:25 -07001632 }
1633
Svetoslav269403b2013-08-14 17:31:04 -07001634 public void showUi(int ui, final Runnable postSwitchCallback) {
1635 if (ui == UI_NONE) {
1636 throw new IllegalStateException("cannot remove the ui");
1637 }
Svetoslav Ganov32c5eb32013-08-06 23:49:25 -07001638
Svetoslav269403b2013-08-14 17:31:04 -07001639 if (mCurrentUi == ui) {
1640 return;
1641 }
1642
Svetoslav Ganovcaff3882013-10-09 10:53:26 -07001643 final int oldUi = mCurrentUi;
1644 mCurrentUi = ui;
1645
1646 switch (oldUi) {
Svetoslav269403b2013-08-14 17:31:04 -07001647 case UI_NONE: {
1648 switch (ui) {
1649 case UI_EDITING_PRINT_JOB: {
1650 doUiSwitch(R.layout.print_job_config_activity_content_editing);
1651 registerPrintButtonClickListener();
1652 if (postSwitchCallback != null) {
1653 postSwitchCallback.run();
1654 }
1655 } break;
1656
1657 case UI_GENERATING_PRINT_JOB: {
1658 doUiSwitch(R.layout.print_job_config_activity_content_generating);
1659 registerCancelButtonClickListener();
1660 if (postSwitchCallback != null) {
1661 postSwitchCallback.run();
1662 }
1663 } break;
1664 }
1665 } break;
1666
1667 case UI_EDITING_PRINT_JOB: {
1668 switch (ui) {
1669 case UI_GENERATING_PRINT_JOB: {
1670 animateUiSwitch(R.layout.print_job_config_activity_content_generating,
1671 new Runnable() {
1672 @Override
1673 public void run() {
1674 registerCancelButtonClickListener();
1675 if (postSwitchCallback != null) {
1676 postSwitchCallback.run();
1677 }
1678 }
Svetoslav3aa2e2b2013-10-10 16:46:06 -07001679 },
1680 new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
1681 ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER));
Svetoslav269403b2013-08-14 17:31:04 -07001682 } break;
Svetoslav Ganovcaff3882013-10-09 10:53:26 -07001683
1684 case UI_ERROR: {
1685 animateUiSwitch(R.layout.print_job_config_activity_content_error,
1686 new Runnable() {
1687 @Override
1688 public void run() {
1689 registerOkButtonClickListener();
1690 if (postSwitchCallback != null) {
1691 postSwitchCallback.run();
1692 }
1693 }
1694 },
1695 new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
1696 ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER));
1697 } break;
Svetoslav269403b2013-08-14 17:31:04 -07001698 }
1699 } break;
1700
1701 case UI_GENERATING_PRINT_JOB: {
1702 switch (ui) {
1703 case UI_EDITING_PRINT_JOB: {
1704 animateUiSwitch(R.layout.print_job_config_activity_content_editing,
1705 new Runnable() {
1706 @Override
1707 public void run() {
1708 registerPrintButtonClickListener();
1709 if (postSwitchCallback != null) {
1710 postSwitchCallback.run();
1711 }
1712 }
Svetoslav3aa2e2b2013-10-10 16:46:06 -07001713 },
1714 new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
1715 ViewGroup.LayoutParams.MATCH_PARENT, Gravity.CENTER));
Svetoslav269403b2013-08-14 17:31:04 -07001716 } break;
Svetoslav Ganovcaff3882013-10-09 10:53:26 -07001717
1718 case UI_ERROR: {
1719 animateUiSwitch(R.layout.print_job_config_activity_content_error,
1720 new Runnable() {
1721 @Override
1722 public void run() {
1723 registerOkButtonClickListener();
1724 if (postSwitchCallback != null) {
1725 postSwitchCallback.run();
1726 }
1727 }
1728 },
1729 new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
1730 ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER));
1731 } break;
1732 }
1733 } break;
1734
1735 case UI_ERROR: {
1736 switch (ui) {
1737 case UI_EDITING_PRINT_JOB: {
1738 animateUiSwitch(R.layout.print_job_config_activity_content_editing,
1739 new Runnable() {
1740 @Override
1741 public void run() {
1742 registerPrintButtonClickListener();
1743 if (postSwitchCallback != null) {
1744 postSwitchCallback.run();
1745 }
1746 }
1747 },
1748 new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
1749 ViewGroup.LayoutParams.MATCH_PARENT, Gravity.CENTER));
1750 } break;
Svetoslav269403b2013-08-14 17:31:04 -07001751 }
1752 } break;
1753 }
Svetoslav269403b2013-08-14 17:31:04 -07001754 }
1755
Svetoslavb4fda132013-10-25 18:57:43 -07001756 private void registerAdvancedPrintOptionsButtonClickListener() {
1757 Button advancedOptionsButton = (Button) findViewById(R.id.advanced_settings_button);
1758 advancedOptionsButton.setOnClickListener(new OnClickListener() {
1759 @Override
1760 public void onClick(View v) {
1761 ComponentName serviceName = mCurrentPrinter.getId().getServiceName();
1762 String activityName = getAdvancedOptionsActivityName(serviceName);
1763 if (TextUtils.isEmpty(activityName)) {
1764 return;
1765 }
1766 Intent intent = new Intent(Intent.ACTION_MAIN);
1767 intent.setComponent(new ComponentName(serviceName.getPackageName(),
1768 activityName));
1769
1770 List<ResolveInfo> resolvedActivities = getPackageManager()
1771 .queryIntentActivities(intent, 0);
1772 if (resolvedActivities.isEmpty()) {
1773 return;
1774 }
1775 // The activity is a component name, therefore it is one or none.
1776 if (resolvedActivities.get(0).activityInfo.exported) {
1777 PrintJobInfo printJobInfo = mSpoolerProvider.getSpooler().getPrintJobInfo(
1778 mPrintJobId, PrintManager.APP_ID_ANY);
1779 intent.putExtra(PrintService.EXTRA_PRINT_JOB_INFO, printJobInfo);
1780 // TODO: Make this an API for the next release.
1781 intent.putExtra("android.intent.extra.print.EXTRA_PRINTER_INFO",
1782 mCurrentPrinter);
1783 try {
1784 startActivityForResult(intent,
1785 ACTIVITY_POPULATE_ADVANCED_PRINT_OPTIONS);
1786 } catch (ActivityNotFoundException anfe) {
1787 Log.e(LOG_TAG, "Error starting activity for intent: " + intent, anfe);
1788 }
1789 }
1790 }
1791 });
1792 }
1793
Svetoslav269403b2013-08-14 17:31:04 -07001794 private void registerPrintButtonClickListener() {
1795 Button printButton = (Button) findViewById(R.id.print_button);
1796 printButton.setOnClickListener(new OnClickListener() {
1797 @Override
1798 public void onClick(View v) {
1799 PrinterInfo printer = (PrinterInfo) mDestinationSpinner.getSelectedItem();
1800 if (printer != null) {
1801 mEditor.confirmPrint();
1802 mController.update();
1803 if (!printer.equals(mDestinationSpinnerAdapter.mFakePdfPrinter)) {
Svetoslav Ganov44720af2013-08-20 16:32:53 -07001804 mEditor.refreshCurrentPrinter();
Svetoslav269403b2013-08-14 17:31:04 -07001805 }
1806 } else {
1807 mEditor.cancel();
1808 PrintJobConfigActivity.this.finish();
1809 }
1810 }
1811 });
1812 }
1813
1814 private void registerCancelButtonClickListener() {
1815 Button cancelButton = (Button) findViewById(R.id.cancel_button);
Svetoslav Ganov32c5eb32013-08-06 23:49:25 -07001816 cancelButton.setOnClickListener(new OnClickListener() {
1817 @Override
1818 public void onClick(View v) {
1819 if (!mController.isWorking()) {
1820 PrintJobConfigActivity.this.finish();
1821 }
1822 mEditor.cancel();
1823 }
1824 });
Svetoslav269403b2013-08-14 17:31:04 -07001825 }
1826
Svetoslav Ganovcaff3882013-10-09 10:53:26 -07001827 private void registerOkButtonClickListener() {
1828 Button okButton = (Button) findViewById(R.id.ok_button);
1829 okButton.setOnClickListener(new OnClickListener() {
1830 @Override
1831 public void onClick(View v) {
1832 mEditor.showUi(Editor.UI_EDITING_PRINT_JOB, new Runnable() {
1833 @Override
1834 public void run() {
1835 // Start over with a clean slate.
1836 mOldPrintAttributes.clear();
1837 mController.initialize();
1838 mEditor.initialize();
1839 mEditor.bindUi();
1840 mEditor.reselectCurrentPrinter();
Svetoslavb4fda132013-10-25 18:57:43 -07001841 if (!mController.hasPerformedLayout()) {
1842 mController.update();
1843 }
Svetoslav Ganovcaff3882013-10-09 10:53:26 -07001844 }
1845 });
1846 }
1847 });
1848 }
1849
Svetoslav269403b2013-08-14 17:31:04 -07001850 private void doUiSwitch(int showLayoutId) {
1851 ViewGroup contentContainer = (ViewGroup) findViewById(R.id.content_container);
1852 contentContainer.removeAllViews();
1853 getLayoutInflater().inflate(showLayoutId, contentContainer, true);
1854 }
1855
Svetoslav Ganovcaff3882013-10-09 10:53:26 -07001856 private void animateUiSwitch(int showLayoutId, final Runnable beforeShowNewUiAction,
Svetoslav3aa2e2b2013-10-10 16:46:06 -07001857 final LayoutParams containerParams) {
Svetoslav269403b2013-08-14 17:31:04 -07001858 // Find everything we will shuffle around.
1859 final ViewGroup contentContainer = (ViewGroup) findViewById(R.id.content_container);
1860 final View hidingView = contentContainer.getChildAt(0);
1861 final View showingView = getLayoutInflater().inflate(showLayoutId,
1862 null, false);
Svetoslav Ganov32c5eb32013-08-06 23:49:25 -07001863
1864 // First animation - fade out the old content.
Svetoslav Ganov860f8a62013-09-14 00:59:03 -07001865 AutoCancellingAnimator.animate(hidingView).alpha(0.0f)
1866 .withLayer().withEndAction(new Runnable() {
Svetoslav Ganov32c5eb32013-08-06 23:49:25 -07001867 @Override
1868 public void run() {
Svetoslav269403b2013-08-14 17:31:04 -07001869 hidingView.setVisibility(View.INVISIBLE);
Svetoslav Ganov32c5eb32013-08-06 23:49:25 -07001870
1871 // Prepare the new content with correct size and alpha.
Svetoslav269403b2013-08-14 17:31:04 -07001872 showingView.setMinimumWidth(contentContainer.getWidth());
1873 showingView.setAlpha(0.0f);
Svetoslav Ganov32c5eb32013-08-06 23:49:25 -07001874
Svetoslav269403b2013-08-14 17:31:04 -07001875 // Compute how to much shrink /stretch the content.
Svetoslav Ganov32c5eb32013-08-06 23:49:25 -07001876 final int widthSpec = MeasureSpec.makeMeasureSpec(
Svetoslav269403b2013-08-14 17:31:04 -07001877 contentContainer.getWidth(), MeasureSpec.UNSPECIFIED);
Svetoslav Ganov32c5eb32013-08-06 23:49:25 -07001878 final int heightSpec = MeasureSpec.makeMeasureSpec(
Svetoslav269403b2013-08-14 17:31:04 -07001879 contentContainer.getHeight(), MeasureSpec.UNSPECIFIED);
1880 showingView.measure(widthSpec, heightSpec);
1881 final float scaleY = (float) showingView.getMeasuredHeight()
Svetoslav Ganov32c5eb32013-08-06 23:49:25 -07001882 / (float) contentContainer.getHeight();
1883
1884 // Second animation - resize the container.
Svetoslav Ganovcaff3882013-10-09 10:53:26 -07001885 AutoCancellingAnimator.animate(contentContainer).scaleY(scaleY)
Svetoslav Ganov860f8a62013-09-14 00:59:03 -07001886 .withEndAction(new Runnable() {
Svetoslav Ganov32c5eb32013-08-06 23:49:25 -07001887 @Override
1888 public void run() {
1889 // Swap the old and the new content.
1890 contentContainer.removeAllViews();
1891 contentContainer.setScaleY(1.0f);
Svetoslav269403b2013-08-14 17:31:04 -07001892 contentContainer.addView(showingView);
Svetoslav Ganov32c5eb32013-08-06 23:49:25 -07001893
Svetoslav3aa2e2b2013-10-10 16:46:06 -07001894 contentContainer.setLayoutParams(containerParams);
1895
Svetoslav Ganovcaff3882013-10-09 10:53:26 -07001896 beforeShowNewUiAction.run();
1897
Svetoslav Ganov32c5eb32013-08-06 23:49:25 -07001898 // Third animation - show the new content.
Svetoslav Ganovcaff3882013-10-09 10:53:26 -07001899 AutoCancellingAnimator.animate(showingView).alpha(1.0f);
Svetoslav Ganov32c5eb32013-08-06 23:49:25 -07001900 }
1901 });
1902 }
1903 });
1904 }
1905
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07001906 public void initialize() {
1907 mEditorState = EDITOR_STATE_INITIALIZED;
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07001908 }
1909
1910 public boolean isCancelled() {
1911 return mEditorState == EDITOR_STATE_CANCELLED;
1912 }
1913
1914 public void cancel() {
1915 mEditorState = EDITOR_STATE_CANCELLED;
1916 mController.cancel();
1917 updateUi();
1918 }
1919
1920 public boolean isDone() {
Svetoslava36285f2013-09-05 11:27:45 -07001921 return isPrintConfirmed() || isCancelled();
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07001922 }
1923
1924 public boolean isPrintConfirmed() {
1925 return mEditorState == EDITOR_STATE_CONFIRMED_PRINT;
1926 }
1927
1928 public void confirmPrint() {
Svetoslav Ganov9186d0c2013-09-03 00:11:58 -07001929 addCurrentPrinterToHistory();
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07001930 mEditorState = EDITOR_STATE_CONFIRMED_PRINT;
Svetoslav269403b2013-08-14 17:31:04 -07001931 showUi(UI_GENERATING_PRINT_JOB, null);
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07001932 }
1933
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07001934 public PageRange[] getRequestedPages() {
1935 if (hasErrors()) {
1936 return null;
1937 }
1938 if (mRangeOptionsSpinner.getSelectedItemPosition() > 0) {
1939 List<PageRange> pageRanges = new ArrayList<PageRange>();
Svetoslav Ganov44720af2013-08-20 16:32:53 -07001940 mStringCommaSplitter.setString(mPageRangeEditText.getText().toString());
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07001941
1942 while (mStringCommaSplitter.hasNext()) {
1943 String range = mStringCommaSplitter.next().trim();
Svetoslav Ganovab051ba2013-09-26 00:34:54 -07001944 if (TextUtils.isEmpty(range)) {
1945 continue;
1946 }
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07001947 final int dashIndex = range.indexOf('-');
1948 final int fromIndex;
1949 final int toIndex;
1950
1951 if (dashIndex > 0) {
Svetoslav Ganovab051ba2013-09-26 00:34:54 -07001952 fromIndex = Integer.parseInt(range.substring(0, dashIndex).trim()) - 1;
Svetoslavc3484022013-09-16 19:03:39 -07001953 // It is possible that the dash is at the end since the input
1954 // verification can has to allow the user to keep entering if
1955 // this would lead to a valid input. So we handle this.
1956 toIndex = (dashIndex < range.length() - 1)
1957 ? Integer.parseInt(range.substring(dashIndex + 1,
Svetoslav Ganovab051ba2013-09-26 00:34:54 -07001958 range.length()).trim()) - 1 : fromIndex;
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07001959 } else {
Svetoslav Ganovd26d4892013-08-28 14:37:54 -07001960 fromIndex = toIndex = Integer.parseInt(range) - 1;
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07001961 }
1962
Svetoslavf0c48a72013-09-18 14:09:47 -07001963 PageRange pageRange = new PageRange(Math.min(fromIndex, toIndex),
1964 Math.max(fromIndex, toIndex));
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07001965 pageRanges.add(pageRange);
1966 }
1967
1968 PageRange[] pageRangesArray = new PageRange[pageRanges.size()];
1969 pageRanges.toArray(pageRangesArray);
1970
1971 return PageRangeUtils.normalize(pageRangesArray);
1972 }
1973
1974 return ALL_PAGES_ARRAY;
1975 }
1976
Svetoslav269403b2013-08-14 17:31:04 -07001977 private void bindUi() {
1978 if (mCurrentUi != UI_EDITING_PRINT_JOB) {
1979 return;
1980 }
1981
1982 // Content container
1983 mContentContainer = findViewById(R.id.content_container);
1984
1985 // Copies
1986 mCopiesEditText = (EditText) findViewById(R.id.copies_edittext);
Svetoslav89ed9fc2013-10-11 17:00:45 -07001987 mCopiesEditText.setOnFocusChangeListener(mFocusListener);
Svetoslav269403b2013-08-14 17:31:04 -07001988 mCopiesEditText.setText(MIN_COPIES_STRING);
Svetoslav89ed9fc2013-10-11 17:00:45 -07001989 mCopiesEditText.setSelection(mCopiesEditText.getText().length());
Svetoslav269403b2013-08-14 17:31:04 -07001990 mCopiesEditText.addTextChangedListener(mCopiesTextWatcher);
Svetoslav269403b2013-08-14 17:31:04 -07001991 if (!TextUtils.equals(mCopiesEditText.getText(), MIN_COPIES_STRING)) {
1992 mIgnoreNextCopiesChange = true;
1993 }
Svetoslav7bfbbcb2013-10-10 13:36:23 -07001994 mSpoolerProvider.getSpooler().setPrintJobCopiesNoPersistence(
Svetoslav269403b2013-08-14 17:31:04 -07001995 mPrintJobId, MIN_COPIES);
1996
1997 // Destination.
1998 mDestinationSpinner = (Spinner) findViewById(R.id.destination_spinner);
Svetoslav39e71de2013-10-02 19:30:32 -07001999 mDestinationSpinner.setDropDownWidth(ViewGroup.LayoutParams.MATCH_PARENT);
Svetoslav269403b2013-08-14 17:31:04 -07002000 mDestinationSpinner.setAdapter(mDestinationSpinnerAdapter);
2001 mDestinationSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
Svetoslav7bfbbcb2013-10-10 13:36:23 -07002002 if (mDestinationSpinnerAdapter.getCount() > 0) {
Svetoslav269403b2013-08-14 17:31:04 -07002003 mIgnoreNextDestinationChange = true;
2004 }
2005
2006 // Media size.
2007 mMediaSizeSpinner = (Spinner) findViewById(R.id.paper_size_spinner);
2008 mMediaSizeSpinner.setAdapter(mMediaSizeSpinnerAdapter);
2009 mMediaSizeSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
2010 if (mMediaSizeSpinnerAdapter.getCount() > 0) {
Svetoslavcc65b0c2013-09-10 21:08:32 -07002011 mOldMediaSizeSelectionIndex = 0;
Svetoslav269403b2013-08-14 17:31:04 -07002012 }
2013
2014 // Color mode.
2015 mColorModeSpinner = (Spinner) findViewById(R.id.color_spinner);
2016 mColorModeSpinner.setAdapter(mColorModeSpinnerAdapter);
2017 mColorModeSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
2018 if (mColorModeSpinnerAdapter.getCount() > 0) {
Svetoslavcc65b0c2013-09-10 21:08:32 -07002019 mOldColorModeSelectionIndex = 0;
Svetoslav269403b2013-08-14 17:31:04 -07002020 }
2021
2022 // Orientation
2023 mOrientationSpinner = (Spinner) findViewById(R.id.orientation_spinner);
2024 mOrientationSpinner.setAdapter(mOrientationSpinnerAdapter);
2025 mOrientationSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
2026 if (mOrientationSpinnerAdapter.getCount() > 0) {
2027 mIgnoreNextOrientationChange = true;
2028 }
2029
Svetoslav269403b2013-08-14 17:31:04 -07002030 // Range options
Svetoslav Ganov44720af2013-08-20 16:32:53 -07002031 mRangeOptionsTitle = (TextView) findViewById(R.id.range_options_title);
Svetoslav269403b2013-08-14 17:31:04 -07002032 mRangeOptionsSpinner = (Spinner) findViewById(R.id.range_options_spinner);
2033 mRangeOptionsSpinner.setAdapter(mRangeOptionsSpinnerAdapter);
2034 mRangeOptionsSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
2035 if (mRangeOptionsSpinnerAdapter.getCount() > 0) {
2036 mIgnoreNextRangeOptionChange = true;
2037 }
2038
Svetoslav Ganov44720af2013-08-20 16:32:53 -07002039 // Page range
2040 mPageRangeTitle = (TextView) findViewById(R.id.page_range_title);
2041 mPageRangeEditText = (EditText) findViewById(R.id.page_range_edittext);
Svetoslav89ed9fc2013-10-11 17:00:45 -07002042 mPageRangeEditText.setOnFocusChangeListener(mFocusListener);
Svetoslav Ganov44720af2013-08-20 16:32:53 -07002043 mPageRangeEditText.addTextChangedListener(mRangeTextWatcher);
2044
Svetoslavb4fda132013-10-25 18:57:43 -07002045 // Advanced options button.
2046 mAdvancedPrintOptionsContainer = findViewById(R.id.advanced_settings_container);
2047 mAdvancedOptionsButton = (Button) findViewById(R.id.advanced_settings_button);
2048 registerAdvancedPrintOptionsButtonClickListener();
2049
Svetoslav269403b2013-08-14 17:31:04 -07002050 // Print button
2051 mPrintButton = (Button) findViewById(R.id.print_button);
2052 registerPrintButtonClickListener();
2053 }
2054
Svetoslav Ganov44720af2013-08-20 16:32:53 -07002055 public boolean updateUi() {
Svetoslav269403b2013-08-14 17:31:04 -07002056 if (mCurrentUi != UI_EDITING_PRINT_JOB) {
Svetoslav Ganov44720af2013-08-20 16:32:53 -07002057 return false;
Svetoslav269403b2013-08-14 17:31:04 -07002058 }
Svetoslava36285f2013-09-05 11:27:45 -07002059 if (isPrintConfirmed() || isCancelled()) {
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07002060 mDestinationSpinner.setEnabled(false);
2061 mCopiesEditText.setEnabled(false);
2062 mMediaSizeSpinner.setEnabled(false);
2063 mColorModeSpinner.setEnabled(false);
2064 mOrientationSpinner.setEnabled(false);
2065 mRangeOptionsSpinner.setEnabled(false);
Svetoslav Ganov44720af2013-08-20 16:32:53 -07002066 mPageRangeEditText.setEnabled(false);
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07002067 mPrintButton.setEnabled(false);
Svetoslavb4fda132013-10-25 18:57:43 -07002068 mAdvancedOptionsButton.setEnabled(false);
Svetoslav Ganov44720af2013-08-20 16:32:53 -07002069 return false;
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07002070 }
2071
Svetoslav269403b2013-08-14 17:31:04 -07002072 // If a printer with capabilities is selected, then we enabled all options.
2073 boolean allOptionsEnabled = false;
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07002074 final int selectedIndex = mDestinationSpinner.getSelectedItemPosition();
Svetoslav269403b2013-08-14 17:31:04 -07002075 if (selectedIndex >= 0) {
2076 Object item = mDestinationSpinnerAdapter.getItem(selectedIndex);
2077 if (item instanceof PrinterInfo) {
2078 PrinterInfo printer = (PrinterInfo) item;
Svetoslavcc65b0c2013-09-10 21:08:32 -07002079 if (printer.getCapabilities() != null
2080 && printer.getStatus() != PrinterInfo.STATUS_UNAVAILABLE) {
Svetoslav269403b2013-08-14 17:31:04 -07002081 allOptionsEnabled = true;
2082 }
2083 }
2084 }
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07002085
Svetoslav269403b2013-08-14 17:31:04 -07002086 if (!allOptionsEnabled) {
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07002087 mCopiesEditText.setEnabled(false);
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07002088 mMediaSizeSpinner.setEnabled(false);
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07002089 mColorModeSpinner.setEnabled(false);
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07002090 mOrientationSpinner.setEnabled(false);
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07002091 mRangeOptionsSpinner.setEnabled(false);
Svetoslav Ganov44720af2013-08-20 16:32:53 -07002092 mPageRangeEditText.setEnabled(false);
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07002093 mPrintButton.setEnabled(false);
Svetoslavb4fda132013-10-25 18:57:43 -07002094 mAdvancedOptionsButton.setEnabled(false);
Svetoslav Ganov44720af2013-08-20 16:32:53 -07002095 return false;
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07002096 } else {
Svetoslav Ganov44720af2013-08-20 16:32:53 -07002097 boolean someAttributeSelectionChanged = false;
2098
Svetoslav66160bb2013-08-12 22:36:50 -07002099 PrinterInfo printer = (PrinterInfo) mDestinationSpinner.getSelectedItem();
Svetoslav Ganov798bed62013-08-11 12:29:39 -07002100 PrinterCapabilitiesInfo capabilities = printer.getCapabilities();
Svetoslav651dd4e2013-09-12 14:37:47 -07002101 PrintAttributes defaultAttributes = printer.getCapabilities().getDefaults();
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07002102
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07002103 // Media size.
Svetoslav Ganov7be27ac2013-09-30 09:04:50 -07002104 // Sort the media sizes based on the current locale.
2105 List<MediaSize> mediaSizes = new ArrayList<MediaSize>(capabilities.getMediaSizes());
2106 Collections.sort(mediaSizes, mMediaSizeComparator);
Svetoslav Ganov44720af2013-08-20 16:32:53 -07002107
2108 // If the media sizes changed, we update the adapter and the spinner.
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07002109 boolean mediaSizesChanged = false;
2110 final int mediaSizeCount = mediaSizes.size();
2111 if (mediaSizeCount != mMediaSizeSpinnerAdapter.getCount()) {
2112 mediaSizesChanged = true;
2113 } else {
2114 for (int i = 0; i < mediaSizeCount; i++) {
2115 if (!mediaSizes.get(i).equals(mMediaSizeSpinnerAdapter.getItem(i).value)) {
2116 mediaSizesChanged = true;
2117 break;
2118 }
2119 }
2120 }
2121 if (mediaSizesChanged) {
Svetoslav Ganov44720af2013-08-20 16:32:53 -07002122 // Remember the old media size to try selecting it again.
2123 int oldMediaSizeNewIndex = AdapterView.INVALID_POSITION;
2124 MediaSize oldMediaSize = mCurrPrintAttributes.getMediaSize();
2125
2126 // Rebuild the adapter data.
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07002127 mMediaSizeSpinnerAdapter.clear();
2128 for (int i = 0; i < mediaSizeCount; i++) {
2129 MediaSize mediaSize = mediaSizes.get(i);
Svetoslav Ganov7be27ac2013-09-30 09:04:50 -07002130 if (mediaSize.asPortrait().equals(oldMediaSize.asPortrait())) {
Svetoslav Ganov44720af2013-08-20 16:32:53 -07002131 // Update the index of the old selection.
2132 oldMediaSizeNewIndex = i;
2133 }
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07002134 mMediaSizeSpinnerAdapter.add(new SpinnerItem<MediaSize>(
Svetoslav773f54d2013-09-03 14:01:43 -07002135 mediaSize, mediaSize.getLabel(getPackageManager())));
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07002136 }
Svetoslav Ganov44720af2013-08-20 16:32:53 -07002137
Svetoslavcc65b0c2013-09-10 21:08:32 -07002138 mMediaSizeSpinner.setEnabled(true);
Svetoslav Ganov44720af2013-08-20 16:32:53 -07002139
Svetoslavcc65b0c2013-09-10 21:08:32 -07002140 if (oldMediaSizeNewIndex != AdapterView.INVALID_POSITION) {
2141 // Select the old media size - nothing really changed.
2142 setMediaSizeSpinnerSelectionNoCallback(oldMediaSizeNewIndex);
2143 } else {
2144 // Select the first or the default and mark if selection changed.
2145 final int mediaSizeIndex = Math.max(mediaSizes.indexOf(
2146 defaultAttributes.getMediaSize()), 0);
2147 setMediaSizeSpinnerSelectionNoCallback(mediaSizeIndex);
Svetoslav Ganov7be27ac2013-09-30 09:04:50 -07002148 if (oldMediaSize.isPortrait()) {
2149 mCurrPrintAttributes.setMediaSize(mMediaSizeSpinnerAdapter
2150 .getItem(mediaSizeIndex).value.asPortrait());
2151 } else {
2152 mCurrPrintAttributes.setMediaSize(mMediaSizeSpinnerAdapter
2153 .getItem(mediaSizeIndex).value.asLandscape());
2154 }
Svetoslavcc65b0c2013-09-10 21:08:32 -07002155 someAttributeSelectionChanged = true;
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07002156 }
2157 }
Svetoslav269403b2013-08-14 17:31:04 -07002158 mMediaSizeSpinner.setEnabled(true);
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07002159
2160 // Color mode.
Svetoslav Ganov798bed62013-08-11 12:29:39 -07002161 final int colorModes = capabilities.getColorModes();
Svetoslav Ganovaec14172013-08-27 00:30:54 -07002162
2163 // If the color modes changed, we update the adapter and the spinner.
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07002164 boolean colorModesChanged = false;
2165 if (Integer.bitCount(colorModes) != mColorModeSpinnerAdapter.getCount()) {
2166 colorModesChanged = true;
2167 } else {
2168 int remainingColorModes = colorModes;
Svetoslav Ganov55b5f802013-08-05 11:26:16 -07002169 int adapterIndex = 0;
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07002170 while (remainingColorModes != 0) {
2171 final int colorBitOffset = Integer.numberOfTrailingZeros(
2172 remainingColorModes);
2173 final int colorMode = 1 << colorBitOffset;
2174 remainingColorModes &= ~colorMode;
Svetoslav Ganov55b5f802013-08-05 11:26:16 -07002175 if (colorMode != mColorModeSpinnerAdapter.getItem(adapterIndex).value) {
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07002176 colorModesChanged = true;
2177 break;
2178 }
Svetoslav Ganov55b5f802013-08-05 11:26:16 -07002179 adapterIndex++;
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07002180 }
2181 }
2182 if (colorModesChanged) {
Svetoslav Ganovaec14172013-08-27 00:30:54 -07002183 // Remember the old color mode to try selecting it again.
2184 int oldColorModeNewIndex = AdapterView.INVALID_POSITION;
2185 final int oldColorMode = mCurrPrintAttributes.getColorMode();
2186
2187 // Rebuild the adapter data.
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07002188 mColorModeSpinnerAdapter.clear();
2189 String[] colorModeLabels = getResources().getStringArray(
2190 R.array.color_mode_labels);
2191 int remainingColorModes = colorModes;
2192 while (remainingColorModes != 0) {
2193 final int colorBitOffset = Integer.numberOfTrailingZeros(
2194 remainingColorModes);
2195 final int colorMode = 1 << colorBitOffset;
Svetoslav Ganovaec14172013-08-27 00:30:54 -07002196 if (colorMode == oldColorMode) {
2197 // Update the index of the old selection.
2198 oldColorModeNewIndex = colorBitOffset;
2199 }
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07002200 remainingColorModes &= ~colorMode;
2201 mColorModeSpinnerAdapter.add(new SpinnerItem<Integer>(colorMode,
2202 colorModeLabels[colorBitOffset]));
2203 }
Svetoslavcc65b0c2013-09-10 21:08:32 -07002204 mColorModeSpinner.setEnabled(true);
2205 if (oldColorModeNewIndex != AdapterView.INVALID_POSITION) {
2206 // Select the old color mode - nothing really changed.
2207 setColorModeSpinnerSelectionNoCallback(oldColorModeNewIndex);
Svetoslav Ganov55b5f802013-08-05 11:26:16 -07002208 } else {
Svetoslavb0d97b82014-01-02 11:23:44 -08002209 final int selectedColorMode = colorModes & defaultAttributes.getColorMode();
2210 final int itemCount = mColorModeSpinnerAdapter.getCount();
2211 for (int i = 0; i < itemCount; i++) {
2212 SpinnerItem<Integer> item = mColorModeSpinnerAdapter.getItem(i);
2213 if (selectedColorMode == item.value) {
2214 setColorModeSpinnerSelectionNoCallback(i);
2215 mCurrPrintAttributes.setColorMode(selectedColorMode);
2216 someAttributeSelectionChanged = true;
2217 }
2218 }
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07002219 }
2220 }
Svetoslav269403b2013-08-14 17:31:04 -07002221 mColorModeSpinner.setEnabled(true);
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07002222
Svetoslav773f54d2013-09-03 14:01:43 -07002223 // Orientation
Svetoslavcc65b0c2013-09-10 21:08:32 -07002224 MediaSize mediaSize = mCurrPrintAttributes.getMediaSize();
2225 if (mediaSize.isPortrait()
2226 && mOrientationSpinner.getSelectedItemPosition() != 0) {
2227 mIgnoreNextOrientationChange = true;
2228 mOrientationSpinner.setSelection(0);
2229 } else if (!mediaSize.isPortrait()
2230 && mOrientationSpinner.getSelectedItemPosition() != 1) {
2231 mIgnoreNextOrientationChange = true;
2232 mOrientationSpinner.setSelection(1);
2233 }
Svetoslav269403b2013-08-14 17:31:04 -07002234 mOrientationSpinner.setEnabled(true);
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07002235
2236 // Range options
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07002237 PrintDocumentInfo info = mDocument.info;
Svetoslav Ganovb0e68fd2013-09-21 12:01:54 -07002238 if (info != null && info.getPageCount() > 0) {
Svetoslav Ganovaec14172013-08-27 00:30:54 -07002239 if (info.getPageCount() == 1) {
2240 mRangeOptionsSpinner.setEnabled(false);
Svetoslav269403b2013-08-14 17:31:04 -07002241 } else {
Svetoslav Ganovaec14172013-08-27 00:30:54 -07002242 mRangeOptionsSpinner.setEnabled(true);
2243 if (mRangeOptionsSpinner.getSelectedItemPosition() > 0) {
2244 if (!mPageRangeEditText.isEnabled()) {
2245 mPageRangeEditText.setEnabled(true);
2246 mPageRangeEditText.setVisibility(View.VISIBLE);
2247 mPageRangeTitle.setVisibility(View.VISIBLE);
2248 mPageRangeEditText.requestFocus();
2249 InputMethodManager imm = (InputMethodManager)
2250 getSystemService(INPUT_METHOD_SERVICE);
2251 imm.showSoftInput(mPageRangeEditText, 0);
2252 }
2253 } else {
2254 mPageRangeEditText.setEnabled(false);
2255 mPageRangeEditText.setVisibility(View.INVISIBLE);
2256 mPageRangeTitle.setVisibility(View.INVISIBLE);
2257 }
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07002258 }
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07002259 final int pageCount = mDocument.info.getPageCount();
Svetoslav5ab717f2013-10-04 17:27:33 -07002260 String title = (pageCount != PrintDocumentInfo.PAGE_COUNT_UNKNOWN)
2261 ? getString(R.string.label_pages, String.valueOf(pageCount))
2262 : getString(R.string.page_count_unknown);
2263 mRangeOptionsTitle.setText(title);
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07002264 } else {
Svetoslav Ganov8c433762013-08-02 14:22:19 -07002265 if (mRangeOptionsSpinner.getSelectedItemPosition() != 0) {
2266 mIgnoreNextRangeOptionChange = true;
2267 mRangeOptionsSpinner.setSelection(0);
2268 }
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07002269 mRangeOptionsSpinner.setEnabled(false);
Svetoslav5ab717f2013-10-04 17:27:33 -07002270 mRangeOptionsTitle.setText(getString(R.string.page_count_unknown));
Svetoslav Ganov44720af2013-08-20 16:32:53 -07002271 mPageRangeEditText.setEnabled(false);
2272 mPageRangeEditText.setVisibility(View.INVISIBLE);
2273 mPageRangeTitle.setVisibility(View.INVISIBLE);
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07002274 }
2275
Svetoslavb4fda132013-10-25 18:57:43 -07002276 // Advanced print options
2277 ComponentName serviceName = mCurrentPrinter.getId().getServiceName();
2278 if (!TextUtils.isEmpty(getAdvancedOptionsActivityName(serviceName))) {
2279 mAdvancedPrintOptionsContainer.setVisibility(View.VISIBLE);
2280 mAdvancedOptionsButton.setEnabled(true);
2281 } else {
2282 mAdvancedPrintOptionsContainer.setVisibility(View.GONE);
2283 mAdvancedOptionsButton.setEnabled(false);
2284 }
2285
2286 // Print
Svetoslav Ganov44720af2013-08-20 16:32:53 -07002287 if (mDestinationSpinner.getSelectedItemId()
2288 != DEST_ADAPTER_ITEM_ID_SAVE_AS_PDF) {
2289 String newText = getString(R.string.print_button);
2290 if (!TextUtils.equals(newText, mPrintButton.getText())) {
2291 mPrintButton.setText(R.string.print_button);
2292 }
2293 } else {
2294 String newText = getString(R.string.save_button);
2295 if (!TextUtils.equals(newText, mPrintButton.getText())) {
2296 mPrintButton.setText(R.string.save_button);
2297 }
2298 }
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07002299 if ((mRangeOptionsSpinner.getSelectedItemPosition() == 1
Svetoslav Ganov44720af2013-08-20 16:32:53 -07002300 && (TextUtils.isEmpty(mPageRangeEditText.getText()) || hasErrors()))
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07002301 || (mRangeOptionsSpinner.getSelectedItemPosition() == 0
2302 && (!mController.hasPerformedLayout() || hasErrors()))) {
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07002303 mPrintButton.setEnabled(false);
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07002304 } else {
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07002305 mPrintButton.setEnabled(true);
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07002306 }
2307
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07002308 // Copies
Svetoslav Ganov44720af2013-08-20 16:32:53 -07002309 if (mDestinationSpinner.getSelectedItemId()
2310 != DEST_ADAPTER_ITEM_ID_SAVE_AS_PDF) {
2311 mCopiesEditText.setEnabled(true);
2312 } else {
2313 mCopiesEditText.setEnabled(false);
2314 }
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07002315 if (mCopiesEditText.getError() == null
2316 && TextUtils.isEmpty(mCopiesEditText.getText())) {
Svetoslav Ganov8c433762013-08-02 14:22:19 -07002317 mIgnoreNextCopiesChange = true;
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07002318 mCopiesEditText.setText(String.valueOf(MIN_COPIES));
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07002319 mCopiesEditText.requestFocus();
2320 }
Svetoslav Ganov44720af2013-08-20 16:32:53 -07002321
2322 return someAttributeSelectionChanged;
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07002323 }
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07002324 }
2325
Svetoslavb4fda132013-10-25 18:57:43 -07002326 private String getAdvancedOptionsActivityName(ComponentName serviceName) {
2327 PrintManager printManager = (PrintManager) getSystemService(Context.PRINT_SERVICE);
2328 List<PrintServiceInfo> printServices = printManager.getEnabledPrintServices();
2329 final int printServiceCount = printServices.size();
2330 for (int i = 0; i < printServiceCount; i ++) {
2331 PrintServiceInfo printServiceInfo = printServices.get(i);
2332 ServiceInfo serviceInfo = printServiceInfo.getResolveInfo().serviceInfo;
2333 if (serviceInfo.name.equals(serviceName.getClassName())
2334 && serviceInfo.packageName.equals(serviceName.getPackageName())) {
2335 return printServiceInfo.getAdvancedOptionsActivityName();
2336 }
2337 }
2338 return null;
2339 }
2340
Svetoslavcc65b0c2013-09-10 21:08:32 -07002341 private void setMediaSizeSpinnerSelectionNoCallback(int position) {
Svetoslav Ganov44720af2013-08-20 16:32:53 -07002342 if (mMediaSizeSpinner.getSelectedItemPosition() != position) {
Svetoslavcc65b0c2013-09-10 21:08:32 -07002343 mOldMediaSizeSelectionIndex = position;
Svetoslav Ganov44720af2013-08-20 16:32:53 -07002344 mMediaSizeSpinner.setSelection(position);
Svetoslav Ganov44720af2013-08-20 16:32:53 -07002345 }
Svetoslav Ganov44720af2013-08-20 16:32:53 -07002346 }
2347
Svetoslavcc65b0c2013-09-10 21:08:32 -07002348 private void setColorModeSpinnerSelectionNoCallback(int position) {
Svetoslav Ganovaec14172013-08-27 00:30:54 -07002349 if (mColorModeSpinner.getSelectedItemPosition() != position) {
Svetoslavcc65b0c2013-09-10 21:08:32 -07002350 mOldColorModeSelectionIndex = position;
Svetoslav Ganovaec14172013-08-27 00:30:54 -07002351 mColorModeSpinner.setSelection(position);
Svetoslav Ganovaec14172013-08-27 00:30:54 -07002352 }
Svetoslav Ganov44720af2013-08-20 16:32:53 -07002353 }
2354
2355 private void startSelectPrinterActivity() {
Svetoslav Ganov44720af2013-08-20 16:32:53 -07002356 Intent intent = new Intent(PrintJobConfigActivity.this,
2357 SelectPrinterActivity.class);
2358 startActivityForResult(intent, ACTIVITY_REQUEST_SELECT_PRINTER);
2359 }
2360
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07002361 private boolean hasErrors() {
Svetoslav Ganov44720af2013-08-20 16:32:53 -07002362 if (mCopiesEditText.getError() != null) {
2363 return true;
2364 }
2365 return mPageRangeEditText.getVisibility() == View.VISIBLE
2366 && mPageRangeEditText.getError() != null;
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07002367 }
2368
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07002369 private final class SpinnerItem<T> {
2370 final T value;
2371 CharSequence label;
2372
2373 public SpinnerItem(T value, CharSequence label) {
2374 this.value = value;
2375 this.label = label;
2376 }
2377
2378 public String toString() {
2379 return label.toString();
2380 }
2381 }
2382
Svetoslav Ganov6be4c7642013-09-26 21:12:47 -07002383 private final class WaitForPrinterCapabilitiesTimeout implements Runnable {
2384 private static final long GET_CAPABILITIES_TIMEOUT_MILLIS = 10000; // 10sec
2385
2386 private boolean mIsPosted;
2387
2388 public void post() {
2389 if (!mIsPosted) {
2390 mDestinationSpinner.postDelayed(this,
2391 GET_CAPABILITIES_TIMEOUT_MILLIS);
2392 mIsPosted = true;
2393 }
2394 }
2395
2396 public void remove() {
2397 if (mIsPosted) {
2398 mIsPosted = false;
2399 mDestinationSpinner.removeCallbacks(this);
2400 }
2401 }
2402
2403 public boolean isPosted() {
2404 return mIsPosted;
2405 }
2406
2407 @Override
2408 public void run() {
2409 mIsPosted = false;
2410 if (mDestinationSpinner.getSelectedItemPosition() >= 0) {
2411 View itemView = mDestinationSpinner.getSelectedView();
Svetoslav2fa010c2013-10-14 17:31:26 -07002412 TextView titleView = (TextView) itemView.findViewById(R.id.subtitle);
2413 try {
2414 PackageInfo packageInfo = getPackageManager().getPackageInfo(
2415 mCurrentPrinter.getId().getServiceName().getPackageName(), 0);
2416 CharSequence service = packageInfo.applicationInfo.loadLabel(
2417 getPackageManager());
2418 String subtitle = getString(R.string.printer_unavailable, service.toString());
2419 titleView.setText(subtitle);
2420 } catch (NameNotFoundException nnfe) {
2421 /* ignore */
2422 }
Svetoslav Ganov6be4c7642013-09-26 21:12:47 -07002423 }
2424 }
2425 }
2426
Svetoslav269403b2013-08-14 17:31:04 -07002427 private final class DestinationAdapter extends BaseAdapter
2428 implements LoaderManager.LoaderCallbacks<List<PrinterInfo>>{
2429 private final List<PrinterInfo> mPrinters = new ArrayList<PrinterInfo>();
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07002430
Svetoslav Ganov24c686b2013-10-05 18:52:06 -07002431 private PrinterInfo mFakePdfPrinter;
Svetoslav Ganov44720af2013-08-20 16:32:53 -07002432
Svetoslav269403b2013-08-14 17:31:04 -07002433 public DestinationAdapter() {
2434 getLoaderManager().initLoader(LOADER_ID_PRINTERS_LOADER, null, this);
Svetoslav66160bb2013-08-12 22:36:50 -07002435 }
2436
Svetoslav Ganov44720af2013-08-20 16:32:53 -07002437 public int getPrinterIndex(PrinterId printerId) {
2438 for (int i = 0; i < getCount(); i++) {
2439 PrinterInfo printer = (PrinterInfo) getItem(i);
2440 if (printer != null && printer.getId().equals(printerId)) {
2441 return i;
2442 }
2443 }
2444 return AdapterView.INVALID_POSITION;
2445 }
2446
Svetoslav4e4874b2013-10-01 17:53:17 -07002447 public void ensurePrinterInVisibleAdapterPosition(PrinterId printerId) {
2448 final int printerCount = mPrinters.size();
2449 for (int i = 0; i < printerCount; i++) {
2450 PrinterInfo printer = (PrinterInfo) mPrinters.get(i);
2451 if (printer.getId().equals(printerId)) {
2452 // If already in the list - do nothing.
2453 if (i < getCount() - 2) {
2454 return;
2455 }
2456 // Else replace the last one (two items are not printers).
2457 final int lastPrinterIndex = getCount() - 3;
2458 mPrinters.set(i, mPrinters.get(lastPrinterIndex));
2459 mPrinters.set(lastPrinterIndex, printer);
2460 notifyDataSetChanged();
2461 return;
2462 }
2463 }
Svetoslav Ganov44720af2013-08-20 16:32:53 -07002464 }
2465
Svetoslav66160bb2013-08-12 22:36:50 -07002466 @Override
2467 public int getCount() {
Svetoslav54adee82013-10-11 11:28:30 -07002468 if (mFakePdfPrinter == null) {
2469 return 0;
2470 }
2471 return Math.min(mPrinters.size() + 2, DEST_ADAPTER_MAX_ITEM_COUNT);
Svetoslav66160bb2013-08-12 22:36:50 -07002472 }
2473
2474 @Override
Svetoslav2a7086172013-09-24 15:14:41 -07002475 public boolean isEnabled(int position) {
2476 Object item = getItem(position);
2477 if (item instanceof PrinterInfo) {
2478 PrinterInfo printer = (PrinterInfo) item;
2479 return printer.getStatus() != PrinterInfo.STATUS_UNAVAILABLE;
2480 }
2481 return true;
2482 }
2483
2484 @Override
Svetoslav66160bb2013-08-12 22:36:50 -07002485 public Object getItem(int position) {
Svetoslav Ganov44720af2013-08-20 16:32:53 -07002486 if (mPrinters.isEmpty()) {
Svetoslav Ganov24c686b2013-10-05 18:52:06 -07002487 if (position == 0 && mFakePdfPrinter != null) {
Svetoslav Ganov44720af2013-08-20 16:32:53 -07002488 return mFakePdfPrinter;
2489 }
2490 } else {
2491 if (position < 1) {
Svetoslav269403b2013-08-14 17:31:04 -07002492 return mPrinters.get(position);
Svetoslav Ganov44720af2013-08-20 16:32:53 -07002493 }
Svetoslav Ganov24c686b2013-10-05 18:52:06 -07002494 if (position == 1 && mFakePdfPrinter != null) {
Svetoslav Ganov44720af2013-08-20 16:32:53 -07002495 return mFakePdfPrinter;
2496 }
2497 if (position < getCount() - 1) {
Svetoslav269403b2013-08-14 17:31:04 -07002498 return mPrinters.get(position - 1);
2499 }
2500 }
2501 return null;
Svetoslav66160bb2013-08-12 22:36:50 -07002502 }
2503
2504 @Override
2505 public long getItemId(int position) {
Svetoslav269403b2013-08-14 17:31:04 -07002506 if (mPrinters.isEmpty()) {
Svetoslav54adee82013-10-11 11:28:30 -07002507 if (mFakePdfPrinter != null) {
2508 if (position == 0) {
Svetoslav7bfbbcb2013-10-10 13:36:23 -07002509 return DEST_ADAPTER_ITEM_ID_SAVE_AS_PDF;
Svetoslav54adee82013-10-11 11:28:30 -07002510 } else if (position == 1) {
Svetoslav7bfbbcb2013-10-10 13:36:23 -07002511 return DEST_ADAPTER_ITEM_ID_ALL_PRINTERS;
2512 }
Svetoslav Ganov9186d0c2013-09-03 00:11:58 -07002513 }
Svetoslav Ganov44720af2013-08-20 16:32:53 -07002514 } else {
Svetoslav Ganov24c686b2013-10-05 18:52:06 -07002515 if (position == 1 && mFakePdfPrinter != null) {
Svetoslav Ganov44720af2013-08-20 16:32:53 -07002516 return DEST_ADAPTER_ITEM_ID_SAVE_AS_PDF;
2517 }
2518 if (position == getCount() - 1) {
2519 return DEST_ADAPTER_ITEM_ID_ALL_PRINTERS;
2520 }
Svetoslav269403b2013-08-14 17:31:04 -07002521 }
Svetoslav66160bb2013-08-12 22:36:50 -07002522 return position;
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07002523 }
2524
2525 @Override
2526 public View getDropDownView(int position, View convertView,
2527 ViewGroup parent) {
Svetoslav2a7086172013-09-24 15:14:41 -07002528 View view = getView(position, convertView, parent);
2529 view.setEnabled(isEnabled(position));
2530 return view;
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07002531 }
2532
2533 @Override
2534 public View getView(int position, View convertView, ViewGroup parent) {
2535 if (convertView == null) {
2536 convertView = getLayoutInflater().inflate(
Svetoslavc335eb42013-09-26 15:55:47 -07002537 R.layout.printer_dropdown_item, parent, false);
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07002538 }
2539
Svetoslav269403b2013-08-14 17:31:04 -07002540 CharSequence title = null;
2541 CharSequence subtitle = null;
Svetoslavc335eb42013-09-26 15:55:47 -07002542 Drawable icon = null;
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07002543
Svetoslav Ganov44720af2013-08-20 16:32:53 -07002544 if (mPrinters.isEmpty()) {
Svetoslav Ganov24c686b2013-10-05 18:52:06 -07002545 if (position == 0 && mFakePdfPrinter != null) {
Svetoslav Ganov44720af2013-08-20 16:32:53 -07002546 PrinterInfo printer = (PrinterInfo) getItem(position);
2547 title = printer.getName();
2548 } else if (position == 1) {
2549 title = getString(R.string.all_printers);
2550 }
Svetoslav269403b2013-08-14 17:31:04 -07002551 } else {
Svetoslav Ganov24c686b2013-10-05 18:52:06 -07002552 if (position == 1 && mFakePdfPrinter != null) {
Svetoslav269403b2013-08-14 17:31:04 -07002553 PrinterInfo printer = (PrinterInfo) getItem(position);
2554 title = printer.getName();
2555 } else if (position == getCount() - 1) {
2556 title = getString(R.string.all_printers);
2557 } else {
2558 PrinterInfo printer = (PrinterInfo) getItem(position);
2559 title = printer.getName();
2560 try {
2561 PackageInfo packageInfo = getPackageManager().getPackageInfo(
2562 printer.getId().getServiceName().getPackageName(), 0);
2563 subtitle = packageInfo.applicationInfo.loadLabel(getPackageManager());
Svetoslavc335eb42013-09-26 15:55:47 -07002564 icon = packageInfo.applicationInfo.loadIcon(getPackageManager());
Svetoslav269403b2013-08-14 17:31:04 -07002565 } catch (NameNotFoundException nnfe) {
2566 /* ignore */
2567 }
2568 }
2569 }
2570
2571 TextView titleView = (TextView) convertView.findViewById(R.id.title);
2572 titleView.setText(title);
2573
2574 TextView subtitleView = (TextView) convertView.findViewById(R.id.subtitle);
2575 if (!TextUtils.isEmpty(subtitle)) {
2576 subtitleView.setText(subtitle);
2577 subtitleView.setVisibility(View.VISIBLE);
2578 } else {
2579 subtitleView.setText(null);
2580 subtitleView.setVisibility(View.GONE);
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07002581 }
2582
Svetoslavc335eb42013-09-26 15:55:47 -07002583 ImageView iconView = (ImageView) convertView.findViewById(R.id.icon);
2584 if (icon != null) {
2585 iconView.setImageDrawable(icon);
2586 iconView.setVisibility(View.VISIBLE);
2587 } else {
Svetoslavb1961062013-10-16 16:41:30 -07002588 iconView.setVisibility(View.INVISIBLE);
Svetoslavc335eb42013-09-26 15:55:47 -07002589 }
2590
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07002591 return convertView;
2592 }
Svetoslav269403b2013-08-14 17:31:04 -07002593
2594 @Override
2595 public Loader<List<PrinterInfo>> onCreateLoader(int id, Bundle args) {
2596 if (id == LOADER_ID_PRINTERS_LOADER) {
2597 return new FusedPrintersProvider(PrintJobConfigActivity.this);
2598 }
2599 return null;
2600 }
2601
2602 @Override
2603 public void onLoadFinished(Loader<List<PrinterInfo>> loader,
2604 List<PrinterInfo> printers) {
Svetoslav Ganov24c686b2013-10-05 18:52:06 -07002605 // If this is the first load, create the fake PDF printer.
2606 // We do this to avoid flicker where the PDF printer is the
2607 // only one and as soon as the loader loads the favorites
2608 // it gets switched. Not a great user experience.
2609 if (mFakePdfPrinter == null) {
2610 mCurrentPrinter = mFakePdfPrinter = createFakePdfPrinter();
2611 updatePrintAttributes(mCurrentPrinter.getCapabilities());
2612 updateUi();
2613 }
2614
Svetoslav4e4874b2013-10-01 17:53:17 -07002615 // We rearrange the printers if the user selects a printer
2616 // not shown in the initial short list. Therefore, we have
2617 // to keep the printer order.
2618
2619 // No old printers - do not bother keeping their position.
2620 if (mPrinters.isEmpty()) {
2621 mPrinters.addAll(printers);
2622 mEditor.ensureCurrentPrinterSelected();
2623 notifyDataSetChanged();
2624 return;
2625 }
2626
2627 // Add the new printers to a map.
2628 ArrayMap<PrinterId, PrinterInfo> newPrintersMap =
2629 new ArrayMap<PrinterId, PrinterInfo>();
2630 final int printerCount = printers.size();
2631 for (int i = 0; i < printerCount; i++) {
2632 PrinterInfo printer = printers.get(i);
2633 newPrintersMap.put(printer.getId(), printer);
2634 }
2635
2636 List<PrinterInfo> newPrinters = new ArrayList<PrinterInfo>();
2637
2638 // Update printers we already have.
2639 final int oldPrinterCount = mPrinters.size();
2640 for (int i = 0; i < oldPrinterCount; i++) {
2641 PrinterId oldPrinterId = mPrinters.get(i).getId();
2642 PrinterInfo updatedPrinter = newPrintersMap.remove(oldPrinterId);
2643 if (updatedPrinter != null) {
2644 newPrinters.add(updatedPrinter);
2645 }
2646 }
2647
2648 // Add the rest of the new printers, i.e. what is left.
2649 newPrinters.addAll(newPrintersMap.values());
2650
Svetoslav269403b2013-08-14 17:31:04 -07002651 mPrinters.clear();
Svetoslav4e4874b2013-10-01 17:53:17 -07002652 mPrinters.addAll(newPrinters);
2653
2654 mEditor.ensureCurrentPrinterSelected();
Svetoslav269403b2013-08-14 17:31:04 -07002655 notifyDataSetChanged();
2656 }
2657
2658 @Override
2659 public void onLoaderReset(Loader<List<PrinterInfo>> loader) {
2660 mPrinters.clear();
2661 notifyDataSetInvalidated();
2662 }
2663
Svetoslav Ganov7be27ac2013-09-30 09:04:50 -07002664
Svetoslav269403b2013-08-14 17:31:04 -07002665 private PrinterInfo createFakePdfPrinter() {
Svetoslav Ganov7be27ac2013-09-30 09:04:50 -07002666 MediaSize defaultMediaSize = MediaSizeUtils.getDefault(PrintJobConfigActivity.this);
Svetoslav Ganov0ce3e872013-09-26 08:45:22 -07002667
Svetoslav269403b2013-08-14 17:31:04 -07002668 PrinterId printerId = new PrinterId(getComponentName(), "PDF printer");
2669
Svetoslav Ganov7be27ac2013-09-30 09:04:50 -07002670 PrinterCapabilitiesInfo.Builder builder =
2671 new PrinterCapabilitiesInfo.Builder(printerId);
2672
2673 String[] mediaSizeIds = getResources().getStringArray(
2674 R.array.pdf_printer_media_sizes);
2675 final int mediaSizeIdCount = mediaSizeIds.length;
2676 for (int i = 0; i < mediaSizeIdCount; i++) {
2677 String id = mediaSizeIds[i];
2678 MediaSize mediaSize = MediaSize.getStandardMediaSizeById(id);
2679 builder.addMediaSize(mediaSize, mediaSize.equals(defaultMediaSize));
2680 }
2681
2682 builder.addResolution(new Resolution("PDF resolution", "PDF resolution",
2683 300, 300), true);
2684 builder.setColorModes(PrintAttributes.COLOR_MODE_COLOR
2685 | PrintAttributes.COLOR_MODE_MONOCHROME,
2686 PrintAttributes.COLOR_MODE_COLOR);
Svetoslav269403b2013-08-14 17:31:04 -07002687
2688 return new PrinterInfo.Builder(printerId, getString(R.string.save_as_pdf),
Svetoslav Ganovaec14172013-08-27 00:30:54 -07002689 PrinterInfo.STATUS_IDLE)
Svetoslav Ganov7be27ac2013-09-30 09:04:50 -07002690 .setCapabilities(builder.build())
Svetoslav651dd4e2013-09-12 14:37:47 -07002691 .build();
Svetoslav269403b2013-08-14 17:31:04 -07002692 }
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07002693 }
2694 }
2695
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07002696 /**
2697 * An instance of this class class is intended to be the first focusable
2698 * in a layout to which the system automatically gives focus. It performs
2699 * some voodoo to avoid the first tap on it to start an edit mode, rather
2700 * to bring up the IME, i.e. to get the behavior as if the view was not
2701 * focused.
2702 */
2703 public static final class CustomEditText extends EditText {
2704 private boolean mClickedBeforeFocus;
2705 private CharSequence mError;
2706
2707 public CustomEditText(Context context, AttributeSet attrs) {
2708 super(context, attrs);
2709 }
2710
2711 @Override
2712 public boolean performClick() {
2713 super.performClick();
2714 if (isFocused() && !mClickedBeforeFocus) {
2715 clearFocus();
2716 requestFocus();
2717 }
2718 mClickedBeforeFocus = true;
2719 return true;
2720 }
2721
2722 @Override
2723 public CharSequence getError() {
2724 return mError;
2725 }
2726
2727 @Override
2728 public void setError(CharSequence error, Drawable icon) {
2729 setCompoundDrawables(null, null, icon, null);
2730 mError = error;
2731 }
2732
2733 protected void onFocusChanged(boolean gainFocus, int direction,
2734 Rect previouslyFocusedRect) {
2735 if (!gainFocus) {
2736 mClickedBeforeFocus = false;
2737 }
2738 super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
2739 }
2740 }
2741
2742 private static final class Document {
2743 public PrintDocumentInfo info;
2744 public PageRange[] pages;
2745 }
2746
2747 private static final class PageRangeUtils {
2748
2749 private static final Comparator<PageRange> sComparator = new Comparator<PageRange>() {
2750 @Override
2751 public int compare(PageRange lhs, PageRange rhs) {
2752 return lhs.getStart() - rhs.getStart();
2753 }
2754 };
2755
2756 private PageRangeUtils() {
2757 throw new UnsupportedOperationException();
2758 }
2759
Svetoslava4f64092013-09-20 18:57:21 -07002760 public static boolean contains(PageRange[] ourRanges, PageRange[] otherRanges) {
2761 if (ourRanges == null || otherRanges == null) {
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07002762 return false;
2763 }
2764
Svetoslava4f64092013-09-20 18:57:21 -07002765 if (ourRanges.length == 1
2766 && PageRange.ALL_PAGES.equals(ourRanges[0])) {
Svetoslavf0c48a72013-09-18 14:09:47 -07002767 return true;
2768 }
2769
Svetoslava4f64092013-09-20 18:57:21 -07002770 ourRanges = normalize(ourRanges);
2771 otherRanges = normalize(otherRanges);
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07002772
Svetoslava4f64092013-09-20 18:57:21 -07002773 // Note that the code below relies on the ranges being normalized
2774 // which is they contain monotonically increasing non-intersecting
2775 // subranges whose start is less that or equal to the end.
2776 int otherRangeIdx = 0;
2777 final int ourRangeCount = ourRanges.length;
2778 final int otherRangeCount = otherRanges.length;
2779 for (int ourRangeIdx = 0; ourRangeIdx < ourRangeCount; ourRangeIdx++) {
2780 PageRange ourRange = ourRanges[ourRangeIdx];
2781 for (; otherRangeIdx < otherRangeCount; otherRangeIdx++) {
2782 PageRange otherRange = otherRanges[otherRangeIdx];
2783 if (otherRange.getStart() > ourRange.getEnd()) {
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07002784 break;
2785 }
Svetoslava4f64092013-09-20 18:57:21 -07002786 if (otherRange.getStart() < ourRange.getStart()
2787 || otherRange.getEnd() > ourRange.getEnd()) {
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07002788 return false;
2789 }
2790 }
2791 }
Svetoslava4f64092013-09-20 18:57:21 -07002792 if (otherRangeIdx < otherRangeCount) {
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07002793 return false;
2794 }
2795 return true;
2796 }
2797
2798 public static PageRange[] normalize(PageRange[] pageRanges) {
2799 if (pageRanges == null) {
2800 return null;
2801 }
Svetoslavf0c48a72013-09-18 14:09:47 -07002802 final int oldRangeCount = pageRanges.length;
2803 if (oldRangeCount <= 1) {
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07002804 return pageRanges;
2805 }
2806 Arrays.sort(pageRanges, sComparator);
Svetoslavf0c48a72013-09-18 14:09:47 -07002807 int newRangeCount = 1;
2808 for (int i = 0; i < oldRangeCount - 1; i++) {
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07002809 newRangeCount++;
2810 PageRange currentRange = pageRanges[i];
2811 PageRange nextRange = pageRanges[i + 1];
Svetoslavf0c48a72013-09-18 14:09:47 -07002812 if (currentRange.getEnd() + 1 >= nextRange.getStart()) {
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07002813 newRangeCount--;
2814 pageRanges[i] = null;
2815 pageRanges[i + 1] = new PageRange(currentRange.getStart(),
Svetoslavf0c48a72013-09-18 14:09:47 -07002816 Math.max(currentRange.getEnd(), nextRange.getEnd()));
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07002817 }
2818 }
Svetoslavf0c48a72013-09-18 14:09:47 -07002819 if (newRangeCount == oldRangeCount) {
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07002820 return pageRanges;
2821 }
Svetoslavf0c48a72013-09-18 14:09:47 -07002822 return Arrays.copyOfRange(pageRanges, oldRangeCount - newRangeCount,
2823 oldRangeCount);
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07002824 }
2825
Svetoslava4f64092013-09-20 18:57:21 -07002826 public static void offset(PageRange[] pageRanges, int offset) {
Svetoslav Ganov85b1f882013-07-24 17:00:06 -07002827 if (offset == 0) {
2828 return;
2829 }
2830 final int pageRangeCount = pageRanges.length;
2831 for (int i = 0; i < pageRangeCount; i++) {
2832 final int start = pageRanges[i].getStart() + offset;
2833 final int end = pageRanges[i].getEnd() + offset;
2834 pageRanges[i] = new PageRange(start, end);
2835 }
Svetoslav Ganov0d1daa52013-07-23 13:29:31 -07002836 }
2837 }
Svetoslav Ganov860f8a62013-09-14 00:59:03 -07002838
2839 private static final class AutoCancellingAnimator
2840 implements OnAttachStateChangeListener, Runnable {
2841
2842 private ViewPropertyAnimator mAnimator;
2843
2844 private boolean mCancelled;
2845 private Runnable mEndCallback;
2846
2847 public static AutoCancellingAnimator animate(View view) {
2848 ViewPropertyAnimator animator = view.animate();
2849 AutoCancellingAnimator cancellingWrapper =
2850 new AutoCancellingAnimator(animator);
2851 view.addOnAttachStateChangeListener(cancellingWrapper);
2852 return cancellingWrapper;
2853 }
2854
2855 private AutoCancellingAnimator(ViewPropertyAnimator animator) {
2856 mAnimator = animator;
2857 }
2858
2859 public AutoCancellingAnimator alpha(float alpha) {
2860 mAnimator = mAnimator.alpha(alpha);
2861 return this;
2862 }
2863
2864 public void cancel() {
2865 mAnimator.cancel();
2866 }
2867
2868 public AutoCancellingAnimator withLayer() {
2869 mAnimator = mAnimator.withLayer();
2870 return this;
2871 }
2872
2873 public AutoCancellingAnimator withEndAction(Runnable callback) {
2874 mEndCallback = callback;
2875 mAnimator = mAnimator.withEndAction(this);
2876 return this;
2877 }
2878
2879 public AutoCancellingAnimator scaleY(float scale) {
2880 mAnimator = mAnimator.scaleY(scale);
2881 return this;
2882 }
2883
2884 @Override
2885 public void onViewAttachedToWindow(View v) {
2886 /* do nothing */
2887 }
2888
2889 @Override
2890 public void onViewDetachedFromWindow(View v) {
2891 cancel();
2892 }
2893
2894 @Override
2895 public void run() {
2896 if (!mCancelled) {
2897 mEndCallback.run();
2898 }
2899 }
2900 }
Svetoslav7bfbbcb2013-10-10 13:36:23 -07002901
2902 private static final class PrintSpoolerProvider implements ServiceConnection {
2903 private final Context mContext;
2904 private final Runnable mCallback;
2905
2906 private PrintSpoolerService mSpooler;
2907
2908 public PrintSpoolerProvider(Context context, Runnable callback) {
2909 mContext = context;
2910 mCallback = callback;
2911 Intent intent = new Intent(mContext, PrintSpoolerService.class);
2912 mContext.bindService(intent, this, 0);
2913 }
2914
2915 public PrintSpoolerService getSpooler() {
2916 return mSpooler;
2917 }
2918
2919 public void destroy() {
2920 if (mSpooler != null) {
2921 mContext.unbindService(this);
2922 }
2923 }
2924
2925 @Override
2926 public void onServiceConnected(ComponentName name, IBinder service) {
2927 mSpooler = ((PrintSpoolerService.PrintSpooler) service).getService();
2928 if (mSpooler != null) {
2929 mCallback.run();
2930 }
2931 }
2932
2933 @Override
2934 public void onServiceDisconnected(ComponentName name) {
2935 /* do noting - we are in the same process */
2936 }
2937 }
Svetoslav Ganov858a1852013-10-17 22:20:40 -07002938
2939 private static final class PrintDocumentAdapterObserver
2940 extends IPrintDocumentAdapterObserver.Stub {
2941 private final WeakReference<PrintJobConfigActivity> mWeakActvity;
2942
2943 public PrintDocumentAdapterObserver(PrintJobConfigActivity activity) {
2944 mWeakActvity = new WeakReference<PrintJobConfigActivity>(activity);
2945 }
2946
2947 @Override
2948 public void onDestroy() {
2949 final PrintJobConfigActivity activity = mWeakActvity.get();
2950 if (activity != null) {
2951 activity.mController.mHandler.post(new Runnable() {
2952 @Override
2953 public void run() {
2954 if (activity.mController != null) {
2955 activity.mController.cancel();
2956 }
2957 if (activity.mEditor != null) {
2958 activity.mEditor.cancel();
2959 }
2960 activity.finish();
2961 }
2962 });
2963 }
2964 }
2965 }
Svetoslav Ganov4b9a4d12013-06-11 15:20:06 -07002966}