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
5package org.chromium.ui.base;
6
7import android.content.ContentResolver;
8import android.content.Context;
9import android.content.Intent;
10import android.os.Bundle;
11import android.util.Log;
12import android.util.SparseArray;
13import android.widget.Toast;
14
15import org.chromium.base.CalledByNative;
16import org.chromium.base.JNINamespace;
17
18import java.util.HashMap;
19
20/**
21 * The window base class that has the minimum functionality.
22 */
23@JNINamespace("ui")
24public class WindowAndroid {
25    private static final String TAG = "WindowAndroid";
26
27    // Native pointer to the c++ WindowAndroid object.
28    private long mNativeWindowAndroid = 0;
29
30    // A string used as a key to store intent errors in a bundle
31    static final String WINDOW_CALLBACK_ERRORS = "window_callback_errors";
32
33    protected Context mApplicationContext;
34    protected SparseArray<IntentCallback> mOutstandingIntents;
35    protected HashMap<Integer, String> mIntentErrors;
36
37    /**
38     * @param context The application context.
39     */
40    public WindowAndroid(Context context) {
41        assert context == context.getApplicationContext();
42        mApplicationContext = context;
43        mOutstandingIntents = new SparseArray<IntentCallback>();
44        mIntentErrors = new HashMap<Integer, String>();
45    }
46
47    /**
48     * Shows an intent and returns the results to the callback object.
49     * @param intent The intent that needs to be showed.
50     * @param callback The object that will receive the results for the intent.
51     * @param errorId The ID of error string to be show if activity is paused before intent
52     *        results.
53     * @return Whether the intent was shown.
54     */
55    public boolean showIntent(Intent intent, IntentCallback callback, int errorId) {
56        return showCancelableIntent(intent, callback, errorId) >= 0;
57    }
58
59    /**
60     * Shows an intent that could be canceled and returns the results to the callback object.
61     * @param intent The intent that needs to be showed.
62     * @param callback The object that will receive the results for the intent.
63     * @param errorId The ID of error string to be show if activity is paused before intent
64     *        results.
65     * @return A non-negative request code that could be used for finishActivity, or -1 if failed.
66     */
67    public int showCancelableIntent(Intent intent, IntentCallback callback, int errorId) {
68        Log.d(TAG, "Can't show intent as context is not an Activity: " + intent);
69        return -1;
70    }
71
72    /**
73     * Force finish another activity that you had previously started with showCancelableIntent.
74     * @param requestCode The request code returned from showCancelableIntent.
75     */
76    public void cancelIntent(int requestCode) {
77        Log.d(TAG, "Can't cancel intent as context is not an Activity: " + requestCode);
78    }
79
80    /**
81     * Removes a callback from the list of pending intents, so that nothing happens if/when the
82     * result for that intent is received.
83     * @param callback The object that should have received the results
84     * @return True if the callback was removed, false if it was not found.
85    */
86    public boolean removeIntentCallback(IntentCallback callback) {
87        int requestCode = mOutstandingIntents.indexOfValue(callback);
88        if (requestCode < 0) return false;
89        mOutstandingIntents.remove(requestCode);
90        mIntentErrors.remove(requestCode);
91        return true;
92    }
93
94    /**
95     * Displays an error message with a provided error message string.
96     * @param error The error message string to be displayed.
97     */
98    public void showError(String error) {
99        if (error != null) {
100            Toast.makeText(mApplicationContext, error, Toast.LENGTH_SHORT).show();
101        }
102    }
103
104    /**
105     * Displays an error message from the given resource id.
106     * @param resId The error message string's resource id.
107     */
108    public void showError(int resId) {
109        showError(mApplicationContext.getString(resId));
110    }
111
112    /**
113     * Displays an error message for a nonexistent callback.
114     * @param error The error message string to be displayed.
115     */
116    protected void showCallbackNonExistentError(String error) {
117        showError(error);
118    }
119
120    /**
121     * Broadcasts the given intent to all interested BroadcastReceivers.
122     */
123    public void sendBroadcast(Intent intent) {
124        mApplicationContext.sendBroadcast(intent);
125    }
126
127    /**
128     * TODO(nileshagrawal): Stop returning Activity Context crbug.com/233440.
129     * @return Activity context, it could be null. Note, in most cases, you probably
130     * just need Application Context returned by getApplicationContext().
131     * @see #getApplicationContext()
132     */
133    @Deprecated
134    public Context getContext() {
135        return null;
136    }
137
138    /**
139     * @return The application context for this activity.
140     */
141    public Context getApplicationContext() {
142        return mApplicationContext;
143    }
144
145    /**
146     * Saves the error messages that should be shown if any pending intents would return
147     * after the application has been put onPause.
148     * @param bundle The bundle to save the information in onPause
149     */
150    public void saveInstanceState(Bundle bundle) {
151        bundle.putSerializable(WINDOW_CALLBACK_ERRORS, mIntentErrors);
152    }
153
154    /**
155     * Restores the error messages that should be shown if any pending intents would return
156     * after the application has been put onPause.
157     * @param bundle The bundle to restore the information from onResume
158     */
159    public void restoreInstanceState(Bundle bundle) {
160        if (bundle == null) return;
161
162        Object errors = bundle.getSerializable(WINDOW_CALLBACK_ERRORS);
163        if (errors instanceof HashMap) {
164            @SuppressWarnings("unchecked")
165            HashMap<Integer, String> intentErrors = (HashMap<Integer, String>) errors;
166            mIntentErrors = intentErrors;
167        }
168    }
169
170    /**
171     * Responds to the intent result if the intent was created by the native window.
172     * @param requestCode Request code of the requested intent.
173     * @param resultCode Result code of the requested intent.
174     * @param data The data returned by the intent.
175     * @return Boolean value of whether the intent was started by the native window.
176     */
177    public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
178        return false;
179    }
180
181    /**
182     * An interface that intent callback objects have to implement.
183     */
184    public interface IntentCallback {
185        /**
186         * Handles the data returned by the requested intent.
187         * @param window A window reference.
188         * @param resultCode Result code of the requested intent.
189         * @param contentResolver An instance of ContentResolver class for accessing returned data.
190         * @param data The data returned by the intent.
191         */
192        public void onIntentCompleted(WindowAndroid window, int resultCode,
193                ContentResolver contentResolver, Intent data);
194    }
195
196    /**
197     * Tests that an activity is available to handle the passed in intent.
198     * @param  intent The intent to check.
199     * @return True if an activity is available to process this intent when started, meaning that
200     *         Context.startActivity will not throw ActivityNotFoundException.
201     */
202    public boolean canResolveActivity(Intent intent) {
203        return mApplicationContext.getPackageManager().resolveActivity(intent, 0) != null;
204    }
205
206    /**
207     * Destroys the c++ WindowAndroid object if one has been created.
208     */
209    public void destroy() {
210        if (mNativeWindowAndroid != 0) {
211            nativeDestroy(mNativeWindowAndroid);
212            mNativeWindowAndroid = 0;
213        }
214    }
215
216    /**
217     * Returns a pointer to the c++ AndroidWindow object and calls the initializer if
218     * the object has not been previously initialized.
219     * @return A pointer to the c++ AndroidWindow.
220     */
221    public long getNativePointer() {
222        if (mNativeWindowAndroid == 0) {
223            mNativeWindowAndroid = nativeInit();
224        }
225        return mNativeWindowAndroid;
226    }
227
228    /**
229     * Returns a PNG-encoded screenshot of the the window region at (|windowX|,
230     * |windowY|) with the size |width| by |height| pixels.
231     */
232    @CalledByNative
233    public byte[] grabSnapshot(int windowX, int windowY, int width, int height) {
234        return null;
235    }
236
237    private native long nativeInit();
238    private native void nativeDestroy(long nativeWindowAndroid);
239
240}
241