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