/* * Copyright (C) 2010 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package com.googlecode.android_scripting.facade.ui; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicBoolean; import org.json.JSONArray; import org.json.JSONException; import python.button.FancyButton; import python.gif.GifDrawable; import python.gif.GifImageView; import python.menu.RayMenu; import python.titanic.Titanic; import python.titanic.TitanicTextView; import python.titanic.Typefaces; import android.annotation.SuppressLint; import android.app.ProgressDialog; import android.app.Service; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Build; import android.os.Handler; import android.util.AndroidRuntimeException; import android.view.ContextMenu; import android.view.ContextMenu.ContextMenuInfo; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; import android.widget.ImageView; import android.widget.Toast; import com.googlecode.android_scripting.BaseApplication; import com.googlecode.android_scripting.FileUtils; import com.googlecode.android_scripting.FutureActivityTaskExecutor; import com.googlecode.android_scripting.facade.EventFacade; import com.googlecode.android_scripting.facade.FacadeManager; import com.googlecode.android_scripting.interpreter.html.HtmlActivityTask; import com.googlecode.android_scripting.interpreter.html.HtmlInterpreter; import com.googlecode.android_scripting.jsonrpc.RpcReceiver; import com.googlecode.android_scripting.rpc.Rpc; import com.googlecode.android_scripting.rpc.RpcDefault; import com.googlecode.android_scripting.rpc.RpcOptional; import com.googlecode.android_scripting.rpc.RpcParameter; import de.keyboardsurfer.mobile.app.android.widget.crouton.Crouton; import de.keyboardsurfer.mobile.app.android.widget.crouton.Style; /** * User Interface Facade. <br> * <br> * <b>Usage Notes</b><br> * <br> * The UI facade provides access to a selection of dialog boxes for general user * interaction, and also hosts the {@link #webViewShow} call which allows * interactive use of html pages.<br> * The general use of the dialog functions is as follows:<br> * <ol> * <li>Create a dialog using one of the following calls: * <ul> * <li>{@link #dialogCreateInput} * <li>{@link #dialogCreateAlert} * <li>{@link #dialogCreateDatePicker} * <li>{@link #dialogCreateHorizontalProgress} * <li>{@link #dialogCreatePassword} * <li>{@link #dialogCreateSeekBar} * <li>{@link #dialogCreateSpinnerProgress} * </ul> * <li>Set additional features to your dialog * <ul> * <li>{@link #dialogSetItems} Set a list of items. Used like a menu. * <li>{@link #dialogSetMultiChoiceItems} Set a multichoice list of items. * <li>{@link #dialogSetSingleChoiceItems} Set a single choice list of items. * <li>{@link #dialogSetPositiveButtonText} * <li>{@link #dialogSetNeutralButtonText} * <li>{@link #dialogSetNegativeButtonText} * <li>{@link #dialogSetMaxProgress} Set max progress for your progress bar. * </ul> * <li>Display the dialog using {@link #dialogShow} * <li>Update dialog information if needed * <ul> * <li>{@link #dialogSetCurrentProgress} * </ul> * <li>Get the results * <ul> * <li>Using {@link #dialogGetResponse}, which will wait until the user performs * an action to close the dialog box, or * <li>Use eventPoll to wait for a "dialog" event. * <li>You can find out which list items were selected using * {@link #dialogGetSelectedItems}, which returns an array of numeric indices to * your list. For a single choice list, there will only ever be one of these. * </ul> * <li>Once done, use {@link #dialogDismiss} to remove the dialog. * </ol> * <br> * You can also manipulate menu options. The menu options are available for both * {@link #dialogShow} and {@link #fullShow}. * <ul> * <li>{@link #clearOptionsMenu} * <li>{@link #addOptionsMenuItem} * </ul> * <br> * <b>Some notes:</b><br> * Not every dialogSet function is relevant to every dialog type, ie, * dialogSetMaxProgress obviously only applies to dialogs created with a * progress bar. Also, an Alert Dialog may have a message or items, not both. If * you set both, items will take priority.<br> * In addition to the above functions, {@link #dialogGetInput} and * {@link #dialogGetPassword} are convenience functions that create, display and * return the relevant dialogs in one call.<br> * There is only ever one instance of a dialog. Any dialogCreate call will cause * the existing dialog to be destroyed. * * @author MeanEYE.rcf (meaneye.rcf@gmail.com) Change By ざ凍結の→愛 892768447@qq.com */ @SuppressLint("NewApi") public class UiFacade extends RpcReceiver { // This value should not be used for menu groups outside this class. private static final int MENU_GROUP_ID = Integer.MAX_VALUE; private final Service mService; private final FutureActivityTaskExecutor mTaskQueue; private DialogTask mDialogTask; private FullScreenTask mFullScreenTask; private final List<UiMenuItem> mContextMenuItems; private final List<UiMenuItem> mOptionsMenuItems; // RayMenu 菜单 private RayMenu rayMenu; private final List<UiArcMenuItem> mRayMenuItems; private final AtomicBoolean mRMenuUpdated; private final AtomicBoolean mMenuUpdated; // 数字进度条 // public Map<String, NumberProgressBar> npb; private final Handler mHandler; private final EventFacade mEventFacade; private List<Integer> mOverrideKeys = Collections .synchronizedList(new ArrayList<Integer>()); public UiFacade(FacadeManager manager) { super(manager); mService = manager.getService(); mTaskQueue = ((BaseApplication) mService.getApplication()) .getTaskExecutor(); mHandler = new Handler(mService.getMainLooper()); mContextMenuItems = new CopyOnWriteArrayList<UiMenuItem>(); mRayMenuItems = new CopyOnWriteArrayList<UiArcMenuItem>(); mOptionsMenuItems = new CopyOnWriteArrayList<UiMenuItem>(); mEventFacade = manager.getReceiver(EventFacade.class); mMenuUpdated = new AtomicBoolean(false); mRMenuUpdated = new AtomicBoolean(false); // npb = new HashMap<String, NumberProgressBar>(); } // @Rpc(description = "Create a number progress.") // public void createNumberProgress( // @RpcParameter(name = "id", description = "The NumberProgressBar Id") // final String id, // @RpcParameter(name = "maximum progress") @RpcDefault("100") final Integer // max, // @RpcParameter(name = "textSize") @RpcOptional final String textSize, // @RpcParameter(name = "textColor") @RpcOptional final String textColor, // @RpcParameter(name = "barColor") @RpcOptional final String barColor, // @RpcParameter(name = "progressColor") @RpcOptional final String // progressColor) // throws Exception { // if (mFullScreenTask == null) { // throw new RuntimeException("No screen displayed."); // } // final View v = mFullScreenTask.getViewByName(id); // if (v == null) { // throw new RuntimeException("No Found The Id: " + id); // } // mHandler.post(new Runnable() { // public void run() { // NumberProgressBar np = npb.get(id); // if (np == null) {// 未找到则创建一个 // np = (NumberProgressBar) v; // npb.put(id, np); // } // np.setMax(max); // if (textSize != null) { // try { // np.setProgressTextSize(Float.parseFloat(textSize)); // } catch (Exception e) { // e.printStackTrace(); // } // } // try { // if (textColor != null) { // np.setProgressTextColor(Color.parseColor(textColor)); // } // } catch (Exception e) { // e.printStackTrace(); // } // try { // if (barColor != null) { // np.setUnreachedBarColor(Color.parseColor(barColor)); // } // } catch (Exception e) { // e.printStackTrace(); // } // try { // if (progressColor != null) { // np.setReachedBarColor(Color.parseColor(progressColor)); // } // } catch (Exception e) { // e.printStackTrace(); // } // } // }); // } /** * For inputType, see <a href= * "http://developer.android.com/reference/android/R.styleable.html#TextView_inputType" * >InputTypes</a>. Some useful ones are text, number, and textUri. Multiple * flags can be supplied, seperated by "|", ie: "textUri|textAutoComplete" */ @Rpc(description = "Create a text input dialog.") public void dialogCreateInput( @RpcParameter(name = "title", description = "title of the input box") @RpcDefault("Value") final String title, @RpcParameter(name = "message", description = "message to display above the input box") @RpcDefault("Please enter value:") final String message, @RpcParameter(name = "defaultText", description = "text to insert into the input box") @RpcOptional final String text, @RpcParameter(name = "inputType", description = "type of input data, ie number or text") @RpcOptional final String inputType) throws InterruptedException { dialogDismiss(); mDialogTask = new AlertDialogTask(title, message); ((AlertDialogTask) mDialogTask).setTextInput(text); if (inputType != null) { ((AlertDialogTask) mDialogTask).setEditInputType(inputType); } } @Rpc(description = "Create a password input dialog.") public void dialogCreatePassword( @RpcParameter(name = "title", description = "title of the input box") @RpcDefault("Password") final String title, @RpcParameter(name = "message", description = "message to display above the input box") @RpcDefault("Please enter password:") final String message) { dialogDismiss(); mDialogTask = new AlertDialogTask(title, message); ((AlertDialogTask) mDialogTask).setPasswordInput(); } /** * The result is the user's input, or None (null) if cancel was hit. <br> * Example (python) * * <pre> * import android * droid=android.Android() * * print droid.dialogGetInput("Title","Message","Default").result * </pre> * */ @SuppressWarnings("unchecked") @Rpc(description = "Queries the user for a text input.") public String dialogGetInput( @RpcParameter(name = "title", description = "title of the input box") @RpcDefault("Value") final String title, @RpcParameter(name = "message", description = "message to display above the input box") @RpcDefault("Please enter value:") final String message, @RpcParameter(name = "defaultText", description = "text to insert into the input box") @RpcOptional final String text) throws InterruptedException { dialogCreateInput(title, message, text, "text"); dialogSetNegativeButtonText("Cancel"); dialogSetPositiveButtonText("Ok"); dialogShow(); Map<String, Object> response = (Map<String, Object>) dialogGetResponse(); if ("positive".equals(response.get("which"))) { return (String) response.get("value"); } else { return null; } } @SuppressWarnings("unchecked") @Rpc(description = "Queries the user for a password.") public String dialogGetPassword( @RpcParameter(name = "title", description = "title of the password box") @RpcDefault("Password") final String title, @RpcParameter(name = "message", description = "message to display above the input box") @RpcDefault("Please enter password:") final String message) throws InterruptedException { dialogCreatePassword(title, message); dialogSetNegativeButtonText("Cancel"); dialogSetPositiveButtonText("Ok"); dialogShow(); Map<String, Object> response = (Map<String, Object>) dialogGetResponse(); if ("positive".equals(response.get("which"))) { return (String) response.get("value"); } else { return null; } } @Rpc(description = "Create a spinner progress dialog.") public void dialogCreateSpinnerProgress( @RpcParameter(name = "title") @RpcOptional String title, @RpcParameter(name = "message") @RpcOptional String message, @RpcParameter(name = "maximum progress") @RpcDefault("100") Integer max) { dialogDismiss(); // Dismiss any existing dialog. mDialogTask = new ProgressDialogTask(ProgressDialog.STYLE_SPINNER, max, title, message, true); } @Rpc(description = "Create a horizontal progress dialog.") public void dialogCreateHorizontalProgress( @RpcParameter(name = "title") @RpcOptional String title, @RpcParameter(name = "message") @RpcOptional String message, @RpcParameter(name = "maximum progress") @RpcDefault("100") Integer max) { dialogDismiss(); // Dismiss any existing dialog. mDialogTask = new ProgressDialogTask(ProgressDialog.STYLE_HORIZONTAL, max, title, message, true); } /** * <b>Example (python)</b> * * <pre> * import android * droid=android.Android() * droid.dialogCreateAlert("I like swords.","Do you like swords?") * droid.dialogSetPositiveButtonText("Yes") * droid.dialogSetNegativeButtonText("No") * droid.dialogShow() * response=droid.dialogGetResponse().result * droid.dialogDismiss() * if response.has_key("which"): * result=response["which"] * if result=="positive": * print "Yay! I like swords too!" * elif result=="negative": * print "Oh. How sad." * elif response.has_key("canceled"): # Yes, I know it's mispelled. * print "You can't even make up your mind?" * else: * print "Unknown response=",response * * print "Done" * </pre> */ @Rpc(description = "Create alert dialog.") public void dialogCreateAlert( @RpcParameter(name = "title") @RpcOptional String title, @RpcParameter(name = "message") @RpcOptional String message) { dialogDismiss(); // Dismiss any existing dialog. mDialogTask = new AlertDialogTask(title, message); } /** * Will produce "dialog" events on change, containing: * <ul> * <li>"progress" - Position chosen, between 0 and max * <li>"which" = "seekbar" * <li>"fromuser" = true/false change is from user input * </ul> * Response will contain a "progress" element. */ @Rpc(description = "Create seek bar dialog.") public void dialogCreateSeekBar( @RpcParameter(name = "starting value") @RpcDefault("50") Integer progress, @RpcParameter(name = "maximum value") @RpcDefault("100") Integer max, @RpcParameter(name = "title") String title, @RpcParameter(name = "message") String message) { dialogDismiss(); // Dismiss any existing dialog. mDialogTask = new SeekBarDialogTask(progress, max, title, message); } @Rpc(description = "Create time picker dialog.") public void dialogCreateTimePicker( @RpcParameter(name = "hour") @RpcDefault("0") Integer hour, @RpcParameter(name = "minute") @RpcDefault("0") Integer minute, @RpcParameter(name = "is24hour", description = "Use 24 hour clock") @RpcDefault("false") Boolean is24hour) { dialogDismiss(); // Dismiss any existing dialog. mDialogTask = new TimePickerDialogTask(hour, minute, is24hour); } @Rpc(description = "Create date picker dialog.") public void dialogCreateDatePicker( @RpcParameter(name = "year") @RpcDefault("1970") Integer year, @RpcParameter(name = "month") @RpcDefault("1") Integer month, @RpcParameter(name = "day") @RpcDefault("1") Integer day) { dialogDismiss(); // Dismiss any existing dialog. mDialogTask = new DatePickerDialogTask(year, month, day); } @Rpc(description = "Dismiss dialog.") public void dialogDismiss() { if (mDialogTask != null) { mDialogTask.dismissDialog(); mDialogTask = null; } } @Rpc(description = "Show dialog.") public void dialogShow() throws InterruptedException { if (mDialogTask != null && mDialogTask.getDialog() == null) { mDialogTask.setEventFacade(mEventFacade); mTaskQueue.execute(mDialogTask); mDialogTask.getShowLatch().await(); } else { throw new RuntimeException("No dialog to show."); } } @Rpc(description = "Set progress dialog current value.") public void dialogSetCurrentProgress( @RpcParameter(name = "current") Integer current) { if (mDialogTask != null && mDialogTask instanceof ProgressDialogTask) { ((ProgressDialog) mDialogTask.getDialog()).setProgress(current); } else { throw new RuntimeException("No valid dialog to assign value to."); } } @Rpc(description = "Set progress dialog maximum value.") public void dialogSetMaxProgress(@RpcParameter(name = "max") Integer max) { if (mDialogTask != null && mDialogTask instanceof ProgressDialogTask) { ((ProgressDialog) mDialogTask.getDialog()).setMax(max); } else { throw new RuntimeException( "No valid dialog to set maximum value of."); } } @Rpc(description = "Set alert dialog positive button text.") public void dialogSetPositiveButtonText( @RpcParameter(name = "text") String text) { if (mDialogTask != null && mDialogTask instanceof AlertDialogTask) { ((AlertDialogTask) mDialogTask).setPositiveButtonText(text); } else if (mDialogTask != null && mDialogTask instanceof SeekBarDialogTask) { ((SeekBarDialogTask) mDialogTask).setPositiveButtonText(text); } else { throw new AndroidRuntimeException("No dialog to add button to."); } } @Rpc(description = "Set alert dialog button text.") public void dialogSetNegativeButtonText( @RpcParameter(name = "text") String text) { if (mDialogTask != null && mDialogTask instanceof AlertDialogTask) { ((AlertDialogTask) mDialogTask).setNegativeButtonText(text); } else if (mDialogTask != null && mDialogTask instanceof SeekBarDialogTask) { ((SeekBarDialogTask) mDialogTask).setNegativeButtonText(text); } else { throw new AndroidRuntimeException("No dialog to add button to."); } } @Rpc(description = "Set alert dialog button text.") public void dialogSetNeutralButtonText( @RpcParameter(name = "text") String text) { if (mDialogTask != null && mDialogTask instanceof AlertDialogTask) { ((AlertDialogTask) mDialogTask).setNeutralButtonText(text); } else { throw new AndroidRuntimeException("No dialog to add button to."); } } // TODO(damonkohler): Make RPC layer translate between JSONArray and // List<Object>. /** * This effectively creates list of options. Clicking on an item will * immediately return an "item" element, which is the index of the selected * item. */ @Rpc(description = "Set alert dialog list items.") public void dialogSetItems(@RpcParameter(name = "items") JSONArray items) { if (mDialogTask != null && mDialogTask instanceof AlertDialogTask) { ((AlertDialogTask) mDialogTask).setItems(items); } else { throw new AndroidRuntimeException("No dialog to add list to."); } } /** * This creates a list of radio buttons. You can select one item out of the * list. A response will not be returned until the dialog is closed, either * with the Cancel key or a button (positive/negative/neutral). Use * {@link #dialogGetSelectedItems()} to find out what was selected. */ @Rpc(description = "Set dialog single choice items and selected item.") public void dialogSetSingleChoiceItems( @RpcParameter(name = "items") JSONArray items, @RpcParameter(name = "selected", description = "selected item index") @RpcDefault("0") Integer selected) { if (mDialogTask != null && mDialogTask instanceof AlertDialogTask) { ((AlertDialogTask) mDialogTask).setSingleChoiceItems(items, selected); } else { throw new AndroidRuntimeException("No dialog to add list to."); } } /** * This creates a list of check boxes. You can select multiple items out of * the list. A response will not be returned until the dialog is closed, * either with the Cancel key or a button (positive/negative/neutral). Use * {@link #dialogGetSelectedItems()} to find out what was selected. */ @Rpc(description = "Set dialog multiple choice items and selection.") public void dialogSetMultiChoiceItems( @RpcParameter(name = "items") JSONArray items, @RpcParameter(name = "selected", description = "list of selected items") @RpcOptional JSONArray selected) throws JSONException { if (mDialogTask != null && mDialogTask instanceof AlertDialogTask) { ((AlertDialogTask) mDialogTask) .setMultiChoiceItems(items, selected); } else { throw new AndroidRuntimeException("No dialog to add list to."); } } @Rpc(description = "Returns dialog response.") public Object dialogGetResponse() { try { return mDialogTask.getResult(); } catch (Exception e) { throw new AndroidRuntimeException(e); } } @Rpc(description = "This method provides list of items user selected.", returns = "Selected items") public Set<Integer> dialogGetSelectedItems() { if (mDialogTask != null && mDialogTask instanceof AlertDialogTask) { return ((AlertDialogTask) mDialogTask).getSelectedItems(); } else { throw new AndroidRuntimeException("No dialog to add list to."); } } /** * See <a * href=http://code.google.com/p/android-scripting/wiki/UsingWebView>wiki * page</a> for more detail. */ @Rpc(description = "Display a WebView with the given URL.") public void webViewShow( @RpcParameter(name = "url") String url, @RpcParameter(name = "wait", description = "block until the user exits the WebView") @RpcOptional Boolean wait) throws IOException { String jsonSrc = FileUtils.readFromAssetsFile(mService, HtmlInterpreter.JSON_FILE); String AndroidJsSrc = FileUtils.readFromAssetsFile(mService, HtmlInterpreter.ANDROID_JS_FILE); HtmlActivityTask task = new HtmlActivityTask(mManager, AndroidJsSrc, jsonSrc, url, false); mTaskQueue.execute(task); if (wait != null && wait) { try { task.getResult(); } catch (InterruptedException e) { throw new RuntimeException(e); } } } /** * Context menus are used primarily with {@link #webViewShow} */ @Rpc(description = "Adds a new item to context menu.") public void addContextMenuItem( @RpcParameter(name = "label", description = "label for this menu item") String label, @RpcParameter(name = "event", description = "event that will be generated on menu item click") String event, @RpcParameter(name = "eventData") @RpcOptional Object data) { mContextMenuItems.add(new UiMenuItem(label, event, data, null)); } /** * <b>Example (python)</b> * * <pre> * import android * droid=android.Android() * * droid.addOptionsMenuItem("Silly","silly",None,"star_on") * droid.addOptionsMenuItem("Sensible","sensible","I bet.","star_off") * droid.addOptionsMenuItem("Off","off",None,"ic_menu_revert") * * print "Hit menu to see extra options." * print "Will timeout in 10 seconds if you hit nothing." * * while True: # Wait for events from the menu. * response=droid.eventWait(10000).result * if response==None: * break * print response * if response["name"]=="off": * break * print "And done." * * </pre> */ @Rpc(description = "Adds a new item to options menu.") public void addOptionsMenuItem( @RpcParameter(name = "label", description = "label for this menu item") String label, @RpcParameter(name = "event", description = "event that will be generated on menu item click") String event, @RpcParameter(name = "eventData") @RpcOptional Object data, @RpcParameter(name = "iconName", description = "Android system menu icon, see http://developer.android.com/reference/android/R.drawable.html") @RpcOptional String iconName) { mOptionsMenuItems.add(new UiMenuItem(label, event, data, iconName)); mMenuUpdated.set(true); } @Rpc(description = "Adds a ray item to RayMenu.\n添加滑动菜单子项") public void addRayMenuItem( @RpcParameter(name = "event", description = "event that will be generated on menu item click\n点击时产生的事件名称") String event, @RpcParameter(name = "eventData\n事件内容") @RpcOptional Object data, @RpcParameter(name = "iconPath", description = "the icon path\n图标路径") String iconPath) { try { mRayMenuItems.add(new UiArcMenuItem(event, data, iconPath)); } catch (Exception e) { e.printStackTrace(); } } @Rpc(description = "App Msg Toast.\n消息提示") public void appMsg( @RpcParameter(name = "message", description = "The Toast Message\n消息提示") final String message, @RpcParameter(name = "level", description = "The Toast Level Are: 0-ALERT,1-CONFIRM,2-INFO\n提示级别: 0-错误提示,1-确认提示,2-一般提示") @RpcDefault("2") Integer level, @RpcParameter(name = "clear", description = "Remove all tips\n清除所有还未提示的内容") @RpcDefault("false") final Boolean clear) throws Exception { if (mFullScreenTask == null) { mHandler.post(new Runnable() { public void run() { Toast.makeText(mService, message, Toast.LENGTH_SHORT) .show(); } }); } else { final Crouton appMsg; switch (level) { case 0: appMsg = Crouton.makeText(mFullScreenTask.getActivity(), message, Style.ALERT); break; case 1: appMsg = Crouton.makeText(mFullScreenTask.getActivity(), message, Style.CONFIRM); break; case 2: appMsg = Crouton.makeText(mFullScreenTask.getActivity(), message, Style.INFO); break; default: appMsg = Crouton.makeText(mFullScreenTask.getActivity(), message, Style.INFO); break; } mHandler.post(new Runnable() { public void run() { try { if (clear) { Crouton.cancelAllCroutons(); } appMsg.show(); } catch (Exception e) { e.printStackTrace(); } } }); } } @Rpc(description = "Removes all items previously added to ray menu.\n清除所有ray菜单的子项") public void clearRayMenu() { mRayMenuItems.clear(); } @Rpc(description = "Removes all items previously added to context menu.") public void clearContextMenu() { mContextMenuItems.clear(); } @Rpc(description = "Removes all items previously added to options menu.") public void clearOptionsMenu() { mOptionsMenuItems.clear(); mMenuUpdated.set(true); } public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { for (UiMenuItem item : mContextMenuItems) { MenuItem menuItem = menu.add(item.mmTitle); menuItem.setOnMenuItemClickListener(item.mmListener); } } public boolean onPrepareOptionsMenu(Menu menu) { if (mMenuUpdated.getAndSet(false)) { menu.removeGroup(MENU_GROUP_ID); for (UiMenuItem item : mOptionsMenuItems) { MenuItem menuItem = menu.add(MENU_GROUP_ID, Menu.NONE, Menu.NONE, item.mmTitle); if (item.mmIcon != null) { menuItem.setIcon(mService.getResources().getIdentifier( item.mmIcon, "drawable", "android")); } menuItem.setOnMenuItemClickListener(item.mmListener); } return true; } return true; } /** * 显示ray菜单 * * @param id */ @Rpc(description = "Init RayMenu") public void fullShowRayMenu( @RpcParameter(name = "id", description = "id of layout widget\n显示raymenu菜单") String id) { if (mFullScreenTask == null) { throw new RuntimeException("No screen displayed."); } try { if (rayMenu == null) {// 只允许存在一个 View view = mFullScreenTask.getViewByName(id); if (view == null) { throw new RuntimeException("The RayMenu Id: +" + id + " Not Found."); } rayMenu = (RayMenu) view; } mRMenuUpdated.set(true); if (mRMenuUpdated.getAndSet(false)) { for (final UiArcMenuItem item : mRayMenuItems) { File file = new File(item.mmIcon); final ImageView image = new ImageView( mFullScreenTask.getActivity()); if (file.exists()) { Bitmap bm = BitmapFactory.decodeFile(file .getAbsolutePath()); image.setImageBitmap(bm); } else { image.setImageResource(android.R.drawable.ic_dialog_info); } mHandler.post(new Runnable() { public void run() { rayMenu.addItem(image, item.mmListener); } }); } } } catch (Exception e) { e.printStackTrace(); } } /** * See <a * href=http://code.google.com/p/android-scripting/wiki/FullScreenUI>wiki * page</a> for more detail. */ @Rpc(description = "Show Full Screen.") public List<String> fullShow( @RpcParameter(name = "xml", description = "String containing View layout Or Xml File Path") String layout, @RpcParameter(name = "title", description = "Activity Title") @RpcOptional String title) throws InterruptedException { if (mFullScreenTask != null) { // fullDismiss(); mFullScreenTask.setLayout(layout); if (title != null) { mFullScreenTask.setTitle(title); } } else { mFullScreenTask = new FullScreenTask(layout, title); mFullScreenTask.setEventFacade(mEventFacade); mFullScreenTask.setUiFacade(this); mFullScreenTask.setOverrideKeys(mOverrideKeys); mTaskQueue.execute(mFullScreenTask); mFullScreenTask.getShowLatch().await(); } return mFullScreenTask.mInflater.getErrors(); } @Rpc(description = "Dismiss Full Screen.") public void fullDismiss() { if (mFullScreenTask != null) { rayMenu = null; mFullScreenTask.finish(); mFullScreenTask = null; } } @Rpc(description = "Get Fullscreen Properties") public Map<String, Map<String, String>> fullQuery() { if (mFullScreenTask == null) { throw new RuntimeException("No screen displayed."); } return mFullScreenTask.getViewAsMap(); } @Rpc(description = "Get fullscreen properties for a specific widget") public Map<String, String> fullQueryDetail( @RpcParameter(name = "id", description = "id of layout widget") String id) { if (mFullScreenTask == null) { throw new RuntimeException("No screen displayed."); } return mFullScreenTask.getViewDetail(id); } @Rpc(description = "Set fullscreen widget property") public String fullSetProperty( @RpcParameter(name = "id", description = "id of layout widget") String id, @RpcParameter(name = "property", description = "name of property to set") String property, @RpcParameter(name = "value", description = "value to set property to") String value) { if (mFullScreenTask == null) { throw new RuntimeException("No screen displayed."); } return mFullScreenTask.setViewProperty(id, property, value); } @Rpc(description = "Attach a list to a fullscreen widget") public String fullSetList( @RpcParameter(name = "id", description = "id of layout widget") String id, @RpcParameter(name = "list", description = "List to set") JSONArray items) { if (mFullScreenTask == null) { throw new RuntimeException("No screen displayed."); } return mFullScreenTask.setList(id, items); } @Rpc(description = "Attach a Jlist to a fullscreen widget\n特效listview") public String fullSetJList( @RpcParameter(name = "id", description = "id of layout widget\n特效listview的id") String id, @RpcParameter(name = "list", description = "List to set\n列表数据") JSONArray items) { if (mFullScreenTask == null) { throw new RuntimeException("No screen displayed."); } return mFullScreenTask.setJList(id, items); } @Rpc(description = "Set the Full Screen Activity Title") public void fullSetTitle( @RpcParameter(name = "title", description = "Activity Title") String title) { if (mFullScreenTask == null) { throw new RuntimeException("No screen displayed."); } mFullScreenTask.setTitle(title); } /** * This will override the default behaviour of keys while in the fullscreen * mode. ie: * * <pre> * droid.fullKeyOverride([24,25],True) * </pre> * * This will override the default behaviour of the volume keys (codes 24 and * 25) so that they do not actually adjust the volume. <br> * Returns a list of currently overridden keycodes. */ @Rpc(description = "Override default key actions") public JSONArray fullKeyOverride( @RpcParameter(name = "keycodes", description = "List of keycodes to override") JSONArray keycodes, @RpcParameter(name = "enable", description = "Turn overriding or off") @RpcDefault(value = "true") Boolean enable) throws JSONException { for (int i = 0; i < keycodes.length(); i++) { int value = (int) keycodes.getLong(i); if (value > 0) { if (enable) { if (!mOverrideKeys.contains(value)) { mOverrideKeys.add(value); } } else { int index = mOverrideKeys.indexOf(value); if (index >= 0) { mOverrideKeys.remove(index); } } } } if (mFullScreenTask != null) { mFullScreenTask.setOverrideKeys(mOverrideKeys); } return new JSONArray(mOverrideKeys); } @Rpc(description = "set titanic text\n上升下降文字") public void setTitanicText( @RpcParameter(name = "id", description = "The TextView Id") String id, @RpcParameter(name = "usefont", description = "Use Satisfy-Regular.ttf\n是否使用字体") @RpcOptional @RpcDefault("true") Boolean font) throws Exception { if (mFullScreenTask == null) { throw new RuntimeException("No screen displayed."); } if (Build.VERSION.SDK_INT < 11) { throw new RuntimeException("This Api Need Sdk >= 11"); } View view = mFullScreenTask.getViewByName(id); if (view == null) { throw new RuntimeException("No Found The Id: " + id); } try { final TitanicTextView textView = (TitanicTextView) view; if (font != null) { if (font) { mHandler.post(new Runnable() { public void run() { textView.setTypeface(Typefaces.get( mService.getApplicationContext(), "Satisfy-Regular.ttf")); } }); } } mHandler.post(new Runnable() { public void run() { new Titanic().start(textView); } }); } catch (Exception e) { e.printStackTrace(); } } /** * 自定义按钮 * * @param id * @param defaultColor * @param text * @param textColor * @param textSize * @param textFont * @param radius * @param focusColor * @param fontIconSize * @param fontIconResource * @param iconPosition * @param borderColor * @param borderWidth * @throws Exception */ @Rpc(description = "set fancy button\n自定义按钮") public void setFancyButton( @RpcParameter(name = "id", description = "The FancyButton Id\n按钮id") String id, @RpcParameter(name = "defaultColor", description = "The Button DefaultColor\n按钮默认颜色") @RpcOptional final String defaultColor, @RpcParameter(name = "text", description = "Text\n按钮文本") @RpcOptional @RpcDefault("FancyButton") final String text, @RpcParameter(name = "textColor", description = "The Text Color\n文本颜色") @RpcOptional final String textColor, @RpcParameter(name = "textSize", description = "The Text Size\n文字大小") @RpcOptional final Integer textSize, @RpcParameter(name = "textFont", description = "The Text Font: robotothin.ttf,robotoregular.ttf,Satisfy-Regular.ttf\n文字字体") @RpcOptional final String textFont, @RpcParameter(name = "radius", description = "The Button Radius\n按钮圆角") @RpcOptional final Integer radius, @RpcParameter(name = "focusColor", description = "The Button FocusColor\n按钮焦点颜色") @RpcOptional final String focusColor, @RpcParameter(name = "fontIconSize", description = "The Button FontIconSize\n按钮字体大小") @RpcOptional final Integer fontIconSize, @RpcParameter(name = "fontIconResource", description = "The Button FontIconResource\n按钮字体图标") @RpcOptional final String fontIconResource, @RpcParameter(name = "iconPosition", description = "The Button IconPosition\n图标位置") @RpcOptional final String iconPosition, @RpcParameter(name = "borderColor", description = "The Button BorderColor\n边框颜色") @RpcOptional final String borderColor, @RpcParameter(name = "borderWidth", description = "The Button BorderWidth\n边框大小") @RpcOptional final Integer borderWidth) throws Exception { if (mFullScreenTask == null) { throw new RuntimeException("No screen displayed."); } View view = mFullScreenTask.getViewByName(id); if (view == null) { throw new RuntimeException("No Found The Id: " + id); } final FancyButton button; try { button = (FancyButton) view; } catch (Exception e) { throw new RuntimeException("This Button Isn't FancyButton"); } if (defaultColor != null) { mHandler.post(new Runnable() { public void run() { button.setBackgroundColor(mFullScreenTask.getInflater() .getColor(defaultColor)); } }); } if (text != null) { mHandler.post(new Runnable() { public void run() { button.setText(text); } }); } if (textColor != null) { mHandler.post(new Runnable() { public void run() { button.setTextColor(mFullScreenTask.getInflater().getColor( textColor)); } }); } if (textSize != null) { mHandler.post(new Runnable() { public void run() { button.setTextSize(textSize); } }); } if (textFont != null) { mHandler.post(new Runnable() { public void run() { button.setCustomTextFont(textFont); } }); } if (radius != null) { mHandler.post(new Runnable() { public void run() { button.setRadius(radius); } }); } if (focusColor != null) { mHandler.post(new Runnable() { public void run() { button.setFocusBackgroundColor(mFullScreenTask .getInflater().getColor(focusColor)); } }); } if (fontIconSize != null) { mHandler.post(new Runnable() { public void run() { button.setFontIconSize(fontIconSize); } }); } if (fontIconResource != null) { if (fontIconResource.startsWith("@string")) { mHandler.post(new Runnable() { public void run() { button.setIconResource(mService.getResources() .getString( mService.getResources().getIdentifier( fontIconResource.replace( "@string/", ""), "string", mService.getPackageName()))); } }); } else if (fontIconResource.indexOf("&#") > -1) { mHandler.post(new Runnable() { public void run() { button.setIconResource(fontIconResource); } }); } } if (iconPosition != null) {// left top right bottom mHandler.post(new Runnable() { public void run() { button.setIconPosition(mService.getResources() .getIdentifier(iconPosition, "id", mService.getPackageName())); } }); } if (borderColor != null) { mHandler.post(new Runnable() { public void run() { button.setBorderColor(mFullScreenTask.getInflater() .getColor(borderColor)); } }); } if (borderWidth != null) { mHandler.post(new Runnable() { public void run() { button.setBorderWidth(borderWidth); } }); } } @SuppressWarnings("deprecation") @Rpc(description = "Set Gif View\nGif动画控件") public void setGifView( @RpcParameter(name = "id", description = "The View Id") String id, @RpcParameter(name = "image", description = "The gif path\ngif文件路径") String path) throws Exception { if (mFullScreenTask == null) { throw new RuntimeException("No screen displayed."); } if (path == null) { throw new RuntimeException("No Found The Path: " + path); } View view = mFullScreenTask.getViewByName(id); if (view == null) { throw new RuntimeException("No Found The Id: " + id); } final GifImageView gifImageView; try { gifImageView = (GifImageView) view; } catch (Exception e) { throw new RuntimeException("This View Can't Use Gif"); } final GifDrawable gifDrawable; try { gifDrawable = new GifDrawable(new File(path)); if (gifDrawable != null) { mFullScreenTask.addGifTask(id, gifDrawable); if (Build.VERSION.SDK_INT >= 16) { mHandler.post(new Runnable() { public void run() { gifImageView.setBackground(gifDrawable); } }); } else { mHandler.post(new Runnable() { public void run() { gifImageView.setBackgroundDrawable(gifDrawable); } }); } if (!gifDrawable.isPlaying()) { mHandler.post(new Runnable() { public void run() { gifDrawable.start(); } }); } } } catch (IOException e) { e.printStackTrace(); } } /** * 设置数字进度条进度 * * @param id * @param current * @param hideText * @throws Exception */ // @Rpc(description = "Set the progress.") // public void setNumberProgress( // @RpcParameter(name = "id", description = "The NumberProgressBar Id") // String id, // @RpcParameter(name = "current", description = "current progress") final // Integer current, // @RpcParameter(name = "hideText", description = "nedd hide the text?") // @RpcDefault("false") final Boolean hideText) // throws Exception { // final NumberProgressBar np = npb.get(id); // if (np == null) {// 未找到 // throw new RuntimeException( // "The Id NumberProgressBar Not Found,Are You Create?"); // } // if (mFullScreenTask == null) { // throw new RuntimeException("No screen displayed."); // } // View v = mFullScreenTask.getViewByName(id); // if (v == null) { // throw new RuntimeException("No Found The Id: " + id); // } // if (hideText != null) { // mHandler.post(new Runnable() { // public void run() { // if (!hideText) { // np.setProgress(current); // } else { // np.setProgressTextVisibility(ProgressTextVisibility.Invisible); // } // } // }); // } // } @Override public void shutdown() { fullDismiss(); HtmlActivityTask.shutdown(); } private class UiMenuItem { private final String mmTitle; private final String mmEvent; private final Object mmEventData; private final String mmIcon; private final MenuItem.OnMenuItemClickListener mmListener; public UiMenuItem(String title, String event, Object data, String icon) { mmTitle = title; mmEvent = event; mmEventData = data; mmIcon = icon; mmListener = new MenuItem.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { mEventFacade.postEvent(mmEvent, mmEventData); return true; } }; } } private class UiArcMenuItem { private final String mmEvent; private final Object mmEventData; private final String mmIcon; private final OnClickListener mmListener; public UiArcMenuItem(String event, Object data, String icon) { mmEvent = event; mmEventData = data; mmIcon = icon; mmListener = new OnClickListener() { @Override public void onClick(View v) { mEventFacade.postEvent(mmEvent, mmEventData); } }; } } }