/* * Copyright (C) 2012 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.support.v4.os; import android.os.Build; /** * Static library support version of the framework's {@link android.os.CancellationSignal}. * Used to write apps that run on platforms prior to Android 4.1. See the framework SDK * documentation for a class overview. */ public final class CancellationSignal { private boolean mIsCanceled; private OnCancelListener mOnCancelListener; private Object mCancellationSignalObj; private boolean mCancelInProgress; /** * Creates a cancellation signal, initially not canceled. */ public CancellationSignal() { } /** * Returns true if the operation has been canceled. * * @return True if the operation has been canceled. */ public boolean isCanceled() { synchronized (this) { return mIsCanceled; } } /** * Throws {@link OperationCanceledException} if the operation has been canceled. * * @throws OperationCanceledException if the operation has been canceled. */ public void throwIfCanceled() { if (isCanceled()) { throw new OperationCanceledException(); } } /** * Cancels the operation and signals the cancellation listener. * If the operation has not yet started, then it will be canceled as soon as it does. */ public void cancel() { final OnCancelListener listener; final Object obj; synchronized (this) { if (mIsCanceled) { return; } mIsCanceled = true; mCancelInProgress = true; listener = mOnCancelListener; obj = mCancellationSignalObj; } try { if (listener != null) { listener.onCancel(); } if (obj != null) { CancellationSignalCompatJellybean.cancel(obj); } } finally { synchronized (this) { mCancelInProgress = false; notifyAll(); } } } /** * Sets the cancellation listener to be called when canceled. * * This method is intended to be used by the recipient of a cancellation signal * such as a database or a content provider to handle cancellation requests * while performing a long-running operation. This method is not intended to be * used by applications themselves. * * If {@link CancellationSignal#cancel} has already been called, then the provided * listener is invoked immediately. * * This method is guaranteed that the listener will not be called after it * has been removed. * * @param listener The cancellation listener, or null to remove the current listener. */ public void setOnCancelListener(OnCancelListener listener) { synchronized (this) { waitForCancelFinishedLocked(); if (mOnCancelListener == listener) { return; } mOnCancelListener = listener; if (!mIsCanceled || listener == null) { return; } } listener.onCancel(); } /** * Gets the framework {@link android.os.CancellationSignal} associated with this object. * <p> * Framework support for cancellation signals was added in * {@link android.os.Build.VERSION_CODES#JELLY_BEAN} so this method will always * return null on older versions of the platform. * </p> * * @return A framework cancellation signal object, or null on platform versions * prior to Jellybean. */ public Object getCancellationSignalObject() { if (Build.VERSION.SDK_INT < 16) { return null; } synchronized (this) { if (mCancellationSignalObj == null) { mCancellationSignalObj = CancellationSignalCompatJellybean.create(); if (mIsCanceled) { CancellationSignalCompatJellybean.cancel(mCancellationSignalObj); } } return mCancellationSignalObj; } } private void waitForCancelFinishedLocked() { while (mCancelInProgress) { try { wait(); } catch (InterruptedException ex) { } } } /** * Listens for cancellation. */ public interface OnCancelListener { /** * Called when {@link CancellationSignal#cancel} is invoked. */ void onCancel(); } }