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