Home | History | Annotate | Download | only in base
      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")
     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      */
     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      */
     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      */
    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      */
    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      */
    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     */
    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      */
    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      */
    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      */
    167     protected void showCallbackNonExistentError(String error) {
    168         showError(error);
    169     }
    170 
    171     /**
    172      * Broadcasts the given intent to all interested BroadcastReceivers.
    173      */
    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      */
    183     public WeakReference<Activity> getActivity() {
    184         return new WeakReference<Activity>(null);
    185     }
    186 
    187     /**
    188      * @return The application context for this activity.
    189      */
    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      */
    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      */
    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      */
    226     public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
    227         return false;
    228     }
    229 
    230     @CalledByNative
    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          */
    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      */
    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      */
    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      */
    275     public long getNativePointer() {
    276         if (mNativeWindowAndroid == 0) {
    277             mNativeWindowAndroid = nativeInit(mVSyncMonitor.getVSyncPeriodInMicroseconds());
    278         }
    279         return mNativeWindowAndroid;
    280     }
    281 
    282     private native long nativeInit(long vsyncPeriod);
    283     private native void nativeOnVSync(long nativeWindowAndroid, long vsyncTimeMicros);
    284     private native void nativeDestroy(long nativeWindowAndroid);
    285 
    286 }
    287