| 1 | // Copyright (c) 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; |
| 6 | |
| 7 | import android.app.Activity; |
| 8 | import android.content.ActivityNotFoundException; |
| 9 | import android.content.ContentResolver; |
| 10 | import android.content.Context; |
| 11 | import android.content.Intent; |
| 12 | import android.os.Bundle; |
| 13 | import android.util.SparseArray; |
| 14 | import android.widget.Toast; |
| 15 | |
| 16 | import java.util.HashMap; |
| 17 | |
| 18 | import org.chromium.base.JNINamespace; |
| 19 | |
| 20 | /** |
| 21 | * The window base class that has the minimum functionality. |
| 22 | */ |
| 23 | @JNINamespace("ui") |
| 24 | public class WindowAndroid { |
| 25 | |
| 26 | // Native pointer to the c++ WindowAndroid object. |
| 27 | private int mNativeWindowAndroid = 0; |
| 28 | |
| 29 | // Constants used for intent request code bounding. |
| 30 | private static final int REQUEST_CODE_PREFIX = 1000; |
| 31 | private static final int REQUEST_CODE_RANGE_SIZE = 100; |
| 32 | // A string used as a key to store intent errors in a bundle |
| 33 | static final String WINDOW_CALLBACK_ERRORS = "window_callback_errors"; |
| 34 | |
| 35 | private int mNextRequestCode = 0; |
| 36 | protected Activity mActivity; |
| 37 | protected SparseArray<IntentCallback> mOutstandingIntents; |
| 38 | protected HashMap<Integer, String> mIntentErrors; |
| 39 | |
| 40 | /** |
| 41 | * @param activity |
| 42 | */ |
| 43 | public WindowAndroid(Activity activity) { |
| 44 | mActivity = activity; |
| 45 | mOutstandingIntents = new SparseArray<IntentCallback>(); |
| 46 | mIntentErrors = new HashMap<Integer, String>(); |
| 47 | |
| 48 | } |
| 49 | |
| 50 | /** |
| 51 | * Shows an intent and returns the results to the callback object. |
| 52 | * @param intent The intent that needs to be showed. |
| 53 | * @param callback The object that will receive the results for the intent. |
| 54 | * @param errorId The ID of error string to be show if activity is paused before intent |
| 55 | * results. |
| 56 | * @return Whether the intent was shown. |
| 57 | */ |
| 58 | public boolean showIntent(Intent intent, IntentCallback callback, int errorId) { |
| 59 | int requestCode = REQUEST_CODE_PREFIX + mNextRequestCode; |
| 60 | mNextRequestCode = (mNextRequestCode + 1) % REQUEST_CODE_RANGE_SIZE; |
| 61 | |
| 62 | try { |
| 63 | mActivity.startActivityForResult(intent, requestCode); |
| 64 | } catch (ActivityNotFoundException e) { |
| 65 | return false; |
| 66 | } |
| 67 | |
| 68 | mOutstandingIntents.put(requestCode, callback); |
| 69 | mIntentErrors.put(requestCode, mActivity.getString(errorId)); |
| 70 | |
| 71 | return true; |
| 72 | } |
| 73 | |
| 74 | /** |
| 75 | * Displays an error message with a provided error message string. |
| 76 | * @param error The error message string to be displayed. |
| 77 | */ |
| 78 | public void showError(String error) { |
| 79 | if (error != null) { |
| 80 | Toast.makeText(mActivity, error, Toast.LENGTH_SHORT).show(); |
| 81 | } |
| 82 | } |
| 83 | |
| 84 | /** |
| 85 | * Displays an error message from the given resource id. |
| 86 | * @param resId The error message string's resource id. |
| 87 | */ |
| 88 | public void showError(int resId) { |
| 89 | showError(mActivity.getString(resId)); |
| 90 | } |
| 91 | |
| 92 | /** |
| 93 | * Displays an error message for a nonexistent callback. |
| 94 | * @param error The error message string to be displayed. |
| 95 | */ |
| 96 | protected void showCallbackNonExistentError(String error) { |
| 97 | showError(error); |
| 98 | } |
| 99 | |
| 100 | /** |
| 101 | * Broadcasts the given intent to all interested BroadcastReceivers. |
| 102 | */ |
| 103 | public void sendBroadcast(Intent intent) { |
| 104 | mActivity.sendBroadcast(intent); |
| 105 | } |
| 106 | |
| 107 | /** |
| 108 | * TODO(nileshagrawal): Stop returning Activity Context crbug.com/233440. |
| 109 | * @return Activity context. |
| 110 | */ |
| 111 | @Deprecated |
| 112 | public Context getContext() { |
| 113 | return mActivity; |
| 114 | } |
| 115 | |
| 116 | /** |
| 117 | * Saves the error messages that should be shown if any pending intents would return |
| 118 | * after the application has been put onPause. |
| 119 | * @param bundle The bundle to save the information in onPause |
| 120 | */ |
| 121 | public void saveInstanceState(Bundle bundle) { |
| 122 | bundle.putSerializable(WINDOW_CALLBACK_ERRORS, mIntentErrors); |
| 123 | } |
| 124 | |
| 125 | /** |
| 126 | * Restores the error messages that should be shown if any pending intents would return |
| 127 | * after the application has been put onPause. |
| 128 | * @param bundle The bundle to restore the information from onResume |
| 129 | */ |
| 130 | public void restoreInstanceState(Bundle bundle) { |
| 131 | if (bundle == null) return; |
| 132 | |
| 133 | Object errors = bundle.getSerializable(WINDOW_CALLBACK_ERRORS); |
| 134 | if (errors instanceof HashMap) { |
| 135 | @SuppressWarnings("unchecked") |
| 136 | HashMap<Integer, String> intentErrors = (HashMap<Integer, String>) errors; |
| 137 | mIntentErrors = intentErrors; |
| 138 | } |
| 139 | } |
| 140 | |
| 141 | /** |
| 142 | * Responds to the intent result if the intent was created by the native window. |
| 143 | * @param requestCode Request code of the requested intent. |
| 144 | * @param resultCode Result code of the requested intent. |
| 145 | * @param data The data returned by the intent. |
| 146 | * @return Boolean value of whether the intent was started by the native window. |
| 147 | */ |
| 148 | public boolean onActivityResult(int requestCode, int resultCode, Intent data) { |
| 149 | IntentCallback callback = mOutstandingIntents.get(requestCode); |
| 150 | mOutstandingIntents.delete(requestCode); |
| 151 | String errorMessage = mIntentErrors.remove(requestCode); |
| 152 | |
| 153 | if (callback != null) { |
| 154 | callback.onIntentCompleted(this, resultCode, |
| 155 | mActivity.getContentResolver(), data); |
| 156 | return true; |
| 157 | } else { |
| 158 | if (errorMessage != null) { |
| 159 | showCallbackNonExistentError(errorMessage); |
| 160 | return true; |
| 161 | } |
| 162 | } |
| 163 | return false; |
| 164 | } |
| 165 | |
| 166 | /** |
| 167 | * An interface that intent callback objects have to implement. |
| 168 | */ |
| 169 | public interface IntentCallback { |
| 170 | /** |
| 171 | * Handles the data returned by the requested intent. |
| 172 | * @param window A window reference. |
| 173 | * @param resultCode Result code of the requested intent. |
| 174 | * @param contentResolver An instance of ContentResolver class for accessing returned data. |
| 175 | * @param data The data returned by the intent. |
| 176 | */ |
| 177 | public void onIntentCompleted(WindowAndroid window, int resultCode, |
| 178 | ContentResolver contentResolver, Intent data); |
| 179 | } |
| 180 | |
| 181 | /** |
| 182 | * Destroys the c++ WindowAndroid object if one has been created. |
| 183 | */ |
| 184 | public void destroy() { |
| 185 | if (mNativeWindowAndroid != 0) { |
| 186 | nativeDestroy(mNativeWindowAndroid); |
| 187 | mNativeWindowAndroid = 0; |
| 188 | } |
| 189 | } |
| 190 | |
| 191 | /** |
| 192 | * Returns a pointer to the c++ AndroidWindow object and calls the initializer if |
| 193 | * the object has not been previously initialized. |
| 194 | * @return A pointer to the c++ AndroidWindow. |
| 195 | */ |
| 196 | public int getNativePointer() { |
| 197 | if (mNativeWindowAndroid == 0) { |
| 198 | mNativeWindowAndroid = nativeInit(); |
| 199 | } |
| 200 | return mNativeWindowAndroid; |
| 201 | } |
| 202 | |
| 203 | private native int nativeInit(); |
| 204 | private native void nativeDestroy(int nativeWindowAndroid); |
| 205 | |
| 206 | } |