1 // Copyright 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 package org.chromium.ui.base;
7 import android.annotation.SuppressLint;
8 import android.app.Activity;
9 import android.app.PendingIntent;
10 import android.content.ContentResolver;
11 import android.content.Context;
12 import android.content.Intent;
13 import android.os.Bundle;
14 import android.util.Log;
15 import android.util.SparseArray;
16 import android.widget.Toast;
18 import org.chromium.base.CalledByNative;
19 import org.chromium.base.JNINamespace;
20 import org.chromium.ui.VSyncMonitor;
22 import java.lang.ref.WeakReference;
23 import java.util.HashMap;
26 * The window base class that has the minimum functionality.
29 public class WindowAndroid {
30 private static final String TAG = "WindowAndroid";
32 // Native pointer to the c++ WindowAndroid object.
33 private long mNativeWindowAndroid = 0;
34 private final VSyncMonitor mVSyncMonitor;
35 private VSyncClient mVSyncClient = null;
37 // A string used as a key to store intent errors in a bundle
38 static final String WINDOW_CALLBACK_ERRORS = "window_callback_errors";
40 // Error code returned when an Intent fails to start an Activity.
41 public static final int START_INTENT_FAILURE = -1;
43 protected Context mApplicationContext;
44 protected SparseArray<IntentCallback> mOutstandingIntents;
46 // Ideally, this would be a SparseArray<String>, but there's no easy way to store a
47 // SparseArray<String> in a bundle during saveInstanceState(). So we use a HashMap and suppress
48 // the Android lint warning "UseSparseArrays".
49 protected HashMap<Integer, String> mIntentErrors;
51 private final VSyncMonitor.Listener mVSyncListener = new VSyncMonitor.Listener() {
53 public void onVSync(VSyncMonitor monitor, long vsyncTimeMicros) {
54 if (mVSyncClient != null) {
55 mVSyncClient.onVSync(vsyncTimeMicros);
57 if (mNativeWindowAndroid != 0) {
58 nativeOnVSync(mNativeWindowAndroid, vsyncTimeMicros);
64 * @param context The application context.
66 @SuppressLint("UseSparseArrays")
67 public WindowAndroid(Context context) {
68 assert context == context.getApplicationContext();
69 mApplicationContext = context;
70 mOutstandingIntents = new SparseArray<IntentCallback>();
71 mIntentErrors = new HashMap<Integer, String>();
72 mVSyncMonitor = new VSyncMonitor(context, mVSyncListener);
76 * Shows an intent and returns the results to the callback object.
77 * @param intent The PendingIntent that needs to be shown.
78 * @param callback The object that will receive the results for the intent.
79 * @param errorId The ID of error string to be show if activity is paused before intent
81 * @return Whether the intent was shown.
83 public boolean showIntent(PendingIntent intent, IntentCallback callback, int errorId) {
84 return showCancelableIntent(intent, callback, errorId) >= 0;
88 * Shows an intent and returns the results to the callback object.
89 * @param intent The intent that needs to be shown.
90 * @param callback The object that will receive the results for the intent.
91 * @param errorId The ID of error string to be show if activity is paused before intent
93 * @return Whether the intent was shown.
95 public boolean showIntent(Intent intent, IntentCallback callback, int errorId) {
96 return showCancelableIntent(intent, callback, errorId) >= 0;
100 * Shows an intent that could be canceled and returns the results to the callback object.
101 * @param intent The PendingIntent that needs to be shown.
102 * @param callback The object that will receive the results for the intent.
103 * @param errorId The ID of error string to be show if activity is paused before intent
105 * @return A non-negative request code that could be used for finishActivity, or
106 * START_INTENT_FAILURE if failed.
108 public int showCancelableIntent(PendingIntent intent, IntentCallback callback, int errorId) {
109 Log.d(TAG, "Can't show intent as context is not an Activity: " + intent);
110 return START_INTENT_FAILURE;
114 * Shows an intent that could be canceled and returns the results to the callback object.
115 * @param intent The intent that needs to be showed.
116 * @param callback The object that will receive the results for the intent.
117 * @param errorId The ID of error string to be show if activity is paused before intent
119 * @return A non-negative request code that could be used for finishActivity, or
120 * START_INTENT_FAILURE if failed.
122 public int showCancelableIntent(Intent intent, IntentCallback callback, int errorId) {
123 Log.d(TAG, "Can't show intent as context is not an Activity: " + intent);
124 return START_INTENT_FAILURE;
128 * Force finish another activity that you had previously started with showCancelableIntent.
129 * @param requestCode The request code returned from showCancelableIntent.
131 public void cancelIntent(int requestCode) {
132 Log.d(TAG, "Can't cancel intent as context is not an Activity: " + requestCode);
136 * Removes a callback from the list of pending intents, so that nothing happens if/when the
137 * result for that intent is received.
138 * @param callback The object that should have received the results
139 * @return True if the callback was removed, false if it was not found.
141 public boolean removeIntentCallback(IntentCallback callback) {
142 int requestCode = mOutstandingIntents.indexOfValue(callback);
143 if (requestCode < 0) return false;
144 mOutstandingIntents.remove(requestCode);
145 mIntentErrors.remove(requestCode);
150 * Displays an error message with a provided error message string.
151 * @param error The error message string to be displayed.
153 public void showError(String error) {
155 Toast.makeText(mApplicationContext, error, Toast.LENGTH_SHORT).show();
160 * Displays an error message from the given resource id.
161 * @param resId The error message string's resource id.
163 public void showError(int resId) {
164 showError(mApplicationContext.getString(resId));
168 * Displays an error message for a nonexistent callback.
169 * @param error The error message string to be displayed.
171 protected void showCallbackNonExistentError(String error) {
176 * Broadcasts the given intent to all interested BroadcastReceivers.
178 public void sendBroadcast(Intent intent) {
179 mApplicationContext.sendBroadcast(intent);
183 * @return A reference to owning Activity. The returned WeakReference will never be null, but
184 * the contained Activity can be null (either if it has been garbage collected or if
185 * this is in the context of a WebView that was not created using an Activity).
187 public WeakReference<Activity> getActivity() {
188 return new WeakReference<Activity>(null);
192 * @return The application context for this activity.
194 public Context getApplicationContext() {
195 return mApplicationContext;
199 * Saves the error messages that should be shown if any pending intents would return
200 * after the application has been put onPause.
201 * @param bundle The bundle to save the information in onPause
203 public void saveInstanceState(Bundle bundle) {
204 bundle.putSerializable(WINDOW_CALLBACK_ERRORS, mIntentErrors);
208 * Restores the error messages that should be shown if any pending intents would return
209 * after the application has been put onPause.
210 * @param bundle The bundle to restore the information from onResume
212 public void restoreInstanceState(Bundle bundle) {
213 if (bundle == null) return;
215 Object errors = bundle.getSerializable(WINDOW_CALLBACK_ERRORS);
216 if (errors instanceof HashMap) {
217 @SuppressWarnings("unchecked")
218 HashMap<Integer, String> intentErrors = (HashMap<Integer, String>) errors;
219 mIntentErrors = intentErrors;
224 * Responds to the intent result if the intent was created by the native window.
225 * @param requestCode Request code of the requested intent.
226 * @param resultCode Result code of the requested intent.
227 * @param data The data returned by the intent.
228 * @return Boolean value of whether the intent was started by the native window.
230 public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
235 * An interface to receive VSync notifications from the window.
236 * The one and only client is set with setVSyncClient(client).
238 public interface VSyncClient {
240 * Called very soon after the start of the display's vertical sync period.
241 * @param vsyncTimeMicros Absolute frame time in microseconds.
243 void onVSync(long vsyncTimeMicros);
247 * Sets the VSyncClient.
248 * @param client The client receiving VSync notifications.
250 public void setVSyncClient(VSyncClient client) {
251 assert mVSyncClient == null || client == null;
252 mVSyncClient = client;
256 * Request a VSync callback.
257 * VSyncClient.onVSync() will be called at least once.
260 public void requestVSyncUpdate() {
261 mVSyncMonitor.requestUpdate();
265 * An interface that intent callback objects have to implement.
267 public interface IntentCallback {
269 * Handles the data returned by the requested intent.
270 * @param window A window reference.
271 * @param resultCode Result code of the requested intent.
272 * @param contentResolver An instance of ContentResolver class for accessing returned data.
273 * @param data The data returned by the intent.
275 void onIntentCompleted(WindowAndroid window, int resultCode,
276 ContentResolver contentResolver, Intent data);
280 * Tests that an activity is available to handle the passed in intent.
281 * @param intent The intent to check.
282 * @return True if an activity is available to process this intent when started, meaning that
283 * Context.startActivity will not throw ActivityNotFoundException.
285 public boolean canResolveActivity(Intent intent) {
286 return mApplicationContext.getPackageManager().resolveActivity(intent, 0) != null;
290 * Destroys the c++ WindowAndroid object if one has been created.
292 public void destroy() {
293 if (mNativeWindowAndroid != 0) {
294 nativeDestroy(mNativeWindowAndroid);
295 mNativeWindowAndroid = 0;
300 * Returns a pointer to the c++ AndroidWindow object and calls the initializer if
301 * the object has not been previously initialized.
302 * @return A pointer to the c++ AndroidWindow.
304 public long getNativePointer() {
305 if (mNativeWindowAndroid == 0) {
306 mNativeWindowAndroid = nativeInit(mVSyncMonitor.getVSyncPeriodInMicroseconds());
308 return mNativeWindowAndroid;
312 * Returns a PNG-encoded screenshot of the the window region at (|windowX|,
313 * |windowY|) with the size |width| by |height| pixels.
316 public byte[] grabSnapshot(int windowX, int windowY, int width, int height) {
320 private native long nativeInit(long vsyncPeriod);
321 private native void nativeOnVSync(long nativeWindowAndroid, long vsyncTimeMicros);
322 private native void nativeDestroy(long nativeWindowAndroid);