• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 package org.chromium.ui.base;
6 
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;
17 
18 import org.chromium.base.CalledByNative;
19 import org.chromium.base.JNINamespace;
20 import org.chromium.ui.VSyncMonitor;
21 
22 import java.lang.ref.WeakReference;
23 import java.util.HashMap;
24 
25 /**
26  * The window base class that has the minimum functionality.
27  */
28 @JNINamespace("ui")
29 public class WindowAndroid {
30     private static final String TAG = "WindowAndroid";
31 
32     // Native pointer to the c++ WindowAndroid object.
33     private long mNativeWindowAndroid = 0;
34     private final VSyncMonitor mVSyncMonitor;
35 
36     // A string used as a key to store intent errors in a bundle
37     static final String WINDOW_CALLBACK_ERRORS = "window_callback_errors";
38 
39     // Error code returned when an Intent fails to start an Activity.
40     public static final int START_INTENT_FAILURE = -1;
41 
42     protected Context mApplicationContext;
43     protected SparseArray<IntentCallback> mOutstandingIntents;
44 
45     // Ideally, this would be a SparseArray<String>, but there's no easy way to store a
46     // SparseArray<String> in a bundle during saveInstanceState(). So we use a HashMap and suppress
47     // the Android lint warning "UseSparseArrays".
48     protected HashMap<Integer, String> mIntentErrors;
49 
50     private final VSyncMonitor.Listener mVSyncListener = new VSyncMonitor.Listener() {
51         @Override
52         public void onVSync(VSyncMonitor monitor, long vsyncTimeMicros) {
53             if (mNativeWindowAndroid != 0) {
54                 nativeOnVSync(mNativeWindowAndroid, vsyncTimeMicros);
55             }
56         }
57     };
58 
59     /**
60      * @return true if onVSync handler is executing.
61      * @see org.chromium.ui.VSyncMonitor#isInsideVSync().
62      */
isInsideVSync()63     public boolean isInsideVSync() {
64         return mVSyncMonitor.isInsideVSync();
65     }
66 
67     /**
68      * @param context The application context.
69      */
70     @SuppressLint("UseSparseArrays")
WindowAndroid(Context context)71     public WindowAndroid(Context context) {
72         assert context == context.getApplicationContext();
73         mApplicationContext = context;
74         mOutstandingIntents = new SparseArray<IntentCallback>();
75         mIntentErrors = new HashMap<Integer, String>();
76         mVSyncMonitor = new VSyncMonitor(context, mVSyncListener);
77     }
78 
79     /**
80      * Shows an intent and returns the results to the callback object.
81      * @param intent   The PendingIntent that needs to be shown.
82      * @param callback The object that will receive the results for the intent.
83      * @param errorId  The ID of error string to be show if activity is paused before intent
84      *                 results.
85      * @return Whether the intent was shown.
86      */
showIntent(PendingIntent intent, IntentCallback callback, int errorId)87     public boolean showIntent(PendingIntent intent, IntentCallback callback, int errorId) {
88         return showCancelableIntent(intent, callback, errorId) >= 0;
89     }
90 
91     /**
92      * Shows an intent and returns the results to the callback object.
93      * @param intent   The intent that needs to be shown.
94      * @param callback The object that will receive the results for the intent.
95      * @param errorId  The ID of error string to be show if activity is paused before intent
96      *                 results.
97      * @return Whether the intent was shown.
98      */
showIntent(Intent intent, IntentCallback callback, int errorId)99     public boolean showIntent(Intent intent, IntentCallback callback, int errorId) {
100         return showCancelableIntent(intent, callback, errorId) >= 0;
101     }
102 
103     /**
104      * Shows an intent that could be canceled and returns the results to the callback object.
105      * @param  intent   The PendingIntent that needs to be shown.
106      * @param  callback The object that will receive the results for the intent.
107      * @param  errorId  The ID of error string to be show if activity is paused before intent
108      *                  results.
109      * @return A non-negative request code that could be used for finishActivity, or
110      *         START_INTENT_FAILURE if failed.
111      */
showCancelableIntent(PendingIntent intent, IntentCallback callback, int errorId)112     public int showCancelableIntent(PendingIntent intent, IntentCallback callback, int errorId) {
113         Log.d(TAG, "Can't show intent as context is not an Activity: " + intent);
114         return START_INTENT_FAILURE;
115     }
116 
117     /**
118      * Shows an intent that could be canceled and returns the results to the callback object.
119      * @param  intent   The intent that needs to be showed.
120      * @param  callback The object that will receive the results for the intent.
121      * @param  errorId  The ID of error string to be show if activity is paused before intent
122      *                  results.
123      * @return A non-negative request code that could be used for finishActivity, or
124      *         START_INTENT_FAILURE if failed.
125      */
showCancelableIntent(Intent intent, IntentCallback callback, int errorId)126     public int showCancelableIntent(Intent intent, IntentCallback callback, int errorId) {
127         Log.d(TAG, "Can't show intent as context is not an Activity: " + intent);
128         return START_INTENT_FAILURE;
129     }
130 
131     /**
132      * Force finish another activity that you had previously started with showCancelableIntent.
133      * @param requestCode The request code returned from showCancelableIntent.
134      */
cancelIntent(int requestCode)135     public void cancelIntent(int requestCode) {
136         Log.d(TAG, "Can't cancel intent as context is not an Activity: " + requestCode);
137     }
138 
139     /**
140      * Removes a callback from the list of pending intents, so that nothing happens if/when the
141      * result for that intent is received.
142      * @param callback The object that should have received the results
143      * @return True if the callback was removed, false if it was not found.
144     */
removeIntentCallback(IntentCallback callback)145     public boolean removeIntentCallback(IntentCallback callback) {
146         int requestCode = mOutstandingIntents.indexOfValue(callback);
147         if (requestCode < 0) return false;
148         mOutstandingIntents.remove(requestCode);
149         mIntentErrors.remove(requestCode);
150         return true;
151     }
152 
153     /**
154      * Displays an error message with a provided error message string.
155      * @param error The error message string to be displayed.
156      */
showError(String error)157     public void showError(String error) {
158         if (error != null) {
159             Toast.makeText(mApplicationContext, error, Toast.LENGTH_SHORT).show();
160         }
161     }
162 
163     /**
164      * Displays an error message from the given resource id.
165      * @param resId The error message string's resource id.
166      */
showError(int resId)167     public void showError(int resId) {
168         showError(mApplicationContext.getString(resId));
169     }
170 
171     /**
172      * Displays an error message for a nonexistent callback.
173      * @param error The error message string to be displayed.
174      */
showCallbackNonExistentError(String error)175     protected void showCallbackNonExistentError(String error) {
176         showError(error);
177     }
178 
179     /**
180      * Broadcasts the given intent to all interested BroadcastReceivers.
181      */
sendBroadcast(Intent intent)182     public void sendBroadcast(Intent intent) {
183         mApplicationContext.sendBroadcast(intent);
184     }
185 
186     /**
187      * @return A reference to owning Activity.  The returned WeakReference will never be null, but
188      *         the contained Activity can be null (either if it has been garbage collected or if
189      *         this is in the context of a WebView that was not created using an Activity).
190      */
getActivity()191     public WeakReference<Activity> getActivity() {
192         return new WeakReference<Activity>(null);
193     }
194 
195     /**
196      * @return The application context for this activity.
197      */
getApplicationContext()198     public Context getApplicationContext() {
199         return mApplicationContext;
200     }
201 
202     /**
203      * Saves the error messages that should be shown if any pending intents would return
204      * after the application has been put onPause.
205      * @param bundle The bundle to save the information in onPause
206      */
saveInstanceState(Bundle bundle)207     public void saveInstanceState(Bundle bundle) {
208         bundle.putSerializable(WINDOW_CALLBACK_ERRORS, mIntentErrors);
209     }
210 
211     /**
212      * Restores the error messages that should be shown if any pending intents would return
213      * after the application has been put onPause.
214      * @param bundle The bundle to restore the information from onResume
215      */
restoreInstanceState(Bundle bundle)216     public void restoreInstanceState(Bundle bundle) {
217         if (bundle == null) return;
218 
219         Object errors = bundle.getSerializable(WINDOW_CALLBACK_ERRORS);
220         if (errors instanceof HashMap) {
221             @SuppressWarnings("unchecked")
222             HashMap<Integer, String> intentErrors = (HashMap<Integer, String>) errors;
223             mIntentErrors = intentErrors;
224         }
225     }
226 
227     /**
228      * Responds to the intent result if the intent was created by the native window.
229      * @param requestCode Request code of the requested intent.
230      * @param resultCode Result code of the requested intent.
231      * @param data The data returned by the intent.
232      * @return Boolean value of whether the intent was started by the native window.
233      */
onActivityResult(int requestCode, int resultCode, Intent data)234     public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
235         return false;
236     }
237 
238     @CalledByNative
requestVSyncUpdate()239     private void requestVSyncUpdate() {
240        mVSyncMonitor.requestUpdate();
241     }
242 
243     /**
244      * An interface that intent callback objects have to implement.
245      */
246     public interface IntentCallback {
247         /**
248          * Handles the data returned by the requested intent.
249          * @param window A window reference.
250          * @param resultCode Result code of the requested intent.
251          * @param contentResolver An instance of ContentResolver class for accessing returned data.
252          * @param data The data returned by the intent.
253          */
onIntentCompleted(WindowAndroid window, int resultCode, ContentResolver contentResolver, Intent data)254         void onIntentCompleted(WindowAndroid window, int resultCode,
255                 ContentResolver contentResolver, Intent data);
256     }
257 
258     /**
259      * Tests that an activity is available to handle the passed in intent.
260      * @param  intent The intent to check.
261      * @return True if an activity is available to process this intent when started, meaning that
262      *         Context.startActivity will not throw ActivityNotFoundException.
263      */
canResolveActivity(Intent intent)264     public boolean canResolveActivity(Intent intent) {
265         return mApplicationContext.getPackageManager().resolveActivity(intent, 0) != null;
266     }
267 
268     /**
269      * Destroys the c++ WindowAndroid object if one has been created.
270      */
destroy()271     public void destroy() {
272         if (mNativeWindowAndroid != 0) {
273             nativeDestroy(mNativeWindowAndroid);
274             mNativeWindowAndroid = 0;
275         }
276     }
277 
278     /**
279      * Returns a pointer to the c++ AndroidWindow object and calls the initializer if
280      * the object has not been previously initialized.
281      * @return A pointer to the c++ AndroidWindow.
282      */
getNativePointer()283     public long getNativePointer() {
284         if (mNativeWindowAndroid == 0) {
285             mNativeWindowAndroid = nativeInit(mVSyncMonitor.getVSyncPeriodInMicroseconds());
286         }
287         return mNativeWindowAndroid;
288     }
289 
nativeInit(long vsyncPeriod)290     private native long nativeInit(long vsyncPeriod);
nativeOnVSync(long nativeWindowAndroid, long vsyncTimeMicros)291     private native void nativeOnVSync(long nativeWindowAndroid, long vsyncTimeMicros);
nativeDestroy(long nativeWindowAndroid)292     private native void nativeDestroy(long nativeWindowAndroid);
293 
294 }
295