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.
5 package org.xwalk.core.internal;
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;
21 * This class notifies the embedder UI events/callbacks.
23 @XWalkAPI(createExternally = true)
24 public class XWalkUIClientInternal {
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;
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;
44 * @param view the owner XWalkViewInternal instance.
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;
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);
70 * Request display and focus for this XWalkViewInternal.
71 * @param view the owner XWalkViewInternal instance.
75 public void onRequestFocus(XWalkViewInternal view) {
79 * Notify the client to close the given XWalkViewInternal.
80 * @param view the owner XWalkViewInternal instance.
84 public void onJavascriptCloseWindow(XWalkViewInternal view) {
85 if (view != null && view.getActivity() != null) {
86 view.getActivity().finish();
91 * The type of JavaScript modal dialog.
95 public enum JavascriptMessageTypeInternal {
96 /** JavaScript alert dialog. */
98 /** JavaScript confirm dialog. */
100 /** JavaScript prompt dialog. */
102 /** JavaScript dialog for a window-before-unload notification. */
103 JAVASCRIPT_BEFOREUNLOAD
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.
117 public boolean onJavascriptModalDialog(XWalkViewInternal view, JavascriptMessageTypeInternal type,
118 String url, String message, String defaultValue, XWalkJavascriptResultInternal result) {
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);
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.
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);
152 mOriginalForceNotFullscreen = false;
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);
164 if ((activity.getWindow().getAttributes().flags &
165 WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0) {
166 mOriginalFullscreen = true;
168 mOriginalFullscreen = false;
169 activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
173 if (mOriginalForceNotFullscreen) {
174 activity.getWindow().addFlags(
175 WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
177 if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) {
178 mDecorView.setSystemUiVisibility(mSystemUiFlag);
180 // Clear the activity fullscreen flag.
181 if (!mOriginalFullscreen) {
182 activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
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
201 public void openFileChooser(XWalkViewInternal view, ValueCallback<Uri> uploadFile,
202 String acceptType, String capture) {
203 uploadFile.onReceiveValue(null);
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.
214 public void onScaleChanged(XWalkViewInternal view, float oldScale, float newScale) {
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.
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
232 public boolean shouldOverrideKeyEvent(XWalkViewInternal view, KeyEvent event) {
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.
243 * @param view The XWalkViewInternal that is initiating the callback.
244 * @param event The key event.
249 public void onUnhandledKeyEvent(XWalkViewInternal view, KeyEvent event) {
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.
259 public void onReceivedTitle(XWalkViewInternal view, String title) {
264 * The status when a page stopped loading
268 public enum LoadStatusInternal {
269 /** Loading finished. */
271 /** Loading failed. */
273 /** Loading cancelled by user. */
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.
284 * @param view The XWalkViewInternal that is initiating the callback.
285 * @param url The url to be loaded.
290 public void onPageLoadStarted(XWalkViewInternal view, String url) {
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.
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.
305 public void onPageLoadStopped(XWalkViewInternal view, String url, LoadStatusInternal status) {
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)
315 .setPositiveButton(mOKButton, new DialogInterface.OnClickListener() {
317 public void onClick(DialogInterface dialog, int which) {
322 .setOnCancelListener(new DialogInterface.OnCancelListener() {
324 public void onCancel(DialogInterface dialog) {
328 mDialog = dialogBuilder.create();
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)
340 .setPositiveButton(mOKButton, new DialogInterface.OnClickListener() {
342 public void onClick(DialogInterface dialog, int which) {
347 // Need to implement 'onClick' and call the dialog.cancel. Otherwise, the
348 // UI will be locked.
349 .setNegativeButton(mCancelButton, new DialogInterface.OnClickListener() {
351 public void onClick(DialogInterface dialog, int which) {
352 // This will call OnCancelLisitener.onCancel().
356 .setOnCancelListener(new DialogInterface.OnCancelListener() {
358 public void onCancel(DialogInterface dialog) {
362 mDialog = dialogBuilder.create();
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)
373 .setPositiveButton(mOKButton, new DialogInterface.OnClickListener() {
375 public void onClick(DialogInterface dialog, int which) {
376 fResult.confirmWithResult(mPromptText.getText().toString());
380 // Need to implement 'onClick' and call the dialog.cancel. Otherwise, the
381 // UI will be locked.
382 .setNegativeButton(mCancelButton, new DialogInterface.OnClickListener() {
384 public void onClick(DialogInterface dialog, int which) {
385 // This will call OnCancelLisitener.onCancel().
389 .setOnCancelListener(new DialogInterface.OnCancelListener() {
391 public void onCancel(DialogInterface dialog) {
395 mPromptText = new EditText(mContext);
396 mPromptText.setVisibility(View.VISIBLE);
397 mPromptText.setText(defaultValue);
398 mPromptText.selectAll();
400 dialogBuilder.setView(mPromptText);
401 mDialog = dialogBuilder.create();