/*
* Copyright (C) 2011 Virginia Tech Department of Computer Science
*
* 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 sofia.internal;
import android.os.Bundle;
import android.os.Looper;
// -------------------------------------------------------------------------
/**
* <p>
* Android does not provide any real support for synchronously invoking modal
* UI constructs on the GUI thread, such as dialog boxes or sub-activities.
* Instead, callbacks are used to communicate this information back to the
* caller. This class abuses knowledge of the internal workings of the Android
* {@code MessageQueue} class in order to provide a synchronous modal
* construct, by causing what is essentially a "nested" invocation of the GUI
* thread's run loop so that messages can still be dispatched while the GUI
* thread otherwise blocks, waiting for the modal task to communicate its
* result.
* </p><p>
* This class is used as follows:
* <ol>
* <li>Create an anonymous instance of this class and implement its
* {@link #run()} method to display the modal construct.</li>
* <li>In the listener(s) for the modal construct, call the
* {@link #endModal(Object)} method, passing to it the result that should be
* returned by the modal task.</li>
* <li>After the instance of this class is created, call the
* {@link #executeTask()} method. This method will block until the listener(s)
* for the modal construct indicate that the result is ready, which is then
* returned by this method.
* </li>
* </ol>
* </p><p>
* TODO What this class is doing is incredibly fragile and unsafe and it may
* be wise to reconsider doing this.
* </p>
*
* @param <E> the type of the result returned by the modal task
*
* @author Tony Allevato
*/
public abstract class ModalTask<E>
{
//~ Instance/static variables .............................................
// The result returned by the modal task.
private E result;
private Bundle extras;
// ----------------------------------------------------------
public ModalTask()
{
extras = new Bundle();
}
//~ Methods ...............................................................
// ----------------------------------------------------------
/**
* Subclasses should override this to present a modal construct such as a
* dialog box on the screen.
*/
protected abstract void run();
// ----------------------------------------------------------
public Bundle getExtras()
{
return extras;
}
// ----------------------------------------------------------
/**
* Called by user code to run the task and wait until its result is ready.
* While the call is waiting, GUI events will continue to be processed, so
* mouse clicks and repaint events will still be processed as expected.
*
* @return the result of the modal task
*/
public E executeTask()
{
// Execute the user's code, which is expected to present a GUI
// construct that we want to synchronously wait for, such as an alert
// dialog.
run();
// Manually re-run the event dispatch loop. This method will block
// until a quit message is put into the message queue, which happens
// when the endModal method is called.
Looper.loop();
// Return the result that was sent to endModal.
return result;
}
// ----------------------------------------------------------
/**
* Called from inside the modal task to notify that the task has ended and
* generated a result.
*
* @param modalResult the result generated by the task
*/
public void endModal(E modalResult)
{
this.result = modalResult;
SofiaUtils.quitLoop();
}
}