Merge "Hide the print dialog if the printing activity is destroyed." into klp-dev

This commit is contained in:
Svetoslav Ganov
2013-10-19 00:26:44 +00:00
committed by Android (Google) Code Review
5 changed files with 286 additions and 89 deletions

View File

@ -165,6 +165,7 @@ LOCAL_SRC_FILES += \
core/java/android/print/ILayoutResultCallback.aidl \
core/java/android/print/IPrinterDiscoveryObserver.aidl \
core/java/android/print/IPrintDocumentAdapter.aidl \
core/java/android/print/IPrintDocumentAdapterObserver.aidl \
core/java/android/print/IPrintJobStateChangeListener.aidl \
core/java/android/print/IPrintManager.aidl \
core/java/android/print/IPrintSpooler.aidl \

View File

@ -19,6 +19,7 @@ package android.print;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.print.ILayoutResultCallback;
import android.print.IPrintDocumentAdapterObserver;
import android.print.IWriteResultCallback;
import android.print.PageRange;
import android.print.PrintAttributes;
@ -29,6 +30,7 @@ import android.print.PrintAttributes;
* @hide
*/
oneway interface IPrintDocumentAdapter {
void setObserver(in IPrintDocumentAdapterObserver observer);
void start();
void layout(in PrintAttributes oldAttributes, in PrintAttributes newAttributes,
ILayoutResultCallback callback, in Bundle metadata, int sequence);

View File

@ -0,0 +1,26 @@
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.print;
/**
* Interface for observing the state of a print document adapter.
*
* @hide
*/
oneway interface IPrintDocumentAdapterObserver {
void onDestroy();
}

View File

@ -16,6 +16,8 @@
package android.print;
import android.app.Activity;
import android.app.Application.ActivityLifecycleCallbacks;
import android.content.Context;
import android.content.IntentSender;
import android.content.IntentSender.SendIntentException;
@ -302,8 +304,8 @@ public final class PrintManager {
if (TextUtils.isEmpty(printJobName)) {
throw new IllegalArgumentException("priintJobName cannot be empty");
}
PrintDocumentAdapterDelegate delegate = new PrintDocumentAdapterDelegate(documentAdapter,
mContext.getMainLooper());
PrintDocumentAdapterDelegate delegate = new PrintDocumentAdapterDelegate(
mContext, documentAdapter);
try {
Bundle result = mService.print(printJobName, delegate,
attributes, mContext.getPackageName(), mAppId, mUserId);
@ -369,17 +371,21 @@ public final class PrintManager {
return new PrinterDiscoverySession(mService, mContext, mUserId);
}
private static final class PrintDocumentAdapterDelegate extends IPrintDocumentAdapter.Stub {
private static final class PrintDocumentAdapterDelegate extends IPrintDocumentAdapter.Stub
implements ActivityLifecycleCallbacks {
private final Object mLock = new Object();
private CancellationSignal mLayoutOrWriteCancellation;
private PrintDocumentAdapter mDocumentAdapter; // Strong reference OK -
// cleared in finish()
private Activity mActivity; // Strong reference OK - cleared in finish()
private PrintDocumentAdapter mDocumentAdapter; // Strong reference OK - cleared in finish
private Handler mHandler; // Strong reference OK - cleared in finish()
private IPrintDocumentAdapterObserver mObserver; // Strong reference OK - cleared in finish
private LayoutSpec mLastLayoutSpec;
private WriteSpec mLastWriteSpec;
@ -390,16 +396,42 @@ public final class PrintManager {
private boolean mFinishRequested;
private boolean mFinished;
public PrintDocumentAdapterDelegate(PrintDocumentAdapter documentAdapter, Looper looper) {
private boolean mDestroyed;
public PrintDocumentAdapterDelegate(Context context,
PrintDocumentAdapter documentAdapter) {
if (!(context instanceof Activity)) {
throw new IllegalStateException("Can print only from an activity");
}
mActivity = (Activity) context;
mDocumentAdapter = documentAdapter;
mHandler = new MyHandler(looper);
mHandler = new MyHandler(mActivity.getMainLooper());
mActivity.getApplication().registerActivityLifecycleCallbacks(this);
}
@Override
public void setObserver(IPrintDocumentAdapterObserver observer) {
final boolean destroyed;
synchronized (mLock) {
if (!mDestroyed) {
mObserver = observer;
}
destroyed = mDestroyed;
}
if (destroyed) {
try {
observer.onDestroy();
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error announcing destroyed state", re);
}
}
}
@Override
public void start() {
synchronized (mLock) {
// Started or finished - nothing to do.
if (mStartReqeusted || mFinishRequested) {
// Started called or finish called or destroyed - nothing to do.
if (mStartReqeusted || mFinishRequested || mDestroyed) {
return;
}
@ -412,71 +444,85 @@ public final class PrintManager {
@Override
public void layout(PrintAttributes oldAttributes, PrintAttributes newAttributes,
ILayoutResultCallback callback, Bundle metadata, int sequence) {
final boolean destroyed;
synchronized (mLock) {
// Start not called or finish called - nothing to do.
if (!mStartReqeusted || mFinishRequested) {
return;
destroyed = mDestroyed;
// If start called and not finished called and not destroyed - do some work.
if (mStartReqeusted && !mFinishRequested && !mDestroyed) {
// Layout cancels write and overrides layout.
if (mLastWriteSpec != null) {
IoUtils.closeQuietly(mLastWriteSpec.fd);
mLastWriteSpec = null;
}
mLastLayoutSpec = new LayoutSpec();
mLastLayoutSpec.callback = callback;
mLastLayoutSpec.oldAttributes = oldAttributes;
mLastLayoutSpec.newAttributes = newAttributes;
mLastLayoutSpec.metadata = metadata;
mLastLayoutSpec.sequence = sequence;
// Cancel the previous cancellable operation.When the
// cancellation completes we will do the pending work.
if (cancelPreviousCancellableOperationLocked()) {
return;
}
doPendingWorkLocked();
}
// Layout cancels write and overrides layout.
if (mLastWriteSpec != null) {
IoUtils.closeQuietly(mLastWriteSpec.fd);
mLastWriteSpec = null;
}
if (destroyed) {
try {
callback.onLayoutFailed(null, sequence);
} catch (RemoteException re) {
Log.i(LOG_TAG, "Error notifying for cancelled layout", re);
}
mLastLayoutSpec = new LayoutSpec();
mLastLayoutSpec.callback = callback;
mLastLayoutSpec.oldAttributes = oldAttributes;
mLastLayoutSpec.newAttributes = newAttributes;
mLastLayoutSpec.metadata = metadata;
mLastLayoutSpec.sequence = sequence;
// Cancel the previous cancellable operation.When the
// cancellation completes we will do the pending work.
if (cancelPreviousCancellableOperationLocked()) {
return;
}
doPendingWorkLocked();
}
}
@Override
public void write(PageRange[] pages, ParcelFileDescriptor fd,
IWriteResultCallback callback, int sequence) {
final boolean destroyed;
synchronized (mLock) {
// Start not called or finish called - nothing to do.
if (!mStartReqeusted || mFinishRequested) {
return;
destroyed = mDestroyed;
// If start called and not finished called and not destroyed - do some work.
if (mStartReqeusted && !mFinishRequested && !mDestroyed) {
// Write cancels previous writes.
if (mLastWriteSpec != null) {
IoUtils.closeQuietly(mLastWriteSpec.fd);
mLastWriteSpec = null;
}
mLastWriteSpec = new WriteSpec();
mLastWriteSpec.callback = callback;
mLastWriteSpec.pages = pages;
mLastWriteSpec.fd = fd;
mLastWriteSpec.sequence = sequence;
// Cancel the previous cancellable operation.When the
// cancellation completes we will do the pending work.
if (cancelPreviousCancellableOperationLocked()) {
return;
}
doPendingWorkLocked();
}
// Write cancels previous writes.
if (mLastWriteSpec != null) {
IoUtils.closeQuietly(mLastWriteSpec.fd);
mLastWriteSpec = null;
}
if (destroyed) {
try {
callback.onWriteFailed(null, sequence);
} catch (RemoteException re) {
Log.i(LOG_TAG, "Error notifying for cancelled write", re);
}
mLastWriteSpec = new WriteSpec();
mLastWriteSpec.callback = callback;
mLastWriteSpec.pages = pages;
mLastWriteSpec.fd = fd;
mLastWriteSpec.sequence = sequence;
// Cancel the previous cancellable operation.When the
// cancellation completes we will do the pending work.
if (cancelPreviousCancellableOperationLocked()) {
return;
}
doPendingWorkLocked();
}
}
@Override
public void finish() {
synchronized (mLock) {
// Start not called or finish called - nothing to do.
if (!mStartReqeusted || mFinishRequested) {
// Start not called or finish called or destroyed - nothing to do.
if (!mStartReqeusted || mFinishRequested || mDestroyed) {
return;
}
@ -495,15 +541,78 @@ public final class PrintManager {
}
}
@Override
public void onActivityPaused(Activity activity) {
/* do nothing */
}
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
/* do nothing */
}
@Override
public void onActivityStarted(Activity activity) {
/* do nothing */
}
@Override
public void onActivityResumed(Activity activity) {
/* do nothing */
}
@Override
public void onActivityStopped(Activity activity) {
/* do nothing */
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
/* do nothing */
}
@Override
public void onActivityDestroyed(Activity activity) {
// We really care only if the activity is being destroyed to
// notify the the print spooler so it can close the print dialog.
// Note the the spooler has a death recipient that observes if
// this process gets killed so we cover the case of onDestroy not
// being called due to this process being killed to reclaim memory.
final IPrintDocumentAdapterObserver observer;
synchronized (mLock) {
if (activity == mActivity) {
mDestroyed = true;
observer = mObserver;
clearLocked();
} else {
observer = null;
activity = null;
}
}
if (observer != null) {
activity.getApplication().unregisterActivityLifecycleCallbacks(
PrintDocumentAdapterDelegate.this);
try {
observer.onDestroy();
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error announcing destroyed state", re);
}
}
}
private boolean isFinished() {
return mDocumentAdapter == null;
}
private void doFinish() {
private void clearLocked() {
mActivity = null;
mDocumentAdapter = null;
mHandler = null;
synchronized (mLock) {
mLayoutOrWriteCancellation = null;
mLayoutOrWriteCancellation = null;
mLastLayoutSpec = null;
if (mLastWriteSpec != null) {
IoUtils.closeQuietly(mLastWriteSpec.fd);
mLastWriteSpec = null;
}
}
@ -564,63 +673,81 @@ public final class PrintManager {
}
switch (message.what) {
case MSG_START: {
mDocumentAdapter.onStart();
}
break;
final PrintDocumentAdapter adapter;
synchronized (mLock) {
adapter = mDocumentAdapter;
}
if (adapter != null) {
adapter.onStart();
}
} break;
case MSG_LAYOUT: {
final PrintDocumentAdapter adapter;
final CancellationSignal cancellation;
final LayoutSpec layoutSpec;
synchronized (mLock) {
adapter = mDocumentAdapter;
layoutSpec = mLastLayoutSpec;
mLastLayoutSpec = null;
cancellation = new CancellationSignal();
mLayoutOrWriteCancellation = cancellation;
}
if (layoutSpec != null) {
if (layoutSpec != null && adapter != null) {
if (DEBUG) {
Log.i(LOG_TAG, "Performing layout");
}
mDocumentAdapter.onLayout(layoutSpec.oldAttributes,
adapter.onLayout(layoutSpec.oldAttributes,
layoutSpec.newAttributes, cancellation,
new MyLayoutResultCallback(layoutSpec.callback,
layoutSpec.sequence), layoutSpec.metadata);
}
}
break;
} break;
case MSG_WRITE: {
final PrintDocumentAdapter adapter;
final CancellationSignal cancellation;
final WriteSpec writeSpec;
synchronized (mLock) {
adapter = mDocumentAdapter;
writeSpec = mLastWriteSpec;
mLastWriteSpec = null;
cancellation = new CancellationSignal();
mLayoutOrWriteCancellation = cancellation;
}
if (writeSpec != null) {
if (writeSpec != null && adapter != null) {
if (DEBUG) {
Log.i(LOG_TAG, "Performing write");
}
mDocumentAdapter.onWrite(writeSpec.pages, writeSpec.fd,
adapter.onWrite(writeSpec.pages, writeSpec.fd,
cancellation, new MyWriteResultCallback(writeSpec.callback,
writeSpec.fd, writeSpec.sequence));
}
}
break;
} break;
case MSG_FINISH: {
if (DEBUG) {
Log.i(LOG_TAG, "Performing finish");
}
mDocumentAdapter.onFinish();
doFinish();
}
break;
final PrintDocumentAdapter adapter;
final Activity activity;
synchronized (mLock) {
adapter = mDocumentAdapter;
activity = mActivity;
clearLocked();
}
if (adapter != null) {
adapter.onFinish();
}
if (activity != null) {
activity.getApplication().unregisterActivityLifecycleCallbacks(
PrintDocumentAdapterDelegate.this);
}
} break;
default: {
throw new IllegalArgumentException("Unknown message: "

View File

@ -40,6 +40,7 @@ import android.os.Message;
import android.os.RemoteException;
import android.print.ILayoutResultCallback;
import android.print.IPrintDocumentAdapter;
import android.print.IPrintDocumentAdapterObserver;
import android.print.IWriteResultCallback;
import android.print.PageRange;
import android.print.PrintAttributes;
@ -201,6 +202,14 @@ public class PrintJobConfigActivity extends Activity {
throw new IllegalArgumentException("PrintDocumentAdapter cannot be null");
}
try {
IPrintDocumentAdapter.Stub.asInterface(mIPrintDocumentAdapter)
.setObserver(new PrintDocumentAdapterObserver(this));
} catch (RemoteException re) {
finish();
return;
}
PrintAttributes attributes = printJob.getAttributes();
if (attributes != null) {
mCurrPrintAttributes.copyFrom(attributes);
@ -249,27 +258,29 @@ public class PrintJobConfigActivity extends Activity {
// We can safely do the work in here since at this point
// the system is bound to our (spooler) process which
// guarantees that this process will not be killed.
if (mController.hasStarted()) {
if (mController != null && mController.hasStarted()) {
mController.finish();
}
if (mEditor.isPrintConfirmed() && mController.isFinished()) {
mSpoolerProvider.getSpooler().setPrintJobState(mPrintJobId,
PrintJobInfo.STATE_QUEUED, null);
if (mEditor != null && mEditor.isPrintConfirmed()
&& mController != null && mController.isFinished()) {
mSpoolerProvider.getSpooler().setPrintJobState(mPrintJobId,
PrintJobInfo.STATE_QUEUED, null);
} else {
mSpoolerProvider.getSpooler().setPrintJobState(mPrintJobId,
PrintJobInfo.STATE_CANCELED, null);
}
mIPrintDocumentAdapter.unlinkToDeath(mDeathRecipient, 0);
if (mGeneratingPrintJobDialog != null) {
mGeneratingPrintJobDialog.dismiss();
mGeneratingPrintJobDialog = null;
}
mIPrintDocumentAdapter.unlinkToDeath(mDeathRecipient, 0);
mSpoolerProvider.destroy();
super.onDestroy();
}
public boolean onTouchEvent(MotionEvent event) {
if (!mEditor.isPrintConfirmed() && mEditor.shouldCloseOnTouch(event)) {
if (mController != null && mEditor != null &&
!mEditor.isPrintConfirmed() && mEditor.shouldCloseOnTouch(event)) {
if (!mController.isWorking()) {
PrintJobConfigActivity.this.finish();
}
@ -287,17 +298,19 @@ public class PrintJobConfigActivity extends Activity {
}
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
if (mEditor.isShwoingGeneratingPrintJobUi()) {
if (mController != null && mEditor != null) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
if (mEditor.isShwoingGeneratingPrintJobUi()) {
return true;
}
if (event.isTracking() && !event.isCanceled()) {
if (!mController.isWorking()) {
PrintJobConfigActivity.this.finish();
}
}
mEditor.cancel();
return true;
}
if (event.isTracking() && !event.isCanceled()) {
if (!mController.isWorking()) {
PrintJobConfigActivity.this.finish();
}
}
mEditor.cancel();
return true;
}
return super.onKeyUp(keyCode, event);
}
@ -2701,4 +2714,32 @@ public class PrintJobConfigActivity extends Activity {
/* do noting - we are in the same process */
}
}
private static final class PrintDocumentAdapterObserver
extends IPrintDocumentAdapterObserver.Stub {
private final WeakReference<PrintJobConfigActivity> mWeakActvity;
public PrintDocumentAdapterObserver(PrintJobConfigActivity activity) {
mWeakActvity = new WeakReference<PrintJobConfigActivity>(activity);
}
@Override
public void onDestroy() {
final PrintJobConfigActivity activity = mWeakActvity.get();
if (activity != null) {
activity.mController.mHandler.post(new Runnable() {
@Override
public void run() {
if (activity.mController != null) {
activity.mController.cancel();
}
if (activity.mEditor != null) {
activity.mEditor.cancel();
}
activity.finish();
}
});
}
}
}
}