blob: 6d3f3a6d1538a90ed7137b6f992c74610cfc56d8 [file] [log] [blame]
// Copyright 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.ui.base;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.util.SparseArray;
import android.widget.Toast;
import org.chromium.base.CalledByNative;
import org.chromium.base.JNINamespace;
import org.chromium.ui.VSyncMonitor;
import java.lang.ref.WeakReference;
import java.util.HashMap;
/**
* The window base class that has the minimum functionality.
*/
@JNINamespace("ui")
public class WindowAndroid {
private static final String TAG = "WindowAndroid";
// Native pointer to the c++ WindowAndroid object.
private long mNativeWindowAndroid = 0;
private final VSyncMonitor mVSyncMonitor;
// A string used as a key to store intent errors in a bundle
static final String WINDOW_CALLBACK_ERRORS = "window_callback_errors";
// Error code returned when an Intent fails to start an Activity.
public static final int START_INTENT_FAILURE = -1;
protected Context mApplicationContext;
protected SparseArray<IntentCallback> mOutstandingIntents;
// Ideally, this would be a SparseArray<String>, but there's no easy way to store a
// SparseArray<String> in a bundle during saveInstanceState(). So we use a HashMap and suppress
// the Android lint warning "UseSparseArrays".
protected HashMap<Integer, String> mIntentErrors;
private final VSyncMonitor.Listener mVSyncListener = new VSyncMonitor.Listener() {
@Override
public void onVSync(VSyncMonitor monitor, long vsyncTimeMicros) {
if (mNativeWindowAndroid != 0) {
nativeOnVSync(mNativeWindowAndroid, vsyncTimeMicros);
}
}
};
/**
* @return true if onVSync handler is executing.
* @see org.chromium.ui.VSyncMonitor#isInsideVSync().
*/
public boolean isInsideVSync() {
return mVSyncMonitor.isInsideVSync();
}
/**
* @param context The application context.
*/
@SuppressLint("UseSparseArrays")
public WindowAndroid(Context context) {
assert context == context.getApplicationContext();
mApplicationContext = context;
mOutstandingIntents = new SparseArray<IntentCallback>();
mIntentErrors = new HashMap<Integer, String>();
mVSyncMonitor = new VSyncMonitor(context, mVSyncListener);
}
/**
* Shows an intent and returns the results to the callback object.
* @param intent The PendingIntent that needs to be shown.
* @param callback The object that will receive the results for the intent.
* @param errorId The ID of error string to be show if activity is paused before intent
* results.
* @return Whether the intent was shown.
*/
public boolean showIntent(PendingIntent intent, IntentCallback callback, int errorId) {
return showCancelableIntent(intent, callback, errorId) >= 0;
}
/**
* Shows an intent and returns the results to the callback object.
* @param intent The intent that needs to be shown.
* @param callback The object that will receive the results for the intent.
* @param errorId The ID of error string to be show if activity is paused before intent
* results.
* @return Whether the intent was shown.
*/
public boolean showIntent(Intent intent, IntentCallback callback, int errorId) {
return showCancelableIntent(intent, callback, errorId) >= 0;
}
/**
* Shows an intent that could be canceled and returns the results to the callback object.
* @param intent The PendingIntent that needs to be shown.
* @param callback The object that will receive the results for the intent.
* @param errorId The ID of error string to be show if activity is paused before intent
* results.
* @return A non-negative request code that could be used for finishActivity, or
* START_INTENT_FAILURE if failed.
*/
public int showCancelableIntent(PendingIntent intent, IntentCallback callback, int errorId) {
Log.d(TAG, "Can't show intent as context is not an Activity: " + intent);
return START_INTENT_FAILURE;
}
/**
* Shows an intent that could be canceled and returns the results to the callback object.
* @param intent The intent that needs to be showed.
* @param callback The object that will receive the results for the intent.
* @param errorId The ID of error string to be show if activity is paused before intent
* results.
* @return A non-negative request code that could be used for finishActivity, or
* START_INTENT_FAILURE if failed.
*/
public int showCancelableIntent(Intent intent, IntentCallback callback, int errorId) {
Log.d(TAG, "Can't show intent as context is not an Activity: " + intent);
return START_INTENT_FAILURE;
}
/**
* Force finish another activity that you had previously started with showCancelableIntent.
* @param requestCode The request code returned from showCancelableIntent.
*/
public void cancelIntent(int requestCode) {
Log.d(TAG, "Can't cancel intent as context is not an Activity: " + requestCode);
}
/**
* Removes a callback from the list of pending intents, so that nothing happens if/when the
* result for that intent is received.
* @param callback The object that should have received the results
* @return True if the callback was removed, false if it was not found.
*/
public boolean removeIntentCallback(IntentCallback callback) {
int requestCode = mOutstandingIntents.indexOfValue(callback);
if (requestCode < 0) return false;
mOutstandingIntents.remove(requestCode);
mIntentErrors.remove(requestCode);
return true;
}
/**
* Displays an error message with a provided error message string.
* @param error The error message string to be displayed.
*/
public void showError(String error) {
if (error != null) {
Toast.makeText(mApplicationContext, error, Toast.LENGTH_SHORT).show();
}
}
/**
* Displays an error message from the given resource id.
* @param resId The error message string's resource id.
*/
public void showError(int resId) {
showError(mApplicationContext.getString(resId));
}
/**
* Displays an error message for a nonexistent callback.
* @param error The error message string to be displayed.
*/
protected void showCallbackNonExistentError(String error) {
showError(error);
}
/**
* Broadcasts the given intent to all interested BroadcastReceivers.
*/
public void sendBroadcast(Intent intent) {
mApplicationContext.sendBroadcast(intent);
}
/**
* @return A reference to owning Activity. The returned WeakReference will never be null, but
* the contained Activity can be null (either if it has been garbage collected or if
* this is in the context of a WebView that was not created using an Activity).
*/
public WeakReference<Activity> getActivity() {
return new WeakReference<Activity>(null);
}
/**
* @return The application context for this activity.
*/
public Context getApplicationContext() {
return mApplicationContext;
}
/**
* Saves the error messages that should be shown if any pending intents would return
* after the application has been put onPause.
* @param bundle The bundle to save the information in onPause
*/
public void saveInstanceState(Bundle bundle) {
bundle.putSerializable(WINDOW_CALLBACK_ERRORS, mIntentErrors);
}
/**
* Restores the error messages that should be shown if any pending intents would return
* after the application has been put onPause.
* @param bundle The bundle to restore the information from onResume
*/
public void restoreInstanceState(Bundle bundle) {
if (bundle == null) return;
Object errors = bundle.getSerializable(WINDOW_CALLBACK_ERRORS);
if (errors instanceof HashMap) {
@SuppressWarnings("unchecked")
HashMap<Integer, String> intentErrors = (HashMap<Integer, String>) errors;
mIntentErrors = intentErrors;
}
}
/**
* Responds to the intent result if the intent was created by the native window.
* @param requestCode Request code of the requested intent.
* @param resultCode Result code of the requested intent.
* @param data The data returned by the intent.
* @return Boolean value of whether the intent was started by the native window.
*/
public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
return false;
}
@CalledByNative
private void requestVSyncUpdate() {
mVSyncMonitor.requestUpdate();
}
/**
* An interface that intent callback objects have to implement.
*/
public interface IntentCallback {
/**
* Handles the data returned by the requested intent.
* @param window A window reference.
* @param resultCode Result code of the requested intent.
* @param contentResolver An instance of ContentResolver class for accessing returned data.
* @param data The data returned by the intent.
*/
void onIntentCompleted(WindowAndroid window, int resultCode,
ContentResolver contentResolver, Intent data);
}
/**
* Tests that an activity is available to handle the passed in intent.
* @param intent The intent to check.
* @return True if an activity is available to process this intent when started, meaning that
* Context.startActivity will not throw ActivityNotFoundException.
*/
public boolean canResolveActivity(Intent intent) {
return mApplicationContext.getPackageManager().resolveActivity(intent, 0) != null;
}
/**
* Destroys the c++ WindowAndroid object if one has been created.
*/
public void destroy() {
if (mNativeWindowAndroid != 0) {
nativeDestroy(mNativeWindowAndroid);
mNativeWindowAndroid = 0;
}
}
/**
* Returns a pointer to the c++ AndroidWindow object and calls the initializer if
* the object has not been previously initialized.
* @return A pointer to the c++ AndroidWindow.
*/
public long getNativePointer() {
if (mNativeWindowAndroid == 0) {
mNativeWindowAndroid = nativeInit(mVSyncMonitor.getVSyncPeriodInMicroseconds());
}
return mNativeWindowAndroid;
}
private native long nativeInit(long vsyncPeriod);
private native void nativeOnVSync(long nativeWindowAndroid, long vsyncTimeMicros);
private native void nativeDestroy(long nativeWindowAndroid);
}