Upstream version 9.37.195.0
[platform/framework/web/crosswalk.git] / src / xwalk / runtime / android / core_internal / src / org / xwalk / core / internal / XWalkUIClientInternal.java
1 // Copyright (c) 2014 Intel Corporation. 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.xwalk.core.internal;
6
7 import android.app.Activity;
8 import android.app.AlertDialog;
9 import android.content.Context;
10 import android.content.DialogInterface;
11 import android.net.Uri;
12 import android.os.Build.VERSION;
13 import android.os.Build.VERSION_CODES;
14 import android.view.KeyEvent;
15 import android.view.View;
16 import android.view.WindowManager;
17 import android.webkit.ValueCallback;
18 import android.widget.EditText;
19
20 /**
21  * This class notifies the embedder UI events/callbacks.
22  */
23 @XWalkAPI(createExternally = true)
24 public class XWalkUIClientInternal {
25
26     // Strings for displaying Dialog.
27     private static String mJSAlertTitle;
28     private static String mJSConfirmTitle;
29     private static String mJSPromptTitle;
30     private static String mOKButton;
31     private static String mCancelButton;
32
33     private Context mContext;
34     private AlertDialog mDialog;
35     private EditText mPromptText;
36     private int mSystemUiFlag;
37     private View mDecorView;
38     private XWalkViewInternal mXWalkView;
39     private boolean mOriginalFullscreen;
40     private boolean mOriginalForceNotFullscreen;
41
42     /**
43      * Constructor.
44      * @param view the owner XWalkViewInternal instance.
45      * @since 1.0
46      */
47     @XWalkAPI
48     public XWalkUIClientInternal(XWalkViewInternal view) {
49         mContext = view.getContext();
50         mDecorView = view.getActivity().getWindow().getDecorView();
51         if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) {
52             mSystemUiFlag = View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
53                     View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
54                     View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
55         }
56         mXWalkView = view;
57         initResources();
58     }
59
60     private void initResources() {
61         if (mJSAlertTitle != null) return;
62         mJSAlertTitle = mContext.getString(R.string.js_alert_title);
63         mJSConfirmTitle = mContext.getString(R.string.js_confirm_title);
64         mJSPromptTitle = mContext.getString(R.string.js_prompt_title);
65         mOKButton = mContext.getString(android.R.string.ok);
66         mCancelButton = mContext.getString(android.R.string.cancel);
67     }
68
69     /**
70      * Request display and focus for this XWalkViewInternal.
71      * @param view the owner XWalkViewInternal instance.
72      * @since 1.0
73      */
74     @XWalkAPI
75     public void onRequestFocus(XWalkViewInternal view) {
76     }
77
78     /**
79      * Notify the client to close the given XWalkViewInternal.
80      * @param view the owner XWalkViewInternal instance.
81      * @since 1.0
82      */
83     @XWalkAPI
84     public void onJavascriptCloseWindow(XWalkViewInternal view) {
85         if (view != null && view.getActivity() != null) {
86             view.getActivity().finish();
87         }
88     }
89
90     /**
91      * The type of JavaScript modal dialog.
92      * @since 1.0
93      */
94     @XWalkAPI
95     public enum JavascriptMessageTypeInternal {
96         /** JavaScript alert dialog. */
97         JAVASCRIPT_ALERT,
98         /** JavaScript confirm dialog. */
99         JAVASCRIPT_CONFIRM,
100         /** JavaScript prompt dialog. */
101         JAVASCRIPT_PROMPT,
102         /** JavaScript dialog for a window-before-unload notification. */
103         JAVASCRIPT_BEFOREUNLOAD
104     }
105
106     /**
107      * Tell the client to display a prompt dialog to the user.
108      * @param view the owner XWalkViewInternal instance.
109      * @param type the type of JavaScript modal dialog.
110      * @param url the url of the web page which wants to show this dialog.
111      * @param message the message to be shown.
112      * @param defaultValue the default value string. Only valid for Prompt dialog.
113      * @param result the callback to handle the result from caller.
114      * @since 1.0
115      */
116     @XWalkAPI
117     public boolean onJavascriptModalDialog(XWalkViewInternal view, JavascriptMessageTypeInternal type,
118             String url, String message, String defaultValue, XWalkJavascriptResultInternal result) {
119         switch(type) {
120             case JAVASCRIPT_ALERT:
121                 return onJsAlert(view, url, message, result);
122             case JAVASCRIPT_CONFIRM:
123                 return onJsConfirm(view, url, message, result);
124             case JAVASCRIPT_PROMPT:
125                 return onJsPrompt(view, url, message, defaultValue, result);
126             case JAVASCRIPT_BEFOREUNLOAD:
127                 // Reuse onJsConfirm to show the dialog.
128                 return onJsConfirm(view, url, message, result);
129             default:
130                 break;
131         }
132         assert(false);
133         return false;
134     }
135
136     /**
137      * Tell the client to toggle fullscreen mode.
138      * @param view the owner XWalkViewInternal instance.
139      * @param enterFullscreen true if it has entered fullscreen mode.
140      * @since 1.0
141      */
142     @XWalkAPI
143     public void onFullscreenToggled(XWalkViewInternal view, boolean enterFullscreen) {
144         Activity activity = view.getActivity();
145         if (enterFullscreen) {
146             if ((activity.getWindow().getAttributes().flags &
147                     WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN) != 0) {
148                 mOriginalForceNotFullscreen = true;
149                 activity.getWindow().clearFlags(
150                         WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
151             } else {
152                 mOriginalForceNotFullscreen = false;
153             }
154             if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) {
155                 mSystemUiFlag = mDecorView.getSystemUiVisibility();
156                 mDecorView.setSystemUiVisibility(
157                         View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
158                         View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
159                         View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
160                         View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
161                         View.SYSTEM_UI_FLAG_FULLSCREEN |
162                         View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
163             } else {
164                 if ((activity.getWindow().getAttributes().flags &
165                         WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0) {
166                     mOriginalFullscreen = true;
167                 } else {
168                     mOriginalFullscreen = false;
169                     activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
170                 }
171             }
172         } else {
173             if (mOriginalForceNotFullscreen) {
174                 activity.getWindow().addFlags(
175                         WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
176             }
177             if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) {
178                 mDecorView.setSystemUiVisibility(mSystemUiFlag);
179             } else {
180                 // Clear the activity fullscreen flag.
181                 if (!mOriginalFullscreen) {
182                     activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
183                 }
184             }
185         }
186     }
187
188     /**
189      * Tell the client to open a file chooser.
190      * @param view the owner XWalkViewInternal instance.
191      * @param uploadFile the callback class to handle the result from caller. It MUST
192      *        be invoked in all cases. Leave it not invoked will block all following
193      *        requests to open file chooser.
194      * @param acceptType value of the 'accept' attribute of the input tag associated
195      *        with this file picker.
196      * @param capture value of the 'capture' attribute of the input tag associated
197      *        with this file picker
198      * @since 1.0
199      */
200     @XWalkAPI
201     public void openFileChooser(XWalkViewInternal view, ValueCallback<Uri> uploadFile,
202             String acceptType, String capture) {
203         uploadFile.onReceiveValue(null);
204     }
205
206     /**
207      * Notify the client that the scale applied to the XWalkViewInternal has changed.
208      * @param view the owner XWalkViewInternal instance.
209      * @param oldScale the old scale before scaling.
210      * @param newScale the current scale factor after scaling.
211      * @since 1.0
212      */
213     @XWalkAPI
214     public void onScaleChanged(XWalkViewInternal view, float oldScale, float newScale) {
215     }
216
217     /**
218      * Give the host application a chance to handle the key event synchronously.
219      * e.g. menu shortcut key events need to be filtered this way. If return
220      * true, XWalkViewInternal will not handle the key event. If return false, XWalkViewInternal
221      * will always handle the key event, so none of the super in the view chain
222      * will see the key event. The default behavior returns false.
223      *
224      * @param view The XWalkViewInternal that is initiating the callback.
225      * @param event The key event.
226      * @return True if the host application wants to handle the key event
227      *         itself, otherwise return false
228      *
229      * @since 2.1
230      */
231     @XWalkAPI
232     public boolean shouldOverrideKeyEvent(XWalkViewInternal view, KeyEvent event) {
233         return false;
234     }
235
236     /**
237      * Notify the host application that a key was not handled by the XWalkViewInternal.
238      * Except system keys, XWalkViewInternal always consumes the keys in the normal flow
239      * or if shouldOverrideKeyEvent returns true. This is called asynchronously
240      * from where the key is dispatched. It gives the host application a chance
241      * to handle the unhandled key events.
242      *
243      * @param view The XWalkViewInternal that is initiating the callback.
244      * @param event The key event.
245      *
246      * @since 2.1
247      */
248     @XWalkAPI
249     public void onUnhandledKeyEvent(XWalkViewInternal view, KeyEvent event) {
250     }
251
252     /**
253      * Notify the host application of a change in the document title.
254      * @param view The XWalkViewInternal that initiated the callback.
255      * @param title A String containing the new title of the document.
256      * @since 2.1
257      */
258     @XWalkAPI
259     public void onReceivedTitle(XWalkViewInternal view, String title) {
260     }
261
262
263     /**
264      * The status when a page stopped loading
265      * @since 2.1
266      */
267     @XWalkAPI
268     public enum LoadStatusInternal {
269         /** Loading finished. */
270         FINISHED,
271         /** Loading failed. */
272         FAILED,
273         /** Loading cancelled by user. */
274         CANCELLED
275     }
276
277     /**
278      * Notify the host application that a page has started loading. This method
279      * is called once for each main frame load so a page with iframes or
280      * framesets will call onPageLoadStarted one time for the main frame. This also
281      * means that onPageLoadStarted will not be called when the contents of an
282      * embedded frame changes, i.e. clicking a link whose target is an iframe.
283      *
284      * @param view The XWalkViewInternal that is initiating the callback.
285      * @param url The url to be loaded.
286      *
287      * @since 2.1
288      */
289     @XWalkAPI
290     public void onPageLoadStarted(XWalkViewInternal view, String url) {
291     }
292
293     /**
294      * Notify the host application that a page has stopped loading. This method
295      * is called only for main frame. When onPageLoadStopped() is called, the
296      * rendering picture may not be updated yet.
297      *
298      * @param view The XWalkViewInternal that is initiating the callback.
299      * @param url The url of the page.
300      * @param status The status when the page stopped loading.
301      *
302      * @since 2.1
303      */
304     @XWalkAPI
305     public void onPageLoadStopped(XWalkViewInternal view, String url, LoadStatusInternal status) {
306     }
307
308     private boolean onJsAlert(XWalkViewInternal view, String url, String message,
309             XWalkJavascriptResultInternal result) {
310         final XWalkJavascriptResultInternal fResult = result;
311         AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(mContext);
312         dialogBuilder.setTitle(mJSAlertTitle)
313                 .setMessage(message)
314                 .setCancelable(true)
315                 .setPositiveButton(mOKButton, new DialogInterface.OnClickListener() {
316                     @Override
317                     public void onClick(DialogInterface dialog, int which) {
318                         fResult.confirm();
319                         dialog.dismiss();
320                     }
321                 })
322                 .setOnCancelListener(new DialogInterface.OnCancelListener() {
323                     @Override
324                     public void onCancel(DialogInterface dialog) {
325                         fResult.cancel();
326                     }
327                 });
328         mDialog = dialogBuilder.create();
329         mDialog.show();
330         return false;
331     }
332
333     private boolean onJsConfirm(XWalkViewInternal view, String url, String message,
334             XWalkJavascriptResultInternal result) {
335         final XWalkJavascriptResultInternal fResult = result;
336         AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(mContext);
337         dialogBuilder.setTitle(mJSConfirmTitle)
338                 .setMessage(message)
339                 .setCancelable(true)
340                 .setPositiveButton(mOKButton, new DialogInterface.OnClickListener() {
341                     @Override
342                     public void onClick(DialogInterface dialog, int which) {
343                         fResult.confirm();
344                         dialog.dismiss();
345                     }
346                 })
347                 // Need to implement 'onClick' and call the dialog.cancel. Otherwise, the
348                 // UI will be locked.
349                 .setNegativeButton(mCancelButton, new DialogInterface.OnClickListener() {
350                     @Override
351                     public void onClick(DialogInterface dialog, int which) {
352                         // This will call OnCancelLisitener.onCancel().
353                         dialog.cancel();
354                     }
355                 })
356                 .setOnCancelListener(new DialogInterface.OnCancelListener() {
357                     @Override
358                     public void onCancel(DialogInterface dialog) {
359                         fResult.cancel();
360                     }
361                 });
362         mDialog = dialogBuilder.create();
363         mDialog.show();
364         return false;
365     }
366
367     private boolean onJsPrompt(XWalkViewInternal view, String url, String message,
368             String defaultValue, XWalkJavascriptResultInternal result) {
369         final XWalkJavascriptResultInternal fResult = result;
370         AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(mContext);
371         dialogBuilder.setTitle(mJSPromptTitle)
372                 .setMessage(message)
373                 .setPositiveButton(mOKButton, new DialogInterface.OnClickListener() {
374                     @Override
375                     public void onClick(DialogInterface dialog, int which) {
376                         fResult.confirmWithResult(mPromptText.getText().toString());
377                         dialog.dismiss();
378                     }
379                 })
380                 // Need to implement 'onClick' and call the dialog.cancel. Otherwise, the
381                 // UI will be locked.
382                 .setNegativeButton(mCancelButton, new DialogInterface.OnClickListener() {
383                     @Override
384                     public void onClick(DialogInterface dialog, int which) {
385                         // This will call OnCancelLisitener.onCancel().
386                         dialog.cancel();
387                     }
388                 })
389                 .setOnCancelListener(new DialogInterface.OnCancelListener() {
390                     @Override
391                     public void onCancel(DialogInterface dialog) {
392                         fResult.cancel();
393                     }
394                 });
395         mPromptText = new EditText(mContext);
396         mPromptText.setVisibility(View.VISIBLE);
397         mPromptText.setText(defaultValue);
398         mPromptText.selectAll();
399
400         dialogBuilder.setView(mPromptText);
401         mDialog = dialogBuilder.create();
402         mDialog.show();
403         return false;
404     }
405 }