From f6aa57ddcb363ec0763872fa82776b17acc0bc93 Mon Sep 17 00:00:00 2001 From: "ruikai.qrk" Date: Wed, 4 Sep 2019 20:25:31 +0800 Subject: [PATCH 001/158] improve case edit experience, fix sleep bug --- .../cgutman/adblib/ByteQueueInputStream.java | 4 +- src/app/src/main/AndroidManifest.xml | 34 +-- .../actions/ImageCompareActionProvider.java | 8 +- .../actions/PerformanceActionProvider.java | 4 +- .../actions/RecordScreenActionProvider.java | 8 +- .../alipay/hulu/activity/BaseActivity.java | 41 ++- .../hulu/activity/BatchExecutionActivity.java | 34 ++- .../activity/BatchReplayResultActivity.java | 6 +- .../hulu/activity/CaseEditActivity.java | 10 +- .../hulu/activity/CaseParamEditActivity.java | 236 ++++++++++++++++++ .../activity/CaseReplayResultActivity.java | 4 +- .../alipay/hulu/activity/IndexActivity.java | 8 +- .../alipay/hulu/activity/InfoActivity.java | 2 +- .../alipay/hulu/activity/LicenseActivity.java | 2 +- .../alipay/hulu/activity/MyApplication.java | 3 +- .../hulu/activity/NewRecordActivity.java | 132 +++++----- .../hulu/activity/NewReplayListActivity.java | 3 +- .../hulu/activity/PerformanceActivity.java | 19 +- .../activity/PerformanceChartActivity.java | 4 +- .../alipay/hulu/activity/QRScanActivity.java | 7 + .../hulu/activity/RecordManageActivity.java | 7 +- .../hulu/activity/SettingsActivity.java | 202 +++++++++------ .../adapter/BatchExecutionListAdapter.java | 2 +- .../alipay/hulu/adapter/CaseStepAdapter.java | 111 +++++++- .../hulu/adapter/CaseStepMethodAdapter.java | 78 +++++- .../hulu/adapter/CaseStepNodeAdapter.java | 29 ++- .../alipay/hulu/adapter/ParamListAdapter.java | 86 +++++++ .../hulu/adapter/PerformStressAdapter.java | 14 +- .../hulu/adapter/ReplayListAdapter.java | 12 +- .../alipay/hulu/adapter/SoloBaseAdapter.java | 69 +++++ .../alipay/hulu/bean/AdvanceCaseSetting.java | 21 ++ .../com/alipay/hulu/bean/CaseParamBean.java | 66 +++++ .../alipay/hulu/bean/CaseRunningParam.java | 52 ++++ .../com/alipay/hulu/bean/CaseStepHolder.java | 33 +++ .../alipay/hulu/event/ScanSuccessEvent.java | 1 + .../hulu/fragment/BatchExecutionFragment.java | 10 +- .../hulu/fragment/CaseDescEditFragment.java | 62 ++++- .../fragment/CaseParamSeparateFragment.java | 204 +++++++++++++++ .../hulu/fragment/CaseParamUnionFragment.java | 191 ++++++++++++++ .../hulu/fragment/CaseStepEditFragment.java | 236 ++++++++++++++---- .../hulu/fragment/ReplayListFragment.java | 92 +++++-- .../hulu/fragment/ReplayStepFragment.java | 14 +- .../hulu/replay/AbstractStepProvider.java | 6 +- .../alipay/hulu/replay/BatchStepProvider.java | 4 +- .../hulu/replay/MultiParamStepProvider.java | 196 +++++++++++++++ .../hulu/replay/OperationStepProvider.java | 150 +++++++++-- .../hulu/replay/RepeatStepProvider.java | 4 +- .../hulu/screenRecord/RecordService.java | 14 +- .../screenRecord/RecorderConfigActivity.java | 20 +- .../hulu/service/CaseRecordManager.java | 139 ++++++----- .../hulu/service/CaseReplayManager.java | 38 +-- .../alipay/hulu/service/FloatWinService.java | 2 +- .../alipay/hulu/ui/TwoLevelSelectLayout.java | 8 +- .../com/alipay/hulu/upgrade/PatchRequest.java | 2 +- .../util/CaseAppendOperationProcessor.java | 85 +++++++ .../com/alipay/hulu/util/CaseReplayUtil.java | 82 ++++++ .../com/alipay/hulu/util/DialogUtils.java | 86 ++++++- .../alipay/hulu/util/FunctionSelectUtil.java | 154 +++++++----- .../res/drawable/accent_button_background.xml | 29 +++ .../main/res/drawable/accent_button_layer.xml | 18 ++ .../main/res/drawable/bg_template_list.xml | 37 +++ src/app/src/main/res/drawable/case_play.xml | 19 ++ .../src/main/res/drawable/case_step_copy.xml | 25 ++ .../src/main/res/drawable/case_step_paste.xml | 25 ++ .../main/res/drawable/case_step_select.xml | 25 ++ .../main/res/layout-v23/item_tools_grid.xml | 3 +- .../layout/activity_batch_replay_result.xml | 5 +- .../res/layout/activity_case_param_edit.xml | 67 +++++ .../layout/activity_display_replay_result.xml | 2 +- src/app/src/main/res/layout/activity_info.xml | 2 +- .../main/res/layout/activity_performance.xml | 8 +- .../main/res/layout/activity_record_chart.xml | 4 +- .../res/layout/activity_record_config.xml | 14 +- .../res/layout/activity_record_manage.xml | 9 +- .../src/main/res/layout/activity_settings.xml | 30 ++- .../src/main/res/layout/clickable_item.xml | 2 +- .../res/layout/dialog_action_crop_image.xml | 2 +- .../src/main/res/layout/dialog_action_let.xml | 10 +- .../layout/dialog_action_record_config.xml | 10 +- .../main/res/layout/dialog_assert_number.xml | 14 +- .../main/res/layout/dialog_assert_string.xml | 10 +- .../res/layout/dialog_first_level_item.xml | 19 +- .../layout/dialog_global_param_setting.xml | 128 ++++++++++ .../src/main/res/layout/dialog_loading.xml | 3 +- .../src/main/res/layout/dialog_param_edit.xml | 27 ++ .../main/res/layout/dialog_record_name.xml | 1 - .../main/res/layout/dialog_repeat_count.xml | 2 +- .../main/res/layout/dialog_select_view.xml | 4 +- .../res/layout/dialog_while_setting_panel.xml | 5 +- src/app/src/main/res/layout/float_win.xml | 1 - .../src/main/res/layout/float_win_list.xml | 3 - .../res/layout/fragment_case_desc_edit.xml | 37 ++- .../main/res/layout/fragment_replay_list.xml | 5 + .../main/res/layout/fragment_union_param.xml | 53 ++++ src/app/src/main/res/layout/input_view.xml | 2 +- .../src/main/res/layout/item_case_param.xml | 58 +++++ .../layout/item_case_result_step_actions.xml | 15 +- .../res/layout/item_case_step_content.xml | 11 +- .../res/layout/item_case_step_edit_input.xml | 50 ++-- .../res/layout/item_case_step_edit_select.xml | 2 +- .../src/main/res/layout/item_edit_field.xml | 43 ++++ .../main/res/layout/item_operation_node.xml | 26 +- .../src/main/res/layout/item_param_info.xml | 42 ++++ .../res/layout/item_record_time_select.xml | 3 +- .../src/main/res/layout/item_replay_list.xml | 6 +- .../main/res/layout/item_replay_result.xml | 5 +- .../src/main/res/layout/item_tools_grid.xml | 4 +- .../main/res/layout/perform_float_list.xml | 3 +- .../main/res/layout/perform_stress_list.xml | 1 - .../src/main/res/layout/record_service.xml | 4 +- .../main/res/layout/screen_record_entry.xml | 4 +- src/app/src/main/res/values/colors.xml | 1 + src/app/src/main/res/values/strings.xml | 211 +++++++++++++++- .../application/LauncherApplication.java | 15 ++ .../alipay/hulu/common/bean/DeviceInfo.java | 18 +- .../alipay/hulu/common/service/SPService.java | 1 + .../com/alipay/hulu/common/tools/CmdLine.java | 36 +++ .../alipay/hulu/common/tools/CmdTools.java | 32 +-- .../com/alipay/hulu/common/utils/LogUtil.java | 35 +++ .../hulu/common/utils/PermissionUtil.java | 4 +- .../activity/FileChooseDialogActivity.java | 6 +- .../activity/PermissionDialogActivity.java | 44 ++-- .../res/layout/file_choose_dialog_layout.xml | 7 +- .../res/layout/permission_dialog_layout.xml | 5 +- src/common/src/main/res/values/strings.xml | 13 + .../permission/FloatWindowManager.java | 5 +- .../android/permission/rom/HuaweiUtils.java | 4 +- .../com/android/permission/rom/VivoUtils.java | 8 +- .../src/main/res/values/strings.xml | 9 + .../hulu/shared/event/EventService.java | 3 +- ...stant.java => OperationStepProcessor.java} | 20 +- .../hulu/shared/io/OperationStepService.java | 33 ++- .../shared/io/db/OperationLogHandler.java | 15 +- .../hulu/shared/node/OperationService.java | 131 ++++++++-- .../hulu/shared/node/action/CmdExecutor.java | 7 +- .../shared/node/action/OperationExecutor.java | 51 +++- .../shared/node/action/PerformActionEnum.java | 145 ++++++----- .../node/action/PerformActionMethodMaps.java | 137 ++++++++++ .../node/locater/OperationNodeLocator.java | 32 ++- .../tree/AccessibilityNodeTree.java | 80 +++++- ...tepProvider.java => BaseStepExporter.java} | 2 +- ...ovider.java => OperationStepExporter.java} | 5 +- .../hulu/shared/node/utils/AssetsManager.java | 10 +- .../hulu/shared/node/utils/LogicUtil.java | 4 +- .../hulu/shared/node/utils/NodeTreeUtil.java | 47 ++-- .../hulu/shared/node/utils/OperationUtil.java | 6 +- .../hulu/shared/node/utils/PrepareUtil.java | 1 + .../dialog_action_drawable_load_param.xml | 11 + .../dialog_action_drawable_params_gen.xml | 30 +++ .../src/main/res/layout/activity_download.xml | 103 -------- src/shared/src/main/res/values/strings.xml | 85 ++++++- 151 files changed, 4469 insertions(+), 1031 deletions(-) create mode 100644 src/app/src/main/java/com/alipay/hulu/activity/CaseParamEditActivity.java create mode 100644 src/app/src/main/java/com/alipay/hulu/adapter/ParamListAdapter.java create mode 100644 src/app/src/main/java/com/alipay/hulu/adapter/SoloBaseAdapter.java create mode 100644 src/app/src/main/java/com/alipay/hulu/bean/CaseParamBean.java create mode 100644 src/app/src/main/java/com/alipay/hulu/bean/CaseRunningParam.java create mode 100644 src/app/src/main/java/com/alipay/hulu/fragment/CaseParamSeparateFragment.java create mode 100644 src/app/src/main/java/com/alipay/hulu/fragment/CaseParamUnionFragment.java create mode 100644 src/app/src/main/java/com/alipay/hulu/replay/MultiParamStepProvider.java create mode 100644 src/app/src/main/java/com/alipay/hulu/util/CaseAppendOperationProcessor.java create mode 100644 src/app/src/main/java/com/alipay/hulu/util/CaseReplayUtil.java create mode 100644 src/app/src/main/res/drawable/accent_button_background.xml create mode 100644 src/app/src/main/res/drawable/accent_button_layer.xml create mode 100644 src/app/src/main/res/drawable/bg_template_list.xml create mode 100644 src/app/src/main/res/drawable/case_play.xml create mode 100644 src/app/src/main/res/drawable/case_step_copy.xml create mode 100644 src/app/src/main/res/drawable/case_step_paste.xml create mode 100644 src/app/src/main/res/drawable/case_step_select.xml create mode 100644 src/app/src/main/res/layout/activity_case_param_edit.xml create mode 100644 src/app/src/main/res/layout/dialog_global_param_setting.xml create mode 100644 src/app/src/main/res/layout/dialog_param_edit.xml create mode 100644 src/app/src/main/res/layout/fragment_union_param.xml create mode 100644 src/app/src/main/res/layout/item_case_param.xml create mode 100644 src/app/src/main/res/layout/item_edit_field.xml create mode 100644 src/app/src/main/res/layout/item_param_info.xml create mode 100644 src/permission/src/main/res/values/strings.xml rename src/shared/src/main/java/com/alipay/hulu/shared/io/{constant/Constant.java => OperationStepProcessor.java} (58%) create mode 100644 src/shared/src/main/java/com/alipay/hulu/shared/node/action/PerformActionMethodMaps.java rename src/shared/src/main/java/com/alipay/hulu/shared/node/tree/export/{BaseStepProvider.java => BaseStepExporter.java} (96%) rename src/shared/src/main/java/com/alipay/hulu/shared/node/tree/export/{OperationStepProvider.java => OperationStepExporter.java} (97%) create mode 100644 src/shared/src/main/res/drawable/dialog_action_drawable_load_param.xml create mode 100644 src/shared/src/main/res/drawable/dialog_action_drawable_params_gen.xml delete mode 100644 src/shared/src/main/res/layout/activity_download.xml diff --git a/src/AdbLib/src/com/cgutman/adblib/ByteQueueInputStream.java b/src/AdbLib/src/com/cgutman/adblib/ByteQueueInputStream.java index 194a0b1..4e1202c 100644 --- a/src/AdbLib/src/com/cgutman/adblib/ByteQueueInputStream.java +++ b/src/AdbLib/src/com/cgutman/adblib/ByteQueueInputStream.java @@ -110,7 +110,7 @@ class ByteQueueInputStream extends InputStream { * buf. * */ - protected ByteQueueInputStream() { + public ByteQueueInputStream() { this.readQueue = new ConcurrentLinkedQueue<>(); this.pos = 0; this.count = 0; @@ -133,7 +133,7 @@ public void closeSocketForwardingMode() { * 添加bytes到队列中 * @param bytes */ - protected void addBytes(byte[] bytes) { + public void addBytes(byte[] bytes) { this.readQueue.add(bytes); if (socketForward) { diff --git a/src/app/src/main/AndroidManifest.xml b/src/app/src/main/AndroidManifest.xml index efc0b14..82a07c7 100644 --- a/src/app/src/main/AndroidManifest.xml +++ b/src/app/src/main/AndroidManifest.xml @@ -66,62 +66,66 @@ + android:label="@string/activity__setting" /> - + + diff --git a/src/app/src/main/java/com/alipay/hulu/actions/ImageCompareActionProvider.java b/src/app/src/main/java/com/alipay/hulu/actions/ImageCompareActionProvider.java index aae5143..4463e0b 100644 --- a/src/app/src/main/java/com/alipay/hulu/actions/ImageCompareActionProvider.java +++ b/src/app/src/main/java/com/alipay/hulu/actions/ImageCompareActionProvider.java @@ -218,7 +218,7 @@ public void run() { // 还没有,无法执行 if (rs == null) { - LauncherApplication.getInstance().showToast("图像断言失败"); + LauncherApplication.getInstance().showToast(StringUtil.getString(R.string.image_compare__assert_failed)); return false; } } @@ -228,7 +228,7 @@ public void run() { Rect target = findTargetRect(rs, query, context.screenWidth, context.screenHeight, defaultWidth); if (target == null) { LogUtil.e(TAG, "Can't find target Image"); - LauncherApplication.getInstance().showToast("图像断言失败"); + LauncherApplication.getInstance().showToast(StringUtil.getString(R.string.image_compare__assert_failed)); return false; } else { // 高亮控件 @@ -242,7 +242,7 @@ public void run() { // 执行adb命令 context.notifyOperationFinish(); - LauncherApplication.getInstance().showToast("图像断言成功"); + LauncherApplication.getInstance().showToast(StringUtil.getString(R.string.image_compare__assert_success)); return true; } } catch (Exception e) { @@ -250,7 +250,7 @@ public void run() { context.notifyOperationFinish(); } - LauncherApplication.getInstance().showToast("图像断言失败"); + LauncherApplication.getInstance().showToast(StringUtil.getString(R.string.image_compare__assert_failed)); return false; } diff --git a/src/app/src/main/java/com/alipay/hulu/actions/PerformanceActionProvider.java b/src/app/src/main/java/com/alipay/hulu/actions/PerformanceActionProvider.java index bf104f6..6c3f59c 100644 --- a/src/app/src/main/java/com/alipay/hulu/actions/PerformanceActionProvider.java +++ b/src/app/src/main/java/com/alipay/hulu/actions/PerformanceActionProvider.java @@ -113,10 +113,10 @@ public void run() { File folder = RecordUtil.saveToFile(records); // 显示提示框 - LauncherApplication.getInstance().showToast("录制数据已经保存到\"" + folder.getPath() + "\"下"); + LauncherApplication.getInstance().showToast(StringUtil.getString(R.string.performance__record_save, folder.getPath())); } else { String response = RecordUtil.uploadData(uploadUrl, records); - LauncherApplication.getInstance().showToast("录制数据已经上传至\"" + uploadUrl + "\",响应结果: " + response); + LauncherApplication.getInstance().showToast(StringUtil.getString(R.string.performance__record_upload, uploadUrl, response)); } } }); diff --git a/src/app/src/main/java/com/alipay/hulu/actions/RecordScreenActionProvider.java b/src/app/src/main/java/com/alipay/hulu/actions/RecordScreenActionProvider.java index 1dc7a79..d109086 100644 --- a/src/app/src/main/java/com/alipay/hulu/actions/RecordScreenActionProvider.java +++ b/src/app/src/main/java/com/alipay/hulu/actions/RecordScreenActionProvider.java @@ -135,7 +135,7 @@ public boolean processAction(String targetAction, AbstractNodeTree node, final O uploadUrl = method.getParam(KEY_RECORD_UPLOAD_URL); if (ClassUtil.getPatchInfo(VideoAnalyzer.SCREEN_RECORD_PATCH) == null) { - LauncherApplication.getInstance().showToast("加载计算插件中"); + LauncherApplication.getInstance().showToast(StringUtil.getString(R.string.settings__load_plugin)); context.notifyOnFinish(new Runnable() { @Override public void run() { @@ -176,7 +176,7 @@ public void run() { @Override public void run() { try { - injectorService.pushMessage(SHOW_LOADING_DIALOG, "正在计算响应耗时"); + injectorService.pushMessage(SHOW_LOADING_DIALOG, StringUtil.getString(R.string.record_screen__calculating_response_time)); long startTime = binder.stopRecord(); @@ -252,8 +252,8 @@ private void processVideo(String path, long videoStartTime) { public void onAnalyzeFinished(final long result) { UIOperationMessage message = new UIOperationMessage(); message.eventType = UIOperationMessage.TYPE_DIALOG; - message.params.put("msg", "耗时:" + result + "ms"); - message.params.put("title", "响应耗时"); + message.params.put("msg", StringUtil.getString(R.string.record_screen__cost_time, result)); + message.params.put("title", StringUtil.getString(R.string.record_screen__response_time)); injectorService.pushMessage(null, message, false); // 如果有配置上传信息 diff --git a/src/app/src/main/java/com/alipay/hulu/activity/BaseActivity.java b/src/app/src/main/java/com/alipay/hulu/activity/BaseActivity.java index 166263b..a65b4da 100644 --- a/src/app/src/main/java/com/alipay/hulu/activity/BaseActivity.java +++ b/src/app/src/main/java/com/alipay/hulu/activity/BaseActivity.java @@ -19,6 +19,7 @@ import android.content.Context; import android.os.Bundle; import android.support.annotation.Nullable; +import android.support.annotation.StringRes; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v7.app.AppCompatActivity; @@ -31,6 +32,7 @@ import com.alipay.hulu.common.application.LauncherApplication; import com.alipay.hulu.common.utils.DeviceInfoUtil; import com.alipay.hulu.common.utils.LogUtil; +import com.alipay.hulu.common.utils.StringUtil; import java.util.HashSet; import java.util.Set; @@ -59,7 +61,7 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { // 主线程等待 LauncherApplication.getInstance().prepareInMain(); - LogUtil.w("BaseActivity", "Activity: %s, 等待Launcher初始化耗时: %dms", getClass().getSimpleName(), System.currentTimeMillis() - startTime); + LogUtil.w("BaseActivity", "Activity: %s, waiting launcher to initialize: %dms", getClass().getSimpleName(), System.currentTimeMillis() - startTime); } // 为了正常初始化 @@ -116,6 +118,22 @@ public void hideSoftInputMethod() { } } + /** + * 短toast + * @param stringRes + */ + public void toastShort(@StringRes final int stringRes) { + toastShort(getString(stringRes)); + } + + /** + * 短toast + * @param stringRes + */ + public void toastShort(@StringRes final int stringRes, final Object... args) { + toastShort(getString(stringRes, args)); + } + /** * toast短时间提示 * @@ -143,6 +161,22 @@ public void toastShort(String msg, Object... args) { toastShort(formatMsg); } + /** + * 短toast + * @param stringRes + */ + public void toastLong(@StringRes final int stringRes) { + toastLong(getString(stringRes)); + } + + /** + * 短toast + * @param stringRes + */ + public void toastLong(@StringRes final int stringRes, final Object... args) { + toastLong(getString(stringRes, args)); + } + /** * toast长时间提示 * @@ -165,11 +199,6 @@ public void run() { }); } - public void toastLong(String msg, Object... args) { - String formatMsg = String.format(msg, args); - toastLong(formatMsg); - } - public void showProgressDialog(final String str) { runOnUiThread(new Runnable() { public void run() { diff --git a/src/app/src/main/java/com/alipay/hulu/activity/BatchExecutionActivity.java b/src/app/src/main/java/com/alipay/hulu/activity/BatchExecutionActivity.java index adcc068..965bc12 100644 --- a/src/app/src/main/java/com/alipay/hulu/activity/BatchExecutionActivity.java +++ b/src/app/src/main/java/com/alipay/hulu/activity/BatchExecutionActivity.java @@ -31,14 +31,15 @@ import com.alipay.hulu.R; import com.alipay.hulu.adapter.BatchExecutionListAdapter; -import com.alipay.hulu.common.application.LauncherApplication; +import com.alipay.hulu.common.tools.BackgroundExecutor; +import com.alipay.hulu.common.utils.LogUtil; import com.alipay.hulu.common.utils.MiscUtil; import com.alipay.hulu.common.utils.PermissionUtil; import com.alipay.hulu.fragment.BatchExecutionFragment; -import com.alipay.hulu.replay.BatchStepProvider; -import com.alipay.hulu.service.CaseReplayManager; import com.alipay.hulu.shared.io.bean.RecordCaseInfo; +import com.alipay.hulu.shared.node.utils.AppUtil; import com.alipay.hulu.ui.HeadControlPanel; +import com.alipay.hulu.util.CaseReplayUtil; import com.zhy.view.flowlayout.FlowLayout; import com.zhy.view.flowlayout.TagAdapter; import com.zhy.view.flowlayout.TagFlowLayout; @@ -72,7 +73,7 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { mPager = (ViewPager) findViewById(R.id.pager); mTabLayout = (TabLayout) findViewById(R.id.tab_layout); mHeadPanel = (HeadControlPanel) findViewById(R.id.head_replay_list); - mHeadPanel.setMiddleTitle("批量回放"); + mHeadPanel.setMiddleTitle(getString(R.string.activity__batch_replay)); mHeadPanel.setBackIconClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -118,7 +119,7 @@ public View getView(FlowLayout parent, int position, RecordCaseInfo o) { @Override public void onClick(View v) { if (currentCases.size() == 0) { - toastShort("请选择用例"); + toastShort(getString(R.string.batch__select_case)); return; } @@ -126,9 +127,8 @@ public void onClick(View v) { @Override public void onPermissionResult(boolean result, String reason) { if (result) { - BatchStepProvider provider = new BatchStepProvider(new ArrayList<>(currentCases), mRestartApp.isChecked()); - CaseReplayManager manager = LauncherApplication.getInstance().findServiceByName(CaseReplayManager.class.getName()); - manager.start(provider, MyApplication.MULTI_REPLAY_LISTENER); + CaseReplayUtil.startReplayMultiCase(currentCases, mRestartApp.isChecked()); + startApp(currentCases.get(0).getTargetAppPackage()); } } }; @@ -155,6 +155,24 @@ public boolean onTagClick(View view, int position, FlowLayout parent) { return false; } + private void startApp(final String packageName) { + if (packageName == null) { + return; + } + + //如果是支付宝,点击后通过scheme跳到首页 + BackgroundExecutor.execute(new Runnable() { + @Override + public void run() { + AppUtil.forceStopApp(packageName); + + LogUtil.e("NewRecordActivity", "强制终止应用:" + packageName); + MiscUtil.sleep(500); + AppUtil.startApp(packageName); + } + }); + } + /** * 检察权限 * @param callback diff --git a/src/app/src/main/java/com/alipay/hulu/activity/BatchReplayResultActivity.java b/src/app/src/main/java/com/alipay/hulu/activity/BatchReplayResultActivity.java index fd4f181..b9f613f 100644 --- a/src/app/src/main/java/com/alipay/hulu/activity/BatchReplayResultActivity.java +++ b/src/app/src/main/java/com/alipay/hulu/activity/BatchReplayResultActivity.java @@ -120,7 +120,7 @@ public void onClick(View v) { finish(); } }); - mPanel.setMiddleTitle("批量回放执行结果"); + mPanel.setMiddleTitle(getString(R.string.activity__batch_replay_result)); } private static class ResultAdapter extends BaseAdapter { @@ -166,10 +166,10 @@ public View getView(int position, View convertView, ViewGroup parent) { if (bean != null) { holder.caseName.setText(bean.getCaseName()); if (TextUtils.isEmpty(bean.getExceptionMessage())) { - holder.result.setText("成功"); + holder.result.setText(R.string.constant__success); holder.result.setTextColor(0xff65c0ba); } else { - holder.result.setText("失败"); + holder.result.setText(R.string.constant__fail); holder.result.setTextColor(0xfff76262); } } diff --git a/src/app/src/main/java/com/alipay/hulu/activity/CaseEditActivity.java b/src/app/src/main/java/com/alipay/hulu/activity/CaseEditActivity.java index b8563dd..cd16ae2 100644 --- a/src/app/src/main/java/com/alipay/hulu/activity/CaseEditActivity.java +++ b/src/app/src/main/java/com/alipay/hulu/activity/CaseEditActivity.java @@ -97,7 +97,7 @@ private void initData() { saved = false; caseSaveListeners.clear(); - mHeadPanel.setMiddleTitle("用例编辑"); + mHeadPanel.setMiddleTitle(getString(R.string.activity__case_edit)); mHeadPanel.setBackIconClickListener(new View.OnClickListener() { @Override @@ -152,7 +152,7 @@ public void run() { /** * 包装用例信息 */ - private void wrapRecordCase() { + public void wrapRecordCase() { for (WeakReference listenerRef: caseSaveListeners) { if (listenerRef.get() != null) { listenerRef.get().onCaseSave(); @@ -169,7 +169,7 @@ private void updateLocalCase() { public void run() { wrapRecordCase(); GreenDaoManager.getInstance().getRecordCaseInfoDao().save(mRecordCase); - toastShort("更新成功"); + toastShort(getString(R.string.case__update_success)); InjectorService.g().pushMessage(NewRecordActivity.NEED_REFRESH_LOCAL_CASES_LIST); saved = true; } @@ -185,6 +185,10 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { } } + public RecordCaseInfo getRecordCase() { + return mRecordCase; + } + private static class CustomPagerAdapter extends FragmentPagerAdapter { private RecordCaseInfo caseInfo; private WeakReference ref; diff --git a/src/app/src/main/java/com/alipay/hulu/activity/CaseParamEditActivity.java b/src/app/src/main/java/com/alipay/hulu/activity/CaseParamEditActivity.java new file mode 100644 index 0000000..9770309 --- /dev/null +++ b/src/app/src/main/java/com/alipay/hulu/activity/CaseParamEditActivity.java @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2015-present, Ant Financial Services Group + * + * 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.alipay.hulu.activity; + +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.design.widget.TabLayout; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentPagerAdapter; +import android.support.v4.view.ViewPager; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import com.alibaba.fastjson.JSON; +import com.alipay.hulu.R; +import com.alipay.hulu.bean.AdvanceCaseSetting; +import com.alipay.hulu.bean.CaseRunningParam; +import com.alipay.hulu.bean.CaseStepHolder; +import com.alipay.hulu.common.application.LauncherApplication; +import com.alipay.hulu.common.injector.InjectorService; +import com.alipay.hulu.common.tools.BackgroundExecutor; +import com.alipay.hulu.common.utils.LogUtil; +import com.alipay.hulu.common.utils.MiscUtil; +import com.alipay.hulu.fragment.BaseFragment; +import com.alipay.hulu.fragment.CaseParamSeparateFragment; +import com.alipay.hulu.fragment.CaseParamUnionFragment; +import com.alipay.hulu.shared.io.bean.RecordCaseInfo; +import com.alipay.hulu.shared.io.db.GreenDaoManager; +import com.alipay.hulu.ui.HeadControlPanel; + +/** + * Created by qiaoruikai on 2019-08-19 21:16. + */ +public class CaseParamEditActivity extends BaseActivity { + public static final String RECORD_CASE_EXTRA = "record_case"; + private static final String TAG = CaseParamEditActivity.class.getSimpleName(); + + // display + private TextView mCaseName; + private TextView mCaseDesc; + + private HeadControlPanel mHead; + private ViewPager mPager; + private TabLayout mTabLayout; + private CaseParamFragmentAdapter mParamAdapter; + + private RecordCaseInfo mRecordCase; + private AdvanceCaseSetting mSettings; + + private boolean saved = false; + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_case_param_edit); + + initView(); + initData(); + } + + /** + * 渲染数据 + */ + private void initData() { + int caseId = getIntent().getIntExtra(RECORD_CASE_EXTRA, 0); + mRecordCase = CaseStepHolder.getCase(caseId); + + // 如果Intent中没有 + if (mRecordCase == null) { + LogUtil.e(TAG, "There is no record case"); + return; + } + + mSettings = JSON.parseObject(mRecordCase.getAdvanceSettings(), AdvanceCaseSetting.class); + + mCaseName.setText(mRecordCase.getCaseName()); + mCaseDesc.setText(getString(R.string.case_param_edit__case_desc, mRecordCase.getCaseDesc())); + + mHead.setMiddleTitle(getString(R.string.activity__gen_param_case)); + mHead.setBackIconClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + onBackPressed(); + } + }); + + // 自己的用例 + mHead.setInfoIconClickListener(R.drawable.icon_save, new View.OnClickListener() { + @Override + public void onClick(View v) { + saveCase(); + } + }); + + mPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(mTabLayout)); + mTabLayout.setupWithViewPager(mPager); + mTabLayout.setTabGravity(TabLayout.GRAVITY_FILL); + mTabLayout.setTabMode(TabLayout.MODE_FIXED); + mTabLayout.setSelectedTabIndicatorColor(getResources().getColor(R.color.mainBlue)); + mTabLayout.post(new Runnable() { + @Override + public void run() { + MiscUtil.setIndicator(mTabLayout, 0, 0); + } + }); + + mParamAdapter = new CaseParamFragmentAdapter(getSupportFragmentManager(), mSettings); + mPager.setAdapter(mParamAdapter); + } + + /** + * 初始化界面 + */ + private void initView() { + mHead = (HeadControlPanel) findViewById(R.id.head_layout); + + mCaseName = (TextView) findViewById(R.id.case_param_edit_name); + mCaseDesc = (TextView) findViewById(R.id.case_param_edit_desc); + + mPager = (ViewPager) findViewById(R.id.case_param_pager); + mTabLayout = (TabLayout) findViewById(R.id.case_param_tab_layout); + } + + @Override + public void onBackPressed() { + if (!saved) { + LauncherApplication.getInstance().showDialog(this, "是否保存用例", "是", new Runnable() { + @Override + public void run() { + saveCase(); + finish(); + } + }, "否", new Runnable() { + @Override + public void run() { + finish(); + } + }); + } else { + finish(); + } + } + + /** + * 保存用例 + */ + private void saveCase() { + CaseRunningParam param = mParamAdapter.getCurrentFragment().getRunningParam(); + mSettings.setRunningParam(param); + mRecordCase.setAdvanceSettings(JSON.toJSONString(mSettings)); + + updateLocalCase(); + } + + /** + * 更新本地用例 + */ + private void updateLocalCase() { + BackgroundExecutor.execute(new Runnable() { + @Override + public void run() { + GreenDaoManager.getInstance().getRecordCaseInfoDao().save(mRecordCase); + toastShort(getString(R.string.case__update_success)); + InjectorService.g().pushMessage(NewRecordActivity.NEED_REFRESH_LOCAL_CASES_LIST); + saved = true; + } + }); + } + + public static class CaseParamFragmentAdapter extends FragmentPagerAdapter { + private AdvanceCaseSetting advanceCaseSetting; + private CaseParamFragment mCurrentFragment; + + public CaseParamFragmentAdapter(FragmentManager fm, AdvanceCaseSetting advanceCaseSetting) { + super(fm); + this.advanceCaseSetting = advanceCaseSetting; + } + + @Override + public int getCount() { + return 2; + } + + @Override + public Fragment getItem(int position) { + CaseParamFragment fragment; + if (position == 0) { + fragment = new CaseParamSeparateFragment(); + } else { + fragment = new CaseParamUnionFragment(); + } + fragment.setAdvanceCaseSetting(advanceCaseSetting); + return fragment; + } + + @Override + public CharSequence getPageTitle(int position) { + if (position == 0) { + return "独立模式"; + } else { + return "联合模式"; + } + } + + @Override + public void setPrimaryItem(ViewGroup container, int position, Object object) { + this.mCurrentFragment = (CaseParamFragment) object; + super.setPrimaryItem(container, position, object); + } + + public CaseParamFragment getCurrentFragment() { + return mCurrentFragment; + } + } + + public static abstract class CaseParamFragment extends BaseFragment { + public abstract void setAdvanceCaseSetting(@NonNull AdvanceCaseSetting advanceCaseSetting); + public abstract CaseRunningParam getRunningParam(); + } +} \ No newline at end of file diff --git a/src/app/src/main/java/com/alipay/hulu/activity/CaseReplayResultActivity.java b/src/app/src/main/java/com/alipay/hulu/activity/CaseReplayResultActivity.java index f374300..3cfced2 100644 --- a/src/app/src/main/java/com/alipay/hulu/activity/CaseReplayResultActivity.java +++ b/src/app/src/main/java/com/alipay/hulu/activity/CaseReplayResultActivity.java @@ -103,7 +103,7 @@ private void initData() { return; } - mHeadPanel.setMiddleTitle("回放结果"); + mHeadPanel.setMiddleTitle(getString(R.string.activity__replay_result)); mHeadPanel.setBackIconClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -118,7 +118,7 @@ public void onClick(View v) { mHeadPanel.setInfoIconClickListener(R.drawable.icon_save, new View.OnClickListener() { @Override public void onClick(View v) { - showProgressDialog("保存中"); + showProgressDialog(getString(R.string.case_replay__saving)); BackgroundExecutor.execute(new Runnable() { @Override public void run() { diff --git a/src/app/src/main/java/com/alipay/hulu/activity/IndexActivity.java b/src/app/src/main/java/com/alipay/hulu/activity/IndexActivity.java index c81ad6a..5d0d4a6 100644 --- a/src/app/src/main/java/com/alipay/hulu/activity/IndexActivity.java +++ b/src/app/src/main/java/com/alipay/hulu/activity/IndexActivity.java @@ -135,16 +135,16 @@ public void run() { webSettings.setLoadWithOverviewMode(true); webSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NARROW_COLUMNS); webView.loadData(content, null, null); - new AlertDialog.Builder(IndexActivity.this).setTitle("发现新版本: " + release.getTag_name()) + new AlertDialog.Builder(IndexActivity.this).setTitle(getString(R.string.index__new_version, release.getTag_name())) .setView(webView) - .setPositiveButton("前往更新", new DialogInterface.OnClickListener() { + .setPositiveButton(R.string.index__go_update, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { Uri uri = Uri.parse(release.getHtml_url()); Intent intent = new Intent(Intent.ACTION_VIEW, uri); startActivity(intent); } - }).setNegativeButton("取消", new DialogInterface.OnClickListener() { + }).setNegativeButton(R.string.constant__cancel, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); @@ -333,7 +333,7 @@ public void run() { } }); } else { - toastLong("日志打包失败"); + toastLong(getString(R.string.index__package_crash_failed)); // 回设检查时间,以便下次上报 SPService.putLong(SPService.KEY_ERROR_CHECK_TIME, errorTime - 10); diff --git a/src/app/src/main/java/com/alipay/hulu/activity/InfoActivity.java b/src/app/src/main/java/com/alipay/hulu/activity/InfoActivity.java index fabd531..16a7f65 100644 --- a/src/app/src/main/java/com/alipay/hulu/activity/InfoActivity.java +++ b/src/app/src/main/java/com/alipay/hulu/activity/InfoActivity.java @@ -49,7 +49,7 @@ public void onClick(View v) { InfoActivity.this.finish(); } }); - panel.setMiddleTitle("关于"); + panel.setMiddleTitle(getString(R.string.activity__about)); TextView versionName = (TextView) findViewById(R.id.version_name); versionName.setText(getString(R.string.info__version_text, SystemUtil.getAppVersionName())); diff --git a/src/app/src/main/java/com/alipay/hulu/activity/LicenseActivity.java b/src/app/src/main/java/com/alipay/hulu/activity/LicenseActivity.java index de6054d..d6a1e6b 100644 --- a/src/app/src/main/java/com/alipay/hulu/activity/LicenseActivity.java +++ b/src/app/src/main/java/com/alipay/hulu/activity/LicenseActivity.java @@ -43,7 +43,7 @@ public void onClick(View v) { finish(); } }); - panel.setMiddleTitle("开源许可"); + panel.setMiddleTitle(getString(R.string.activity__license)); final WebView licenseText = (WebView) findViewById(R.id.license_text); licenseText.loadUrl(NOTICE_HTML); diff --git a/src/app/src/main/java/com/alipay/hulu/activity/MyApplication.java b/src/app/src/main/java/com/alipay/hulu/activity/MyApplication.java index 43b82c0..b5ca786 100644 --- a/src/app/src/main/java/com/alipay/hulu/activity/MyApplication.java +++ b/src/app/src/main/java/com/alipay/hulu/activity/MyApplication.java @@ -26,6 +26,7 @@ import android.provider.Settings; import android.view.WindowManager; +import com.alipay.hulu.R; import com.alipay.hulu.bean.CaseStepHolder; import com.alipay.hulu.bean.ReplayResultBean; import com.alipay.hulu.common.application.LauncherApplication; @@ -181,7 +182,7 @@ public void run() { LogUtil.w(TAG, "Copy anr file result: " + result); - MyApplication.getInstance().showToast("发现anr信息,已拷贝至: " + pathInShell); + MyApplication.getInstance().showToast(getString(R.string.app__find_anr_info, pathInShell)); } } } diff --git a/src/app/src/main/java/com/alipay/hulu/activity/NewRecordActivity.java b/src/app/src/main/java/com/alipay/hulu/activity/NewRecordActivity.java index 8460c44..23d6664 100644 --- a/src/app/src/main/java/com/alipay/hulu/activity/NewRecordActivity.java +++ b/src/app/src/main/java/com/alipay/hulu/activity/NewRecordActivity.java @@ -56,13 +56,16 @@ import com.alipay.hulu.replay.OperationStepProvider; import com.alipay.hulu.service.CaseRecordManager; import com.alipay.hulu.service.CaseReplayManager; +import com.alipay.hulu.shared.io.OperationStepService; import com.alipay.hulu.shared.io.bean.RecordCaseInfo; import com.alipay.hulu.shared.io.db.GreenDaoManager; +import com.alipay.hulu.shared.io.db.OperationLogHandler; import com.alipay.hulu.shared.io.db.RecordCaseInfoDao; import com.alipay.hulu.shared.node.action.RunningModeEnum; import com.alipay.hulu.shared.node.utils.AppUtil; import com.alipay.hulu.shared.node.utils.PrepareUtil; import com.alipay.hulu.ui.HeadControlPanel; +import com.alipay.hulu.util.CaseReplayUtil; import com.alipay.hulu.util.SystemUtil; import java.util.Arrays; @@ -71,7 +74,7 @@ /** * Created by lezhou.wyl on 2018/2/1. */ -@EntryActivity(icon = R.drawable.icon_luxiang, nameRes = R.string.record__name, permissions = {"adb", "float", "toast:请将Soloπ添加到后台白名单中"}, index = 1, cornerText = "New", cornerPersist = 3, cornerBg = 0xFFFF5900) +@EntryActivity(icon = R.drawable.icon_luxiang, nameRes = R.string.activity__record, permissions = {"adb", "float", "toast:请将Soloπ添加到后台白名单中"}, index = 1, cornerText = "New", cornerPersist = 3, cornerBg = 0xFFFF5900) public class NewRecordActivity extends BaseActivity { private static final String TAG = NewRecordActivity.class.getSimpleName(); @@ -154,20 +157,11 @@ private void initRecentCaseLayout() { mEmptyView = findViewById(R.id.empty_hint); mCheckAllCasesBtn = findViewById(R.id.check_all_cases); mRecentCaseAdapter = new ReplayListAdapter(this); - mRecentCaseAdapter.setOnEditClickListener(new AdapterView.OnItemClickListener() { + mRecentCaseAdapter.setOnPlayClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { RecordCaseInfo caseInfo = (RecordCaseInfo) mRecentCaseAdapter.getItem(position); - if (caseInfo == null) { - return; - } - caseInfo = caseInfo.clone(); - - // 启动编辑页 - Intent intent = new Intent(NewRecordActivity.this, CaseEditActivity.class); - int storeId = CaseStepHolder.storeCase(caseInfo); - intent.putExtra(CaseEditActivity.RECORD_CASE_EXTRA, storeId); - startActivity(intent); + playCase(caseInfo); } }); mRecentCaseListView.setAdapter(mRecentCaseAdapter); @@ -182,61 +176,85 @@ public void onClick(View v) { mRecentCaseListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { - final RecordCaseInfo caseInfo = (RecordCaseInfo) mRecentCaseAdapter.getItem(position); - if (caseInfo == null) { - return; - } + RecordCaseInfo caseInfo = (RecordCaseInfo) mRecentCaseAdapter.getItem(position); + editCase(caseInfo); + } + }); - // 检查权限 - PermissionUtil.requestPermissions(Arrays.asList("adb", Settings.ACTION_ACCESSIBILITY_SETTINGS), NewRecordActivity.this, new PermissionUtil.OnPermissionCallback() { - @Override - public void onPermissionResult(boolean result, String reason) { - if (result) { - showProgressDialog(getString(R.string.record__preparing)); + } - BackgroundExecutor.execute(new Runnable() { - @Override - public void run() { - boolean prepareResult = PrepareUtil.doPrepareWork(caseInfo.getTargetAppPackage(), new PrepareUtil.PrepareStatus() { - @Override - public void currentStatus(int progress, int total, String message, boolean status) { - updateProgressDialog(progress, total, message); - } - }); + /** + * 编辑用例 + * @param caseInfo + */ + private void editCase(RecordCaseInfo caseInfo) { + if (caseInfo == null) { + return; + } - if (prepareResult) { - runOnUiThread(new Runnable() { - @Override - public void run() { - dismissProgressDialog(); - startReplay(caseInfo); - startTargetApp(caseInfo.getTargetAppPackage()); - } - }); - } else { - runOnUiThread(new Runnable() { - @Override - public void run() { - dismissProgressDialog(); - Toast.makeText(NewRecordActivity.this, R.string.record__prepare_failed, Toast.LENGTH_SHORT).show(); - } - }); - } + caseInfo = caseInfo.clone(); + + // 启动编辑页 + Intent intent = new Intent(NewRecordActivity.this, CaseEditActivity.class); + int storeId = CaseStepHolder.storeCase(caseInfo); + intent.putExtra(CaseEditActivity.RECORD_CASE_EXTRA, storeId); + startActivity(intent); + } + + /** + * 执行用例 + * @param caseInfo + */ + private void playCase(final RecordCaseInfo caseInfo) { + if (caseInfo == null) { + return; + } +// 检查权限 + PermissionUtil.requestPermissions(Arrays.asList("adb", Settings.ACTION_ACCESSIBILITY_SETTINGS), NewRecordActivity.this, new PermissionUtil.OnPermissionCallback() { + @Override + public void onPermissionResult(boolean result, String reason) { + if (result) { + showProgressDialog(getString(R.string.record__preparing)); + BackgroundExecutor.execute(new Runnable() { + @Override + public void run() { + boolean prepareResult = PrepareUtil.doPrepareWork(caseInfo.getTargetAppPackage(), new PrepareUtil.PrepareStatus() { + @Override + public void currentStatus(int progress, int total, String message, boolean status) { + updateProgressDialog(progress, total, message); } }); + + if (prepareResult) { + runOnUiThread(new Runnable() { + @Override + public void run() { + dismissProgressDialog(); + CaseReplayUtil.startReplay(caseInfo); + startTargetApp(caseInfo.getTargetAppPackage()); + } + }); + } else { + runOnUiThread(new Runnable() { + @Override + public void run() { + dismissProgressDialog(); + toastShort(getString(R.string.record__prepare_env_fail)); + } + }); + } } - } - }); + }); + } } }); - } private void initHeadPanel() { mPanel = (HeadControlPanel) findViewById(R.id.head_layout); - mPanel.setMiddleTitle(getString(R.string.record__name)); + mPanel.setMiddleTitle(getString(R.string.activity__record)); mPanel.setBackIconClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -343,7 +361,7 @@ private void initAppHeadView() { @Override public void onClick(View v) { if (StringUtil.isEmpty(mCaseName.getText().toString().trim())) { - Toast.makeText(NewRecordActivity.this, R.string.record__case_name_empty, Toast.LENGTH_SHORT).show(); + toastShort(R.string.record__case_name_empty); return; } @@ -376,7 +394,6 @@ public void onClick(View v) { @Override public void onPermissionResult(boolean result, String reason) { if (result) { - showProgressDialog(getString(R.string.record__preparing)); BackgroundExecutor.execute(new Runnable() { @@ -394,6 +411,9 @@ public void currentStatus(int progress, int total, String message, boolean statu @Override public void run() { dismissProgressDialog(); + + LauncherApplication.service(OperationStepService.class).registerStepProcessor(new OperationLogHandler()); + startRecord(caseInfo); startTargetApp(caseInfo.getTargetAppPackage()); } @@ -403,7 +423,7 @@ public void run() { @Override public void run() { dismissProgressDialog(); - Toast.makeText(NewRecordActivity.this, R.string.record__prepare_failed, Toast.LENGTH_SHORT).show(); + toastShort(R.string.record__prepare_failed); } }); } diff --git a/src/app/src/main/java/com/alipay/hulu/activity/NewReplayListActivity.java b/src/app/src/main/java/com/alipay/hulu/activity/NewReplayListActivity.java index ad24656..e24460b 100644 --- a/src/app/src/main/java/com/alipay/hulu/activity/NewReplayListActivity.java +++ b/src/app/src/main/java/com/alipay/hulu/activity/NewReplayListActivity.java @@ -72,7 +72,7 @@ public void onClick(View v) { }); mHeadPanel.addMenuFromLeft(rightTitle); - mHeadPanel.setMiddleTitle(getString(R.string.constant__case_list)); + mHeadPanel.setMiddleTitle(getString(R.string.activity__case_list)); mHeadPanel.setBackIconClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -93,6 +93,7 @@ public void run() { ReplayPagerAdapter pagerAdapter = new ReplayPagerAdapter(getSupportFragmentManager()); mPager.setAdapter(pagerAdapter); + mPager.setOffscreenPageLimit(2); } diff --git a/src/app/src/main/java/com/alipay/hulu/activity/PerformanceActivity.java b/src/app/src/main/java/com/alipay/hulu/activity/PerformanceActivity.java index e881016..dc4df5e 100644 --- a/src/app/src/main/java/com/alipay/hulu/activity/PerformanceActivity.java +++ b/src/app/src/main/java/com/alipay/hulu/activity/PerformanceActivity.java @@ -65,7 +65,7 @@ /** * Created by lezhou.wyl on 2018/1/28. */ -@EntryActivity(icon = R.drawable.icon_xingneng, name = "性能工具", permissions = {"adb", "float"}, index = 2) +@EntryActivity(icon = R.drawable.icon_xingneng, nameRes = R.string.activity__performance_test, permissions = {"adb", "float"}, index = 2) public class PerformanceActivity extends BaseActivity { private String TAG = "PerformanceFragment"; @@ -99,7 +99,7 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { mPerfStressAdapter = new PerformStressAdapter(this); mPanel = (HeadControlPanel) findViewById(R.id.head_layout); - mPanel.setMiddleTitle("性能测试"); + mPanel.setMiddleTitle(getString(R.string.activity__performance_test)); mPanel.setBackIconClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -214,7 +214,7 @@ public boolean isEmpty() { public void onItemSelected(AdapterView parent, View view, int position, long id) { // 全局特殊处理 if (position == 0) { - ((MyApplication)getApplication()).updateAppAndName("-", "全局"); + ((MyApplication)getApplication()).updateAppAndName("-", getString(R.string.constant__gloabl)); } else { ApplicationInfo info = listPack.get(position - 1); LogUtil.i(TAG, "Select info: " + StringUtil.hide(info.packageName)); @@ -237,15 +237,15 @@ public void onNothingSelected(AdapterView parent) { @Override public void onClick(View v) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { - toastShort("此功能不支持Android5.0以下设备"); + toastShort(getString(R.string.performance__not_support_for_android_l)); return; } if (ClassUtil.getPatchInfo(VideoAnalyzer.SCREEN_RECORD_PATCH) == null) { - LauncherApplication.getInstance().showDialog(PerformanceActivity.this, "是否加载录屏耗时计算插件?", "是", new Runnable() { + LauncherApplication.getInstance().showDialog(PerformanceActivity.this, getString(R.string.performance__load_record_plugin), getString(R.string.constant__yes), new Runnable() { @Override public void run() { - showProgressDialog("插件下载中"); + showProgressDialog(getString(R.string.performance__downloading_plugin)); BackgroundExecutor.execute(new Runnable() { @Override public void run() { @@ -258,7 +258,7 @@ public void currentStatus(int progress, int total, String message, boolean statu if (rs == null) { // 降级到网络模式 dismissProgressDialog(); - toastLong("无法加载计算插件"); + toastLong(getString(R.string.performance__load_plugin_failed)); return; } @@ -268,7 +268,7 @@ public void currentStatus(int progress, int total, String message, boolean statu }); } - }, "否", null); + }, getString(R.string.constant__no), null); return; } @@ -284,8 +284,7 @@ public void onGrantSuccess() { @Override public void onGrantFail(String msg) { - toastLong("设备需要开启ADB 5555端口并授权调试才可使用" + - "\n请在命令行执行 adb tcpip 5555"); + toastLong(getString(R.string.performance__grant_adb)); } }); } diff --git a/src/app/src/main/java/com/alipay/hulu/activity/PerformanceChartActivity.java b/src/app/src/main/java/com/alipay/hulu/activity/PerformanceChartActivity.java index 9bbae63..77dc39c 100644 --- a/src/app/src/main/java/com/alipay/hulu/activity/PerformanceChartActivity.java +++ b/src/app/src/main/java/com/alipay/hulu/activity/PerformanceChartActivity.java @@ -131,7 +131,7 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { private void initView(){ setContentView(R.layout.activity_record_chart); headPanel = (HeadControlPanel) findViewById(R.id.head_layout); - headPanel.setMiddleTitle("录制数据"); + headPanel.setMiddleTitle(getString(R.string.activity__performance_display)); headPanel.setBackIconClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -227,7 +227,7 @@ public void onItemSelected(AdapterView parent, View view, int position, long } if (realPattern == null) { - Toast.makeText(PerformanceChartActivity.this, "录制数据未找到,请重新打开应用", Toast.LENGTH_SHORT).show(); + toastShort(R.string.performance_chart__no_record_data); return; } diff --git a/src/app/src/main/java/com/alipay/hulu/activity/QRScanActivity.java b/src/app/src/main/java/com/alipay/hulu/activity/QRScanActivity.java index 99156bf..f9e261b 100644 --- a/src/app/src/main/java/com/alipay/hulu/activity/QRScanActivity.java +++ b/src/app/src/main/java/com/alipay/hulu/activity/QRScanActivity.java @@ -156,6 +156,13 @@ public void onQRCodeRead(String text, PointF[] points) { if (curScanType == ScanSuccessEvent.SCAN_TYPE_SCHEME) { notifyScanSuccess(text); + } else if (curScanType == ScanSuccessEvent.SCAN_TYPE_PARAM) { + if (StringUtil.startWith(text, "http://") || StringUtil.startWith(text, "https://")) { + notifyScanSuccess(text); + } else { + resultTextView.setText(getString(R.string.qr_scan__url_not_support, text)); + enableQRCodeReadListener(); + } } } diff --git a/src/app/src/main/java/com/alipay/hulu/activity/RecordManageActivity.java b/src/app/src/main/java/com/alipay/hulu/activity/RecordManageActivity.java index db3391e..d4fe4d6 100644 --- a/src/app/src/main/java/com/alipay/hulu/activity/RecordManageActivity.java +++ b/src/app/src/main/java/com/alipay/hulu/activity/RecordManageActivity.java @@ -15,7 +15,6 @@ */ package com.alipay.hulu.activity; -import android.app.Activity; import android.os.Bundle; import android.support.annotation.Nullable; import android.view.View; @@ -25,7 +24,7 @@ import android.widget.Toast; import com.alipay.hulu.R; -import com.alipay.hulu.common.service.SPService; +import com.alipay.hulu.common.application.LauncherApplication; import com.alipay.hulu.common.utils.FileUtils; import com.alipay.hulu.common.utils.LogUtil; import com.alipay.hulu.common.utils.StringUtil; @@ -75,7 +74,7 @@ private void initView() { setContentView(R.layout.activity_record_manage); headPanel = (HeadControlPanel) findViewById(R.id.head_layout); - headPanel.setMiddleTitle("性能数据管理"); + headPanel.setMiddleTitle(getString(R.string.activity__performance_manage)); headPanel.setBackIconClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -169,7 +168,7 @@ private void deleteSelectFolders(String[] select) { } if (!folder.delete()) { - Toast.makeText(this, "文件夹\"" + folderName + "\"无法删除,请手动删除", Toast.LENGTH_LONG).show(); + LauncherApplication.toast(R.string.record__fail_delete_folder, folder); } } } diff --git a/src/app/src/main/java/com/alipay/hulu/activity/SettingsActivity.java b/src/app/src/main/java/com/alipay/hulu/activity/SettingsActivity.java index 092b9c4..d53c53b 100644 --- a/src/app/src/main/java/com/alipay/hulu/activity/SettingsActivity.java +++ b/src/app/src/main/java/com/alipay/hulu/activity/SettingsActivity.java @@ -22,15 +22,13 @@ import android.util.Pair; import android.view.LayoutInflater; import android.view.View; -import android.view.ViewGroup; import android.widget.EditText; -import android.widget.LinearLayout; -import android.widget.ScrollView; import android.widget.TextView; import android.widget.Toast; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONException; +import com.alibaba.fastjson.JSONObject; import com.alipay.hulu.R; import com.alipay.hulu.common.constant.Constant; import com.alipay.hulu.common.service.SPService; @@ -52,7 +50,11 @@ import com.alipay.hulu.shared.node.action.OperationMethod; import com.alipay.hulu.shared.node.tree.export.bean.OperationStep; import com.alipay.hulu.ui.HeadControlPanel; +import com.alipay.hulu.util.DialogUtils; import com.alipay.hulu.upgrade.PatchRequest; +import com.zhy.view.flowlayout.FlowLayout; +import com.zhy.view.flowlayout.TagAdapter; +import com.zhy.view.flowlayout.TagFlowLayout; import java.io.BufferedReader; import java.io.File; @@ -65,6 +67,9 @@ import java.util.List; import java.util.Map; +import static com.alipay.hulu.util.DialogUtils.showMultipleEditDialog; +import com.alipay.hulu.util.DialogUtils.OnDialogResultListener; + /** * Created by lezhou.wyl on 01/01/2018. */ @@ -85,6 +90,8 @@ public class SettingsActivity extends BaseActivity { private View mPatchListWrapper; private TextView mPatchListInfo; + private View mGlobalParamSettingWrapper; + private View mResolutionSettingWrapper; private TextView mResolutionSettingInfo; @@ -142,10 +149,17 @@ public void onClick(View v) { } }); + mGlobalParamSettingWrapper.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + showGlobalParamEdit(); + } + }); + mRecordUploadWrapper.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - showMultipleEditDialog(new OnDialogResultListener() { + showMultipleEditDialog(SettingsActivity.this, new OnDialogResultListener() { @Override public void onDialogPositive(List data) { if (data.size() == 1) { @@ -165,7 +179,7 @@ public void onDialogPositive(List data) { mRecordScreenUploadWrapper.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - showMultipleEditDialog(new OnDialogResultListener() { + showMultipleEditDialog(SettingsActivity.this, new OnDialogResultListener() { @Override public void onDialogPositive(List data) { if (data.size() == 1) { @@ -185,7 +199,7 @@ public void onDialogPositive(List data) { mPatchListWrapper.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - showMultipleEditDialog(new OnDialogResultListener() { + showMultipleEditDialog(SettingsActivity.this, new OnDialogResultListener() { @Override public void onDialogPositive(List data) { if (data.size() == 1) { @@ -210,7 +224,7 @@ public void onDialogPositive(List data) { mOutputCharsetSettingWrapper.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - showMultipleEditDialog(new OnDialogResultListener() { + showMultipleEditDialog(SettingsActivity.this, new DialogUtils.OnDialogResultListener() { @Override public void onDialogPositive(List data) { if (data.size() == 1) { @@ -263,7 +277,7 @@ public void onClick(View v) { mAesSeedSettingWrapper.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - showMultipleEditDialog(new OnDialogResultListener() { + showMultipleEditDialog(SettingsActivity.this, new DialogUtils.OnDialogResultListener() { @Override public void onDialogPositive(List data) { if (data.size() == 1) { @@ -285,7 +299,7 @@ public void onDialogPositive(List data) { mClearFilesSettingWrapper.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - showMultipleEditDialog(new OnDialogResultListener() { + showMultipleEditDialog(SettingsActivity.this, new OnDialogResultListener() { @Override public void onDialogPositive(List data) { if (data.size() == 1) { @@ -300,7 +314,7 @@ public void onDialogPositive(List data) { SPService.putInt(SPService.KEY_AUTO_CLEAR_FILES_DAYS, daysNum); mClearFilesSettingInfo.setText(days); } else { - Toast.makeText(SettingsActivity.this, R.string.settings__config_failed, Toast.LENGTH_SHORT).show(); + toastShort(R.string.settings__config_failed); } } } @@ -313,7 +327,7 @@ public void onDialogPositive(List data) { public void onClick(View v) { List> data = new ArrayList<>(2); data.add(new Pair<>("图像查找截图分辨率", "" + SPService.getInt(SPService.KEY_SCREENSHOT_RESOLUTION, 720))); - showMultipleEditDialog(new OnDialogResultListener() { + showMultipleEditDialog(SettingsActivity.this, new DialogUtils.OnDialogResultListener() { @Override public void onDialogPositive(List data) { if (data.size() != 2) { @@ -503,74 +517,9 @@ public boolean accept(File dir, String name) { }); } - private interface OnDialogResultListener { - void onDialogPositive(List data); - } - - /** - * 为多个字段配置输入框 - * - * @param title - * @param data - */ - private void showMultipleEditDialog(final OnDialogResultListener listener, String title, List> data) { - ScrollView v = (ScrollView) LayoutInflater.from(ContextUtil.getContextThemeWrapper( - SettingsActivity.this, R.style.AppDialogTheme)) - .inflate(R.layout.dialog_setting, null); - LinearLayout view = (LinearLayout) v.getChildAt(0); - final List editTexts = new ArrayList<>(); - - LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); - - // 对每一个字段添加EditText - for (Pair source : data) { - EditText edit = new EditText(this); - - // 配置字段 - edit.setHint(source.first); - edit.setText(source.second); - - // 设置其他参数 - edit.setTextColor(getResources().getColor(R.color.primaryText)); - edit.setHintTextColor(getResources().getColor(R.color.secondaryText)); - edit.setTextSize(18); - edit.setHighlightColor(getResources().getColor(R.color.colorAccent)); - - view.addView(edit, layoutParams); - editTexts.add(edit); - } - - // 显示Dialog - new AlertDialog.Builder(SettingsActivity.this, R.style.AppDialogTheme) - .setTitle(title) - .setView(v) - .setPositiveButton(R.string.constant__confirm, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - List result = new ArrayList<>(editTexts.size() + 1); - - // 获取每个编辑框的文字 - for (EditText data : editTexts) { - result.add(data.getText().toString().trim()); - } - - if (listener != null) { - listener.onDialogPositive(result); - } - dialog.dismiss(); - } - }).setNegativeButton(R.string.constant__cancel, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - dialog.dismiss(); - } - }).setCancelable(true) - .show(); - } - private void initView() { mPanel = (HeadControlPanel) findViewById(R.id.head_layout); - mPanel.setMiddleTitle(getString(R.string.constant__setting)); + mPanel.setMiddleTitle(getString(R.string.activity__setting)); mRecordScreenUploadWrapper = findViewById(R.id.recordscreen_upload_setting_wrapper); mRecordScreenUploadInfo = (TextView) findViewById(R.id.recordscreen_upload_setting_info); @@ -595,10 +544,11 @@ private void initView() { path = SPService.getString(SPService.KEY_PATCH_URL, "https://raw.githubusercontent.com/alipay/SoloPi/master/.json"); if (StringUtil.isEmpty(path)) { - mPatchListInfo.setText("未设置"); + mPatchListInfo.setText(R.string.settings__unset); } else { mPatchListInfo.setText(path); } + mGlobalParamSettingWrapper = findViewById(R.id.global_param_setting_wrapper); mOutputCharsetSettingWrapper = findViewById(R.id.output_charset_setting_wrapper); mOutputCharsetSettingInfo = (TextView) findViewById(R.id.output_charset_setting_info); @@ -666,6 +616,102 @@ private void initView() { mAboutBtn = findViewById(R.id.about_wrapper); } + + /** + * 展示全局变量配置窗口 + */ + private void showGlobalParamEdit() { + final List> paramList = new ArrayList<>(); + + String globalParam = SPService.getString(SPService.KEY_GLOBAL_SETTINGS); + JSONObject params = JSON.parseObject(globalParam); + if (params != null && params.size() > 0) { + for (String key: params.keySet()) { + paramList.add(new Pair<>(key, params.getString(key))); + } + } + + final LayoutInflater inflater = LayoutInflater.from(ContextUtil.getContextThemeWrapper( + SettingsActivity.this, R.style.AppDialogTheme)); + final View view = inflater.inflate(R.layout.dialog_global_param_setting, null); + final TagFlowLayout tagFlowLayout = (TagFlowLayout) view.findViewById(R.id.global_param_group); + final EditText paramName= (EditText) view.findViewById(R.id.global_param_name); + final EditText paramValue = (EditText) view.findViewById(R.id.global_param_value); + View paramAdd = view.findViewById(R.id.global_param_add); + + tagFlowLayout.setAdapter(new TagAdapter>(paramList) { + @Override + public View getView(FlowLayout parent, int position, Pair o) { + View root = inflater.inflate(R.layout.item_param_info, parent, false); + + TextView title = (TextView) root.findViewById(R.id.batch_execute_tag_name); + title.setText(getString(R.string.settings__global_param_key_value, o.first, o.second)); + return root; + } + }); + tagFlowLayout.setOnTagClickListener(new TagFlowLayout.OnTagClickListener() { + @Override + public boolean onTagClick(View view, int position, FlowLayout parent) { + paramList.remove(position); + tagFlowLayout.getAdapter().notifyDataChanged(); + return true; + } + }); + + paramAdd.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + String key = paramName.getText().toString().trim(); + String value = paramValue.getText().toString().trim(); + if (StringUtil.isEmpty(key) || key.contains("=")) { + toastShort(getString(R.string.setting__invalid_param_name)); + } + + // 清空输入框 + paramName.setText(""); + paramValue.setText(""); + + int replacePosition = -1; + for (int i = 0; i < paramList.size(); i++) { + if (key.equals(paramList.get(i).first)) { + replacePosition = i; + break; + } + } + + // 如果有相同的,就进行替换 + if (replacePosition > -1) { + paramList.set(replacePosition, new Pair<>(key, value)); + } else { + paramList.add(new Pair<>(key, value)); + } + + tagFlowLayout.getAdapter().notifyDataChanged(); + } + }); + + new AlertDialog.Builder(SettingsActivity.this, R.style.AppDialogTheme) + .setView(view) + .setPositiveButton(R.string.constant__confirm, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + JSONObject newGlobalParam = new JSONObject(paramList.size() + 1); + for (Pair param: paramList) { + newGlobalParam.put(param.first, param.second); + } + SPService.putString(SPService.KEY_GLOBAL_SETTINGS, newGlobalParam.toJSONString()); + dialog.dismiss(); + } + }).setNegativeButton(R.string.constant__cancel, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + } + }).setCancelable(true) + .show(); + } + + @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == REQUEST_FILE_CHOOSE) { diff --git a/src/app/src/main/java/com/alipay/hulu/adapter/BatchExecutionListAdapter.java b/src/app/src/main/java/com/alipay/hulu/adapter/BatchExecutionListAdapter.java index ece5a23..98db9f3 100644 --- a/src/app/src/main/java/com/alipay/hulu/adapter/BatchExecutionListAdapter.java +++ b/src/app/src/main/java/com/alipay/hulu/adapter/BatchExecutionListAdapter.java @@ -99,7 +99,7 @@ public View getView(int position, View convertView, ViewGroup parent) { holder.createTime.setText(DateFormat.getDateTimeInstance().format(sDate)); String caseDesc = recordCaseInfo.getCaseDesc(); if (StringUtil.isEmpty(caseDesc)) { - holder.caseDesc.setText("暂无描述"); + holder.caseDesc.setText(R.string.batch_adapter__no_desc); } else { holder.caseDesc.setText(recordCaseInfo.getCaseDesc()); } diff --git a/src/app/src/main/java/com/alipay/hulu/adapter/CaseStepAdapter.java b/src/app/src/main/java/com/alipay/hulu/adapter/CaseStepAdapter.java index 1378007..9a08b12 100644 --- a/src/app/src/main/java/com/alipay/hulu/adapter/CaseStepAdapter.java +++ b/src/app/src/main/java/com/alipay/hulu/adapter/CaseStepAdapter.java @@ -21,6 +21,8 @@ import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; +import android.widget.CheckBox; +import android.widget.CompoundButton; import android.widget.ImageView; import android.widget.RelativeLayout; import android.widget.TextView; @@ -34,7 +36,7 @@ import com.alipay.hulu.shared.node.action.PerformActionEnum; import com.alipay.hulu.shared.node.action.provider.ActionProviderManager; import com.alipay.hulu.shared.node.tree.OperationNode; -import com.alipay.hulu.shared.node.tree.export.OperationStepProvider; +import com.alipay.hulu.shared.node.tree.export.OperationStepExporter; import com.alipay.hulu.shared.node.tree.export.bean.OperationStep; import com.alipay.hulu.shared.node.utils.BitmapUtil; import com.alipay.hulu.shared.node.utils.LogicUtil; @@ -44,16 +46,23 @@ import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Locale; +import java.util.Set; /** * Created by qiaoruikai on 2019/2/18 9:50 PM. */ -public class CaseStepAdapter extends BaseAdapter implements View.OnClickListener, SlideAndDragListView.OnDragDropListener { +public class CaseStepAdapter extends BaseAdapter implements View.OnClickListener, + SlideAndDragListView.OnDragDropListener, CompoundButton.OnCheckedChangeListener { private Context context; private int SCOPE_OFFSET_DP = 10; private List data; + private boolean selectMode = false; + + private Set selectSet; private List runningScope = new ArrayList<>(); @@ -62,6 +71,7 @@ public class CaseStepAdapter extends BaseAdapter implements View.OnClickListener public CaseStepAdapter(Context context, List data) { this.context = context; this.data = data; + selectSet = new HashSet<>(); reloadScope(); } @@ -85,6 +95,64 @@ public void notifyDataSetChanged() { super.notifyDataSetChanged(); } + /** + * 设置当前模式 + * @param selectMode + */ + public void setCurrentMode(boolean selectMode) { + this.selectMode = selectMode; + notifyDataSetChanged(); + } + + /** + * 获取选中的IDX + * @return + */ + public List getAndClearSelectOperationSteps() { + reloadScope(); + Set selected = new HashSet<>(selectSet); + selectSet.clear(); + List operations = new ArrayList<>(selected.size() + 1); + if (selected.size() > 0) { + for (MyDataWrapper wrapper : data) { + if (selected.contains(wrapper.idx)) { + operations.add(wrapper); + } + } + } + + notifyDataSetChanged(); + return operations; + } + + /** + * 替换steps为step + * @param idxs + * @param step + */ + public void changeStepsToStep(Set idxs, MyDataWrapper step) { + int position = 0; + boolean findFlag = false; + Iterator wrapperIterator = data.iterator(); + while (wrapperIterator.hasNext()) { + MyDataWrapper wrapper = wrapperIterator.next(); + if (idxs.contains(wrapper.idx)) { + findFlag = true; + wrapperIterator.remove(); + } else if (!findFlag) { + position++; + } + } + + if (findFlag) { + data.add(position, step); + } else { + data.add(step); + } + + notifyDataSetChanged(); + } + @Override public int getViewTypeCount() { return 2; @@ -102,7 +170,7 @@ public Object getItem(int position) { @Override public long getItemId(int position) { - return 0; + return position; } @Override @@ -125,13 +193,30 @@ public View getView(int position, View convertView, ViewGroup parent) { TextView title = (TextView) convertView.findViewById(R.id.case_step_edit_content_title); TextView param = (TextView) convertView.findViewById(R.id.case_step_edit_content_param); ImageView icon = (ImageView) convertView.findViewById(R.id.case_step_edit_content_close); - icon.setTag(position); + CheckBox select = (CheckBox) convertView.findViewById(R.id.case_step_edit_content_check); + select.setTag(position); - // 如果是第一次加载,设置下ClickListener if (init) { icon.setOnClickListener(this); + select.setOnCheckedChangeListener(this); + } + + if (selectMode) { + select.setVisibility(View.VISIBLE); + icon.setVisibility(View.GONE); + + if (selectSet.contains(data.get(position).idx)) { + select.setChecked(true); + } else { + select.setChecked(false); + } + } else { + select.setVisibility(View.GONE); + icon.setVisibility(View.VISIBLE); } + icon.setTag(position); + // 如果是第一次加载,设置下ClickListener List occurred = new ArrayList<>(); int start = -1; List end = new ArrayList<>(); @@ -175,8 +260,8 @@ public View getView(int position, View convertView, ViewGroup parent) { String base64 = null; if (method.containsParam(ImageCompareActionProvider.KEY_TARGET_IMAGE)) { base64 = method.getParam(ImageCompareActionProvider.KEY_TARGET_IMAGE); - } else if (node != null && node.containsExtra(OperationStepProvider.CAPTURE_IMAGE_BASE64)) { - base64 = node.getExtraValue(OperationStepProvider.CAPTURE_IMAGE_BASE64); + } else if (node != null && node.containsExtra(OperationStepExporter.CAPTURE_IMAGE_BASE64)) { + base64 = node.getExtraValue(OperationStepExporter.CAPTURE_IMAGE_BASE64); } // 如果有截图的话,使用截图作为图标 @@ -275,6 +360,18 @@ public void onClick(View v) { notifyDataSetChanged(); } + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + int position = (int) buttonView.getTag(); + MyDataWrapper dataWrapper = data.get(position); + if (isChecked) { + selectSet.add(dataWrapper.idx); + } else { + selectSet.remove(dataWrapper.idx); + } + + } + public static class MyDataWrapper { public OperationStep currentStep; /** diff --git a/src/app/src/main/java/com/alipay/hulu/adapter/CaseStepMethodAdapter.java b/src/app/src/main/java/com/alipay/hulu/adapter/CaseStepMethodAdapter.java index 27f33b7..44d0f74 100644 --- a/src/app/src/main/java/com/alipay/hulu/adapter/CaseStepMethodAdapter.java +++ b/src/app/src/main/java/com/alipay/hulu/adapter/CaseStepMethodAdapter.java @@ -15,10 +15,10 @@ */ package com.alipay.hulu.adapter; -import android.support.design.widget.TextInputLayout; import android.support.v7.widget.RecyclerView; import android.text.Editable; import android.text.TextWatcher; +import android.util.Pair; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -29,11 +29,19 @@ import android.widget.TextView; import com.alipay.hulu.R; +import com.alipay.hulu.bean.CaseParamBean; +import com.alipay.hulu.common.injector.InjectorService; +import com.alipay.hulu.common.utils.LogUtil; import com.alipay.hulu.common.utils.StringUtil; +import com.alipay.hulu.shared.node.action.OperationExecutor; import com.alipay.hulu.shared.node.action.OperationMethod; +import com.alipay.hulu.util.DialogUtils; import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; import java.util.List; +import java.util.Map; import static com.alipay.hulu.shared.node.utils.LogicUtil.SCOPE; @@ -41,18 +49,35 @@ * Created by qiaoruikai on 2019/2/21 9:17 PM. */ public class CaseStepMethodAdapter extends RecyclerView.Adapter { + private static final String TAG = "CaseMethodAdapter"; private List laterList; private OperationMethod method; + private Map paramKeyMap; + List keys; public CaseStepMethodAdapter(List laterList, OperationMethod method) { this.method = method; + + // 解析实际文案 + Map paramMap = method.getActionEnum().getActionParams(); + paramKeyMap = new HashMap<>(); + for (String key: paramMap.keySet()) { + Integer res = paramMap.get(key); + if (res != null) { + paramKeyMap.put(key, StringUtil.getString(res)); + } + } + this.laterList = laterList; // 组装下参数 keys = new ArrayList<>(method.getParamKeys()); + + // 局部操作坐标字段不展示 + keys.remove(OperationExecutor.LOCAL_CLICK_POS_KEY); } @Override @@ -79,8 +104,9 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { if (holder instanceof CaseStepParamHolder) { String key = keys.get(position); + String desc = paramKeyMap.containsKey(key)? paramKeyMap.get(key): key; String value = method.getParam(key); - ((CaseStepParamHolder) holder).bindData(key, value); + ((CaseStepParamHolder) holder).bindData(key, desc, value); } else { ((SelectAdapter) holder).wrapData(laterList, method.getParam(keys.get(position))); } @@ -145,7 +171,7 @@ public void run() { public void wrapData(List list, String value) { String[] result = new String[list.size()]; int idx = 0; - for (CaseStepAdapter.MyDataWrapper item: list) { + for (CaseStepAdapter.MyDataWrapper item : list) { result[idx++] = item.currentStep.getOperationMethod().getActionEnum().getDesc(); } @@ -163,9 +189,10 @@ public void wrapData(List list, String value) { /** * 用例参数Holder */ - public static class CaseStepParamHolder extends RecyclerView.ViewHolder implements TextWatcher { - private TextInputLayout layout; + public static class CaseStepParamHolder extends RecyclerView.ViewHolder implements TextWatcher, View.OnClickListener { + private TextView title; private EditText editText; + private TextView createParamText; private String key; private String value; @@ -175,17 +202,22 @@ public static class CaseStepParamHolder extends RecyclerView.ViewHolder implemen super(itemView); this.method = method; - layout = (TextInputLayout) itemView.findViewById(R.id.item_case_step_edit_input_layout); - editText = (EditText) itemView.findViewById(R.id.item_case_step_edit_input_edit); + title = (TextView) itemView.findViewById(R.id.item_case_step_name); + editText = (EditText) itemView.findViewById(R.id.item_case_step_edit); editText.addTextChangedListener(this); + + createParamText = (TextView) itemView.findViewById(R.id.item_case_step_create_param); + createParamText.setText(R.string.method_param__set_param); + createParamText.setOnClickListener(this); } - void bindData(String key, String value) { + void bindData(String key, String desc, String value) { this.key = key; this.value = value; editText.setText(value); - layout.setHint(key); + title.setText(desc); + createParamText.setTag(key); } @Override @@ -203,5 +235,33 @@ public void afterTextChanged(Editable s) { value = s.toString(); method.putParam(key, value); } + + @Override + public void onClick(View v) { + final String key = (String) v.getTag(); + DialogUtils.showMultipleEditDialog(v.getContext(), new DialogUtils.OnDialogResultListener() { + @Override + public void onDialogPositive(List data) { + if (data == null || data.size() != 3) { + LogUtil.w(TAG, ""); + return; + } + if (StringUtil.isEmpty(data.get(0))) { + LogUtil.w(TAG, "Param name is empty" + data); + } + + CaseParamBean paramBean = new CaseParamBean(); + paramBean.setParamName(data.get(0)); + paramBean.setParamDesc(data.get(1)); + paramBean.setParamDefaultValue(data.get(2)); + InjectorService.g().pushMessage(null, paramBean); + + value = "${" + data.get(0) + "}"; + method.putParam(key, value); + editText.setText(value); + } + }, "配置参数", Arrays.asList(new Pair<>("参数名", ""), new Pair<>("参数描述", ""), + new Pair<>("默认值", value))); + } } } diff --git a/src/app/src/main/java/com/alipay/hulu/adapter/CaseStepNodeAdapter.java b/src/app/src/main/java/com/alipay/hulu/adapter/CaseStepNodeAdapter.java index 4497c26..8e2bb94 100644 --- a/src/app/src/main/java/com/alipay/hulu/adapter/CaseStepNodeAdapter.java +++ b/src/app/src/main/java/com/alipay/hulu/adapter/CaseStepNodeAdapter.java @@ -17,7 +17,6 @@ import android.graphics.Rect; import android.support.annotation.NonNull; -import android.support.design.widget.TextInputLayout; import android.support.v7.widget.RecyclerView; import android.text.Editable; import android.text.TextWatcher; @@ -25,12 +24,14 @@ import android.view.View; import android.view.ViewGroup; import android.widget.EditText; +import android.widget.TextView; import com.alibaba.fastjson.JSON; import com.alipay.hulu.R; import com.alipay.hulu.common.utils.LogUtil; import com.alipay.hulu.common.utils.StringUtil; import com.alipay.hulu.shared.node.tree.OperationNode; +import com.alipay.hulu.shared.node.tree.export.OperationStepExporter; import java.util.ArrayList; import java.util.List; @@ -60,6 +61,11 @@ public NodePropertyHolder onCreateViewHolder(ViewGroup parent, int viewType) { return new NodePropertyHolder(view, node); } + @Override + public int getItemViewType(int position) { + return StringUtil.equals(properties.get(position), OperationStepExporter.CAPTURE_IMAGE_BASE64)? 1: 0; + } + @Override public void onBindViewHolder(NodePropertyHolder holder, int position) { String property = properties.get(position); @@ -75,24 +81,27 @@ public int getItemCount() { public static class NodePropertyHolder extends RecyclerView.ViewHolder implements TextWatcher { private String key; - private TextInputLayout layout; + private TextView title; private EditText editText; + private TextView infoText; private OperationNode node; public NodePropertyHolder(View itemView, OperationNode node) { super(itemView); this.node = node; - layout = (TextInputLayout) itemView.findViewById(R.id.item_case_step_edit_input_layout); - editText = (EditText) itemView.findViewById(R.id.item_case_step_edit_input_edit); + title = (TextView) itemView.findViewById(R.id.item_case_step_name); + editText = (EditText) itemView.findViewById(R.id.item_case_step_edit); editText.addTextChangedListener(this); + infoText = (TextView) itemView.findViewById(R.id.item_case_step_create_param); + infoText.setText(""); } private void wrapData(String key, String value) { this.key = key; editText.setText(value); - layout.setHint(key); + title.setText(key); } @Override @@ -109,9 +118,9 @@ public void onTextChanged(CharSequence s, int start, int before, int count) { public void afterTextChanged(Editable s) { boolean result = updateNodeProperty(key, s.toString(), node); if (!result) { - layout.setError("格式不合法"); + infoText.setText(R.string.node__invalid_param); } else { - layout.setError(null); + infoText.setText(""); } } } @@ -173,6 +182,12 @@ static List loadPropertiesKey(OperationNode node) { list.addAll(extras.keySet()); } + // 截图放最前面 + if (list.contains(OperationStepExporter.CAPTURE_IMAGE_BASE64)) { + list.remove(OperationStepExporter.CAPTURE_IMAGE_BASE64); + list.add(0, OperationStepExporter.CAPTURE_IMAGE_BASE64); + } + return list; } diff --git a/src/app/src/main/java/com/alipay/hulu/adapter/ParamListAdapter.java b/src/app/src/main/java/com/alipay/hulu/adapter/ParamListAdapter.java new file mode 100644 index 0000000..148b6b6 --- /dev/null +++ b/src/app/src/main/java/com/alipay/hulu/adapter/ParamListAdapter.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2015-present, Ant Financial Services Group + * + * 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.alipay.hulu.adapter; + +import android.content.Context; +import android.util.Pair; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import com.alipay.hulu.R; +import com.alipay.hulu.bean.CaseParamBean; +import com.alipay.hulu.common.utils.LogUtil; +import com.alipay.hulu.util.DialogUtils; + +import java.util.Arrays; +import java.util.List; + +public class ParamListAdapter extends SoloBaseAdapter implements View.OnClickListener { + private static final String TAG = "ParamListAdapter"; + + private Context context; + + public ParamListAdapter(Context context) { + super(context); + this.context = context; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + if (convertView == null) { + convertView = LayoutInflater.from(context).inflate(R.layout.item_case_param, parent, false); + convertView.setOnClickListener(this); + } + + convertView.setTag(position); + + TextView title = (TextView) convertView.findViewById(R.id.param_item_title); + TextView desc = (TextView) convertView.findViewById(R.id.param_item_desc); + TextView defaultValue = (TextView) convertView.findViewById(R.id.param_item_default_value); + + CaseParamBean param = (CaseParamBean) getItem(position); + title.setText(param.getParamName()); + desc.setText(param.getParamDesc()); + defaultValue.setText(param.getParamDefaultValue()); + + return convertView; + } + + @Override + public void onClick(View v) { + int position = (int) v.getTag(); + final CaseParamBean param = (CaseParamBean) getItem(position); + + DialogUtils.showMultipleEditDialog(context, new DialogUtils.OnDialogResultListener() { + @Override + public void onDialogPositive(List data) { + if (data == null || data.size() != 2) { + LogUtil.w(TAG, "Edit param %s failed, not suitable result %s", param, data); + return; + } + + param.setParamDesc(data.get(0)); + param.setParamDefaultValue(data.get(1)); + + notifyDataSetChanged(); + } + }, "编辑参数-" + param.getParamName(), + Arrays.asList(new Pair<>("参数描述", param.getParamDesc()), + new Pair<>("默认值", param.getParamDefaultValue()))); + } +} diff --git a/src/app/src/main/java/com/alipay/hulu/adapter/PerformStressAdapter.java b/src/app/src/main/java/com/alipay/hulu/adapter/PerformStressAdapter.java index 179a316..a9b7a12 100644 --- a/src/app/src/main/java/com/alipay/hulu/adapter/PerformStressAdapter.java +++ b/src/app/src/main/java/com/alipay/hulu/adapter/PerformStressAdapter.java @@ -26,6 +26,7 @@ import android.widget.Toast; import com.alipay.hulu.R; +import com.alipay.hulu.common.application.LauncherApplication; import com.alipay.hulu.common.utils.LogUtil; import com.alipay.hulu.shared.display.items.MemoryTools; import com.alipay.hulu.tools.PerformStressImpl; @@ -55,21 +56,21 @@ private void init() { Map map = new HashMap(); map.put("img", android.R.drawable.ic_menu_crop); - map.put("title", "CPU负载(%)"); + map.put("title", cx.getString(R.string.stress__cpu_load)); map.put("process", 0); map.put("max", 100); mData.add(map); map = new HashMap(); map.put("img", android.R.drawable.ic_menu_crop); - map.put("title", "CPU多核(n)"); + map.put("title", cx.getString(R.string.stress__cpu_core)); map.put("process", 1); map.put("max", getCpuCoreNum()); mData.add(map); map = new HashMap(); map.put("img", android.R.drawable.ic_menu_crop); - map.put("title", "内存占用(m)"); + map.put("title", cx.getString(R.string.stress__memory)); map.put("process", 0); map.put("max", MemoryTools.getAvailMemory(cx).intValue()); @@ -138,11 +139,6 @@ public void onStopTrackingTouch(SeekBar seekBar) { // TODO 改成接口定义通用加压方法 switch (position) { case 0:// CPU占用率 - performStressImpl.performCpuStressByCount((int) mData.get(0).get("process"), (int) mData.get(1) - .get("process")); - // CPUTools.performStress((int) - // mData.get(position).get("process")); - break; case 1:// CPU多核 performStressImpl.performCpuStressByCount((int) mData.get(0).get("process"), (int) mData.get(1) .get("process")); @@ -154,7 +150,7 @@ public void onStopTrackingTouch(SeekBar seekBar) { seekBar.setProgress(allocMemory); } } catch (OutOfMemoryError e) { - Toast.makeText(cx, "内存不足:" + e,Toast.LENGTH_SHORT).show(); + LauncherApplication.toast(R.string.stress__insufficient_memory, e.getMessage()); } break; default: diff --git a/src/app/src/main/java/com/alipay/hulu/adapter/ReplayListAdapter.java b/src/app/src/main/java/com/alipay/hulu/adapter/ReplayListAdapter.java index 007225e..4ab73a6 100644 --- a/src/app/src/main/java/com/alipay/hulu/adapter/ReplayListAdapter.java +++ b/src/app/src/main/java/com/alipay/hulu/adapter/ReplayListAdapter.java @@ -82,8 +82,8 @@ public View getView(int position, View convertView, ViewGroup parent) { holder.caseName = (TextView) convertView.findViewById(R.id.case_name); holder.caseDesc = (TextView) convertView.findViewById(R.id.case_desc); holder.createTime = (TextView) convertView.findViewById(R.id.create_time); - holder.edit = (RelativeLayout) convertView.findViewById(R.id.case_edit); - holder.edit.setOnClickListener(this); + holder.play = (RelativeLayout) convertView.findViewById(R.id.case_play); + holder.play.setOnClickListener(this); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); @@ -96,11 +96,11 @@ public View getView(int position, View convertView, ViewGroup parent) { holder.createTime.setText(DateFormat.getDateTimeInstance().format(sDate)); String caseDesc = recordCaseInfo.getCaseDesc(); if (StringUtil.isEmpty(caseDesc)) { - holder.caseDesc.setText("暂无描述"); + holder.caseDesc.setText(R.string.replay_list__no_desc); } else { holder.caseDesc.setText(recordCaseInfo.getCaseDesc()); } - holder.edit.setTag(position); + holder.play.setTag(position); } return convertView; } @@ -137,7 +137,7 @@ public void deleteCaseById(long id) { } } - public void setOnEditClickListener(AdapterView.OnItemClickListener onItemClickListener) { + public void setOnPlayClickListener(AdapterView.OnItemClickListener onItemClickListener) { this.onItemClickListener = onItemClickListener; } @@ -145,7 +145,7 @@ static class ViewHolder { TextView caseName; TextView caseDesc; TextView createTime; - RelativeLayout edit; + RelativeLayout play; } } diff --git a/src/app/src/main/java/com/alipay/hulu/adapter/SoloBaseAdapter.java b/src/app/src/main/java/com/alipay/hulu/adapter/SoloBaseAdapter.java new file mode 100644 index 0000000..969d2f8 --- /dev/null +++ b/src/app/src/main/java/com/alipay/hulu/adapter/SoloBaseAdapter.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2015-present, Ant Financial Services Group + * + * 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.alipay.hulu.adapter; + +import android.content.Context; +import android.widget.BaseAdapter; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by lezhou.wyl on 2018/8/15. + */ + +public abstract class SoloBaseAdapter extends BaseAdapter { + + protected Context mContext; + protected List mData = new ArrayList<>(); + + public SoloBaseAdapter(Context context) { + mContext = context; + } + + @Override + public int getCount() { + return mData.size(); + } + + @Override + public Object getItem(int position) { + return mData.get(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + public void setData(List data) { + if (data == null) { + mData = new ArrayList<>(); + } else { + mData = new ArrayList<>(data); + } + notifyDataSetChanged(); + } + + public List getData() { + List result = new ArrayList<>(); + if (mData != null) { + result.addAll(mData); + } + + return result; + } +} diff --git a/src/app/src/main/java/com/alipay/hulu/bean/AdvanceCaseSetting.java b/src/app/src/main/java/com/alipay/hulu/bean/AdvanceCaseSetting.java index 1597742..242f7f9 100644 --- a/src/app/src/main/java/com/alipay/hulu/bean/AdvanceCaseSetting.java +++ b/src/app/src/main/java/com/alipay/hulu/bean/AdvanceCaseSetting.java @@ -15,6 +15,8 @@ */ package com.alipay.hulu.bean; +import java.util.List; + /** * Created by lezhou.wyl on 2018/7/16. */ @@ -22,6 +24,8 @@ public class AdvanceCaseSetting { private String descriptorMode; private int version; + private List params; + private CaseRunningParam runningParam; public String getDescriptorMode() { return descriptorMode; @@ -38,4 +42,21 @@ public int getVersion() { public void setVersion(int version) { this.version = version; } + + + public List getParams() { + return params; + } + + public void setParams(List params) { + this.params = params; + } + + public CaseRunningParam getRunningParam() { + return runningParam; + } + + public void setRunningParam(CaseRunningParam runningParam) { + this.runningParam = runningParam; + } } \ No newline at end of file diff --git a/src/app/src/main/java/com/alipay/hulu/bean/CaseParamBean.java b/src/app/src/main/java/com/alipay/hulu/bean/CaseParamBean.java new file mode 100644 index 0000000..663d0ca --- /dev/null +++ b/src/app/src/main/java/com/alipay/hulu/bean/CaseParamBean.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2015-present, Ant Financial Services Group + * + * 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.alipay.hulu.bean; + +public class CaseParamBean { + /** + * 参数名称 + */ + private String paramName; + + /** + * 参数描述 + */ + private String paramDesc; + + /** + * 参数默认值 + */ + private String paramDefaultValue; + + public String getParamName() { + return paramName; + } + + public void setParamName(String paramName) { + this.paramName = paramName; + } + + public String getParamDesc() { + return paramDesc; + } + + public void setParamDesc(String paramDesc) { + this.paramDesc = paramDesc; + } + + public String getParamDefaultValue() { + return paramDefaultValue; + } + + public void setParamDefaultValue(String paramDefaultValue) { + this.paramDefaultValue = paramDefaultValue; + } + + @Override + public String toString() { + return "CaseParamBean{" + + "paramName='" + paramName + '\'' + + ", paramDesc='" + paramDesc + '\'' + + ", paramDefaultValue='" + paramDefaultValue + '\'' + + '}'; + } +} diff --git a/src/app/src/main/java/com/alipay/hulu/bean/CaseRunningParam.java b/src/app/src/main/java/com/alipay/hulu/bean/CaseRunningParam.java new file mode 100644 index 0000000..d5f76a8 --- /dev/null +++ b/src/app/src/main/java/com/alipay/hulu/bean/CaseRunningParam.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2015-present, Ant Financial Services Group + * + * 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.alipay.hulu.bean; + +import com.alibaba.fastjson.JSONObject; + +import java.util.List; + +/** + * Created by qiaoruikai on 2019-08-19 21:05. + */ +public class CaseRunningParam { + private ParamMode mode; + private List paramList; + + public ParamMode getMode() { + return mode; + } + + public void setMode(ParamMode mode) { + this.mode = mode; + } + + public List getParamList() { + return paramList; + } + + public void setParamList(List paramList) { + this.paramList = paramList; + } + + /** + * 可选模式 + */ + public enum ParamMode { + SEPARATE, + UNION + } +} diff --git a/src/app/src/main/java/com/alipay/hulu/bean/CaseStepHolder.java b/src/app/src/main/java/com/alipay/hulu/bean/CaseStepHolder.java index 5009937..f7729b1 100644 --- a/src/app/src/main/java/com/alipay/hulu/bean/CaseStepHolder.java +++ b/src/app/src/main/java/com/alipay/hulu/bean/CaseStepHolder.java @@ -16,8 +16,12 @@ package com.alipay.hulu.bean; import com.alipay.hulu.shared.io.bean.RecordCaseInfo; +import com.alipay.hulu.shared.node.tree.export.bean.OperationStep; +import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; @@ -27,6 +31,7 @@ public class CaseStepHolder { private static Map caseHolder = new HashMap<>(); private static Map replayHolder = new HashMap<>(); + private static List pasteContentHolder; private static final AtomicInteger counter = new AtomicInteger(1); private static final AtomicInteger replayCounter = new AtomicInteger(1); @@ -42,6 +47,34 @@ public static int storeCase(RecordCaseInfo caseInfo) { return id; } + /** + * 暂存拷贝步骤 + * @param pasteContent + */ + public static void storePasteContent(List pasteContent) { + pasteContentHolder = new ArrayList<>(pasteContent); + } + + /** + * 获取并置空拷贝步骤 + * @return + */ + public static List getPasteContent() { + if (pasteContentHolder == null) { + return Collections.EMPTY_LIST; + } + + return new ArrayList<>(pasteContentHolder); + } + + /** + * 是否包含拷贝步骤 + * @return + */ + public static boolean containsPasteContent() { + return pasteContentHolder != null; + } + /** * 获取用例 * @param id diff --git a/src/app/src/main/java/com/alipay/hulu/event/ScanSuccessEvent.java b/src/app/src/main/java/com/alipay/hulu/event/ScanSuccessEvent.java index 5e3ecad..79ee252 100644 --- a/src/app/src/main/java/com/alipay/hulu/event/ScanSuccessEvent.java +++ b/src/app/src/main/java/com/alipay/hulu/event/ScanSuccessEvent.java @@ -25,6 +25,7 @@ public class ScanSuccessEvent implements Parcelable { public static final int SCAN_TYPE_SCHEME = 1; + public static final int SCAN_TYPE_PARAM = 6; private int type; private String content; diff --git a/src/app/src/main/java/com/alipay/hulu/fragment/BatchExecutionFragment.java b/src/app/src/main/java/com/alipay/hulu/fragment/BatchExecutionFragment.java index 017a966..6034175 100644 --- a/src/app/src/main/java/com/alipay/hulu/fragment/BatchExecutionFragment.java +++ b/src/app/src/main/java/com/alipay/hulu/fragment/BatchExecutionFragment.java @@ -16,24 +16,17 @@ package com.alipay.hulu.fragment; import android.os.Bundle; -import android.provider.Settings; import android.support.annotation.Nullable; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.Button; -import android.widget.CheckBox; -import android.widget.CompoundButton; import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; import com.alipay.hulu.R; -import com.alipay.hulu.activity.BaseActivity; import com.alipay.hulu.activity.BatchExecutionActivity; -import com.alipay.hulu.activity.MyApplication; import com.alipay.hulu.adapter.BatchExecutionListAdapter; -import com.alipay.hulu.common.application.LauncherApplication; import com.alipay.hulu.common.tools.BackgroundExecutor; import com.alipay.hulu.common.utils.PermissionUtil; import com.alipay.hulu.replay.BatchStepProvider; @@ -42,7 +35,6 @@ import com.alipay.hulu.shared.io.db.GreenDaoManager; import com.alipay.hulu.shared.io.db.RecordCaseInfoDao; -import java.util.Arrays; import java.util.List; /** @@ -143,7 +135,7 @@ private void initListView(View view) { private void initEmptyView(View view) { mEmptyView = view.findViewById(R.id.empty_view_container); mEmptyTextView = (TextView) view.findViewById(R.id.empty_text); - mEmptyTextView.setText("没有发现用例"); + mEmptyTextView.setText(R.string.batch__no_case); } private void showEnableAccessibilityServiceHint() { diff --git a/src/app/src/main/java/com/alipay/hulu/fragment/CaseDescEditFragment.java b/src/app/src/main/java/com/alipay/hulu/fragment/CaseDescEditFragment.java index 75e9bb4..f1014d2 100644 --- a/src/app/src/main/java/com/alipay/hulu/fragment/CaseDescEditFragment.java +++ b/src/app/src/main/java/com/alipay/hulu/fragment/CaseDescEditFragment.java @@ -21,28 +21,45 @@ import android.view.View; import android.view.ViewGroup; import android.widget.EditText; +import android.widget.ListView; import com.alibaba.fastjson.JSON; +import com.alipay.hulu.adapter.ParamListAdapter; import com.alipay.hulu.R; import com.alipay.hulu.activity.CaseEditActivity; import com.alipay.hulu.bean.AdvanceCaseSetting; +import com.alipay.hulu.bean.CaseParamBean; +import com.alipay.hulu.common.injector.InjectorService; +import com.alipay.hulu.common.injector.param.RunningThread; +import com.alipay.hulu.common.injector.param.Subscriber; +import com.alipay.hulu.common.injector.provider.Param; import com.alipay.hulu.common.utils.LogUtil; import com.alipay.hulu.common.utils.StringUtil; import com.alipay.hulu.shared.io.bean.RecordCaseInfo; +import java.util.Collections; +import java.util.List; + public class CaseDescEditFragment extends BaseFragment implements CaseEditActivity.OnCaseSaveListener { private static final String TAG = "CaseStepEditFrag"; - public static final String RECORD_CASE_EXTRA = "record_case"; - private RecordCaseInfo mRecordCase; + private AdvanceCaseSetting setting; + private EditText mCaseName; private EditText mCaseDesc; + private ListView mParams; - // 用例版本号 - private int caseVersion = 0; + private ParamListAdapter adapter; + + @Subscriber(value = @Param(sticky = false), thread = RunningThread.MAIN_THREAD) + public void receiveNewParam(CaseParamBean param) { + List paramBeanList = adapter.getData(); + paramBeanList.add(param); + adapter.setData(paramBeanList); + } /** * 通过RecordCase初始化 @@ -62,6 +79,7 @@ public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, // 获取各项控件 mCaseName = (EditText) root.findViewById(R.id.case_name); mCaseDesc = (EditText) root.findViewById(R.id.case_desc); + mParams = (ListView) root.findViewById(R.id.case_params); return root; } @@ -83,12 +101,17 @@ private void initData() { mCaseName.setText(mRecordCase.getCaseName()); mCaseDesc.setText(mRecordCase.getCaseDesc()); + setting = JSON.parseObject(mRecordCase.getAdvanceSettings() + , AdvanceCaseSetting.class); + + // 参数列表 + adapter = new ParamListAdapter(getActivity()); + mParams.setAdapter(adapter); - // 如果有高级设置 - if (!StringUtil.isEmpty(mRecordCase.getAdvanceSettings())) { - AdvanceCaseSetting setting = JSON.parseObject(mRecordCase.getAdvanceSettings(), - AdvanceCaseSetting.class); - caseVersion = setting.getVersion(); + if (setting != null) { + adapter.setData(setting.getParams()); + } else { + mParams.setVisibility(View.GONE); } } @@ -97,9 +120,24 @@ public void onCaseSave() { mRecordCase.setCaseName(mCaseName.getText().toString()); mRecordCase.setCaseDesc(mCaseDesc.getText().toString()); - AdvanceCaseSetting advanceCaseSetting = new AdvanceCaseSetting(); - advanceCaseSetting.setVersion(caseVersion); + if (setting == null) { + setting = new AdvanceCaseSetting(); + } + if (adapter != null && mParams.getVisibility() == View.VISIBLE) { + setting.setParams(adapter.getData()); + } + mRecordCase.setAdvanceSettings(JSON.toJSONString(setting)); + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + InjectorService.g().register(this); + } - mRecordCase.setAdvanceSettings(JSON.toJSONString(advanceCaseSetting)); + @Override + public void onDestroy() { + super.onDestroy(); + InjectorService.g().unregister(this); } } diff --git a/src/app/src/main/java/com/alipay/hulu/fragment/CaseParamSeparateFragment.java b/src/app/src/main/java/com/alipay/hulu/fragment/CaseParamSeparateFragment.java new file mode 100644 index 0000000..9b354ad --- /dev/null +++ b/src/app/src/main/java/com/alipay/hulu/fragment/CaseParamSeparateFragment.java @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2015-present, Ant Financial Services Group + * + * 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.alipay.hulu.fragment; + +import android.content.Intent; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.EditText; +import android.widget.ListView; +import android.widget.TextView; + +import com.alibaba.fastjson.JSONObject; +import com.alipay.hulu.R; +import com.alipay.hulu.activity.CaseParamEditActivity; +import com.alipay.hulu.bean.AdvanceCaseSetting; +import com.alipay.hulu.bean.CaseParamBean; +import com.alipay.hulu.bean.CaseRunningParam; +import com.alipay.hulu.common.utils.LogUtil; +import com.alipay.hulu.common.utils.StringUtil; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * Created by qiaoruikai on 2019-08-19 23:37. + */ +public class CaseParamSeparateFragment extends CaseParamEditActivity.CaseParamFragment { + private static final String TAG = "CaseParamSeparateFragment"; + private ListView paramList; + + // 用例参数设置 + private List presetParams; + private List holders; + private CaseRunningParam runningParam; + private Map storedParams; + private ParamHolder waitingHolder; + + /** + * 设置高级设置 + * + * @param advanceCaseSetting + */ + @Override + public void setAdvanceCaseSetting(@NonNull AdvanceCaseSetting advanceCaseSetting) { + storedParams = new LinkedHashMap<>(); + presetParams = advanceCaseSetting.getParams(); + runningParam = advanceCaseSetting.getRunningParam(); + if (runningParam == null) { + runningParam = new CaseRunningParam(); + } + + // 如果之前有存储p + if (runningParam.getMode() == CaseRunningParam.ParamMode.SEPARATE) { + List params = runningParam.getParamList(); + if (params != null) { + for (JSONObject obj: params) { + for (String key: obj.keySet()) { + storedParams.put(key, obj.getString(key)); + } + } + } + } + } + + @Override + public CaseRunningParam getRunningParam() { + int count = paramList.getCount(); + List params = new ArrayList<>(count + 1); + for (String key: storedParams.keySet()) { + JSONObject paramInfo = new JSONObject(2); + paramInfo.put(key, storedParams.get(key)); + params.add(paramInfo); + } + LogUtil.d(TAG,"message:" + params); + + runningParam.setMode(CaseRunningParam.ParamMode.SEPARATE); + runningParam.setParamList(params); + return runningParam; + } + + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + View root = inflater.inflate(R.layout.dialog_param_edit, container, false); + paramList = (ListView) root.findViewById(R.id.dialog_param_list); + + return root; + } + + @Override + public void onActivityCreated(@Nullable Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + if (presetParams != null) { + holders = new ArrayList<>(presetParams.size() + 1); + for (CaseParamBean param : presetParams) { + ParamHolder holder = new ParamHolder(); + holder.param = param; + holders.add(holder); + } + + final LayoutInflater inflater = LayoutInflater.from(getActivity()); + + paramList.setAdapter(new BaseAdapter() { + @Override + public int getCount() { + return holders.size(); + } + + @Override + public Object getItem(int position) { + return holders.get(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + if (convertView == null) { + convertView = inflater.inflate(R.layout.item_case_step_edit_input, parent, false); + convertView.findViewById(R.id.item_case_step_create_param).setVisibility(View.GONE); + } + TextView title = (TextView) convertView.findViewById(R.id.item_case_step_name); + final EditText edit = (EditText) convertView.findViewById(R.id.item_case_step_edit); + + // 移除旧的textWatcher + TextWatcher oldTextWatcher = (TextWatcher) edit.getTag(); + if (oldTextWatcher != null) { + edit.removeTextChangedListener(oldTextWatcher); + } + + final ParamHolder holder = (ParamHolder) getItem(position); + final CaseParamBean paramBean = holder.param; + String desc = StringUtil.isEmpty(paramBean.getParamDesc()) ? paramBean.getParamName() : paramBean.getParamDesc(); + + String defaultValue = storedParams.get(paramBean.getParamName()); + if (defaultValue == null) { + defaultValue = ""; + } + edit.setText(defaultValue); + TextWatcher textWatcher = new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + storedParams.put(paramBean.getParamName(), s.toString()); + } + + @Override + public void afterTextChanged(Editable s) { + + } + }; + edit.setTag(textWatcher); + edit.addTextChangedListener(textWatcher); + + title.setText(desc); + + return convertView; + } + }); + } + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + + LogUtil.d(TAG, "On activity result: %d, %d, %s", requestCode, resultCode, data); + } + + private static class ParamHolder { + private CaseParamBean param; + } +} diff --git a/src/app/src/main/java/com/alipay/hulu/fragment/CaseParamUnionFragment.java b/src/app/src/main/java/com/alipay/hulu/fragment/CaseParamUnionFragment.java new file mode 100644 index 0000000..995a9a9 --- /dev/null +++ b/src/app/src/main/java/com/alipay/hulu/fragment/CaseParamUnionFragment.java @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2015-present, Ant Financial Services Group + * + * 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.alipay.hulu.fragment; + +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.Button; +import android.widget.EditText; +import android.widget.ListView; +import android.widget.TextView; + +import com.alibaba.fastjson.JSONObject; +import com.alipay.hulu.R; +import com.alipay.hulu.activity.CaseParamEditActivity; +import com.alipay.hulu.bean.AdvanceCaseSetting; +import com.alipay.hulu.bean.CaseParamBean; +import com.alipay.hulu.bean.CaseRunningParam; +import com.alipay.hulu.common.utils.StringUtil; +import com.zhy.view.flowlayout.FlowLayout; +import com.zhy.view.flowlayout.TagAdapter; +import com.zhy.view.flowlayout.TagFlowLayout; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by qiaoruikai on 2019-08-19 22:25. + */ +public class CaseParamUnionFragment extends CaseParamEditActivity.CaseParamFragment { + private TagFlowLayout tagFlowLayout; + private ListView paramList; + private Button addBtn; + + // 用例参数设置 + private List presetParams; + private CaseRunningParam runningParam; + private List storedParams; + + + /** + * 设置高级设置 + * + * @param advanceCaseSetting + */ + @Override + public void setAdvanceCaseSetting(@NonNull AdvanceCaseSetting advanceCaseSetting) { + storedParams = null; + presetParams = advanceCaseSetting.getParams(); + runningParam = advanceCaseSetting.getRunningParam(); + if (runningParam == null) { + runningParam = new CaseRunningParam(); + } + + // 如果之前有存储p + if (runningParam.getMode() == CaseRunningParam.ParamMode.UNION) { + storedParams = new ArrayList<>(runningParam.getParamList()); + } + + if (storedParams == null) { + storedParams = new ArrayList<>(); + } + } + + @Override + public CaseRunningParam getRunningParam() { + runningParam.setMode(CaseRunningParam.ParamMode.UNION); + runningParam.setParamList(storedParams); + return runningParam; + } + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + View root = inflater.inflate(R.layout.fragment_union_param, container, false); + tagFlowLayout = (TagFlowLayout) root.findViewById(R.id.union_param_group); + paramList = (ListView) root.findViewById(R.id.union_param_list); + addBtn = (Button) root.findViewById(R.id.union_param_add); + + return root; + } + + @Override + public void onActivityCreated(@Nullable Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + final LayoutInflater inflater = LayoutInflater.from(getActivity()); + + tagFlowLayout.setAdapter(new TagAdapter(storedParams) { + @Override + public View getView(FlowLayout parent, int position, JSONObject o) { + View root = inflater.inflate(R.layout.item_param_info, parent, false); + List diffParams = new ArrayList<>(); + for (CaseParamBean paramBean: presetParams) { + diffParams.add(o.getString(paramBean.getParamName())); + } + + TextView title = (TextView) root.findViewById(R.id.batch_execute_tag_name); + title.setText(StringUtil.join(",", diffParams)); + return root; + } + }); + tagFlowLayout.setOnTagClickListener(new TagFlowLayout.OnTagClickListener() { + @Override + public boolean onTagClick(View view, int position, FlowLayout parent) { + storedParams.remove(position); + tagFlowLayout.getAdapter().notifyDataChanged(); + return true; + } + }); + + final List holders = new ArrayList<>(); + for (CaseParamBean param: presetParams) { + ParamHolder holder = new ParamHolder(); + holder.param = param; + holders.add(holder); + } + paramList.setAdapter(new BaseAdapter() { + @Override + public int getCount() { + return holders.size(); + } + + @Override + public Object getItem(int position) { + return holders.get(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + if (convertView == null) { + convertView = inflater.inflate(R.layout.item_case_step_edit_input, parent, false); + convertView.findViewById(R.id.item_case_step_create_param).setVisibility(View.GONE); + } + TextView title = (TextView) convertView.findViewById(R.id.item_case_step_name); + EditText edit = (EditText) convertView.findViewById(R.id.item_case_step_edit); + + ParamHolder holder = (ParamHolder) getItem(position); + CaseParamBean paramBean = holder.param; + String desc = StringUtil.isEmpty(paramBean.getParamDesc())? paramBean.getParamName(): paramBean.getParamDesc(); + + title.setText(desc); + holder.edit = edit; + + return convertView; + } + }); + + addBtn.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + JSONObject obj = new JSONObject(holders.size() + 1); + for (ParamHolder holder : holders) { + obj.put(holder.param.getParamName(), holder.edit.getText().toString()); + holder.edit.setText(""); + } + + storedParams.add(obj); + ((BaseAdapter)paramList.getAdapter()).notifyDataSetChanged(); + tagFlowLayout.getAdapter().notifyDataChanged(); + } + }); + } + + private static class ParamHolder { + private CaseParamBean param; + private EditText edit; + } +} diff --git a/src/app/src/main/java/com/alipay/hulu/fragment/CaseStepEditFragment.java b/src/app/src/main/java/com/alipay/hulu/fragment/CaseStepEditFragment.java index 50bbfce..028a410 100644 --- a/src/app/src/main/java/com/alipay/hulu/fragment/CaseStepEditFragment.java +++ b/src/app/src/main/java/com/alipay/hulu/fragment/CaseStepEditFragment.java @@ -24,6 +24,7 @@ import android.os.Build; import android.os.Bundle; import android.os.Parcel; +import android.provider.Settings; import android.support.annotation.Nullable; import android.support.design.widget.TabLayout; import android.support.v7.app.AlertDialog; @@ -48,6 +49,7 @@ import com.alipay.hulu.adapter.CaseStepAdapter; import com.alipay.hulu.adapter.CaseStepMethodAdapter; import com.alipay.hulu.adapter.CaseStepNodeAdapter; +import com.alipay.hulu.bean.CaseStepHolder; import com.alipay.hulu.common.application.LauncherApplication; import com.alipay.hulu.common.injector.InjectorService; import com.alipay.hulu.common.injector.param.RunningThread; @@ -55,9 +57,11 @@ import com.alipay.hulu.common.injector.provider.Param; import com.alipay.hulu.common.tools.BackgroundExecutor; import com.alipay.hulu.common.utils.ContextUtil; -import com.alipay.hulu.common.utils.LogUtil; +import com.alipay.hulu.common.utils.PermissionUtil; import com.alipay.hulu.common.utils.StringUtil; import com.alipay.hulu.event.ScanSuccessEvent; +import com.alipay.hulu.service.CaseRecordManager; +import com.alipay.hulu.shared.io.OperationStepService; import com.alipay.hulu.shared.io.bean.GeneralOperationLogBean; import com.alipay.hulu.shared.io.bean.RecordCaseInfo; import com.alipay.hulu.shared.io.db.GreenDaoManager; @@ -67,9 +71,12 @@ import com.alipay.hulu.shared.node.action.PerformActionEnum; import com.alipay.hulu.shared.node.tree.AbstractNodeTree; import com.alipay.hulu.shared.node.tree.export.bean.OperationStep; +import com.alipay.hulu.shared.node.utils.AppUtil; import com.alipay.hulu.shared.node.utils.LogicUtil; +import com.alipay.hulu.shared.node.utils.PrepareUtil; import com.alipay.hulu.ui.MaxHeightScrollView; import com.alipay.hulu.ui.TwoLevelSelectLayout; +import com.alipay.hulu.util.CaseAppendOperationProcessor; import com.alipay.hulu.util.FunctionSelectUtil; import com.yydcdut.sdlv.Menu; import com.yydcdut.sdlv.MenuItem; @@ -78,11 +85,8 @@ import com.zhy.view.flowlayout.TagAdapter; import com.zhy.view.flowlayout.TagFlowLayout; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileReader; -import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -94,6 +98,8 @@ public class CaseStepEditFragment extends BaseFragment implements TagFlowLayout. private static final String TAG = "CaseStepEditFragment"; private boolean isOverrideInstall = false; + private boolean selectMode = false; + private RecordCaseInfo recordCase; private TagFlowLayout tagGroup; @@ -158,6 +164,21 @@ public void onScanEvent(final ScanSuccessEvent event) { dragEntities.add(wrapper); + adapter.notifyDataSetChanged(); + break; + case ScanSuccessEvent.SCAN_TYPE_PARAM: + // 向handler发送请求 + method = new OperationMethod(PerformActionEnum.LOAD_PARAM); + method.putParam(OperationExecutor.APP_URL_KEY, event.getContent()); + step = new OperationStep(); + step.setOperationMethod(method); + step.setOperationIndex(currentIdx.get()); + step.setOperationId(stepList.get(stepList.size() - 1).getOperationId()); + + wrapper = new CaseStepAdapter.MyDataWrapper(step, currentIdx.getAndIncrement()); + + dragEntities.add(wrapper); + adapter.notifyDataSetChanged(); // 录制模式需要记录下 @@ -197,7 +218,10 @@ private void initData() { final List stepTags = new ArrayList<>(stepList.size() + 2); // 每一步添加一个实体 - stepTags.add("新步骤"); + stepTags.add(getString(R.string.step_edit__new_step)); + stepTags.add(getString(R.string.step_edit__select_mode)); + stepTags.add(getString(R.string.step_edit__paste)); + stepTags.add(getString(R.string.step_edit__record_step)); for (OperationStep step: stepList) { CaseStepAdapter.MyDataWrapper entity = new CaseStepAdapter.MyDataWrapper(clone(step), currentIdx.getAndIncrement()); dragEntities.add(entity); @@ -218,12 +242,30 @@ public View getView(FlowLayout parent, int position, String o) { ImageView icon = (ImageView) tag.findViewById(R.id.case_step_edit_tag_icon); if (position == 0) { - title.setText("新步骤"); - icon.setImageResource(R.drawable.case_step_add); + if (!selectMode) { + title.setText(R.string.step_edit__new_step); + icon.setImageResource(R.drawable.case_step_add); + } else { + title.setText(R.string.step_edit__copy); + icon.setImageResource(R.drawable.case_step_copy); + } + } else if (position == 1) { + if (selectMode) { + title.setText(R.string.step_edit__exit_select); + } else { + title.setText(R.string.step_edit__select_mode); + } + icon.setImageResource(R.drawable.case_step_select); + } else if (position == 2) { + title.setText(o); + icon.setImageResource(R.drawable.case_step_paste); + } else if (position == 3) { + title.setText(o); + icon.setImageResource(R.drawable.recording); } else { // 加载下 - OperationStep step = stepList.get(position - 1); + OperationStep step = stepList.get(position - 4); PerformActionEnum actionEnum = step.getOperationMethod().getActionEnum(); // 设置资源 @@ -237,6 +279,7 @@ public View getView(FlowLayout parent, int position, String o) { // 用例adapter adapter = new CaseStepAdapter(getActivity(), dragEntities); + adapter.setCurrentMode(selectMode); // 设置菜单相关样式 int dp64 = ContextUtil.dip2px(getActivity(), 64); @@ -252,10 +295,10 @@ public View getView(FlowLayout parent, int position, String o) { // 转换模式 Menu menu = new Menu(true, 0); - menu.addItem(new MenuItem.Builder().setText("转换为IF").setTextColor(Color.WHITE).setWidth(dp64) + menu.addItem(new MenuItem.Builder().setText(getString(R.string.step_edit__convert_if)).setTextColor(Color.WHITE).setWidth(dp64) .setDirection(MenuItem.DIRECTION_RIGHT) .setBackground(new ColorDrawable(colorIf)).build()); - menu.addItem(new MenuItem.Builder().setText("转换为WHILE").setTextColor(Color.WHITE) + menu.addItem(new MenuItem.Builder().setText(getString(R.string.step_edit__convert_while)).setTextColor(Color.WHITE) .setWidth(dp64) .setDirection(MenuItem.DIRECTION_RIGHT) .setBackground(new ColorDrawable(colorWhile)).build()); @@ -302,14 +345,106 @@ public void onItemClick(AdapterView parent, View view, int position, long id) }); } + /** + * 切换选择模式 + */ + private void switchSelectMode() { + getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + selectMode = !selectMode; + tagGroup.getAdapter().notifyDataChanged(); + adapter.setCurrentMode(selectMode); + } + }); + } + @Override public boolean onTagClick(View view, int position, FlowLayout parent) { if (position == 0) { - showAddFunctionView(); + if (!selectMode) { + showAddFunctionView(); + } else { + List wrappers = adapter.getAndClearSelectOperationSteps(); + if (wrappers.size() == 0) { + return true; + } + List steps = new ArrayList<>(wrappers.size() + 1); + for (CaseStepAdapter.MyDataWrapper wrapper: wrappers) { + steps.add(wrapper.currentStep); + } + + CaseStepHolder.storePasteContent(steps); + switchSelectMode(); + } + return true; + } else if (position == 1) { + switchSelectMode(); + return true; + } else if (position == 2) { + List pasteSteps = CaseStepHolder.getPasteContent(); + if (pasteSteps != null && pasteSteps.size() > 0) { + for (OperationStep step: pasteSteps) { + CaseStepAdapter.MyDataWrapper wrapper = new CaseStepAdapter.MyDataWrapper(step, currentIdx.getAndIncrement()); + dragEntities.add(wrapper); + } + + adapter.notifyDataSetChanged(); + } + return true; + } else if (position == 3) { + final CaseEditActivity activity = (CaseEditActivity) getActivity(); + activity.wrapRecordCase(); + + final RecordCaseInfo caseInfo = activity.getRecordCase(); + if (caseInfo == null) { + return false; + } + + // 检查权限 + PermissionUtil.requestPermissions(Arrays.asList("adb", Settings.ACTION_ACCESSIBILITY_SETTINGS), activity, new PermissionUtil.OnPermissionCallback() { + @Override + public void onPermissionResult(boolean result, String reason) { + if (result) { + showProgressDialog(getString(R.string.step_edit__now_loading)); + BackgroundExecutor.execute(new Runnable() { + @Override + public void run() { + boolean prepareResult = PrepareUtil.doPrepareWork(caseInfo.getTargetAppPackage(), new PrepareUtil.PrepareStatus() { + @Override + public void currentStatus(int progress, int total, String message, boolean status) { + updateProgressDialog(progress, total, message); + } + }); + + + if (prepareResult) { + final CaseAppendOperationProcessor processor = new CaseAppendOperationProcessor(caseInfo); + dismissProgressDialog(); + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + LauncherApplication.service(OperationStepService.class).registerStepProcessor(processor); + CaseRecordManager manager = LauncherApplication.service(CaseRecordManager.class); + manager.setRecordCase(caseInfo); + + AppUtil.startApp(caseInfo.getTargetAppPackage()); + activity.finish(); + } + }); + } else { + dismissProgressDialog(); + toastShort(getString(R.string.step_edit__prepare_env_fail)); + } + } + }); + } + } + }); return true; } - OperationStep step = stepList.get(position - 1); + OperationStep step = stepList.get(position - 4); CaseStepAdapter.MyDataWrapper entity = new CaseStepAdapter.MyDataWrapper(clone(step), currentIdx.getAndIncrement()); // 如果是if和while,需要设置为0 @@ -384,17 +519,16 @@ private void showEditDialog(final CaseStepAdapter.MyDataWrapper wrapper) { final TabLayout tab = (TabLayout) v.findViewById(R.id.dialog_case_step_edit_tab); if (clone.getOperationNode() != null) { TabLayout.Tab tabItem = tab.newTab(); - tabItem.setText("Node"); + tabItem.setText(R.string.step_edit__node_info); tab.addTab(tabItem); tabItem.select(); nodeAdapter = new CaseStepNodeAdapter(clone.getOperationNode()); } else { nodeAdapter = null; } - TabLayout.Tab tabItem = tab.newTab(); - tabItem.setText("Method"); - tab.addTab(tabItem); + tabItem.setText(R.string.step_edit__method_info); + tab.addTab(tabItem, 0); // 配置后续列表 final List laterList; @@ -420,11 +554,10 @@ private void showEditDialog(final CaseStepAdapter.MyDataWrapper wrapper) { final CaseStepMethodAdapter paramAdapter = new CaseStepMethodAdapter(laterList, clone.getOperationMethod()); - r.setAdapter(nodeAdapter != null? nodeAdapter: paramAdapter); tab.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() { @Override public void onTabSelected(TabLayout.Tab tab) { - if (StringUtil.equals(tab.getText(), "Node")) { + if (StringUtil.equals(tab.getText(), getString(R.string.step_edit__node_info))) { r.setAdapter(nodeAdapter); } else { r.setAdapter(paramAdapter); @@ -441,6 +574,25 @@ public void onTabReselected(TabLayout.Tab tab) { } }); + + // 配置选中的tab + if (paramAdapter.getItemCount() > 0) { + tabItem.select(); + r.setAdapter(paramAdapter); + } else { + if (nodeAdapter == null) { + tabItem.select(); + r.setAdapter(paramAdapter); + } else { + TabLayout.Tab nodeTab = tab.getTabAt(1); + if (nodeTab != null) { + nodeTab.select(); + } + + r.setAdapter(nodeAdapter); + } + } + DialogInterface.OnClickListener dialogClick = new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { @@ -459,8 +611,8 @@ public void onClick(DialogInterface dialog, int which) { }; final AlertDialog dialog = new AlertDialog.Builder(getActivity()) - .setView(v).setPositiveButton("确定", dialogClick) - .setNegativeButton("取消", dialogClick) + .setView(v).setPositiveButton(R.string.constant__confirm, dialogClick) + .setNegativeButton(R.string.constant__cancel, dialogClick) .setTitle(clone.getOperationMethod().getActionEnum().getDesc()).create(); dialog.show(); @@ -480,22 +632,6 @@ private OperationStep clone(OperationStep origin) { return OperationStep.CREATOR.createFromParcel(p); } - /** - * 更新用例 - * @param mRecordCase - */ - private void doUpdateCase(final RecordCaseInfo mRecordCase) { - BackgroundExecutor.execute(new Runnable() { - @Override - public void run() { - mRecordCase.setGmtModify(System.currentTimeMillis()); - GreenDaoManager.getInstance().getRecordCaseInfoDao().save(mRecordCase); - toastShort("更新成功"); - InjectorService.g().pushMessage(NewRecordActivity.NEED_REFRESH_LOCAL_CASES_LIST); - } - }); - } - /** * 显示添加操作界面 */ @@ -506,7 +642,8 @@ private void showAddFunctionView() { @Override public void onProcessFunction(OperationMethod method, AbstractNodeTree node) { PerformActionEnum action = method.getActionEnum(); - if (action == PerformActionEnum.JUMP_TO_PAGE) { + if (action == PerformActionEnum.JUMP_TO_PAGE + || action == PerformActionEnum.LOAD_PARAM) { if (StringUtil.equals(method.getParam("scan"), "1")) { // 注册下Service @@ -518,6 +655,8 @@ public void onProcessFunction(OperationMethod method, AbstractNodeTree node) { if (action == PerformActionEnum.JUMP_TO_PAGE) { intent.putExtra(QRScanActivity.KEY_SCAN_TYPE, ScanSuccessEvent.SCAN_TYPE_SCHEME); + } else if (action == PerformActionEnum.LOAD_PARAM) { + intent.putExtra(QRScanActivity.KEY_SCAN_TYPE, ScanSuccessEvent.SCAN_TYPE_PARAM); } intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); @@ -557,16 +696,16 @@ public void onCancel() { }); } - protected static final List GLOBAL_KEYS = new ArrayList<>(); + protected static final List GLOBAL_KEYS = new ArrayList<>(); protected static final List GLOBAL_ICONS = new ArrayList<>(); - protected static final Map> GLOBAL_ACTION_MAP = new HashMap<>(); + protected static final Map> GLOBAL_ACTION_MAP = new HashMap<>(); // 初始化二级菜单 static { // 全局操作 - GLOBAL_KEYS.add("device"); + GLOBAL_KEYS.add(R.string.function_group__device); GLOBAL_ICONS.add(R.drawable.dialog_action_drawable_device_operation); List gDeviceActions = new ArrayList<>(); gDeviceActions.add(convertPerformActionToSubMenu(PerformActionEnum.BACK)); @@ -577,35 +716,36 @@ public void onCancel() { gDeviceActions.add(convertPerformActionToSubMenu(PerformActionEnum.EXECUTE_SHELL)); gDeviceActions.add(convertPerformActionToSubMenu(PerformActionEnum.NOTIFICATION)); gDeviceActions.add(convertPerformActionToSubMenu(PerformActionEnum.RECENT_TASK)); - GLOBAL_ACTION_MAP.put("device", gDeviceActions); + GLOBAL_ACTION_MAP.put(R.string.function_group__device, gDeviceActions); - GLOBAL_KEYS.add("app"); + GLOBAL_KEYS.add(R.string.function_group__app); GLOBAL_ICONS.add(R.drawable.dialog_action_drawable_app_operation); List gAppActions = new ArrayList<>(); gAppActions.add(convertPerformActionToSubMenu(PerformActionEnum.GOTO_INDEX)); gAppActions.add(convertPerformActionToSubMenu(PerformActionEnum.CHANGE_MODE)); gAppActions.add(convertPerformActionToSubMenu(PerformActionEnum.JUMP_TO_PAGE)); gAppActions.add(convertPerformActionToSubMenu(PerformActionEnum.KILL_PROCESS)); - GLOBAL_ACTION_MAP.put("app", gAppActions); + GLOBAL_ACTION_MAP.put(R.string.function_group__app, gAppActions); - GLOBAL_KEYS.add("scroll"); + GLOBAL_KEYS.add(R.string.function_group__scroll); GLOBAL_ICONS.add(R.drawable.dialog_action_drawable_scroll); List gScrollActions = new ArrayList<>(); gScrollActions.add(convertPerformActionToSubMenu(PerformActionEnum.GLOBAL_SCROLL_TO_BOTTOM)); gScrollActions.add(convertPerformActionToSubMenu(PerformActionEnum.GLOBAL_SCROLL_TO_TOP)); gScrollActions.add(convertPerformActionToSubMenu(PerformActionEnum.GLOBAL_SCROLL_TO_LEFT)); gScrollActions.add(convertPerformActionToSubMenu(PerformActionEnum.GLOBAL_SCROLL_TO_RIGHT)); - GLOBAL_ACTION_MAP.put("scroll", gScrollActions); + GLOBAL_ACTION_MAP.put(R.string.function_group__scroll, gScrollActions); // 循环逻辑控制 - GLOBAL_KEYS.add("logic"); + GLOBAL_KEYS.add(R.string.function_group__logic); GLOBAL_ICONS.add(R.drawable.dialog_action_drawable_logic); List gLoopActions = new ArrayList<>(); gLoopActions.add(convertPerformActionToSubMenu(PerformActionEnum.IF)); gLoopActions.add(convertPerformActionToSubMenu(PerformActionEnum.WHILE)); gLoopActions.add(convertPerformActionToSubMenu(PerformActionEnum.CONTINUE)); gLoopActions.add(convertPerformActionToSubMenu(PerformActionEnum.BREAK)); - GLOBAL_ACTION_MAP.put("logic", gLoopActions); + gLoopActions.add(convertPerformActionToSubMenu(PerformActionEnum.LOAD_PARAM)); + GLOBAL_ACTION_MAP.put(R.string.function_group__logic, gLoopActions); } /** diff --git a/src/app/src/main/java/com/alipay/hulu/fragment/ReplayListFragment.java b/src/app/src/main/java/com/alipay/hulu/fragment/ReplayListFragment.java index 8751082..e8c7bcc 100644 --- a/src/app/src/main/java/com/alipay/hulu/fragment/ReplayListFragment.java +++ b/src/app/src/main/java/com/alipay/hulu/fragment/ReplayListFragment.java @@ -20,9 +20,11 @@ import android.os.Bundle; import android.provider.Settings; import android.support.annotation.Nullable; +import android.support.v4.widget.SwipeRefreshLayout; import android.support.v7.app.AlertDialog; import android.text.Editable; import android.text.TextWatcher; +import android.util.Pair; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -30,18 +32,24 @@ import android.widget.Button; import android.widget.CheckBox; import android.widget.EditText; +import android.widget.LinearLayout; import android.widget.ListView; +import android.widget.ScrollView; import android.widget.TextView; import com.alibaba.fastjson.JSON; import com.alipay.hulu.R; import com.alipay.hulu.activity.CaseEditActivity; +import com.alipay.hulu.activity.CaseParamEditActivity; import com.alipay.hulu.activity.MyApplication; import com.alipay.hulu.activity.NewRecordActivity; import com.alipay.hulu.adapter.ReplayListAdapter; +import com.alipay.hulu.bean.AdvanceCaseSetting; +import com.alipay.hulu.bean.CaseParamBean; import com.alipay.hulu.bean.CaseStepHolder; import com.alipay.hulu.common.application.LauncherApplication; import com.alipay.hulu.common.injector.InjectorService; +import com.alipay.hulu.common.injector.param.SubscribeParamEnum; import com.alipay.hulu.common.injector.param.Subscriber; import com.alipay.hulu.common.injector.provider.Param; import com.alipay.hulu.common.tools.BackgroundExecutor; @@ -61,6 +69,7 @@ import com.alipay.hulu.shared.io.util.OperationStepUtil; import com.alipay.hulu.shared.node.action.PerformActionEnum; import com.alipay.hulu.shared.node.utils.AppUtil; +import com.alipay.hulu.util.CaseReplayUtil; import com.alipay.hulu.util.DialogUtils; import java.io.BufferedWriter; @@ -68,6 +77,7 @@ import java.io.FileWriter; import java.io.IOException; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.regex.Pattern; @@ -85,6 +95,8 @@ public class ReplayListFragment extends BaseFragment { private View mEmptyView; private TextView mEmptyTextView; private ReplayListAdapter mAdapter; + private SwipeRefreshLayout refreshLayout; + private String app; public static ReplayListFragment newInstance(int type) { return new ReplayListFragment(); @@ -104,6 +116,11 @@ public void onCreate(@Nullable Bundle savedInstanceState) { InjectorService.g().register(this); } + @Subscriber(@Param(SubscribeParamEnum.APP)) + public void setApp(String app) { + this.app = app; + } + @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { @@ -116,7 +133,7 @@ public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { initEmptyView(view); initListView(view); - getReplayRecordsFromDB(); + getReplayRecordsFromDB(null); } /** @@ -124,10 +141,10 @@ public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { */ @Subscriber(value = @Param(value = NewRecordActivity.NEED_REFRESH_LOCAL_CASES_LIST, sticky = false)) public void reloadLocalCases() { - getReplayRecordsFromDB(); + getReplayRecordsFromDB(null); } - private void getReplayRecordsFromDB() { + private void getReplayRecordsFromDB(final Runnable r) { BackgroundExecutor.execute(new Runnable() { @Override public void run() { @@ -137,6 +154,9 @@ public void run() { getActivity().runOnUiThread(new Runnable() { @Override public void run() { + if (r != null) { + r.run(); + } if (mCases != null && mCases.size() > 0) { mAdapter.updateData(mCases); mListView.setVisibility(View.VISIBLE); @@ -152,21 +172,29 @@ public void run() { } private void initListView(View view) { + refreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.replay_swipe_refresh); + refreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { + @Override + public void onRefresh() { + Runnable r = new Runnable() { + @Override + public void run() { + refreshLayout.setRefreshing(false); + } + }; + + // 读取用例 + getReplayRecordsFromDB(r); + } + }); + mListView = (ListView) view.findViewById(R.id.replay_list); mAdapter = new ReplayListAdapter(getContext()); mListView.setAdapter(mAdapter); - // 设置编辑按键监听器 - mAdapter.setOnEditClickListener(new AdapterView.OnItemClickListener() { - @Override - public void onItemClick(AdapterView parent, View view, int position, long id) { - editCase(position); - } - }); - - // 默认点击播放 - mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + // 设置播放按键监听器 + mAdapter.setOnPlayClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { final RecordCaseInfo caseInfo = (RecordCaseInfo) mAdapter.getItem(position); @@ -178,11 +206,7 @@ public void onItemClick(AdapterView parent, View view, int position, long id) @Override public void onPermissionResult(final boolean result, String reason) { if (result) { - OperationStepProvider stepProvider = new OperationStepProvider(caseInfo); - MyApplication.getInstance().updateAppAndNameTemp(caseInfo.getTargetAppPackage(), caseInfo.getTargetAppLabel()); - CaseReplayManager manager = LauncherApplication.getInstance().findServiceByName(CaseReplayManager.class.getName()); - manager.start(stepProvider, MyApplication.SINGLE_REPLAY_LISTENER); - + CaseReplayUtil.startReplay(caseInfo); startTargetApp(caseInfo.getTargetAppPackage()); } } @@ -190,6 +214,14 @@ public void onPermissionResult(final boolean result, String reason) { } }); + // 默认点击编辑 + mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + editCase(position); + } + }); + mListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() { @Override public boolean onItemLongClick(AdapterView parent, View view, final int position, long id) { @@ -208,6 +240,9 @@ public void onExecute(DialogInterface dialog, PerformActionEnum action) { case PLAY_MULTI_TIMES: repeatPrepare(position); break; + case GEN_MULTI_PARAM: + genMultiParams(position); + break; } } @@ -244,6 +279,19 @@ private void editCase(int position) { startActivity(intent); } + private void genMultiParams(final int position) { + RecordCaseInfo caseInfo = (RecordCaseInfo) mAdapter.getItem(position); + if (caseInfo == null) { + return; + } + caseInfo = caseInfo.clone(); + + Intent intent = new Intent(getActivity(), CaseParamEditActivity.class); + int caseId = CaseStepHolder.storeCase(caseInfo); + intent.putExtra(CaseParamEditActivity.RECORD_CASE_EXTRA, caseId); + startActivity(intent); + } + /** * 删除用例 * @param position @@ -343,7 +391,7 @@ protected void repeatPrepare(final int position) { textPattern = Pattern.compile("\\d{1,3}"); final AlertDialog dialog = new AlertDialog.Builder(getActivity(), R.style.AppDialogTheme) - .setTitle("请输入回放次数") + .setTitle(R.string.replay__set_replay_count) .setView(v) .setPositiveButton(R.string.constant__start_execution, new DialogInterface.OnClickListener() { @Override @@ -409,11 +457,7 @@ private void playMultiTimeCase(final int position, final int count, final boolea @Override public void onPermissionResult(final boolean result, String reason) { if (result) { - RepeatStepProvider stepProvider = new RepeatStepProvider(caseInfo, count, prepare); - MyApplication.getInstance().updateAppAndNameTemp(caseInfo.getTargetAppPackage(), caseInfo.getTargetAppLabel()); - CaseReplayManager manager = LauncherApplication.getInstance().findServiceByName(CaseReplayManager.class.getName()); - manager.start(stepProvider, MyApplication.MULTI_REPLAY_LISTENER); - + CaseReplayUtil.startReplayMultiTimes(caseInfo, count, prepare); startTargetApp(caseInfo.getTargetAppPackage()); } } diff --git a/src/app/src/main/java/com/alipay/hulu/fragment/ReplayStepFragment.java b/src/app/src/main/java/com/alipay/hulu/fragment/ReplayStepFragment.java index 65daf75..54660ba 100644 --- a/src/app/src/main/java/com/alipay/hulu/fragment/ReplayStepFragment.java +++ b/src/app/src/main/java/com/alipay/hulu/fragment/ReplayStepFragment.java @@ -16,7 +16,6 @@ package com.alipay.hulu.fragment; import android.content.DialogInterface; -import android.graphics.Bitmap; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; @@ -38,9 +37,8 @@ import com.alipay.hulu.common.utils.GlideApp; import com.alipay.hulu.common.utils.LogUtil; import com.alipay.hulu.common.utils.StringUtil; -import com.alipay.hulu.shared.node.action.OperationExecutor; import com.alipay.hulu.shared.node.action.OperationMethod; -import com.alipay.hulu.shared.node.tree.export.OperationStepProvider; +import com.alipay.hulu.shared.node.tree.export.OperationStepExporter; import com.alipay.hulu.shared.node.tree.OperationNode; import com.alipay.hulu.shared.node.tree.export.bean.OperationStep; import com.alipay.hulu.shared.node.utils.BitmapUtil; @@ -249,13 +247,13 @@ void bindData(OperationStep operation, ReplayStepInfoBean replay, String status) try { // 获取base64信息 findBytes = BitmapUtil.decodeBase64(findNode == null? null: - findNode.getExtraValue(OperationStepProvider.CAPTURE_IMAGE_BASE64)); + findNode.getExtraValue(OperationStepExporter.CAPTURE_IMAGE_BASE64)); targetBytes = null; if (method != null) { if (method.containsParam(ImageCompareActionProvider.KEY_TARGET_IMAGE)) { targetBytes = BitmapUtil.decodeBase64(method.getParam(ImageCompareActionProvider.KEY_TARGET_IMAGE)); - } else if (node != null && node.containsExtra(OperationStepProvider.CAPTURE_IMAGE_BASE64)) { - targetBytes = BitmapUtil.decodeBase64(node.getExtraValue(OperationStepProvider.CAPTURE_IMAGE_BASE64)); + } else if (node != null && node.containsExtra(OperationStepExporter.CAPTURE_IMAGE_BASE64)) { + targetBytes = BitmapUtil.decodeBase64(node.getExtraValue(OperationStepExporter.CAPTURE_IMAGE_BASE64)); } } @@ -312,8 +310,8 @@ public void onClick(View v) { private void showContentDialog(OperationNode node) { AlertDialog dialog = new AlertDialog.Builder(mTargetNode.getContext()) .setView(wrapView(node)) - .setTitle("节点结构") - .setPositiveButton("确定", new DialogInterface.OnClickListener() { + .setTitle(R.string.replay__node_struct) + .setPositiveButton(R.string.constant__confirm, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); diff --git a/src/app/src/main/java/com/alipay/hulu/replay/AbstractStepProvider.java b/src/app/src/main/java/com/alipay/hulu/replay/AbstractStepProvider.java index b387f40..de38c0e 100644 --- a/src/app/src/main/java/com/alipay/hulu/replay/AbstractStepProvider.java +++ b/src/app/src/main/java/com/alipay/hulu/replay/AbstractStepProvider.java @@ -72,7 +72,7 @@ public boolean canStart() { * @param reason 故障原因 * @return 是否是故障 */ - public abstract boolean reportErrorStep(OperationStep step, String reason); + public abstract boolean reportErrorStep(OperationStep step, String reason, List callStack); /** * 获取回放结果 @@ -121,7 +121,7 @@ protected void showFunctionView(Context context, String message, final Runnable try { AlertDialog dialog = new AlertDialog.Builder(context, R.style.SimpleDialogTheme) .setMessage(message) - .setPositiveButton("确定", new DialogInterface.OnClickListener() { + .setPositiveButton(R.string.constant__confirm, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { if (confirmAction != null) { @@ -130,7 +130,7 @@ public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); } }) - .setNegativeButton("取消", new DialogInterface.OnClickListener() { + .setNegativeButton(R.string.constant__cancel, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { if (cancelAction != null) { diff --git a/src/app/src/main/java/com/alipay/hulu/replay/BatchStepProvider.java b/src/app/src/main/java/com/alipay/hulu/replay/BatchStepProvider.java index 97f4c05..5ff855c 100644 --- a/src/app/src/main/java/com/alipay/hulu/replay/BatchStepProvider.java +++ b/src/app/src/main/java/com/alipay/hulu/replay/BatchStepProvider.java @@ -112,8 +112,8 @@ public boolean hasNext() { } @Override - public boolean reportErrorStep(OperationStep step, String reason) { - boolean errorResult = currentStepProvider.reportErrorStep(step, reason); + public boolean reportErrorStep(OperationStep step, String reason, List stack) { + boolean errorResult = currentStepProvider.reportErrorStep(step, reason, stack); // 如果是关键性错误 if (errorResult) { diff --git a/src/app/src/main/java/com/alipay/hulu/replay/MultiParamStepProvider.java b/src/app/src/main/java/com/alipay/hulu/replay/MultiParamStepProvider.java new file mode 100644 index 0000000..56427e3 --- /dev/null +++ b/src/app/src/main/java/com/alipay/hulu/replay/MultiParamStepProvider.java @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2015-present, Ant Financial Services Group + * + * 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.alipay.hulu.replay; + +import android.support.annotation.NonNull; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.alipay.hulu.bean.AdvanceCaseSetting; +import com.alipay.hulu.bean.CaseRunningParam; +import com.alipay.hulu.bean.ReplayResultBean; +import com.alipay.hulu.bean.ReplayStepInfoBean; +import com.alipay.hulu.common.application.LauncherApplication; +import com.alipay.hulu.common.utils.StringUtil; +import com.alipay.hulu.shared.io.bean.RecordCaseInfo; +import com.alipay.hulu.shared.node.OperationService; +import com.alipay.hulu.shared.node.action.OperationMethod; +import com.alipay.hulu.shared.node.action.PerformActionEnum; +import com.alipay.hulu.shared.node.tree.export.bean.OperationStep; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Created by qiaoruikai on 2019-08-20 19:26. + */ +public class MultiParamStepProvider extends AbstractStepProvider { + private static final String TAG = "RepeatStepProvider"; + + private OperationService operationService; + + private RecordCaseInfo recordCase; + private OperationStep prepareStep; + + private int currentIdx; + private List> repeatParams = new ArrayList<>(); + + OperationStepProvider currentStepProvider; + + List resultBeans; + + @Override + public void prepare() { + loadStep(); + } + + public MultiParamStepProvider(@NonNull RecordCaseInfo recordCase) { + this.recordCase = recordCase; + currentIdx = 0; + operationService = LauncherApplication.service(OperationService.class); + + parseParams(); + resultBeans = new ArrayList<>(repeatParams.size() + 1); + } + + private void parseParams() { + AdvanceCaseSetting setting = JSON.parseObject(recordCase.getAdvanceSettings(), AdvanceCaseSetting.class); + CaseRunningParam runningParam = setting.getRunningParam(); + if (runningParam == null) { + repeatParams.add(Collections.EMPTY_MAP); + return; + } + + if (runningParam.getMode() == CaseRunningParam.ParamMode.UNION) { + List paramUnion = runningParam.getParamList(); + for (JSONObject param: paramUnion) { + Map realParams = new HashMap<>(param.size() + 1); + for (String key: param.keySet()) { + realParams.put(key, param.getString(key)); + } + + repeatParams.add(realParams); + } + } else { + Map> paramSet = new HashMap<>(); + List paramUnion = runningParam.getParamList(); + for (JSONObject param: paramUnion) { + for (String key: param.keySet()) { + paramSet.put(key, Arrays.asList(StringUtil.split(param.getString(key), ","))); + } + } + + List keys = new ArrayList<>(paramSet.keySet()); + if (keys.size() == 0) { + return; + } + + List> stackParam = new ArrayList<>(); + String initKey = keys.get(0); + for (String param: paramSet.get(initKey)) { + HashMap realParams = new HashMap<>(keys.size() + 1); + realParams.put(initKey, param); + stackParam.add(realParams); + } + + // 全连接网络 + for (int i = 1; i < keys.size(); i++) { + List> newStackParam = new ArrayList<>(); + String key = keys.get(i); + for (Map realParam: stackParam) { + for (String param: paramSet.get(key)) { + Map newLevelParam = new HashMap<>(realParam); + newLevelParam.put(key, param); + newStackParam.add(newLevelParam); + } + } + stackParam = newStackParam; + } + + repeatParams.addAll(stackParam); + } + } + + private void loadStep() { + if (currentIdx <= repeatParams.size() - 1) { + currentStepProvider = new OperationStepProvider(recordCase); + currentStepProvider.putParams(repeatParams.get(currentIdx)); + currentIdx++; + + currentStepProvider.prepare(); + + prepareStep = new OperationStep(); + prepareStep.setOperationMethod(new OperationMethod(PerformActionEnum.GOTO_INDEX)); + } else { + currentStepProvider = null; + } + } + + @Override + public OperationStep provideStep() { + if (prepareStep != null) { + OperationStep step = prepareStep; + prepareStep = null; + return step; + } + return currentStepProvider == null? null: currentStepProvider.provideStep(); + } + + @Override + public boolean hasNext() { + if (currentStepProvider != null && !currentStepProvider.hasNext()) { + resultBeans.addAll(currentStepProvider.genReplayResult()); + loadStep(); + } + + return currentStepProvider != null && currentStepProvider.hasNext(); + } + + @Override + public boolean reportErrorStep(OperationStep step, String reason, List stack) { + boolean errorResult = currentStepProvider.reportErrorStep(step, reason, stack); + + // 如果是关键性错误 + if (errorResult) { + // 记录下之前的问题 + resultBeans.addAll(currentStepProvider.genReplayResult()); + + // 加载下一步 + loadStep(); + } + + return false; + } + + @Override + public void onStepInfo(ReplayStepInfoBean bean) { + currentStepProvider.onStepInfo(bean); + } + + @Override + public List genReplayResult() { + if (currentStepProvider != null) { + resultBeans.addAll(currentStepProvider.genReplayResult()); + currentStepProvider = null; + } + + return resultBeans; + } +} diff --git a/src/app/src/main/java/com/alipay/hulu/replay/OperationStepProvider.java b/src/app/src/main/java/com/alipay/hulu/replay/OperationStepProvider.java index a1ef4eb..6d0f1dd 100644 --- a/src/app/src/main/java/com/alipay/hulu/replay/OperationStepProvider.java +++ b/src/app/src/main/java/com/alipay/hulu/replay/OperationStepProvider.java @@ -20,10 +20,12 @@ import com.alibaba.fastjson.JSON; import com.alipay.hulu.activity.MyApplication; import com.alipay.hulu.bean.AdvanceCaseSetting; +import com.alipay.hulu.bean.CaseParamBean; import com.alipay.hulu.bean.ReplayResultBean; import com.alipay.hulu.bean.ReplayStepInfoBean; import com.alipay.hulu.common.application.LauncherApplication; import com.alipay.hulu.common.tools.CmdTools; +import com.alipay.hulu.common.utils.LogUtil; import com.alipay.hulu.common.utils.StringUtil; import com.alipay.hulu.shared.io.bean.GeneralOperationLogBean; import com.alipay.hulu.shared.io.bean.RecordCaseInfo; @@ -32,6 +34,7 @@ import com.alipay.hulu.shared.node.action.OperationExecutor; import com.alipay.hulu.shared.node.action.OperationMethod; import com.alipay.hulu.shared.node.action.PerformActionEnum; +import com.alipay.hulu.shared.node.tree.AbstractNodeTree; import com.alipay.hulu.shared.node.tree.OperationNode; import com.alipay.hulu.shared.node.tree.export.bean.OperationStep; import com.alipay.hulu.shared.node.utils.LogicUtil; @@ -46,7 +49,8 @@ import java.util.Locale; import java.util.Map; import java.util.Stack; -import java.util.Vector; +import java.util.regex.Pattern; + import static com.alipay.hulu.shared.node.utils.LogicUtil.CHECK_PARAM; import static com.alipay.hulu.shared.node.utils.LogicUtil.SCOPE; @@ -55,6 +59,8 @@ */ public class OperationStepProvider extends AbstractStepProvider { private static final String TAG = "OpStepProvider"; + private static final Pattern FILED_CALL_PATTERN = Pattern.compile("\\$\\{[^}\\s]+\\.?[^}\\s]*\\}"); + protected OperationService operationService; private List stepList = new ArrayList<>(); @@ -82,6 +88,11 @@ public class OperationStepProvider extends AbstractStepProvider { */ protected boolean waitForCheck; + /** + * 是否初始化环境 + */ + protected boolean initEnvironment; + /** * check结果 */ @@ -91,23 +102,118 @@ public class OperationStepProvider extends AbstractStepProvider { protected int currentIdx; + protected Map initParams = new HashMap<>(); + + /** + * 参数映射处理 + */ + private OperationMethod.ParamProcessor paramReplacer = new OperationMethod.ParamProcessor() { + @Override + public String filterParam(String key, String value, PerformActionEnum action) { + return getMappedContent(value, operationService); + } + }; + + /** + * 将当期运行时变量映射到字符串中 + * + * @param origin + * @param service + * @return + */ + public static String getMappedContent(String origin, final OperationService service) { + if (service == null) { + return origin; + } + + return StringUtil.patternReplace(origin, FILED_CALL_PATTERN, new StringUtil.PatternReplace() { + @Override + public String replacePattern(String origin) { + String content = origin.substring(2, origin.length() - 1); + // 有子内容调用 + if (content.contains(".")) { + String[] group = content.split("\\.", 2); + + if (group.length != 2) { + return origin; + } + + // 获取当前变量 + Object obj = service.getRuntimeParam(group[0]); + if (obj == null) { + return origin; + } + + LogUtil.d(TAG, "Map key word %s to value %s", group[0], obj); + + // 特殊判断 + // 节点字段,自行操作 + if (obj instanceof AbstractNodeTree) { + String replace = StringUtil.toString(((AbstractNodeTree) obj).getField(group[1])); + if (replace == null) { + return origin; + } else { + return replace; + } + } else { + // 目前只支持length方法 + if (StringUtil.equals(group[1], "length")) { + return Integer.toString(StringUtil.toString(obj).length()); + } else { + return origin; + } + } + } else { + String target = StringUtil.toString(service.getRuntimeParam(content)); + if (target == null) { + return origin; + } else { + return target; + } + } + } + }); + } + public OperationStepProvider(RecordCaseInfo caseInfo) { + this(caseInfo, true); + } + + public OperationStepProvider(RecordCaseInfo caseInfo, boolean initParams) { this.caseInfo = caseInfo; loadOperation(caseInfo.getOperationLog()); currentStepInfo = new HashMap<>(); screenshotFiles = new LinkedHashMap<>(); + initEnvironment = initParams; currentIdx = 0; // 加载OperationService operationService = LauncherApplication.getInstance().findServiceByName(OperationService.class.getName()); } + /** + * 配置初始化参数 + * @param params + */ + public void putParams(Map params) { + if (params == null || params.size() == 0) { + return; + } + + initParams.putAll(params); + } + @Override public void prepare() { super.prepare(); - CmdTools.startAppLog(); - operationService.initParams(); - MyApplication.getInstance().updateAppAndNameTemp(caseInfo.getTargetAppPackage(), caseInfo.getTargetAppLabel()); + if (initEnvironment) { + CmdTools.startAppLog(); + operationService.initParams(); + MyApplication.getInstance().updateAppAndNameTemp(caseInfo.getTargetAppPackage(), caseInfo.getTargetAppLabel()); + } + + operationService.putAllRuntimeParamAtTop(initParams); + targetApp = caseInfo.getTargetAppLabel(); } @@ -141,15 +247,24 @@ protected void addSetupStepsIfNeeded() { , stepList.get(0).getOperationId()); stepList.add(0, changeModeBean); } + + // 参数信息 + if (setting.getParams() != null && setting.getParams().size() > 0) { + Map params = new HashMap<>(setting.getParams().size() + 1); + for (CaseParamBean caseParam: setting.getParams()) { + params.put(caseParam.getParamName(), caseParam.getParamDefaultValue()); + } + + // 设置参数 + initParams.putAll(params); + } } } } @Override public OperationStep provideStep() { - /** - * loop循环 - */ + // loop循环 LoopParam param; while ((param = loopParams.peek()) != null) { @@ -183,7 +298,9 @@ public OperationStep provideStep() { // screen shot改下名 if (method.getActionEnum() == PerformActionEnum.SCREENSHOT) { - String screenShotName = method.getParam(OperationExecutor.INPUT_TEXT_KEY); + String screenShotName = OperationExecutor.getMappedContent( + method.getParam(OperationExecutor.INPUT_TEXT_KEY), operationService); + Date now = new Date(); SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmssSSS_", Locale.CHINA); String newFileName = format.format(now) + screenShotName; @@ -211,9 +328,12 @@ public OperationStep provideStep() { return checkStep; } else if (method.getActionEnum() == PerformActionEnum.WHILE) { String status = method.getParam(CHECK_PARAM); + status = OperationExecutor.getMappedContent(status, operationService); + + String scopeContent = OperationExecutor.getMappedContent(method.getParam(SCOPE), operationService); if (StringUtil.startWith(status, LogicUtil.LOOP_PREFIX)) { LoopParam newParam = new LoopParam(currentIdx, - currentIdx - 1 + Integer.parseInt(method.getParam(SCOPE)), + currentIdx - 1 + Integer.parseInt(scopeContent), Integer.parseInt(status.substring(6)) - 2); // 循环次数小于1,直接跳出去 @@ -245,7 +365,7 @@ public OperationStep provideStep() { // 循环配置 LoopParam newParam = new LoopParam(currentIdx - 1, - currentIdx - 1 + Integer.parseInt(method.getParam(SCOPE)), 0); + currentIdx - 1 + Integer.parseInt(scopeContent), 0); loopParams.push(newParam); return checkStep; @@ -298,7 +418,9 @@ public boolean hasNext() { } @Override - public boolean reportErrorStep(OperationStep step, String reason) { + public boolean reportErrorStep(OperationStep step, String reason, List stack) { + stack.add("Error at step " + currentIdx + " " + step.getOperationMethod().getActionEnum().getDesc()); + // 未查找到,也接收 if (waitForCheck && currentIdx == checkIdx + 1 && (StringUtil.equals(reason, "执行失败") || StringUtil.equals(reason, "节点未查找到"))) { @@ -313,7 +435,7 @@ public boolean reportErrorStep(OperationStep step, String reason) { ifIdx = -1; - // Loop信息校验 + // Loop信息校验 } else if ((l = loopParams.peek()) != null && currentIdx == l.loopPos + 1) { OperationStep whileStep = stepList.get(l.loopPos); @@ -323,7 +445,7 @@ public boolean reportErrorStep(OperationStep step, String reason) { loopParams.pop(); } else { - this.errorReason = reason; + this.errorReason = reason + "\n" + StringUtil.join("\n", stack); return true; } @@ -331,7 +453,7 @@ public boolean reportErrorStep(OperationStep step, String reason) { return false; } - this.errorReason = reason; + this.errorReason = reason + "\n" + StringUtil.join("\n", stack); return true; } diff --git a/src/app/src/main/java/com/alipay/hulu/replay/RepeatStepProvider.java b/src/app/src/main/java/com/alipay/hulu/replay/RepeatStepProvider.java index e6bc683..7e69cee 100644 --- a/src/app/src/main/java/com/alipay/hulu/replay/RepeatStepProvider.java +++ b/src/app/src/main/java/com/alipay/hulu/replay/RepeatStepProvider.java @@ -98,8 +98,8 @@ public boolean hasNext() { } @Override - public boolean reportErrorStep(OperationStep step, String reason) { - boolean errorResult = currentStepProvider.reportErrorStep(step, reason); + public boolean reportErrorStep(OperationStep step, String reason, List stack) { + boolean errorResult = currentStepProvider.reportErrorStep(step, reason, stack); // 如果是关键性错误 if (errorResult) { diff --git a/src/app/src/main/java/com/alipay/hulu/screenRecord/RecordService.java b/src/app/src/main/java/com/alipay/hulu/screenRecord/RecordService.java index 47c6547..58df779 100644 --- a/src/app/src/main/java/com/alipay/hulu/screenRecord/RecordService.java +++ b/src/app/src/main/java/com/alipay/hulu/screenRecord/RecordService.java @@ -144,7 +144,7 @@ private void createView() { view = LayoutInflater.from(this).inflate(R.layout.record_service, null); recordBtn = (TextView) view.findViewById(R.id.record_btn); - recordBtn.setText("开始录制"); + recordBtn.setText(R.string.record__start_record); closeBtn = (ImageView) view.findViewById(R.id.close_btn); resultView = (TextView) view.findViewById(R.id.result); resultView.setVisibility(View.GONE); @@ -373,8 +373,8 @@ public void onStop(Throwable error) { public void run() { isRecording = false; isCalculating = true; - recordBtn.setText("正在计算"); - resultView.setText("请稍候..."); + recordBtn.setText(R.string.record__calculating); + resultView.setText(R.string.record__please_wait); resultView.setVisibility(View.VISIBLE); } }); @@ -389,9 +389,9 @@ public void onAnalyzeFinished(final long result) { @Override public void run() { isCalculating = false; - recordBtn.setText("开始录制"); + recordBtn.setText(R.string.record__start_record); if (result <= 0) { - resultView.setText("操作过快,请重试"); + resultView.setText(R.string.record__operation_fast); } else { resultView.setText(getString(R.string.record_service__cost_time, result)); } @@ -405,7 +405,7 @@ public void onAnalyzeFailed(final String msg) { @Override public void run() { isCalculating = false; - recordBtn.setText("开始录制"); + recordBtn.setText(R.string.record__start_record); resultView.setText(msg); } }); @@ -429,7 +429,7 @@ public void onStart() { public void run() { isRecording = true; hasClicked = false; - recordBtn.setText("结束录制"); + recordBtn.setText(R.string.record__stop_record); resultView.setVisibility(View.GONE); mNotifications.recording(0); } diff --git a/src/app/src/main/java/com/alipay/hulu/screenRecord/RecorderConfigActivity.java b/src/app/src/main/java/com/alipay/hulu/screenRecord/RecorderConfigActivity.java index 6b4c40d..61122a6 100644 --- a/src/app/src/main/java/com/alipay/hulu/screenRecord/RecorderConfigActivity.java +++ b/src/app/src/main/java/com/alipay/hulu/screenRecord/RecorderConfigActivity.java @@ -109,7 +109,7 @@ protected void onDestroy() { private void initViews() { mPanel = (HeadControlPanel) findViewById(R.id.info_head); - mPanel.setMiddleTitle("录屏设置"); + mPanel.setMiddleTitle(getString(R.string.activity__record_config)); mPanel.setBackIconClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -124,7 +124,7 @@ public void onClick(View v) { if (checkVideoSettings()) { startWindow(v); } else { - toastShort("视频参数不支持"); + toastShort(getString(R.string.codec__video_config_unsupport)); } } }); @@ -297,15 +297,15 @@ private void onResolutionChanged(int selectedPosition, String resolution) { int resetPos = Math.max(selectedPosition - 1, 0); if (!videoCapabilities.isSizeSupported(width, height)) { mVideoResolution.setSelectedPosition(resetPos); - toastShort("codec '%s' unsupported size %dx%d ", - codecName, width, height); + toastShort(getString(R.string.codec_config__unsupport_size, + codecName, width, height)); LogUtil.w(TAG, codecName + " height range: " + videoCapabilities.getSupportedHeights() + "\n width range: " + videoCapabilities.getSupportedHeights()); } else if (!videoCapabilities.areSizeAndRateSupported(width, height, selectedFramerate)) { mVideoResolution.setSelectedPosition(resetPos); - toastShort("codec '%s' unsupported size %dx%d\nwith framerate %d", - codecName, width, height, (int) selectedFramerate); + toastShort(getString(R.string.codec_config__unsupport_size_framerate, + codecName, width, height, (int) selectedFramerate)); } } @@ -320,7 +320,7 @@ private void onBitrateChanged(int selectedPosition, String bitrate) { int resetPos = Math.max(selectedPosition - 1, 0); if (!videoCapabilities.getBitrateRange().contains(selectedBitrate)) { mVideoBitrate.setSelectedPosition(resetPos); - toastShort("codec '%s' unsupported bitrate %d", codecName, selectedBitrate); + toastShort(getString(R.string.codec_config__unsupport_bitrate, codecName, selectedBitrate)); LogUtil.w(TAG, codecName + " bitrate range: " + videoCapabilities.getBitrateRange()); } @@ -345,11 +345,11 @@ private void onFramerateChanged(int selectedPosition, String rate) { int resetPos = Math.max(selectedPosition - 1, 0); if (!videoCapabilities.getSupportedFrameRates().contains(selectedFramerate)) { mVideoFrameRate.setSelectedPosition(resetPos); - toastShort("codec '%s' unsupported framerate %d", codecName, selectedFramerate); + toastShort(getString(R.string.codec_config__unsupport_framerate, codecName, selectedFramerate)); } else if (!videoCapabilities.areSizeAndRateSupported(width, height, selectedFramerate)) { mVideoFrameRate.setSelectedPosition(resetPos); - toastShort("codec '%s' unsupported size %dx%d\nwith framerate %d", - codecName, width, height, selectedFramerate); + toastShort(getString(R.string.codec_config__unsupport_size_framerate, + codecName, width, height, selectedFramerate)); } } diff --git a/src/app/src/main/java/com/alipay/hulu/service/CaseRecordManager.java b/src/app/src/main/java/com/alipay/hulu/service/CaseRecordManager.java index dd1abbd..d0c9c5c 100644 --- a/src/app/src/main/java/com/alipay/hulu/service/CaseRecordManager.java +++ b/src/app/src/main/java/com/alipay/hulu/service/CaseRecordManager.java @@ -81,7 +81,7 @@ import com.alipay.hulu.shared.node.tree.accessibility.AccessibilityNodeProcessor; import com.alipay.hulu.shared.node.tree.accessibility.AccessibilityProvider; import com.alipay.hulu.shared.node.tree.capture.CaptureTree; -import com.alipay.hulu.shared.node.tree.export.OperationStepProvider; +import com.alipay.hulu.shared.node.tree.export.OperationStepExporter; import com.alipay.hulu.shared.node.tree.export.bean.OperationStep; import com.alipay.hulu.shared.node.utils.AppUtil; import com.alipay.hulu.shared.node.utils.BitmapUtil; @@ -142,13 +142,13 @@ public class CaseRecordManager implements ExportService { protected volatile boolean forceStopBlocking = false; - private InjectorService injectorService; + protected InjectorService injectorService; protected OperationService operationService; protected EventService eventService; - protected OperationStepProvider stepProvider; + protected OperationStepExporter stepProvider; private WindowManager windowManager; @@ -230,6 +230,14 @@ public void onScanEvent(final ScanSuccessEvent event) { OperationMethod method = new OperationMethod(PerformActionEnum.JUMP_TO_PAGE); method.putParam(OperationExecutor.SCHEME_KEY, event.getContent()); + // 录制模式需要记录下 + operationAndRecord(method, null); + break; + case ScanSuccessEvent.SCAN_TYPE_PARAM: + // 向handler发送请求 + method = new OperationMethod(PerformActionEnum.LOAD_PARAM); + method.putParam(OperationExecutor.APP_URL_KEY, event.getContent()); + // 录制模式需要记录下 operationAndRecord(method, null); break; @@ -253,7 +261,7 @@ public void run() { try { boolean result = CmdTools.generateConnection(); if (!result) { - LauncherApplication.getInstance().showToast("ADB未恢复,请连接PC执行'adb tcpip 5555'开启端口"); + LauncherApplication.getInstance().showToast(StringUtil.getString(R.string.record__adb_hint)); } else { setServiceToTouchBlockMode(); operationService.invalidRoot(); @@ -326,12 +334,13 @@ public void startRecord() { isRecording = true; displayDialog = true; + // 初始化 operationService.initParams(); operationStepService.startRecord(caseInfo); // 刷新数据导出 if (stepProvider == null) { - stepProvider = new OperationStepProvider(currentRecordId); + stepProvider = new OperationStepExporter(currentRecordId); } else { stepProvider.refresh(currentRecordId); } @@ -398,7 +407,7 @@ public void receiveTouchPosition(UniversalEventBean eventBean) { // 看下是否点到Soloπ图标 if (binder.checkInFloat(point)) { LogUtil.i(TAG, "点到了Soloπ"); - showFunctionView(null, 2, 3, 4); + showFunctionView(null); return; } @@ -420,7 +429,7 @@ public void receiveTouchPosition(UniversalEventBean eventBean) { float yFactor = (y - bound.top) / (float) bound.height(); localClickPos = new Pair<>(xFactor, yFactor); - showFunctionView(node, 1); + showFunctionView(node); } finally { nodeLoading = false; } @@ -456,14 +465,14 @@ protected void operationAndRecord(OperationMethod method, AbstractNodeTree targe desc = action.getDesc(); } - LauncherApplication.getInstance().showToast(binder.loadServiceContext(), String.format(Locale.CHINA, "执行操作[%s]失败,请尝试重新执行", desc)); + LauncherApplication.getInstance().showToast(binder.loadServiceContext(), StringUtil.getString(R.string.record__execute_fail, desc)); return; } OperationStepMessage message = new OperationStepMessage(); message.setStepIdx(step.getOperationIndex()); message.setGeneralOperationStep(step); - injectorService.pushMessage(com.alipay.hulu.shared.io.constant.Constant.NOTIFY_RECORD_STEP, message, true); + injectorService.pushMessage(OperationStepService.NOTIFY_OPERATION_STEP, message, true); } /** @@ -553,23 +562,23 @@ public void run() { }); } - protected static final List NODE_KEYS = new ArrayList<>(); + protected static final List NODE_KEYS = new ArrayList<>(); protected static final List NODE_ICONS = new ArrayList<>(); - protected static final Map> NODE_ACTION_MAP = new HashMap<>(); + protected static final Map> NODE_ACTION_MAP = new HashMap<>(); - protected static final List GLOBAL_KEYS = new ArrayList<>(); + protected static final List GLOBAL_KEYS = new ArrayList<>(); protected static final List GLOBAL_ICONS = new ArrayList<>(); - protected static final Map> GLOBAL_ACTION_MAP = new HashMap<>(); + protected static final Map> GLOBAL_ACTION_MAP = new HashMap<>(); // 初始化二级菜单 static { // 节点操作 - NODE_KEYS.add("click"); + NODE_KEYS.add(R.string.function_group__click); NODE_ICONS.add(R.drawable.dialog_action_drawable_quick_click_2); List clickActions = new ArrayList<>(); clickActions.add(convertPerformActionToSubMenu(PerformActionEnum.CLICK)); @@ -577,38 +586,38 @@ public void run() { clickActions.add(convertPerformActionToSubMenu(PerformActionEnum.CLICK_IF_EXISTS)); clickActions.add(convertPerformActionToSubMenu(PerformActionEnum.CLICK_QUICK)); clickActions.add(convertPerformActionToSubMenu(PerformActionEnum.MULTI_CLICK)); - NODE_ACTION_MAP.put("click", clickActions); + NODE_ACTION_MAP.put(R.string.function_group__click, clickActions); - NODE_KEYS.add("input"); + NODE_KEYS.add(R.string.function_group__input); NODE_ICONS.add(R.drawable.dialog_action_drawable_input); List inputActions = new ArrayList<>(); inputActions.add(convertPerformActionToSubMenu(PerformActionEnum.INPUT)); inputActions.add(convertPerformActionToSubMenu(PerformActionEnum.INPUT_SEARCH)); - NODE_ACTION_MAP.put("input", inputActions); + NODE_ACTION_MAP.put(R.string.function_group__input, inputActions); - NODE_KEYS.add("scroll"); + NODE_KEYS.add(R.string.function_group__scroll); NODE_ICONS.add(R.drawable.dialog_action_drawable_scroll); List scrollActions = new ArrayList<>(); scrollActions.add(convertPerformActionToSubMenu(PerformActionEnum.SCROLL_TO_BOTTOM)); scrollActions.add(convertPerformActionToSubMenu(PerformActionEnum.SCROLL_TO_TOP)); scrollActions.add(convertPerformActionToSubMenu(PerformActionEnum.SCROLL_TO_LEFT)); scrollActions.add(convertPerformActionToSubMenu(PerformActionEnum.SCROLL_TO_RIGHT)); - NODE_ACTION_MAP.put("scroll", scrollActions); + NODE_ACTION_MAP.put(R.string.function_group__scroll, scrollActions); - NODE_KEYS.add("assert"); + NODE_KEYS.add(R.string.function_group__assert); NODE_ICONS.add(R.drawable.dialog_action_drawable_assert); List assertActions = new ArrayList<>(); assertActions.add(convertPerformActionToSubMenu(PerformActionEnum.ASSERT)); assertActions.add(convertPerformActionToSubMenu(PerformActionEnum.SLEEP_UNTIL)); assertActions.add(convertPerformActionToSubMenu(PerformActionEnum.LET_NODE)); - NODE_ACTION_MAP.put("assert", assertActions); + NODE_ACTION_MAP.put(R.string.function_group__assert, assertActions); - NODE_KEYS.add("other"); + NODE_KEYS.add(R.string.function_group__extra); NODE_ICONS.add(R.drawable.dialog_action_drawable_extra); // 全局操作 - GLOBAL_KEYS.add("device"); + GLOBAL_KEYS.add(R.string.function_group__device); GLOBAL_ICONS.add(R.drawable.dialog_action_drawable_device_operation); List gDeviceActions = new ArrayList<>(); gDeviceActions.add(convertPerformActionToSubMenu(PerformActionEnum.BACK)); @@ -619,9 +628,9 @@ public void run() { gDeviceActions.add(convertPerformActionToSubMenu(PerformActionEnum.EXECUTE_SHELL)); gDeviceActions.add(convertPerformActionToSubMenu(PerformActionEnum.NOTIFICATION)); gDeviceActions.add(convertPerformActionToSubMenu(PerformActionEnum.RECENT_TASK)); - GLOBAL_ACTION_MAP.put("device", gDeviceActions); + GLOBAL_ACTION_MAP.put(R.string.function_group__device, gDeviceActions); - GLOBAL_KEYS.add("app"); + GLOBAL_KEYS.add(R.string.function_group__app); GLOBAL_ICONS.add(R.drawable.dialog_action_drawable_app_operation); List gAppActions = new ArrayList<>(); gAppActions.add(convertPerformActionToSubMenu(PerformActionEnum.GOTO_INDEX)); @@ -630,33 +639,34 @@ public void run() { gAppActions.add(convertPerformActionToSubMenu(PerformActionEnum.KILL_PROCESS)); gAppActions.add(convertPerformActionToSubMenu(PerformActionEnum.CLEAR_DATA)); gAppActions.add(convertPerformActionToSubMenu(PerformActionEnum.RELOAD)); - GLOBAL_ACTION_MAP.put("app", gAppActions); + GLOBAL_ACTION_MAP.put(R.string.function_group__app, gAppActions); - GLOBAL_KEYS.add("scroll"); + GLOBAL_KEYS.add(R.string.function_group__scroll); GLOBAL_ICONS.add(R.drawable.dialog_action_drawable_scroll); List gScrollActions = new ArrayList<>(); gScrollActions.add(convertPerformActionToSubMenu(PerformActionEnum.GLOBAL_SCROLL_TO_BOTTOM)); gScrollActions.add(convertPerformActionToSubMenu(PerformActionEnum.GLOBAL_SCROLL_TO_TOP)); gScrollActions.add(convertPerformActionToSubMenu(PerformActionEnum.GLOBAL_SCROLL_TO_LEFT)); gScrollActions.add(convertPerformActionToSubMenu(PerformActionEnum.GLOBAL_SCROLL_TO_RIGHT)); - GLOBAL_ACTION_MAP.put("scroll", gScrollActions); + GLOBAL_ACTION_MAP.put(R.string.function_group__scroll, gScrollActions); - GLOBAL_KEYS.add("info"); + GLOBAL_KEYS.add(R.string.function_group__info); GLOBAL_ICONS.add(R.drawable.dialog_action_drawable_device_info); List gInfoActions = new ArrayList<>(); gInfoActions.add(convertPerformActionToSubMenu(PerformActionEnum.DEVICE_INFO)); - GLOBAL_ACTION_MAP.put("info", gInfoActions); + gInfoActions.add(convertPerformActionToSubMenu(PerformActionEnum.LOAD_PARAM)); + GLOBAL_ACTION_MAP.put(R.string.function_group__info, gInfoActions); - GLOBAL_KEYS.add("other"); + GLOBAL_KEYS.add(R.string.function_group__extra); GLOBAL_ICONS.add(R.drawable.dialog_action_drawable_extra); - GLOBAL_KEYS.add("control"); + GLOBAL_KEYS.add(R.string.function_group__control); GLOBAL_ICONS.add(R.drawable.dialog_action_drawable_finish); List gControlActions = new ArrayList<>(); gControlActions.add(convertPerformActionToSubMenu(PerformActionEnum.FINISH)); gControlActions.add(convertPerformActionToSubMenu(PerformActionEnum.PAUSE)); - GLOBAL_ACTION_MAP.put("control", gControlActions); + GLOBAL_ACTION_MAP.put(R.string.function_group__control, gControlActions); } /** @@ -664,12 +674,12 @@ public void run() { * * @param node */ - private void showFunctionView(final AbstractNodeTree node, final Integer... levels) { + private void showFunctionView(final AbstractNodeTree node) { // 没有操作 displayDialog = true; - final List keys; + final List keys; final List icons; - final Map> secondLevel = new HashMap<>(); + final Map> secondLevel = new HashMap<>(); if (node != null) { Pair pos = localClickPos; @@ -699,7 +709,7 @@ public void run() { root.setMinimumHeight(20); - DialogUtils.showCustomView(binder.loadServiceContext(), root, "确定", new Runnable() { + DialogUtils.showCustomView(binder.loadServiceContext(), root, StringUtil.getString(R.string.constant__confirm), new Runnable() { @Override public void run() { Rect scaledRect = crop.getCropRect(); @@ -715,7 +725,7 @@ public void run() { localClickPos = new Pair<>(0.5F, 0.5F); } - showFunctionView(captureTree, levels); + showFunctionView(captureTree); } }, "取消", new Runnable() { @Override @@ -735,7 +745,7 @@ public void run() { keys = new ArrayList<>(NODE_KEYS); icons = new ArrayList<>(NODE_ICONS); secondLevel.putAll(NODE_ACTION_MAP); - secondLevel.put("other", loadOtherActions(PerformActionEnum.OTHER_NODE, node)); + secondLevel.put(R.string.function_group__extra, loadOtherActions(PerformActionEnum.OTHER_NODE, node)); Rect bound = node.getNodeBound(); @@ -753,7 +763,7 @@ public void run() { secondLevel.putAll(GLOBAL_ACTION_MAP); // 加入额外操作 - secondLevel.put("other", loadOtherActions(PerformActionEnum.OTHER_GLOBAL, null)); + secondLevel.put(R.string.function_group__extra, loadOtherActions(PerformActionEnum.OTHER_GLOBAL, null)); } setServiceToNormalMode(); @@ -848,21 +858,25 @@ protected boolean processAction(OperationMethod method, AbstractNodeTree node, f // 初始化运行环境 operationService.initParams(); - operationStepService.stopRecord(); + boolean processed = operationStepService.stopRecord(context); eventService.stopTrackAccessibilityEvent(); eventService.stopTrackTouch(); setServiceToNormalMode(); - // 恢复悬浮窗 - binder.restoreFloat(); - Intent intent = new Intent(context, NewRecordActivity.class); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - intent.putExtra(NewRecordActivity.NEED_REFRESH_PAGE, true); - context.startActivity(intent); + if (!processed) { + // 恢复悬浮窗 + binder.restoreFloat(); + Intent intent = new Intent(context, NewRecordActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.putExtra(NewRecordActivity.NEED_REFRESH_PAGE, true); + context.startActivity(intent); - LauncherApplication.getInstance().stopServiceByName(CaseRecordManager.class.getName()); + LauncherApplication.getInstance().stopServiceByName(CaseRecordManager.class.getName()); + } else { + LauncherApplication.getInstance().stopServiceByName(CaseRecordManager.class.getName()); + } return false; } else if (action == PerformActionEnum.PAUSE) { setServiceToNormalMode(); @@ -877,15 +891,24 @@ public void onFloatClick(boolean hide) { } }); return false; - } else if (action == PerformActionEnum.JUMP_TO_PAGE) { + } else if (action == PerformActionEnum.JUMP_TO_PAGE + || action == PerformActionEnum.LOAD_PARAM) { if (!StringUtil.equals(method.getParam("scan"), "1")) { operationAndRecord(method, node); } else { - Intent intent = new Intent(context, QRScanActivity.class); - intent.putExtra(QRScanActivity.KEY_SCAN_TYPE, ScanSuccessEvent.SCAN_TYPE_SCHEME); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(intent); - setServiceToTouchBlockMode(); + if (action == PerformActionEnum.JUMP_TO_PAGE) { + Intent intent = new Intent(context, QRScanActivity.class); + intent.putExtra(QRScanActivity.KEY_SCAN_TYPE, ScanSuccessEvent.SCAN_TYPE_SCHEME); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(intent); + setServiceToTouchBlockMode(); + } else if (action == PerformActionEnum.LOAD_PARAM) { + Intent intent = new Intent(context, QRScanActivity.class); + intent.putExtra(QRScanActivity.KEY_SCAN_TYPE, ScanSuccessEvent.SCAN_TYPE_PARAM); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(intent); + setServiceToTouchBlockMode(); + } } } else { operationAndRecord(method, node); @@ -945,7 +968,7 @@ public void onGesture(UniversalEventBean gestureEvent) { Integer gestureId; if (gestureEvent != null && (gestureId = gestureEvent.getParam(com.alipay.hulu.shared.event.constant.Constant.KEY_GESTURE_TYPE)) != null) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP && gestureId == GESTURE_SWIPE_UP && !displayDialog && !nodeLoading) { - showFunctionView(null, 2, 3, 4); + showFunctionView(null); } } } @@ -990,14 +1013,14 @@ public void onHandlePermissionEvent(HandlePermissionEvent event) { public void receiveDeviceInfoMessage(UIOperationMessage message) { if (message.eventType == UIOperationMessage.TYPE_DEVICE_INFO) { DeviceInfo info = DeviceInfoUtil.generateDeviceInfo(); - showDialog("设备信息", info.toString(), binder.loadServiceContext(), 0); + showDialog(StringUtil.getString(R.string.ui__device_info), info.toString(), binder.loadServiceContext(), 0); } else if (message.eventType == UIOperationMessage.TYPE_DIALOG) { String info = message.getParam("msg"); String title = message.getParam("title"); showDialog(title, info, binder.loadServiceContext(), 0); } else if (message.eventType == UIOperationMessage.TYPE_COUNT_DOWN) { long timeMillis = message.getParam("time"); - showDialog("SLEEP", "等待" + timeMillis + "ms", binder.loadServiceContext(), timeMillis); + showDialog(StringUtil.getString(R.string.ui__sleep), StringUtil.getString(R.string.ui__sleep_time, timeMillis), binder.loadServiceContext(), timeMillis); } else if (message.eventType == UIOperationMessage.TYPE_DISMISS) { // 如果在显示弹窗,就隐藏下 if (dialogRef != null && dialogRef.get() != null && dialogRef.get().isShowing()) { @@ -1053,7 +1076,7 @@ public void showDialog(String title, String deviceInfo, Context context, long ti final AlertDialog dialog = new AlertDialog.Builder(context, R.style.AppDialogTheme) .setTitle(title) .setView(v) - .setPositiveButton("确定", new DialogInterface.OnClickListener() { + .setPositiveButton(R.string.constant__confirm, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { setServiceToTouchBlockMode(); diff --git a/src/app/src/main/java/com/alipay/hulu/service/CaseReplayManager.java b/src/app/src/main/java/com/alipay/hulu/service/CaseReplayManager.java index 8ccfbdb..fe38639 100644 --- a/src/app/src/main/java/com/alipay/hulu/service/CaseReplayManager.java +++ b/src/app/src/main/java/com/alipay/hulu/service/CaseReplayManager.java @@ -73,7 +73,7 @@ import com.alipay.hulu.shared.node.tree.accessibility.AccessibilityNodeProcessor; import com.alipay.hulu.shared.node.tree.accessibility.AccessibilityProvider; import com.alipay.hulu.shared.node.tree.capture.CaptureTree; -import com.alipay.hulu.shared.node.tree.export.OperationStepProvider; +import com.alipay.hulu.shared.node.tree.export.OperationStepExporter; import com.alipay.hulu.shared.node.tree.export.bean.OperationStep; import com.alipay.hulu.shared.node.utils.AppUtil; import com.alipay.hulu.shared.node.utils.BitmapUtil; @@ -166,7 +166,7 @@ public int onRunClick() { if (provider.canStart()) { startProcess(); } else { - Toast.makeText(binder.loadServiceContext(), "无法手动启动", Toast.LENGTH_SHORT).show(); + LauncherApplication.getInstance().showToast(StringUtil.getString(R.string.replay__not_start_by_hand)); } return 0; } @@ -303,7 +303,7 @@ private void process() { LauncherApplication.getInstance().runOnUiThread(new Runnable() { @Override public void run() { - showDialog("解析异常", e.getClass() + ": " + e.getMessage(), binder.loadServiceContext(), 0); + showDialog(StringUtil.getString(R.string.ui__parse_failed), e.getClass() + ": " + e.getMessage(), binder.loadServiceContext(), 0); } }); break; @@ -325,10 +325,11 @@ public void run() { result = "执行异常:" + e.getMessage(); } - if (result != null) { + boolean isError = result != null; + if (isError) { LogUtil.e(TAG, "执行步骤出现问题:%s", result); - boolean isError = provider.reportErrorStep(step, result); + isError = provider.reportErrorStep(step, result, new ArrayList()); if (StringUtil.equals(result, "回放中止")) { break; @@ -522,7 +523,7 @@ public void notifyOperationFinish() { return "执行失败"; } - OperationNode opNode = OperationStepProvider.exportNodeToOperationNode(node); + OperationNode opNode = OperationStepExporter.exportNodeToOperationNode(node); // 等待操作结束 try { @@ -550,12 +551,21 @@ public void notifyOperationFinish() { return "执行失败"; } - // 成功执行,需要等待 + // 成功执行,需要等待,最长10分钟 // 等待操作结束 - try { - runningFlag.await(600 * 100, TimeUnit.MILLISECONDS); - } catch (InterruptedException e) { - LogUtil.e(TAG, "Catch java.lang.InterruptedException: " + e.getMessage(), e); + if (operation.getOperationMethod().getActionEnum() != PerformActionEnum.SLEEP) { + try { + runningFlag.await(600, TimeUnit.SECONDS); + } catch (InterruptedException e) { + LogUtil.e(TAG, "Catch java.lang.InterruptedException: " + e.getMessage(), e); + } + } else { + // SLEEP特殊处理,等1小时 + try { + runningFlag.await(60, TimeUnit.MINUTES); + } catch (InterruptedException e) { + LogUtil.e(TAG, "Catch java.lang.InterruptedException: " + e.getMessage(), e); + } } } LogUtil.d(TAG, "操作执行完毕"); @@ -619,14 +629,14 @@ public void setApp(String app) { public void receiveDeviceInfoMessage(UIOperationMessage message) { if (message.eventType == UIOperationMessage.TYPE_DEVICE_INFO) { DeviceInfo info = DeviceInfoUtil.generateDeviceInfo(); - showDialog("设备信息", info.toString(), binder.loadServiceContext(), 0); + showDialog(StringUtil.getString(R.string.ui__device_info), info.toString(), binder.loadServiceContext(), 0); } else if (message.eventType == UIOperationMessage.TYPE_DIALOG) { String info = message.getParam("msg"); String title = message.getParam("title"); showDialog(title, info, binder.loadServiceContext(), 0); } else if (message.eventType == UIOperationMessage.TYPE_COUNT_DOWN) { long timeMillis = message.getParam("time"); - showDialog("SLEEP", "等待" + timeMillis + "ms", binder.loadServiceContext(), timeMillis); + showDialog(StringUtil.getString(R.string.ui__sleep), StringUtil.getString(R.string.ui__sleep_time, timeMillis), binder.loadServiceContext(), timeMillis); } else if (message.eventType == UIOperationMessage.TYPE_DISMISS) { // 隐藏掉原来的Dialog if (dialogRef != null && dialogRef.get() != null && dialogRef.get().isShowing()) { @@ -669,7 +679,7 @@ public void showDialog(String title, String deviceInfo, Context context, long ti final AlertDialog dialog = new AlertDialog.Builder(context, R.style.AppDialogTheme) .setTitle(title) .setView(v) - .setPositiveButton("确定", new DialogInterface.OnClickListener() { + .setPositiveButton(R.string.constant__confirm, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); diff --git a/src/app/src/main/java/com/alipay/hulu/service/FloatWinService.java b/src/app/src/main/java/com/alipay/hulu/service/FloatWinService.java index aa45402..714a9d9 100644 --- a/src/app/src/main/java/com/alipay/hulu/service/FloatWinService.java +++ b/src/app/src/main/java/com/alipay/hulu/service/FloatWinService.java @@ -216,7 +216,7 @@ public void startDialog(String message) { messageText = (TextView) v.findViewById(R.id.loading_dialog_text); loadingDialog = new AlertDialog.Builder(this, R.style.AppDialogTheme) .setView(v) - .setNegativeButton("隐藏", new DialogInterface.OnClickListener() { + .setNegativeButton(R.string.float__hide, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); diff --git a/src/app/src/main/java/com/alipay/hulu/ui/TwoLevelSelectLayout.java b/src/app/src/main/java/com/alipay/hulu/ui/TwoLevelSelectLayout.java index 69693a8..fc5fecc 100644 --- a/src/app/src/main/java/com/alipay/hulu/ui/TwoLevelSelectLayout.java +++ b/src/app/src/main/java/com/alipay/hulu/ui/TwoLevelSelectLayout.java @@ -46,10 +46,10 @@ public class TwoLevelSelectLayout extends LinearLayout { private static final String TAG = "TwoLevelSelectLayout"; - private List keys = new ArrayList<>(); + private List keys = new ArrayList<>(); private List icons = new ArrayList<>(); private List currentSecondLevelItems = new ArrayList<>(); - private Map> allSecondLevelItems = new HashMap<>(); + private Map> allSecondLevelItems = new HashMap<>(); private ListView firstLevel; private ListView secondLevel; @@ -151,7 +151,9 @@ public View getView(int position, View convertView, ViewGroup parent) { // 设置图标 ImageView icon = (ImageView) convertView.findViewById(R.id.first_level_icon); + TextView title = (TextView) convertView.findViewById(R.id.first_level_text); icon.setImageResource(icons.get(position)); + title.setText(keys.get(position)); return convertView; } @@ -247,7 +249,7 @@ public void onItemClick(AdapterView parent, View view, int position, long id) * @param resources * @param secondLevels */ - public void updateMenus(List keys, List resources, Map> secondLevels) { + public void updateMenus(List keys, List resources, Map> secondLevels) { // 要求key与icon一一对应 if (keys == null || resources == null || keys.size() != resources.size()) { return; diff --git a/src/app/src/main/java/com/alipay/hulu/upgrade/PatchRequest.java b/src/app/src/main/java/com/alipay/hulu/upgrade/PatchRequest.java index a2b01ca..8be732a 100644 --- a/src/app/src/main/java/com/alipay/hulu/upgrade/PatchRequest.java +++ b/src/app/src/main/java/com/alipay/hulu/upgrade/PatchRequest.java @@ -192,7 +192,7 @@ public void run() { if (loadingCount.get() > 0) { final BaseActivity activity = (BaseActivity) LauncherApplication.getInstance().loadActivityOnTop(); if (activity != null) { - activity.showProgressDialog("加载插件中,请耐心等待"); + activity.showProgressDialog(StringUtil.getString(R.string.patch__loading_plugin_wait)); BackgroundExecutor.execute(new Runnable() { @Override public void run() { diff --git a/src/app/src/main/java/com/alipay/hulu/util/CaseAppendOperationProcessor.java b/src/app/src/main/java/com/alipay/hulu/util/CaseAppendOperationProcessor.java new file mode 100644 index 0000000..b39a578 --- /dev/null +++ b/src/app/src/main/java/com/alipay/hulu/util/CaseAppendOperationProcessor.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2015-present, Ant Financial Services Group + * + * 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.alipay.hulu.util; + +import android.content.Context; +import android.content.Intent; +import android.support.annotation.NonNull; + +import com.alibaba.fastjson.JSON; +import com.alipay.hulu.activity.CaseEditActivity; +import com.alipay.hulu.bean.CaseStepHolder; +import com.alipay.hulu.common.application.LauncherApplication; +import com.alipay.hulu.shared.io.OperationStepProcessor; +import com.alipay.hulu.shared.io.bean.GeneralOperationLogBean; +import com.alipay.hulu.shared.io.bean.RecordCaseInfo; +import com.alipay.hulu.shared.io.util.OperationStepUtil; +import com.alipay.hulu.shared.node.tree.export.bean.OperationStep; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by qiaoruikai on 2019-08-06 15:31. + */ +public class CaseAppendOperationProcessor implements OperationStepProcessor { + private RecordCaseInfo originCase; + private List recordSteps; + + public CaseAppendOperationProcessor(@NonNull RecordCaseInfo originCase) { + this.originCase = originCase; + + // 加载步骤信息 + GeneralOperationLogBean operationLogBean = JSON.parseObject(originCase.getOperationLog(), GeneralOperationLogBean.class); + OperationStepUtil.afterLoad(operationLogBean); + if (operationLogBean != null) { + recordSteps = operationLogBean.getSteps(); + } + if (recordSteps == null) { + recordSteps = new ArrayList<>(); + } + } + + @Override + public void onStartRecord(RecordCaseInfo recordCaseInfo) { + + } + + @Override + public boolean onStopRecord(final Context context) { + GeneralOperationLogBean operationLogBean = new GeneralOperationLogBean(); + operationLogBean.setSteps(recordSteps); + + originCase.setOperationLog(JSON.toJSONString(operationLogBean)); + + final int id = CaseStepHolder.storeCase(originCase); + LauncherApplication.getInstance().runOnUiThread(new Runnable() { + @Override + public void run() { + Intent intent = new Intent(context, CaseEditActivity.class); + intent.putExtra(CaseEditActivity.RECORD_CASE_EXTRA, id); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(intent); + } + }); + return true; + } + + @Override + public void onOperationStep(int operationIdx, OperationStep step) { + recordSteps.add(step); + } +} diff --git a/src/app/src/main/java/com/alipay/hulu/util/CaseReplayUtil.java b/src/app/src/main/java/com/alipay/hulu/util/CaseReplayUtil.java new file mode 100644 index 0000000..4d57835 --- /dev/null +++ b/src/app/src/main/java/com/alipay/hulu/util/CaseReplayUtil.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2015-present, Ant Financial Services Group + * + * 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.alipay.hulu.util; + +import android.support.annotation.NonNull; + +import com.alibaba.fastjson.JSON; +import com.alipay.hulu.activity.MyApplication; +import com.alipay.hulu.bean.AdvanceCaseSetting; +import com.alipay.hulu.common.application.LauncherApplication; +import com.alipay.hulu.replay.BatchStepProvider; +import com.alipay.hulu.replay.MultiParamStepProvider; +import com.alipay.hulu.replay.OperationStepProvider; +import com.alipay.hulu.replay.RepeatStepProvider; +import com.alipay.hulu.service.CaseReplayManager; +import com.alipay.hulu.shared.io.bean.RecordCaseInfo; + +import java.util.ArrayList; +import java.util.List; + +/** + * 用例回放管理器 + * Created by qiaoruikai on 2019-08-21 12:41. + */ +public class CaseReplayUtil { + + /** + * 开始回放单条用例 + * @param recordCase + */ + public static void startReplay(@NonNull RecordCaseInfo recordCase) { + CaseReplayManager manager = LauncherApplication.getInstance().findServiceByName(CaseReplayManager.class.getName()); + String advanceSettings = recordCase.getAdvanceSettings(); + AdvanceCaseSetting setting = JSON.parseObject(advanceSettings, AdvanceCaseSetting.class); + MyApplication.getInstance().updateAppAndNameTemp(recordCase.getTargetAppPackage(), recordCase.getTargetAppLabel()); + + if (setting != null && setting.getRunningParam() != null) { + MultiParamStepProvider stepProvider = new MultiParamStepProvider(recordCase); + manager.start(stepProvider, MyApplication.MULTI_REPLAY_LISTENER); + } else { + OperationStepProvider stepProvider = new OperationStepProvider(recordCase); + manager.start(stepProvider, MyApplication.SINGLE_REPLAY_LISTENER); + } + } + + /** + * 开始重复回放用例 + * @param recordCase + * @param times + * @param restart 执行前重启 + */ + public static void startReplayMultiTimes(@NonNull RecordCaseInfo recordCase, int times, boolean restart) { + RepeatStepProvider stepProvider = new RepeatStepProvider(recordCase, times, restart); + MyApplication.getInstance().updateAppAndNameTemp(recordCase.getTargetAppPackage(), recordCase.getTargetAppLabel()); + CaseReplayManager manager = LauncherApplication.getInstance().findServiceByName(CaseReplayManager.class.getName()); + manager.start(stepProvider, MyApplication.MULTI_REPLAY_LISTENER); + } + + /** + * 开始回放多条用例 + * @param recordCases 用例列表 + * @param restart 执行前重启 + */ + public static void startReplayMultiCase(@NonNull List recordCases, boolean restart) { + BatchStepProvider provider = new BatchStepProvider(new ArrayList<>(recordCases), restart); + CaseReplayManager manager = LauncherApplication.getInstance().findServiceByName(CaseReplayManager.class.getName()); + manager.start(provider, MyApplication.MULTI_REPLAY_LISTENER); + } +} diff --git a/src/app/src/main/java/com/alipay/hulu/util/DialogUtils.java b/src/app/src/main/java/com/alipay/hulu/util/DialogUtils.java index e0ebf0c..617c898 100644 --- a/src/app/src/main/java/com/alipay/hulu/util/DialogUtils.java +++ b/src/app/src/main/java/com/alipay/hulu/util/DialogUtils.java @@ -28,11 +28,13 @@ import android.support.v7.app.AlertDialog; import android.text.TextUtils; import android.util.DisplayMetrics; +import android.util.Pair; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; import android.widget.AdapterView; +import android.widget.EditText; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; @@ -55,6 +57,7 @@ import com.bumptech.glide.request.RequestOptions; import java.io.File; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.CountDownLatch; @@ -314,7 +317,7 @@ public boolean isEmpty() { listView.setDividerHeight(0); final AlertDialog.Builder builder = new AlertDialog.Builder(context, R.style.AppDialogTheme) - .setTitle("请选择操作") + .setTitle(R.string.function__select_function) .setView(listView) .setOnCancelListener(new DialogInterface.OnCancelListener() { @Override @@ -462,9 +465,9 @@ public void onClick(DialogInterface dialog, int which) { * @param secondLevels * @param callback */ - public static void showLeveledFunctionView(final Context context, final List keys, + public static void showLeveledFunctionView(final Context context, final List keys, final List icons, - final Map> secondLevels, + final Map> secondLevels, final FunctionViewCallback callback) { if (callback == null) { LogUtil.e(TAG,"回调函数为空"); @@ -484,7 +487,7 @@ public static void showLeveledFunctionView(final Context context, final List data); + } + + /** + * 为多个字段配置输入框 + * + * @param title + * @param data + */ + public static void showMultipleEditDialog(Context context, final OnDialogResultListener listener, String title, List> data) { + LayoutInflater inflater = LayoutInflater.from(ContextUtil.getContextThemeWrapper( + context, R.style.AppDialogTheme)); + + ScrollView v = (ScrollView) inflater.inflate(R.layout.dialog_setting, null); + + LinearLayout view = (LinearLayout) v.getChildAt(0); + final List editTexts = new ArrayList<>(); + + LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); + + // 对每一个字段添加EditText + for (Pair source : data) { + View editField = inflater.inflate(R.layout.item_edit_field, null); + + EditText edit = (EditText) editField.findViewById(R.id.item_edit_field_edit); + TextView name = (TextView) editField.findViewById(R.id.item_edit_field_name); + + if (StringUtil.isEmpty(source.first)) { + name.setVisibility(View.GONE); + } else { + // 配置字段 + name.setText(source.first); + } + edit.setHint(source.first); + edit.setText(source.second); + + view.addView(editField, layoutParams); + editTexts.add(edit); + } + + // 显示Dialog + new AlertDialog.Builder(context, R.style.AppDialogTheme) + .setTitle(title) + .setView(v) + .setPositiveButton(R.string.constant__confirm, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + List result = new ArrayList<>(editTexts.size() + 1); + + // 获取每个编辑框的文字 + for (EditText data : editTexts) { + result.add(data.getText().toString().trim()); + } + + if (listener != null) { + listener.onDialogPositive(result); + } + dialog.dismiss(); + } + }).setNegativeButton(R.string.constant__cancel, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + } + }).setCancelable(true) + .show(); + } + /** * 显示图像Dialog * @param context diff --git a/src/app/src/main/java/com/alipay/hulu/util/FunctionSelectUtil.java b/src/app/src/main/java/com/alipay/hulu/util/FunctionSelectUtil.java index d789664..663336c 100644 --- a/src/app/src/main/java/com/alipay/hulu/util/FunctionSelectUtil.java +++ b/src/app/src/main/java/com/alipay/hulu/util/FunctionSelectUtil.java @@ -20,6 +20,8 @@ import android.os.Build; import android.support.v7.app.AlertDialog; import android.support.v7.widget.AppCompatSpinner; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; import android.text.Editable; import android.text.InputType; import android.text.TextUtils; @@ -29,6 +31,7 @@ import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; +import android.view.inputmethod.InputMethodManager; import android.widget.AdapterView; import android.widget.Button; import android.widget.EditText; @@ -38,8 +41,12 @@ import android.widget.TextView; import android.widget.Toast; +import com.alibaba.fastjson.JSON; import com.alipay.hulu.R; +import com.alipay.hulu.bean.CaseParamBean; import com.alipay.hulu.common.application.LauncherApplication; +import com.alipay.hulu.common.injector.InjectorService; +import com.alipay.hulu.common.injector.param.SubscribeParamEnum; import com.alipay.hulu.common.tools.BackgroundExecutor; import com.alipay.hulu.common.utils.ContextUtil; import com.alipay.hulu.common.utils.LogUtil; @@ -59,9 +66,12 @@ import com.alipay.hulu.ui.FlowRadioGroup; import com.alipay.hulu.ui.TwoLevelSelectLayout; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Locale; import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; import java.util.regex.Pattern; /** @@ -70,14 +80,16 @@ */ public class FunctionSelectUtil { private static final String TAG = "FunctionSelect"; + public static final String ACTION_EXTRA = "ACTION_EXTRA"; + /** * 展示操作界面 * * @param node */ public static void showFunctionView(final Context context, final AbstractNodeTree node, - final List keys, final List icons, - final Map> secondLevel, + final List keys, final List icons, + final Map> secondLevel, final HighLightService highLightService, final OperationService operationService, final Pair localClickPos, @@ -137,6 +149,11 @@ public void onViewLoaded(View v, Runnable preCall) { final OperationMethod method = new OperationMethod( PerformActionEnum.getActionEnumByCode(action.key)); + // 透传一下 + if (!StringUtil.isEmpty(action.extra)) { + method.putParam(ACTION_EXTRA, action.extra); + } + // 添加控件点击位置 if (localClickPos != null) { method.putParam(OperationExecutor.LOCAL_CLICK_POS_KEY, localClickPos.first + "," + localClickPos.second); @@ -204,21 +221,16 @@ protected static boolean processAction(OperationMethod method, AbstractNodeTree PerformActionEnum action = method.getActionEnum(); if (action == PerformActionEnum.INPUT || action == PerformActionEnum.INPUT_SEARCH - || action == PerformActionEnum.LONG_CLICK) { - showEditView(node, method, context, listener); - return true; - } else if (action == PerformActionEnum.MULTI_CLICK - || action == PerformActionEnum.SLEEP_UNTIL) { - showEditView(node, method, context, listener); - return true; - } else if (action == PerformActionEnum.SLEEP - || action == PerformActionEnum.SCREENSHOT) { - showEditView(null, method, context, listener); - return true; - } else if (action == PerformActionEnum.SCROLL_TO_BOTTOM + || action == PerformActionEnum.LONG_CLICK + || action == PerformActionEnum.MULTI_CLICK + || action == PerformActionEnum.SLEEP_UNTIL + || action == PerformActionEnum.SLEEP + || action == PerformActionEnum.SCREENSHOT + || action == PerformActionEnum.SCROLL_TO_BOTTOM || action == PerformActionEnum.SCROLL_TO_TOP || action == PerformActionEnum.SCROLL_TO_LEFT - || action == PerformActionEnum.SCROLL_TO_RIGHT) { + || action == PerformActionEnum.SCROLL_TO_RIGHT + || action == PerformActionEnum.EXECUTE_SHELL) { showEditView(node, method, context, listener); return true; } else if (action == PerformActionEnum.ASSERT) { @@ -227,15 +239,13 @@ protected static boolean processAction(OperationMethod method, AbstractNodeTree } else if (action == PerformActionEnum.LET_NODE) { chooseLetMode(node, context, listener); return true; - } else if (action == PerformActionEnum.JUMP_TO_PAGE) { + } else if (action == PerformActionEnum.JUMP_TO_PAGE + || action == PerformActionEnum.LOAD_PARAM) { showSelectView(method, context, listener); return true; } else if (action == PerformActionEnum.CHANGE_MODE) { showChangeModeView(context, listener); return true; - }else if (action == PerformActionEnum.EXECUTE_SHELL) { - showEditView(node, method, context, listener); - return true; } else if (action == PerformActionEnum.WHILE) { showWhileView(method, context, listener); return true; @@ -264,7 +274,7 @@ private static void showChangeModeView(Context context, final FunctionListener l } AlertDialog.Builder builder = new AlertDialog.Builder(context, R.style.AppDialogTheme) - .setTitle("请选择要切换的模式") + .setTitle(R.string.function__set_mode) .setSingleChoiceItems(actions, 0, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { @@ -279,7 +289,7 @@ public void onClick(DialogInterface dialog, int which) { method.putParam(OperationExecutor.GET_NODE_MODE, modes[which].getCode()); listener.onProcessFunction(method, null); } - }).setNegativeButton("取消", new DialogInterface.OnClickListener() { + }).setNegativeButton(R.string.constant__cancel, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); @@ -314,8 +324,8 @@ private static void showSelectView(final OperationMethod method, final Context c View itemUrl = customView.findViewById(R.id.item_url); final AlertDialog dialog = new AlertDialog.Builder(context, R.style.AppDialogTheme) .setView(customView) - .setTitle("请选择操作方式") - .setNegativeButton("取消", new DialogInterface.OnClickListener() { + .setTitle(R.string.function__select_function) + .setNegativeButton(R.string.constant__cancel, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); @@ -338,6 +348,15 @@ public void onClick(View v) { showUrlEditView(method, context, listener); return; } + } else if (actionEnum == PerformActionEnum.LOAD_PARAM) { + if (v.getId() == R.id.item_scan) { + method.putParam("scan", "1"); + listener.onProcessFunction(method, null); + } else if (v.getId() == R.id.item_url) { + dialog.dismiss(); + showUrlEditView(method, context, listener); + return; + } } else { dialog.dismiss(); listener.onCancel(); @@ -367,12 +386,12 @@ private static void showUrlEditView(final OperationMethod method, Context contex final PerformActionEnum actionEnum = method.getActionEnum(); View v = LayoutInflater.from(ContextUtil.getContextThemeWrapper(context, R.style.AppDialogTheme)).inflate(R.layout.dialog_record_name, null); final EditText edit = (EditText) v.findViewById(R.id.dialog_record_edit); - edit.setHint("请输入url"); + edit.setHint(R.string.function__please_input_url); AlertDialog dialog = new AlertDialog.Builder(context, R.style.AppDialogTheme) - .setTitle("请输入url") + .setTitle(R.string.function__input_url) .setView(v) - .setPositiveButton("输入", new DialogInterface.OnClickListener() { + .setPositiveButton(R.string.function__input, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { LogUtil.i(TAG, "Positive " + which); @@ -385,9 +404,14 @@ public void onClick(DialogInterface dialog, int which) { // 向handler发送请求 method.putParam(OperationExecutor.SCHEME_KEY, data); listener.onProcessFunction(method, null); + } else if (actionEnum == PerformActionEnum.LOAD_PARAM) { + method.putParam(OperationExecutor.APP_URL_KEY, data); + + // 向handler发送请求 + listener.onProcessFunction(method, null); } } - }).setNegativeButton("取消", new DialogInterface.OnClickListener() { + }).setNegativeButton(R.string.constant__cancel, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { @@ -504,9 +528,9 @@ public void onCheckedChanged(CheckableRelativeLayout checkable, boolean isChecke final AbstractNodeTree finalNode = node; AlertDialog dialog = new AlertDialog.Builder(context, R.style.AppDialogTheme) - .setTitle("请设置变量值") + .setTitle(R.string.function__set_variable) .setView(letView) - .setPositiveButton("确定", new DialogInterface.OnClickListener() { + .setPositiveButton(R.string.constant__confirm, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { LogUtil.i(TAG, "Positive " + which); @@ -527,7 +551,7 @@ public void onClick(DialogInterface dialog, int which) { listener.onProcessFunction(method, finalNode); } - }).setNegativeButton("取消", new DialogInterface.OnClickListener() { + }).setNegativeButton(R.string.constant__cancel, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { @@ -616,10 +640,10 @@ public void afterTextChanged(Editable s) { }); AlertDialog dialog = new AlertDialog.Builder(context, R.style.AppDialogTheme) - .setTitle("输入数字并选择断言模式") + .setTitle(R.string.function__input_number_assert) .setView(content) - .setPositiveButton("确定", null) - .setNegativeButton("取消", new DialogInterface.OnClickListener() { + .setPositiveButton(R.string.constant__confirm, null) + .setNegativeButton(R.string.constant__cancel, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { LogUtil.i(TAG, "Negative " + which); @@ -640,7 +664,7 @@ public void onClick(View v) { param.put(OperationExecutor.ASSERT_INPUT_CONTENT, strResult[0]); postiveClick(action, node, dialog, param, listener); } else { - Toast.makeText(context, "请输入数字", Toast.LENGTH_SHORT).show(); + LauncherApplication.toast(R.string.function__input_number); } } }); @@ -696,10 +720,10 @@ public void afterTextChanged(Editable s) { }); AlertDialog dialog = new AlertDialog.Builder(context, R.style.AppDialogTheme) - .setTitle("输入内容并选择断言模式") + .setTitle(R.string.function__input_select_assert) .setView(content) - .setPositiveButton("确定", null) - .setNegativeButton("取消", new DialogInterface.OnClickListener() { + .setPositiveButton(R.string.constant__confirm, null) + .setNegativeButton(R.string.constant__cancel, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { LogUtil.i(TAG, "Negative " + which); @@ -724,7 +748,7 @@ public void onClick(View v) { param, listener); } else { - Toast.makeText(context, "请输入内容", Toast.LENGTH_SHORT).show(); + LauncherApplication.toast(R.string.function__input_content); } } }); @@ -776,53 +800,53 @@ protected static void showEditView(final AbstractNodeTree node, final OperationM try { PerformActionEnum action = method.getActionEnum(); - String title = "请输入具体内容"; + String title = StringUtil.getString(R.string.function__input_title); View v = LayoutInflater.from(ContextUtil.getContextThemeWrapper(context, R.style.AppDialogTheme)).inflate(R.layout.dialog_record_name, null); final EditText edit = (EditText) v.findViewById(R.id.dialog_record_edit); final Pattern textPattern; if (action == PerformActionEnum.SLEEP) { - edit.setHint("sleep时长(单位ms)"); - title = "请设置Sleep时长"; + edit.setHint(R.string.function__sleep_time); + title = StringUtil.getString(R.string.function__set_sleep_time); textPattern = Pattern.compile("\\d+"); } else if (action == PerformActionEnum.SCREENSHOT) { - edit.setHint("截图名称"); - title = "请输入截图名称"; + edit.setHint(R.string.function__screenshot_name); + title = StringUtil.getString(R.string.function__set_screenshot_name); textPattern = Pattern.compile("\\S+(.*\\S+)?"); } else if (action ==PerformActionEnum.MULTI_CLICK) { - edit.setHint("点击次数"); - title = "请输入连续点击次数(1-99次)"; + edit.setHint(R.string.function__click_time); + title = StringUtil.getString(R.string.function__set_click_time); textPattern = Pattern.compile("\\d{1,2}"); } else if (action ==PerformActionEnum.SLEEP_UNTIL) { - edit.setHint("最长等待时间"); + edit.setHint(R.string.function__max_wait); edit.setText(R.string.default_sleep_time); - title = "请输入最长等待时间(单位ms)"; + title = StringUtil.getString(R.string.function__set_max_wait); textPattern = Pattern.compile("\\d+"); } else if (action == PerformActionEnum.SCROLL_TO_BOTTOM || action == PerformActionEnum.SCROLL_TO_TOP || action == PerformActionEnum.SCROLL_TO_LEFT || action == PerformActionEnum.SCROLL_TO_RIGHT) { - edit.setHint("滑动百分比"); + edit.setHint(R.string.function__scroll_percent); edit.setText(R.string.default_scroll_percentage); - title = "请输入滑动百分比"; + title = StringUtil.getString(R.string.function__set_scroll_percent); textPattern = Pattern.compile("\\d+"); } else if (action == PerformActionEnum.EXECUTE_SHELL) { - edit.setHint("请输入adb shell命令"); - title = "请输入shell命令"; + edit.setHint(R.string.function__adb_cmd); + title = StringUtil.getString(R.string.function__set_adb_cmd); textPattern = null; } else if (action == PerformActionEnum.LONG_CLICK) { - edit.setHint("长按时长"); - title = "请输入长按时长(单位ms)"; + edit.setHint(R.string.function__long_press); + title = StringUtil.getString(R.string.function__set_long_press); textPattern = Pattern.compile("[1-9]\\d+"); edit.setText(R.string.default_long_click_time); } else { - edit.setHint("具体内容"); + edit.setHint(R.string.function__input_content); textPattern = null; } final AlertDialog dialog = new AlertDialog.Builder(context, R.style.AppDialogTheme) .setTitle(title) .setView(v) - .setPositiveButton("输入", new DialogInterface.OnClickListener() { + .setPositiveButton(R.string.function__input, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { LogUtil.i(TAG, "Positive " + which); @@ -843,7 +867,7 @@ public void run() { } }, 500); } - }).setNegativeButton("取消", new DialogInterface.OnClickListener() { + }).setNegativeButton(R.string.constant__cancel, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { LogUtil.i(TAG, "Negative " + which); @@ -914,12 +938,12 @@ public void onItemSelected(AdapterView parent, View view, int position, long edit.clearComposingText(); if (position == 0) { - hint.setText("循环次数"); - edit.setHint("循环次数"); + hint.setText(R.string.function__loop_count); + edit.setHint(R.string.function__loop_count); edit.setInputType(InputType.TYPE_CLASS_NUMBER); } else { - hint.setText("循环条件"); - edit.setHint("循环条件"); + hint.setText(R.string.function__loop_condition); + edit.setHint(R.string.function__loop_condition); edit.setInputType(InputType.TYPE_CLASS_TEXT); } } @@ -931,9 +955,9 @@ public void onNothingSelected(AdapterView parent) { }); final AlertDialog dialog = new AlertDialog.Builder(context, R.style.AppDialogTheme) - .setTitle("添加循环") + .setTitle(R.string.function__add_loop) .setView(v) - .setPositiveButton("添加", new DialogInterface.OnClickListener() { + .setPositiveButton(R.string.function__add, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { LogUtil.i(TAG, "Positive " + which); @@ -958,7 +982,7 @@ public void run() { } }, 500); } - }).setNegativeButton("取消", new DialogInterface.OnClickListener() { + }).setNegativeButton(R.string.constant__cancel, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { LogUtil.i(TAG, "Negative " + which); @@ -999,7 +1023,7 @@ private static void showProvidedView(final AbstractNodeTree node, final Operatio AlertDialog dialog = new AlertDialog.Builder(context, R.style.AppDialogTheme) .setView(view) .setCancelable(false) - .setPositiveButton("确定", new DialogInterface.OnClickListener() { + .setPositiveButton(R.string.constant__confirm, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { LogUtil.i(TAG, "Positive " + which); @@ -1020,7 +1044,7 @@ public void run() { listener.onProcessFunction(method, node); } } - }).setNegativeButton("取消", new DialogInterface.OnClickListener() { + }).setNegativeButton(R.string.constant__cancel, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { LogUtil.i(TAG, "Negative " + which); diff --git a/src/app/src/main/res/drawable/accent_button_background.xml b/src/app/src/main/res/drawable/accent_button_background.xml new file mode 100644 index 0000000..c3860d2 --- /dev/null +++ b/src/app/src/main/res/drawable/accent_button_background.xml @@ -0,0 +1,29 @@ + + + + + + + + \ No newline at end of file diff --git a/src/app/src/main/res/drawable/accent_button_layer.xml b/src/app/src/main/res/drawable/accent_button_layer.xml new file mode 100644 index 0000000..3f27f22 --- /dev/null +++ b/src/app/src/main/res/drawable/accent_button_layer.xml @@ -0,0 +1,18 @@ + + + + \ No newline at end of file diff --git a/src/app/src/main/res/drawable/bg_template_list.xml b/src/app/src/main/res/drawable/bg_template_list.xml new file mode 100644 index 0000000..42ab304 --- /dev/null +++ b/src/app/src/main/res/drawable/bg_template_list.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/app/src/main/res/drawable/case_play.xml b/src/app/src/main/res/drawable/case_play.xml new file mode 100644 index 0000000..298a3c3 --- /dev/null +++ b/src/app/src/main/res/drawable/case_play.xml @@ -0,0 +1,19 @@ + + + + diff --git a/src/app/src/main/res/drawable/case_step_copy.xml b/src/app/src/main/res/drawable/case_step_copy.xml new file mode 100644 index 0000000..420550c --- /dev/null +++ b/src/app/src/main/res/drawable/case_step_copy.xml @@ -0,0 +1,25 @@ + + + + + diff --git a/src/app/src/main/res/drawable/case_step_paste.xml b/src/app/src/main/res/drawable/case_step_paste.xml new file mode 100644 index 0000000..f53f315 --- /dev/null +++ b/src/app/src/main/res/drawable/case_step_paste.xml @@ -0,0 +1,25 @@ + + + + + diff --git a/src/app/src/main/res/drawable/case_step_select.xml b/src/app/src/main/res/drawable/case_step_select.xml new file mode 100644 index 0000000..49e5e41 --- /dev/null +++ b/src/app/src/main/res/drawable/case_step_select.xml @@ -0,0 +1,25 @@ + + + + + diff --git a/src/app/src/main/res/layout-v23/item_tools_grid.xml b/src/app/src/main/res/layout-v23/item_tools_grid.xml index 912073b..9f1927d 100644 --- a/src/app/src/main/res/layout-v23/item_tools_grid.xml +++ b/src/app/src/main/res/layout-v23/item_tools_grid.xml @@ -44,7 +44,7 @@ android:layout_height="55dp" android:layout_centerHorizontal="true" android:layout_marginTop="30dp" - android:src="@drawable/icon_xingneng" /> + /> \ No newline at end of file diff --git a/src/app/src/main/res/layout/activity_batch_replay_result.xml b/src/app/src/main/res/layout/activity_batch_replay_result.xml index a8d32cc..96b55d7 100644 --- a/src/app/src/main/res/layout/activity_batch_replay_result.xml +++ b/src/app/src/main/res/layout/activity_batch_replay_result.xml @@ -26,7 +26,7 @@ android:layout_height="wrap_content" android:paddingLeft="16dp" android:paddingRight="16dp" - android:text="回放总结" + android:text="@string/batch_result__replay_summary" android:textColor="@color/primaryText" android:layout_marginTop="10dp" android:textSize="20dp"/> @@ -38,7 +38,6 @@ android:layout_marginTop="10dp" android:paddingLeft="16dp" android:paddingRight="16dp" - android:text="用例总数" android:textColor="@color/subFont" android:textSize="14dp"/> @@ -49,7 +48,6 @@ android:layout_marginTop="5dp" android:paddingLeft="16dp" android:paddingRight="16dp" - android:text="成功" android:textColor="@color/subFont" android:textSize="14dp"/> @@ -60,7 +58,6 @@ android:layout_marginTop="5dp" android:paddingLeft="16dp" android:paddingRight="16dp" - android:text="失败" android:textColor="@color/subFont" android:textSize="14dp"/> diff --git a/src/app/src/main/res/layout/activity_case_param_edit.xml b/src/app/src/main/res/layout/activity_case_param_edit.xml new file mode 100644 index 0000000..345d9b9 --- /dev/null +++ b/src/app/src/main/res/layout/activity_case_param_edit.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/app/src/main/res/layout/activity_display_replay_result.xml b/src/app/src/main/res/layout/activity_display_replay_result.xml index 6b0b4c8..35ea494 100644 --- a/src/app/src/main/res/layout/activity_display_replay_result.xml +++ b/src/app/src/main/res/layout/activity_display_replay_result.xml @@ -27,7 +27,7 @@ diff --git a/src/app/src/main/res/layout/activity_performance.xml b/src/app/src/main/res/layout/activity_performance.xml index 8b1ef7f..d4e3be7 100644 --- a/src/app/src/main/res/layout/activity_performance.xml +++ b/src/app/src/main/res/layout/activity_performance.xml @@ -39,7 +39,7 @@ + android:text="@string/record_config__title"/> diff --git a/src/app/src/main/res/layout/activity_record_manage.xml b/src/app/src/main/res/layout/activity_record_manage.xml index 1db25ac..3810adb 100644 --- a/src/app/src/main/res/layout/activity_record_manage.xml +++ b/src/app/src/main/res/layout/activity_record_manage.xml @@ -14,7 +14,9 @@ ~ limitations under the License. --> @@ -59,7 +62,7 @@ android:layout_gravity="center_horizontal" android:layout_marginTop="20dp" android:textSize="14dp" - android:text="删除数据" + android:text="@string/record_manage__delete_data" android:textColor="@color/white" android:background="@drawable/bg_solid_round_btn" android:layout_marginBottom="19dp"/> diff --git a/src/app/src/main/res/layout/activity_settings.xml b/src/app/src/main/res/layout/activity_settings.xml index 59de5ab..dfce0d3 100644 --- a/src/app/src/main/res/layout/activity_settings.xml +++ b/src/app/src/main/res/layout/activity_settings.xml @@ -490,7 +490,35 @@ android:ellipsize="end" android:textColor="@color/gray" android:textSize="12dp" - android:text="720P"/> + android:text="@string/settings__resolution_default"/> + + + + + + + + diff --git a/src/app/src/main/res/layout/clickable_item.xml b/src/app/src/main/res/layout/clickable_item.xml index cb85942..544ee6a 100644 --- a/src/app/src/main/res/layout/clickable_item.xml +++ b/src/app/src/main/res/layout/clickable_item.xml @@ -31,7 +31,7 @@ android:textSize="19dp" android:textColor="@color/primaryText" android:layout_gravity="center_vertical" - android:text="录制数据查看" + android:text="@string/performance__view_record" android:layout_weight="1" /> diff --git a/src/app/src/main/res/layout/dialog_action_crop_image.xml b/src/app/src/main/res/layout/dialog_action_crop_image.xml index 5421068..97701fd 100644 --- a/src/app/src/main/res/layout/dialog_action_crop_image.xml +++ b/src/app/src/main/res/layout/dialog_action_crop_image.xml @@ -27,7 +27,7 @@ android:layout_marginLeft="16dp" android:textColor="@color/primaryText" android:layout_marginBottom="8dp" - android:text="请选择待操作区域"/> + android:text="@string/crop__select_action_area"/> + android:text="@string/let__string"/> + android:text="@string/let__integer"/> + android:text="@string/let__custom"/> diff --git a/src/app/src/main/res/layout/dialog_action_record_config.xml b/src/app/src/main/res/layout/dialog_action_record_config.xml index 7d971ed..0a5d805 100644 --- a/src/app/src/main/res/layout/dialog_action_record_config.xml +++ b/src/app/src/main/res/layout/dialog_action_record_config.xml @@ -27,7 +27,7 @@ android:layout_marginRight="16dp" > @@ -41,7 +41,7 @@ android:layout_marginLeft="15dp" android:layout_marginRight="30dp" android:layout_toRightOf="@id/assert_num_title" - android:hint="请输入数字" + android:hint="@string/assert__input_number" android:textColor="@color/primaryText" android:textColorHighlight="@color/colorAccent" android:textColorHint="@color/secondaryText" @@ -62,31 +62,31 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:checked="true" - android:text="大于" /> + android:text="@string/assert__gt" /> + android:text="@string/assert__gte" /> + android:text="@string/assert__lt" /> + android:text="@string/assert__lte" /> + android:text="@string/assert__equal" /> diff --git a/src/app/src/main/res/layout/dialog_assert_string.xml b/src/app/src/main/res/layout/dialog_assert_string.xml index 422516d..448e160 100644 --- a/src/app/src/main/res/layout/dialog_assert_string.xml +++ b/src/app/src/main/res/layout/dialog_assert_string.xml @@ -28,7 +28,7 @@ android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_marginLeft="10dp" - android:text="内容" + android:text="@string/assert__content" android:textColor="@color/black" android:textSize="14dp" /> @@ -41,7 +41,7 @@ android:layout_marginLeft="15dp" android:layout_marginRight="30dp" android:layout_toRightOf="@id/assert_string_title" - android:hint="请输入待匹配内容" + android:hint="@string/assert__input_assert_content" android:textColor="@color/primaryText" android:textColorHighlight="@color/colorAccent" android:textColorHint="@color/secondaryText" @@ -61,19 +61,19 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:checked="true" - android:text="精准匹配" /> + android:text="@string/assert__accurate" /> + android:text="@string/assert__include" /> + android:text="@string/assert__regex" /> \ No newline at end of file diff --git a/src/app/src/main/res/layout/dialog_first_level_item.xml b/src/app/src/main/res/layout/dialog_first_level_item.xml index 3ac9d72..11678d5 100644 --- a/src/app/src/main/res/layout/dialog_first_level_item.xml +++ b/src/app/src/main/res/layout/dialog_first_level_item.xml @@ -18,14 +18,23 @@ android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content" - android:paddingTop="6dp" - android:paddingBottom="6dp" + android:paddingTop="2dp" + android:paddingBottom="2dp" android:duplicateParentState="true" app:selectColor="@color/item_pressed"> + android:layout_width="24dp" + android:layout_height="24dp" /> + + \ No newline at end of file diff --git a/src/app/src/main/res/layout/dialog_global_param_setting.xml b/src/app/src/main/res/layout/dialog_global_param_setting.xml new file mode 100644 index 0000000..8bbfda5 --- /dev/null +++ b/src/app/src/main/res/layout/dialog_global_param_setting.xml @@ -0,0 +1,128 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/app/src/main/res/layout/dialog_loading.xml b/src/app/src/main/res/layout/dialog_loading.xml index c607931..a5cfb94 100644 --- a/src/app/src/main/res/layout/dialog_loading.xml +++ b/src/app/src/main/res/layout/dialog_loading.xml @@ -28,8 +28,7 @@ android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:textColor="@color/secondaryText" - android:textSize="14dp" - android:text="正在加载HTML页面结构,请耐心等待"/> + android:textSize="14dp"/> + + + \ No newline at end of file diff --git a/src/app/src/main/res/layout/dialog_record_name.xml b/src/app/src/main/res/layout/dialog_record_name.xml index 8637c6b..25a9181 100644 --- a/src/app/src/main/res/layout/dialog_record_name.xml +++ b/src/app/src/main/res/layout/dialog_record_name.xml @@ -29,7 +29,6 @@ android:textColorHighlight="@color/colorAccent" android:layout_width="match_parent" android:layout_height="wrap_content" - android:hint="录制名称" android:textSize="18sp" /> @@ -43,7 +43,7 @@ android:layout_marginTop="8dp" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:text="循环条件" + android:text="@string/while__loop_check" android:textColor="@color/secondaryText" android:textSize="14dp" /> @@ -55,7 +55,6 @@ android:textColorHighlight="@color/colorAccent" android:layout_width="match_parent" android:layout_height="wrap_content" - android:hint="循环次数" android:textSize="18sp" /> diff --git a/src/app/src/main/res/layout/float_win_list.xml b/src/app/src/main/res/layout/float_win_list.xml index 5d6a23a..76ba0c8 100644 --- a/src/app/src/main/res/layout/float_win_list.xml +++ b/src/app/src/main/res/layout/float_win_list.xml @@ -23,7 +23,6 @@ @@ -52,7 +50,6 @@ android:focusable="true" android:layout_gravity="center_vertical" android:textColor="@color/colorAccent" - android:text="清零" android:maxLines="2" android:textSize="13.5sp" /> diff --git a/src/app/src/main/res/layout/fragment_case_desc_edit.xml b/src/app/src/main/res/layout/fragment_case_desc_edit.xml index 23e8bf7..a92e14f 100644 --- a/src/app/src/main/res/layout/fragment_case_desc_edit.xml +++ b/src/app/src/main/res/layout/fragment_case_desc_edit.xml @@ -15,7 +15,8 @@ --> + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools"> @@ -54,11 +55,11 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" - android:layout_marginTop="28dp" + android:layout_marginTop="16dp" android:gravity="center_vertical"> + + + + + + \ No newline at end of file diff --git a/src/app/src/main/res/layout/fragment_replay_list.xml b/src/app/src/main/res/layout/fragment_replay_list.xml index 3c13053..2af1621 100644 --- a/src/app/src/main/res/layout/fragment_replay_list.xml +++ b/src/app/src/main/res/layout/fragment_replay_list.xml @@ -18,12 +18,17 @@ android:layout_width="match_parent" android:layout_height="match_parent"> + + + + + + + + + + +