diff --git a/DistributedMail/LICENSE b/DistributedMail/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..80576ef141485b36eea4aebf25af97020bc2de44 --- /dev/null +++ b/DistributedMail/LICENSE @@ -0,0 +1,78 @@ + Copyright (c) 2021 Huawei Device Co., Ltd. All rights reserved. + + 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. + +Apache License, Version 2.0 +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + +2. Grant of Copyright License. + +Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. + +Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. + +You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: +1.You must give any other recipients of the Work or Derivative Works a copy of this License; and +2.You must cause any modified files to carry prominent notices stating that You changed the files; and +3.You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and +4.If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + +You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + +5. Submission of Contributions. + +Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + +6. Trademarks. + +This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. + +Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. + +In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. + +While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS \ No newline at end of file diff --git a/DistributedMail/README.md b/DistributedMail/README.md new file mode 100644 index 0000000000000000000000000000000000000000..97eda3382163227bcf1b8ef3198975ee5572fa2d --- /dev/null +++ b/DistributedMail/README.md @@ -0,0 +1,6 @@ +# Distributed Mail Client + +In this article, we make a simple demonstration by simulating the collaborative email editing between different devices. We can complete the task of cross device migration through the migration button, and call the cross device pictures through the attachment button. + +Licensing +Please see LICENSE for more info. \ No newline at end of file diff --git a/DistributedMail/RELEASE-NOTES.md b/DistributedMail/RELEASE-NOTES.md new file mode 100644 index 0000000000000000000000000000000000000000..ea23a13e3d4407d06cba3aa7155a23e5081b50b4 --- /dev/null +++ b/DistributedMail/RELEASE-NOTES.md @@ -0,0 +1,2 @@ +1.0.0 +1.Initial version \ No newline at end of file diff --git a/DistributedMail/build.gradle b/DistributedMail/build.gradle new file mode 100644 index 0000000000000000000000000000000000000000..bca51e05a07965a3d25dbc382354fb6335224f45 --- /dev/null +++ b/DistributedMail/build.gradle @@ -0,0 +1,43 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +apply plugin: 'com.huawei.ohos.app' + +ohos { + compileSdkVersion 3 + defaultConfig { + compatibleSdkVersion 3 + } +} + +buildscript { + repositories { + maven { + url 'http://repo.ark.tools.huawei.com/artifactory/maven-public/' + } + maven { + url 'https://developer.huawei.com/repo/' + } + maven { + url 'http://artifactory.cde.huawei.com/artifactory/Product-Binary-Release' + } + jcenter() + } + dependencies { + classpath 'com.huawei.ohos:hap:2.4.1.5' + classpath 'com.huawei.ohos:decctest:1.0.0.+' + } +} + +allprojects { + repositories { + maven { + url 'https://mirrors.huaweicloud.com/repository/maven/' + } + maven { + url 'https://developer.huawei.com/repo/' + } + maven { + url 'http://artifactory.cde.huawei.com/artifactory/Product-Binary-Release' + } + jcenter() + } +} diff --git a/DistributedMail/entry/.gitignore b/DistributedMail/entry/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..796b96d1c402326528b4ba3c12ee9d92d0e212e9 --- /dev/null +++ b/DistributedMail/entry/.gitignore @@ -0,0 +1 @@ +/build diff --git a/DistributedMail/entry/build.gradle b/DistributedMail/entry/build.gradle new file mode 100644 index 0000000000000000000000000000000000000000..ed02226046851566b832af5925fa9c0f3e4d789c --- /dev/null +++ b/DistributedMail/entry/build.gradle @@ -0,0 +1,17 @@ +apply plugin: 'com.huawei.ohos.hap' +apply plugin: 'com.huawei.ohos.decctest' +ohos { + compileSdkVersion 3 + defaultConfig { + compatibleSdkVersion 3 + } +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + testCompile 'junit:junit:4.12' + ohosTestImplementation 'decc.testkit:harmonyjunitrunner:0.3' +} +decc { + supportType = ['html', 'xml'] +} diff --git a/DistributedMail/entry/src/main/config.json b/DistributedMail/entry/src/main/config.json new file mode 100644 index 0000000000000000000000000000000000000000..73db12ec8528d2bae448f53707ebe09271ffc377 --- /dev/null +++ b/DistributedMail/entry/src/main/config.json @@ -0,0 +1,80 @@ +{ + "app": { + "bundleName": "com.huawei.maildemo", + "vendor": "huawei", + "version": { + "code": 1, + "name": "1.0" + }, + "apiVersion": { + "compatible": 3, + "target": 3 + } + }, + "deviceConfig": {}, + "module": { + "package": "com.huawei.maildemo", + "name": ".MyApplication", + "reqCapabilities": [ + "video_support" + ], + "deviceType": [ + "phone", + "tablet" + ], + "distro": { + "deliveryWithInstall": true, + "moduleName": "entry", + "moduleType": "entry" + }, + "abilities": [ + { + "skills": [ + { + "entities": [ + "entity.system.home" + ], + "actions": [ + "action.system.home" + ] + } + ], + "orientation": "unspecified", + "visible": true, + "formEnabled": false, + "name": "com.huawei.maildemo.MainAbility", + "icon": "$media:icon", + "description": "$string:mainability_description", + "label": "MailDemo", + "type": "page", + "launchType": "standard" + } + ], + "reqPermissions": [ + { + "name": "ohos.permission.GET_DISTRIBUTED_DEVICE_INFO" + }, + { + "name": "ohos.permission.DISTRIBUTED_DATASYNC" + }, + { + "name": "ohos.permission.DISTRIBUTED_DEVICE_STATE_CHANGE" + }, + { + "name": "ohos.permission.READ_USER_STORAGE" + }, + { + "name": "ohos.permission.WRITE_USER_STORAGE" + }, + { + "name": "ohos.permission.GET_BUNDLE_INFO" + } + ], + "defPermissions": [ + { + "name": "com.huawei.maildemo.DataAbilityShellProvider.PROVIDER", + "grantMode": "system_grant" + } + ] + } +} \ No newline at end of file diff --git a/DistributedMail/entry/src/main/java/com/huawei/maildemo/MainAbility.java b/DistributedMail/entry/src/main/java/com/huawei/maildemo/MainAbility.java new file mode 100644 index 0000000000000000000000000000000000000000..c72cda11f4e0c3f1e0fc124e0585bfaf3c5bf704 --- /dev/null +++ b/DistributedMail/entry/src/main/java/com/huawei/maildemo/MainAbility.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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.huawei.maildemo; + +import com.huawei.maildemo.slice.MailEditSlice; +import com.huawei.maildemo.utils.LogUtil; + +import ohos.aafwk.ability.Ability; +import ohos.aafwk.ability.IAbilityContinuation; +import ohos.aafwk.content.Intent; +import ohos.aafwk.content.IntentParams; + +import java.util.ArrayList; +import java.util.List; + +/** + * MainAbility + * + * @since 2021-02-04 + */ +public class MainAbility extends Ability implements IAbilityContinuation { + private static final String TAG = MainAbility.class.getSimpleName(); + + @Override + public void onStart(Intent intent) { + super.onStart(intent); + super.setMainRoute(MailEditSlice.class.getName()); + requestPermission(); + } + + private void requestPermission() { + String[] permissions = { + "ohos.permission.READ_USER_STORAGE", + "ohos.permission.WRITE_USER_STORAGE", + "ohos.permission.DISTRIBUTED_DATASYNC" + }; + List applyPermissions = new ArrayList<>(); + for (String element : permissions) { + LogUtil.info(TAG, "check permission: " + element); + if (verifySelfPermission(element) != 0) { + if (canRequestPermission(element)) { + applyPermissions.add(element); + } else { + LogUtil.info(TAG, "user deny permission"); + } + } else { + LogUtil.info(TAG, "user granted permission: " + element); + } + } + requestPermissionsFromUser(applyPermissions.toArray(new String[0]), 0); + } + + @Override + public void onInactive() { + super.onInactive(); + LogUtil.info(TAG, "is onInactive"); + } + + @Override + public void onActive() { + super.onActive(); + LogUtil.info(TAG, "is onActive"); + } + + @Override + public void onBackground() { + super.onBackground(); + LogUtil.info(TAG, "is onBackground"); + } + + @Override + public void onForeground(Intent intent) { + super.onForeground(intent); + LogUtil.info(TAG, "is onForeground"); + } + + @Override + public void onStop() { + super.onStop(); + LogUtil.info(TAG, "is onStop"); + } + + @Override + public void onCompleteContinuation(int code) { } + + @Override + public boolean onRestoreData(IntentParams params) { + return true; + } + + @Override + public boolean onSaveData(IntentParams params) { + return true; + } + + @Override + public boolean onStartContinuation() { + return true; + } +} diff --git a/DistributedMail/entry/src/main/java/com/huawei/maildemo/MyApplication.java b/DistributedMail/entry/src/main/java/com/huawei/maildemo/MyApplication.java new file mode 100644 index 0000000000000000000000000000000000000000..fcdcc39bad3d2bb43014013e621d2de6ef743947 --- /dev/null +++ b/DistributedMail/entry/src/main/java/com/huawei/maildemo/MyApplication.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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.huawei.maildemo; + +import ohos.aafwk.ability.AbilityPackage; + +/** + * Mail demo + * + * @since 2021-02-04 + */ +public class MyApplication extends AbilityPackage { + @Override + public void onInitialize() { + super.onInitialize(); + } +} diff --git a/DistributedMail/entry/src/main/java/com/huawei/maildemo/bean/MailDataBean.java b/DistributedMail/entry/src/main/java/com/huawei/maildemo/bean/MailDataBean.java new file mode 100644 index 0000000000000000000000000000000000000000..b61a3cff8a3ea1e7841b73128dd0a431c58180a3 --- /dev/null +++ b/DistributedMail/entry/src/main/java/com/huawei/maildemo/bean/MailDataBean.java @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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.huawei.maildemo.bean; + +import com.huawei.maildemo.utils.LogUtil; + +import ohos.aafwk.content.IntentParams; + +import java.util.List; + +/** + * Mail data + * + * @since 2021-02-04 + */ +public class MailDataBean { + private static final String ARGS_RECEIVER = "receiver"; + + private static final String ARGS_CC = "cc"; + + private static final String ARGS_TITLE = "title"; + + private static final String ARGS_CONTENT = "content"; + + private static final String ARGS_PIC_LIST = "pic_list"; + + private String receiver; + + private String cc; + + private String title; + + private String content; + + private List pictureDataList; + + /** + * Constructor + */ + public MailDataBean() { + super(); + } + + /** + * Constructor + * + * @param receiver mail receiver + * @param cc mail cc + * @param title mail title + * @param content mail content + */ + public MailDataBean(String receiver, String cc, String title, String content) { + super(); + this.receiver = receiver; + this.cc = cc; + this.title = title; + this.content = content; + } + + /** + * Constructor with IntentParams + * + * @param params IntentParams + */ + public MailDataBean(IntentParams params) { + if (params == null) { + LogUtil.info(this.getClass(), "Invalid intent params, can't create MailDataBean"); + return; + } + + this.receiver = getStringParam(params, ARGS_RECEIVER); + this.cc = getStringParam(params, ARGS_CC); + this.title = getStringParam(params, ARGS_TITLE); + this.content = getStringParam(params, ARGS_CONTENT); + this.pictureDataList = (List) params.getParam(ARGS_PIC_LIST); + } + + private String getStringParam(IntentParams intentParams, String key) { + Object value = intentParams.getParam(key); + if ((value != null) && (value instanceof String)) { + return (String) value; + } + return ""; + } + + /** + * MailDataBean to IntentParams + * + * @param params intent params + */ + public void saveDataToParams(IntentParams params) { + params.setParam(ARGS_RECEIVER, this.receiver == null ? "" : this.receiver); + params.setParam(ARGS_CC, this.cc == null ? "" : this.cc); + params.setParam(ARGS_TITLE, this.title == null ? "" : this.title); + params.setParam(ARGS_CONTENT, this.content == null ? "" : this.content); + params.setParam(ARGS_PIC_LIST, this.pictureDataList == null ? null : this.pictureDataList); + } + + public String getReceiver() { + return receiver; + } + + public void setReceiver(String receiver) { + this.receiver = receiver; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public List getPictureDataList() { + return pictureDataList; + } + + public void setPictureDataList(List pictureDataList) { + this.pictureDataList = pictureDataList; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public String getCc() { + return cc; + } + + public void setCc(String cc) { + this.cc = cc; + } +} diff --git a/DistributedMail/entry/src/main/java/com/huawei/maildemo/data/DeviceData.java b/DistributedMail/entry/src/main/java/com/huawei/maildemo/data/DeviceData.java new file mode 100644 index 0000000000000000000000000000000000000000..be9730a2c1951d69e154eaefbf0660ad12cc466e --- /dev/null +++ b/DistributedMail/entry/src/main/java/com/huawei/maildemo/data/DeviceData.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. All rights reserved. + * + * 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.huawei.maildemo.data; + +import ohos.distributedschedule.interwork.DeviceInfo; + +/** + * Device Data + * + * @since 2021-02-04 + */ +public class DeviceData { + private boolean isChecked; + private DeviceInfo deviceInfo; + + /** + * DeviceData + * + * @param isChecked isChecked + * @param deviceInfo deviceInfo + */ + public DeviceData(boolean isChecked, DeviceInfo deviceInfo) { + this.isChecked = isChecked; + this.deviceInfo = deviceInfo; + } + + public DeviceInfo getDeviceInfo() { + return deviceInfo; + } + + public void setDeviceInfo(DeviceInfo deviceInfo) { + this.deviceInfo = deviceInfo; + } + + public boolean isChecked() { + return isChecked; + } + + public void setChecked(boolean checked) { + isChecked = checked; + } +} diff --git a/DistributedMail/entry/src/main/java/com/huawei/maildemo/slice/MailEditSlice.java b/DistributedMail/entry/src/main/java/com/huawei/maildemo/slice/MailEditSlice.java new file mode 100644 index 0000000000000000000000000000000000000000..9b1318a8732697f68d8c60b6f58e5368c6bb6840 --- /dev/null +++ b/DistributedMail/entry/src/main/java/com/huawei/maildemo/slice/MailEditSlice.java @@ -0,0 +1,386 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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.huawei.maildemo.slice; + +import static ohos.agp.components.ComponentContainer.LayoutConfig.MATCH_PARENT; + +import com.huawei.maildemo.ResourceTable; +import com.huawei.maildemo.bean.MailDataBean; +import com.huawei.maildemo.ui.DeviceSelectDialog; +import com.huawei.maildemo.ui.WidgetHelper; +import com.huawei.maildemo.ui.listcomponent.CommentViewHolder; +import com.huawei.maildemo.ui.listcomponent.ListComponentAdapter; +import com.huawei.maildemo.utils.DeviceUtils; +import com.huawei.maildemo.utils.LogUtil; + +import ohos.aafwk.ability.AbilitySlice; +import ohos.aafwk.ability.DataAbilityHelper; +import ohos.aafwk.ability.DataAbilityRemoteException; +import ohos.aafwk.ability.IAbilityContinuation; +import ohos.aafwk.content.Intent; +import ohos.aafwk.content.IntentParams; +import ohos.agp.components.Component; +import ohos.agp.components.ComponentContainer; +import ohos.agp.components.DirectionalLayout; +import ohos.agp.components.Image; +import ohos.agp.components.LayoutScatter; +import ohos.agp.components.ListContainer; +import ohos.agp.components.TextField; +import ohos.agp.utils.LayoutAlignment; +import ohos.agp.window.dialog.CommonDialog; +import ohos.app.Context; +import ohos.data.resultset.ResultSet; +import ohos.distributedschedule.interwork.DeviceInfo; +import ohos.distributedschedule.interwork.DeviceManager; +import ohos.media.image.ImageSource; +import ohos.media.photokit.metadata.AVStorage; +import ohos.utils.net.Uri; + +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; + +/** + * Mail edit slice + * + * @since 2021-02-04 + */ +public class MailEditSlice extends AbilitySlice implements IAbilityContinuation { + private static final String TAG = MailEditSlice.class.getSimpleName(); + private static final int CACHE_SIZE = 8 * 1024; + private static final int IO_END_LEN = -1; + private static final int TIPS_DURATION_TIME = 50; + + private ComponentContainer rootLayout; + private TextField receiver; + private TextField cc; + private TextField title; + private TextField content; + private MailDataBean cachedMailData; + private Image doConnectImg; + + private List mDialogDataList = new ArrayList<>(); + private ListComponentAdapter mRecycleItemProvider; + private CommonDialog fileDialog; + private ListContainer mAttachmentContainer; + private List mAttachmentDataList = new ArrayList<>(); + private ListComponentAdapter mAttachmentProvider; + + @Override + public void onStart(Intent intent) { + super.onStart(intent); + LogUtil.info(TAG, "is onStart begin"); + Component view = LayoutScatter.getInstance(this).parse(ResourceTable.Layout_moudle_mail_edit, null, false); + if (!(view instanceof ComponentContainer)) { + rootLayout = new DirectionalLayout(this); + } else { + adaptView(view); + setClickAction(view); + rootLayout = (ComponentContainer) view; + } + super.setUIContent(rootLayout); + + LogUtil.info(TAG, "is onStart end"); + } + + private void adaptView(Component rootView) { + Component view = rootView.findComponentById(ResourceTable.Id_mail_edit_receiver); + if (view instanceof TextField) { + receiver = (TextField) view; + } + view = rootView.findComponentById(ResourceTable.Id_mail_edit_cc); + if (view instanceof TextField) { + cc = (TextField) view; + } + view = rootView.findComponentById(ResourceTable.Id_mail_edit_title); + if (view instanceof TextField) { + title = (TextField) view; + } + doConnectImg = (Image) rootView.findComponentById(ResourceTable.Id_mail_edit_continue); + view = rootView.findComponentById(ResourceTable.Id_mail_edit_content); + if (view instanceof TextField) { + content = (TextField) view; + } + // 初始化界面 + fillView(); + setAttachmentProvider(rootView); + + rootView.findComponentById(ResourceTable.Id_call_test).setClickedListener(c -> { }); + } + + private void setAttachmentProvider(Component rootView) { + mAttachmentContainer = (ListContainer) rootView.findComponentById(ResourceTable.Id_attachment_list); + mAttachmentProvider = + new ListComponentAdapter( + getContext(), mAttachmentDataList, ResourceTable.Layout_attachment_item_horizontal) { + @Override + public void onBindViewHolder(CommentViewHolder commonViewHolder, String item, int position) { + commonViewHolder + .getTextView(ResourceTable.Id_item_title1) + .setText(item.substring(item.lastIndexOf(File.separator) + 1)); + FileInputStream fileInputStream = null; + try { + fileInputStream = new FileInputStream(item); + ImageSource source = ImageSource.create(fileInputStream, null); + commonViewHolder + .getImageView(ResourceTable.Id_image) + .setPixelMap(source.createPixelmap(0, null)); + } catch (FileNotFoundException e) { + LogUtil.error(TAG, "setAttachmentProvider Error"); + } + } + }; + mAttachmentContainer.setItemProvider(mAttachmentProvider); + } + + private void fillView() { + if (cachedMailData == null) { + receiver.setText("user1;user2"); + cc.setText("user3"); + title.setText("RE:HarmonyOS 2.0 Codelab体验"); + } else { + receiver.setText(cachedMailData.getReceiver()); + cc.setText(cachedMailData.getCc()); + title.setText(cachedMailData.getTitle()); + content.setText(cachedMailData.getContent()); + if (cachedMailData.getPictureDataList().size() > 0) { + // 清空现有数据,并刷新 + mAttachmentDataList.clear(); + mAttachmentDataList.addAll(cachedMailData.getPictureDataList()); + } + } + } + + private void setClickAction(Component rootView) { + doConnectImg.setClickedListener( + clickedView -> { + // 通过FLAG_GET_ONLINE_DEVICE标记获得在线设备列表 + List deviceInfoList = DeviceManager.getDeviceList(DeviceInfo.FLAG_GET_ONLINE_DEVICE); + if (deviceInfoList.size() < 1) { + WidgetHelper.showTips(this, "无在网设备"); + } else { + DeviceSelectDialog dialog = new DeviceSelectDialog(this); + // 点击后迁移到指定设备 + dialog.setListener( + deviceInfo -> { + LogUtil.debug(TAG, deviceInfo.getDeviceName()); + LogUtil.info(TAG, "continue button click"); + try { + // 开始任务迁移 + continueAbility(); + LogUtil.info(TAG, "continue button click end"); + } catch (IllegalStateException | UnsupportedOperationException e) { + WidgetHelper.showTips(this, ResourceTable.String_tips_mail_continue_failed); + } + dialog.hide(); + }); + dialog.show(); + } + }); + + rootView.findComponentById(ResourceTable.Id_open_dir) + .setClickedListener( + c -> { + // 防止多次点击一直弹窗 + if (fileDialog != null && fileDialog.isShowing()) { + return; + } + + // 先获取文件并上传到共享库distributedir + if (mDialogDataList.size() < 1) { + mDialogDataList.clear(); + // 获取设备的分布式文件 + List tempListRemotes = DeviceUtils.getFile(this); + mDialogDataList.addAll(tempListRemotes); + } + // 弹窗 + showDialog(getContext()); + }); + } + + /** + * showDialog + * + * @param context context + */ + public void showDialog(Context context) { + Component rootView = LayoutScatter.getInstance(context).parse(ResourceTable.Layout_dialog_picture, null, false); + ListContainer listContainer = (ListContainer) rootView.findComponentById(ResourceTable.Id_list_container_pic); + if (mRecycleItemProvider == null) { + mRecycleItemProvider = + new ListComponentAdapter( + getContext(), mDialogDataList, ResourceTable.Layout_dialog_picture_item) { + @Override + public void onBindViewHolder(CommentViewHolder commonViewHolder, String item, int position) { + commonViewHolder + .getTextView(ResourceTable.Id_item_desc) + .setText(item.substring(item.lastIndexOf(File.separator) + 1)); + FileInputStream fileInputStream = null; + try { + fileInputStream = new FileInputStream(item); + ImageSource source = ImageSource.create(fileInputStream, null); + commonViewHolder + .getImageView(ResourceTable.Id_image) + .setPixelMap(source.createPixelmap(0, null)); + } catch (FileNotFoundException e) { + LogUtil.error(TAG, "showDialog() FileNotFound Exception"); + } + } + }; + } else { + mRecycleItemProvider.notifyDataChanged(); + } + + clickOnAttachment(listContainer); + fileDialog = new CommonDialog(context); + fileDialog.setSize(MATCH_PARENT, MATCH_PARENT); + fileDialog.setAlignment(LayoutAlignment.BOTTOM); + fileDialog.setAutoClosable(true); + fileDialog.setContentCustomComponent(rootView); + fileDialog.show(); + } + + private void clickOnAttachment(ListContainer listContainer) { + listContainer.setItemProvider(mRecycleItemProvider); + listContainer.setItemClickedListener( + (listContainer1, component, i, l) -> { + if (mAttachmentDataList.contains(mDialogDataList.get(i))) { + WidgetHelper.showTips(this, "此附件已添加!", TIPS_DURATION_TIME); + } else { + mAttachmentDataList.add(mDialogDataList.get(i)); + mAttachmentProvider.notifyDataChanged(); + fileDialog.destroy(); + } + }); + } + + /** + * 获取公共目录照片 + * + * @param context context + */ + public void initPublicPictureFile(Context context) { + DataAbilityHelper helper = DataAbilityHelper.creator(context); + InputStream in = null; + OutputStream out = null; + String[] projections = + new String[] { + AVStorage.Images.Media.ID, AVStorage.Images.Media.DISPLAY_NAME, AVStorage.Images.Media.DATA + }; + try { + ResultSet results = helper.query(AVStorage.Images.Media.EXTERNAL_DATA_ABILITY_URI, projections, null); + while (results != null && results.goToNextRow()) { + int mediaId = results.getInt(results.getColumnIndexForName(AVStorage.Images.Media.ID)); + String fullFileName = results.getString(results.getColumnIndexForName(AVStorage.Images.Media.DATA)); + String fileName = fullFileName.substring(fullFileName.lastIndexOf(File.separator) + 1); + + Uri contentUri = + Uri.appendEncodedPathToUri(AVStorage.Images.Media.EXTERNAL_DATA_ABILITY_URI, "" + mediaId); + FileDescriptor fileDescriptor = helper.openFile(contentUri, "r"); + + if (getDistributedDir() == null) { + WidgetHelper.showTips(this, "注意:分布式文件异常!", TIPS_DURATION_TIME); + return; + } + String distributedFilePath = getContext().getDistributedDir().getPath() + File.separator + fileName; + File fr = new File(distributedFilePath); + in = new FileInputStream(fileDescriptor); + out = new FileOutputStream(fr); + byte[] buffer = new byte[CACHE_SIZE]; + int count = 0; + LogUtil.info(TAG, "START WRITING"); + while ((count = in.read(buffer)) != IO_END_LEN) { + out.write(buffer, 0, count); + } + out.close(); + LogUtil.info(TAG, "STOP WRITING"); + } + } catch (DataAbilityRemoteException | IOException e) { + LogUtil.error(TAG, "initPublicPictureFile exception"); + } finally { + try { + if (in != null) { + in.close(); + } + if (out != null) { + out.close(); + } + } catch (IOException e) { + LogUtil.error(TAG, "io exception"); + } + } + } + + @Override + public boolean onStartContinuation() { + LogUtil.info(TAG, "is start continue"); + return true; + } + + @Override + public boolean onSaveData(IntentParams params) { + MailDataBean mailData = getMailData(); + LogUtil.info(TAG, "begin onSaveData"); + mailData.saveDataToParams(params); + LogUtil.info(TAG, "end onSaveData"); + return true; + } + + @Override + public boolean onRestoreData(IntentParams params) { + LogUtil.info(TAG, "begin onRestoreData"); + cachedMailData = new MailDataBean(params); + LogUtil.info(TAG, "end onRestoreData, mail data"); + return true; + } + + @Override + public void onCompleteContinuation(int i) { + LogUtil.info(TAG, "onCompleteContinuation"); + terminateAbility(); + } + + private MailDataBean getMailData() { + MailDataBean data = new MailDataBean(); + data.setReceiver(receiver.getText()); + data.setCc(cc.getText()); + data.setTitle(title.getText()); + data.setContent(content.getText()); + data.setPictureDataList(mAttachmentDataList); + return data; + } + + @Override + protected void onActive() { + super.onActive(); + LogUtil.info(TAG, "is onActive begin"); + initPublicPictureFile(this); + LogUtil.info(TAG, "is onActive end"); + } + + @Override + protected void onStop() { + super.onStop(); + LogUtil.info(TAG, "is onStop"); + } +} diff --git a/DistributedMail/entry/src/main/java/com/huawei/maildemo/ui/DeviceSelectDialog.java b/DistributedMail/entry/src/main/java/com/huawei/maildemo/ui/DeviceSelectDialog.java new file mode 100644 index 0000000000000000000000000000000000000000..d06cee452734693046d9307d29bfaad108ba0bfc --- /dev/null +++ b/DistributedMail/entry/src/main/java/com/huawei/maildemo/ui/DeviceSelectDialog.java @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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.huawei.maildemo.ui; + +import static ohos.agp.components.ComponentContainer.LayoutConfig.MATCH_CONTENT; +import static ohos.agp.components.ComponentContainer.LayoutConfig.MATCH_PARENT; + +import com.huawei.maildemo.ResourceTable; +import com.huawei.maildemo.data.DeviceData; +import com.huawei.maildemo.ui.listcomponent.CommentViewHolder; +import com.huawei.maildemo.ui.listcomponent.ListComponentAdapter; + +import ohos.agp.components.Component; +import ohos.agp.components.LayoutScatter; +import ohos.agp.components.ListContainer; +import ohos.agp.components.Text; +import ohos.agp.utils.LayoutAlignment; +import ohos.agp.window.dialog.CommonDialog; +import ohos.app.Context; +import ohos.distributedschedule.interwork.DeviceInfo; +import ohos.distributedschedule.interwork.DeviceManager; + +import java.util.ArrayList; +import java.util.List; + +/** + * DeviceSelectDialog + * + * @since 2021-02-04 + */ +public class DeviceSelectDialog extends CommonDialog { + private static final int CORNER_RADIUS = 10; + ListContainer mListContainer; + List mDeviceList = new ArrayList<>(); + ListComponentAdapter listComponentAdapter; + private DeviceInfo mCheckedDevice; + + /** + * 设置确定按钮和取消被点击的接口 + * + * @since 2021-02-04 + */ + public interface OnclickListener { + /** + * onYesClick + * + * @param deviceInfo deviceInfo + */ + void onYesClick(DeviceInfo deviceInfo); + } + + private Context mContext; + private OnclickListener mOnclickListener; + + /** + * DeviceSelectDialog + * + * @param context context + */ + public DeviceSelectDialog(Context context) { + super(context); + this.mContext = context; + } + + /** + * setListener + * + * @param listener listener + */ + public void setListener(OnclickListener listener) { + mOnclickListener = listener; + } + + @Override + protected void onCreate() { + super.onCreate(); + Component rootView = + LayoutScatter.getInstance(mContext).parse(ResourceTable.Layout_dialog_layout_device, null, false); + mListContainer = (ListContainer) rootView.findComponentById(ResourceTable.Id_list_container_device); + List deviceInfoList = DeviceManager.getDeviceList(DeviceInfo.FLAG_GET_ONLINE_DEVICE); + mDeviceList.clear(); + for (DeviceInfo deviceInfo : deviceInfoList) { + mDeviceList.add(new DeviceData(false, deviceInfo)); + } + if (deviceInfoList.size() > 0) { + mCheckedDevice = deviceInfoList.get(0); + } + setItemProvider(); + Text operateYes = (Text) rootView.findComponentById(ResourceTable.Id_operate_yes); + operateYes.setClickedListener( + component -> { + if (mOnclickListener != null && mCheckedDevice != null) { + mOnclickListener.onYesClick(mCheckedDevice); + } + }); + Text operateNo = (Text) rootView.findComponentById(ResourceTable.Id_operate_no); + operateNo.setClickedListener( + component -> { + hide(); + }); + setSize(MATCH_PARENT, MATCH_CONTENT); + setAlignment(LayoutAlignment.BOTTOM); + setCornerRadius(CORNER_RADIUS); + setAutoClosable(true); + setContentCustomComponent(rootView); + setTransparent(true); + } + + private void setItemProvider() { + listComponentAdapter = + new ListComponentAdapter(mContext, mDeviceList, ResourceTable.Layout_dialog_device_item) { + @Override + public void onBindViewHolder(CommentViewHolder commonViewHolder, DeviceData item, int position) { + commonViewHolder.getTextView(ResourceTable.Id_item_desc) + .setText(item.getDeviceInfo().getDeviceName()); + switch (item.getDeviceInfo().getDeviceType()) { + case SMART_PHONE: + commonViewHolder .getImageView(ResourceTable.Id_item_type) + .setPixelMap(ResourceTable.Media_dv_phone); + break; + case SMART_PAD: + commonViewHolder.getImageView(ResourceTable.Id_item_type) + .setPixelMap(ResourceTable.Media_dv_pad); + break; + case SMART_WATCH: + commonViewHolder.getImageView(ResourceTable.Id_item_type) + .setPixelMap(ResourceTable.Media_dv_watch); + break; + default: + } + commonViewHolder + .getImageView(ResourceTable.Id_item_check) + .setPixelMap( + item.isChecked() + ? ResourceTable.Media_checked_point + : ResourceTable.Media_uncheck_point); + } + + @Override + public void onItemClick(Component component, DeviceData item, int position) { + super.onItemClick(component, item, position); + for (DeviceData mDevice : mDeviceList) { + mDevice.setChecked(false); + } + mDeviceList.get(position).setChecked(true); + listComponentAdapter.notifyDataChanged(); + mCheckedDevice = item.getDeviceInfo(); + } + }; + mListContainer.setItemProvider(listComponentAdapter); + } +} \ No newline at end of file diff --git a/DistributedMail/entry/src/main/java/com/huawei/maildemo/ui/WidgetHelper.java b/DistributedMail/entry/src/main/java/com/huawei/maildemo/ui/WidgetHelper.java new file mode 100644 index 0000000000000000000000000000000000000000..35465e135b5ffaef8ff8d4192a7873dd8e3c0af3 --- /dev/null +++ b/DistributedMail/entry/src/main/java/com/huawei/maildemo/ui/WidgetHelper.java @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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.huawei.maildemo.ui; + +import static ohos.agp.components.ComponentContainer.LayoutConfig.MATCH_CONTENT; +import static ohos.agp.components.ComponentContainer.LayoutConfig.MATCH_PARENT; + +import com.huawei.maildemo.utils.LogUtil; + +import ohos.agp.colors.RgbColor; +import ohos.agp.components.DirectionalLayout; +import ohos.agp.components.Text; +import ohos.agp.components.element.ShapeElement; +import ohos.agp.utils.Color; +import ohos.agp.utils.LayoutAlignment; +import ohos.agp.utils.TextAlignment; +import ohos.agp.window.dialog.ToastDialog; +import ohos.app.AbilityContext; +import ohos.app.Context; +import ohos.global.resource.NotExistException; +import ohos.global.resource.ResourceManager; +import ohos.global.resource.WrongTypeException; + +import java.io.IOException; + +/** + * Widget helper + * + * @since 2021-02-04 + */ +public final class WidgetHelper { + private static final String TAG = WidgetHelper.class.getSimpleName(); + + private static final String EMPTY = ""; + + private static final int TOAST_SHOW_TIME_MS = 1000; + + private static final int CORNER_RADIUS = 15; + + private static final int TEXT_SIZE_48 = 48; + + private static final int TEXT_PADDING_30 = 30; + + private static final int TEXT_PADDING_20 = 20; + + private static final int RGB_COLOR = 0x666666FF; + + private WidgetHelper() { } + + /** + * Show tips + * + * @param context ability slice + * @param msg show msg + */ + public static void showTips(AbilityContext context, String msg) { + showTips(context, msg, TOAST_SHOW_TIME_MS); + } + + /** + * Show tips + * + * @param context ability slice + * @param msgId msgId in values.xml + */ + public static void showTips(AbilityContext context, int msgId) { + String msg = getString(context, msgId); + showTips(context, msg, TOAST_SHOW_TIME_MS); + } + + /** + * Show tips + * + * @param context ability slice + * @param msg show msg + * @param durationTime tips duration time + */ + public static void showTips(AbilityContext context, String msg, int durationTime) { + Text text = new Text(context); + text.setWidth(MATCH_CONTENT); + text.setHeight(MATCH_CONTENT); + text.setTextSize(TEXT_SIZE_48); + text.setText(msg); + text.setPadding(TEXT_PADDING_30, TEXT_PADDING_20, TEXT_PADDING_30, TEXT_PADDING_20); + text.setMultipleLine(true); + text.setTextColor(Color.WHITE); + text.setTextAlignment(TextAlignment.CENTER); + + ShapeElement style = new ShapeElement(); + style.setShape(ShapeElement.RECTANGLE); + style.setRgbColor(new RgbColor(RGB_COLOR)); + style.setCornerRadius(CORNER_RADIUS); + text.setBackground(style); + DirectionalLayout mainLayout = new DirectionalLayout(context); + mainLayout.setWidth(MATCH_PARENT); + + mainLayout.setHeight(MATCH_CONTENT); + mainLayout.setAlignment(LayoutAlignment.CENTER); + mainLayout.addComponent(text); + + ToastDialog toastDialog = new ToastDialog(context); + toastDialog.setSize(MATCH_PARENT, MATCH_CONTENT); + toastDialog.setDuration(durationTime); + toastDialog.setAutoClosable(true); + toastDialog.setTransparent(true); + + toastDialog.setAlignment(LayoutAlignment.CENTER); + toastDialog.setComponent(mainLayout); + toastDialog.show(); + } + + /** + * Get string value + * + * @param context ability or ability slice + * @param id resource id + * @return string value + */ + public static String getString(Context context, int id) { + if (context == null) { + LogUtil.info(TAG, "Context is null, getString failed"); + return EMPTY; + } + ResourceManager resMgr = context.getResourceManager(); + if (resMgr == null) { + LogUtil.info(TAG, "ResourceManager is null, getString failed"); + return EMPTY; + } + + String value = EMPTY; + try { + value = resMgr.getElement(id).getString(); + } catch (NotExistException | WrongTypeException | IOException e) { + LogUtil.info(TAG, "get string value from resource manager failed"); + } + return value; + } + + /** + * Stylesheet + * + * @since 2020-02-05 + */ + static class MyStyle { + static final int BG_WHITE = 0xffffffff; + } +} diff --git a/DistributedMail/entry/src/main/java/com/huawei/maildemo/ui/listcomponent/CommentViewHolder.java b/DistributedMail/entry/src/main/java/com/huawei/maildemo/ui/listcomponent/CommentViewHolder.java new file mode 100644 index 0000000000000000000000000000000000000000..ac52539c336af47b9bc5fdbeacf71bf4766f4052 --- /dev/null +++ b/DistributedMail/entry/src/main/java/com/huawei/maildemo/ui/listcomponent/CommentViewHolder.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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.huawei.maildemo.ui.listcomponent; + +import ohos.agp.components.Component; +import ohos.agp.components.Image; +import ohos.agp.components.LayoutScatter; +import ohos.agp.components.Text; +import ohos.app.Context; + +import java.util.HashMap; +import java.util.Map; + +/** + * CommentViewHolder + * + * @since 2021-02-04 + */ +public class CommentViewHolder { + /** + * convertView + */ + private Component convertView; + private Map mViews = new HashMap<>(); + + /** + * 对成员变量赋值,并把当前CommentViewHolder用tag存起来以便复用 + * + * @param convertView convertView + */ + public CommentViewHolder(Component convertView) { + this.convertView = convertView; + convertView.setTag(this); + } + + /** + * getConvertView + * + * @return convertView + */ + public Component getConvertView() { + return convertView; + } + + /** + * 使用单例模式避免多个静态类 + * + * @param context context + * @param convertView convertView + * @param resource resource + * @return CommentViewHolder + */ + public static CommentViewHolder getCommentViewHolder(Context context, Component convertView, int resource) { + if (convertView == null) { + return new CommentViewHolder(LayoutScatter.getInstance(context).parse(resource, null, false)); + } else { + return (CommentViewHolder) convertView.getTag(); + } + } + + /** + * 根据泛型,指定view的类型 + * + * @param resId resId + * @param type type + * @return T + */ + public T getView(int resId, Class type) { + Component view = mViews.get(resId); + if (view == null) { + view = convertView.findComponentById(resId); + mViews.put(resId, view); + } + return (T) view; + } + + /** + * 2个常用的view,简单处理一下对外提供出来 + * + * @param resId resId + * @return Text + */ + public Text getTextView(int resId) { + return getView(resId, Text.class); + } + + /** + * getImageView + * + * @param resId resId + * @return Image + */ + public Image getImageView(int resId) { + return getView(resId, Image.class); + } +} diff --git a/DistributedMail/entry/src/main/java/com/huawei/maildemo/ui/listcomponent/ListComponentAdapter.java b/DistributedMail/entry/src/main/java/com/huawei/maildemo/ui/listcomponent/ListComponentAdapter.java new file mode 100644 index 0000000000000000000000000000000000000000..c08668ef1bad9aaf40930fdd4c6388522aa3ef85 --- /dev/null +++ b/DistributedMail/entry/src/main/java/com/huawei/maildemo/ui/listcomponent/ListComponentAdapter.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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.huawei.maildemo.ui.listcomponent; + +import ohos.agp.components.BaseItemProvider; +import ohos.agp.components.Component; +import ohos.agp.components.ComponentContainer; +import ohos.agp.components.LayoutScatter; +import ohos.app.Context; + +import java.util.List; + +/** + * ListComponentAdapter + * + * @since 2021-02-13 + */ +public abstract class ListComponentAdapter extends BaseItemProvider { + LayoutScatter layoutScatter; + private Context mContext; + private List mListBean; + private int mXmlId; + + /** + * ListComponentAdapter + * + * @param context context + * @param list list + * @param xmlId xmlId + */ + public ListComponentAdapter(Context context, List list, int xmlId) { + this.mContext = context; + this.mListBean = list; + this.mXmlId = xmlId; + layoutScatter = LayoutScatter.getInstance(mContext); + } + + /** + * onBindViewHolder + * + * @param commonViewHolder commonViewHolder + * @param item item + * @param position position + */ + public abstract void onBindViewHolder(CommentViewHolder commonViewHolder, T item, int position); + + @Override + public int getCount() { + return mListBean.size(); + } + + @Override + public T getItem(int i) { + return mListBean.get(i); + } + + @Override + public long getItemId(int i) { + return i; + } + + @Override + public Component getComponent(int i, Component component, ComponentContainer componentContainer) { + CommentViewHolder commentViewHolder = CommentViewHolder.getCommentViewHolder(mContext, component, mXmlId); + T t = mListBean.get(i); + onBindViewHolder(commentViewHolder, t, i); + commentViewHolder.getConvertView().setClickedListener(component1 -> onItemClick(component, t, i)); + return commentViewHolder.getConvertView(); + } + + /** + * onItemClick + * + * @param component component + * @param item item + * @param position position + */ + public void onItemClick(Component component, T item, int position) { } +} \ No newline at end of file diff --git a/DistributedMail/entry/src/main/java/com/huawei/maildemo/utils/DeviceUtils.java b/DistributedMail/entry/src/main/java/com/huawei/maildemo/utils/DeviceUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..8c9c62f8d2c4356c2128c2866c81d2dfad4664c6 --- /dev/null +++ b/DistributedMail/entry/src/main/java/com/huawei/maildemo/utils/DeviceUtils.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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.huawei.maildemo.utils; + +import com.huawei.maildemo.ui.WidgetHelper; + +import ohos.app.AbilityContext; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +/** + * Device method utils + * + * @since 2021-02-04 + */ +public class DeviceUtils { + private DeviceUtils() { + // do nothing + } + + /** + * Get device picture. + * + * @param context context + * @return local device picture + */ + public static List getFile(AbilityContext context) { + if (context.getDistributedDir() == null) { + WidgetHelper.showTips(context, "分布式异常!"); + return new ArrayList<>(); + } + File file = new File(context.getDistributedDir().getPath()); + File[] files = file.listFiles(); + + if (files == null) { + // 空目录 + return new ArrayList<>(); + } + List list = new ArrayList<>(); + + for (File eachFile : files) { + list.add(eachFile.getPath()); + } + return list; + } +} diff --git a/DistributedMail/entry/src/main/java/com/huawei/maildemo/utils/LogUtil.java b/DistributedMail/entry/src/main/java/com/huawei/maildemo/utils/LogUtil.java new file mode 100644 index 0000000000000000000000000000000000000000..9a621d5adcefbc85e5e671682c312b04c5bcb493 --- /dev/null +++ b/DistributedMail/entry/src/main/java/com/huawei/maildemo/utils/LogUtil.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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.huawei.maildemo.utils; + +import ohos.hiviewdfx.HiLog; +import ohos.hiviewdfx.HiLogLabel; + +import java.util.Locale; + +/** + * Log util function + * + * @since 2021-02-04 + */ +public class LogUtil { + private static final HiLogLabel LABEL_LOG = new HiLogLabel(3, 0xD000F00, "MailDemo"); + + private static final String LOG_FORMAT = "%{public}s: %{public}s"; + + private LogUtil() { + /* Do nothing */ + } + + /** + * Print debug log + * + * @param className class name + * @param msg log message + */ + public static void debug(String className, String msg) { + HiLog.debug(LABEL_LOG, LOG_FORMAT, className, msg); + } + + /** + * Print info log + * + * @param className class name + * @param msg log message + */ + public static void info(String className, String msg) { + HiLog.info(LABEL_LOG, LOG_FORMAT, className, msg); + } + + /** + * Print info log + * + * @param classType class name + * @param format format + * @param args args + */ + public static void info(Class classType, final String format, Object... args) { + String buffMsg = String.format(Locale.ROOT, format, args); + HiLog.info(LABEL_LOG, LOG_FORMAT, classType == null ? "null" : classType.getSimpleName(), buffMsg); + } + + /** + * Print info log + * + * @param tag log tag + * @param msg log message + */ + public static void error(String tag, String msg) { + HiLog.info(LABEL_LOG, LOG_FORMAT, tag, msg); + } +} diff --git a/DistributedMail/entry/src/main/resources/base/element/string.json b/DistributedMail/entry/src/main/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..c71564a716cee4f27784e6b143eb179b4baa0766 --- /dev/null +++ b/DistributedMail/entry/src/main/resources/base/element/string.json @@ -0,0 +1,24 @@ +{ + "string": [ + { + "name": "app_name", + "value": "MailDemo" + }, + { + "name": "mainability_description", + "value": "hap sample empty page" + }, + { + "name": "tips_mail_continue_nodevice", + "value": "迁移失败,没有发现可迁移的设备..." + }, + { + "name": "tips_mail_continue_failed", + "value": "迁移失败,请稍后重试..." + }, + { + "name": "studyability_description", + "value": "hap sample empty page" + } + ] +} \ No newline at end of file diff --git a/DistributedMail/entry/src/main/resources/base/graphic/background_white_radius_10.xml b/DistributedMail/entry/src/main/resources/base/graphic/background_white_radius_10.xml new file mode 100644 index 0000000000000000000000000000000000000000..72cbe6e736167587b85c067054633902560855b2 --- /dev/null +++ b/DistributedMail/entry/src/main/resources/base/graphic/background_white_radius_10.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/DistributedMail/entry/src/main/resources/base/layout/attachment_item_horizontal.xml b/DistributedMail/entry/src/main/resources/base/layout/attachment_item_horizontal.xml new file mode 100644 index 0000000000000000000000000000000000000000..48a21415d5cd0e544b5305999bdac3c72f528900 --- /dev/null +++ b/DistributedMail/entry/src/main/resources/base/layout/attachment_item_horizontal.xml @@ -0,0 +1,25 @@ + + + + + + + + \ No newline at end of file diff --git a/DistributedMail/entry/src/main/resources/base/layout/dialog_device_item.xml b/DistributedMail/entry/src/main/resources/base/layout/dialog_device_item.xml new file mode 100644 index 0000000000000000000000000000000000000000..3640392f29bff98d0db9bcc35a59e79696b855a2 --- /dev/null +++ b/DistributedMail/entry/src/main/resources/base/layout/dialog_device_item.xml @@ -0,0 +1,33 @@ + + + + + + + + + \ No newline at end of file diff --git a/DistributedMail/entry/src/main/resources/base/layout/dialog_layout_device.xml b/DistributedMail/entry/src/main/resources/base/layout/dialog_layout_device.xml new file mode 100644 index 0000000000000000000000000000000000000000..49a1f2f10947f227774d14c65c309b2473dfd37c --- /dev/null +++ b/DistributedMail/entry/src/main/resources/base/layout/dialog_layout_device.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DistributedMail/entry/src/main/resources/base/layout/dialog_picture.xml b/DistributedMail/entry/src/main/resources/base/layout/dialog_picture.xml new file mode 100644 index 0000000000000000000000000000000000000000..6960b331ccc941e0d3b6ab21b15983b739693ea9 --- /dev/null +++ b/DistributedMail/entry/src/main/resources/base/layout/dialog_picture.xml @@ -0,0 +1,26 @@ + + + + + + + + + \ No newline at end of file diff --git a/DistributedMail/entry/src/main/resources/base/layout/dialog_picture_item.xml b/DistributedMail/entry/src/main/resources/base/layout/dialog_picture_item.xml new file mode 100644 index 0000000000000000000000000000000000000000..db8ce3c1cc5c3592e553003e4e49b038791ad9d3 --- /dev/null +++ b/DistributedMail/entry/src/main/resources/base/layout/dialog_picture_item.xml @@ -0,0 +1,25 @@ + + + + + + + + \ No newline at end of file diff --git a/DistributedMail/entry/src/main/resources/base/layout/moudle_mail_edit.xml b/DistributedMail/entry/src/main/resources/base/layout/moudle_mail_edit.xml new file mode 100644 index 0000000000000000000000000000000000000000..27f1aad3fc2bdf243f85f20fb4724e04041e1a4b --- /dev/null +++ b/DistributedMail/entry/src/main/resources/base/layout/moudle_mail_edit.xml @@ -0,0 +1,204 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ShoppingJs/entry/src/main/js/default/pages/viewsChart/viewsChart.js b/ShoppingJs/entry/src/main/js/default/pages/viewsChart/viewsChart.js new file mode 100644 index 0000000000000000000000000000000000000000..57a1565c97a52d562fdbdcec5bad8a480d17c034 --- /dev/null +++ b/ShoppingJs/entry/src/main/js/default/pages/viewsChart/viewsChart.js @@ -0,0 +1,68 @@ +export default { + data: { + temp: 0, + lineData: [ + { + strokeColor: '#0081ff', + fillColor: '#cce5ff', + data: [763, 550, 551, 554, 731, 654, 525, 696, 595, 628, 791, 505, 613, 575, 475, 553, 491, 680, 657, 716], + gradient: true, + } + ], + lineOps: { + xAxis: { + lineStylemin: 0, + max: 20, + display: false, + }, + yAxis: { + min: 0, + max: 1000, + display: false + }, + series: { + lineStyle: { + width: "5px", + smooth: true, + }, + headPoint: { + shape: "circle", + size: 10, + strokeWidth: 1, + fillColor: '#ffffff', + strokeColor: "#2C4CFF", + display: true + }, + loop: { + margin: 2, + gradient: true, + }, + }, + }, + }, + onInit() { + this.autoAddData(); + }, + addData() { + this.$refs.linechart.append({ + serial: 0, + data: [Math.floor(Math.random() * 400) + 400] + }) + }, + autoAddData() { + setInterval(() => { + let temp = [Math.floor(Math.random() * 400) + 400]; + if (this.temp % 20 === 0) { + this.lineData[0].data = []; + this.lineData = this.lineData.slice(); + } else { + this.lineData[0].data.push(temp); + this.$refs.linechart.append({ + serial: 0, + data: temp + }); + } + this.temp++; + }, 800); + } +} \ No newline at end of file diff --git a/ShoppingJs/entry/src/main/resources/base/element/string.json b/ShoppingJs/entry/src/main/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..b9890866dd98f1a62e1aa31b087829a8573cc9d3 --- /dev/null +++ b/ShoppingJs/entry/src/main/resources/base/element/string.json @@ -0,0 +1,12 @@ +{ + "string": [ + { + "name": "app_name", + "value": "codelab" + }, + { + "name": "mainability_description", + "value": "JS_Phone_Empty Feature Ability" + } + ] +} \ No newline at end of file diff --git a/ShoppingJs/entry/src/main/resources/base/media/icon.png b/ShoppingJs/entry/src/main/resources/base/media/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..2e24ee37e7ea2d1b35599b838f2a01ee97c1dfb0 Binary files /dev/null and b/ShoppingJs/entry/src/main/resources/base/media/icon.png differ diff --git a/ShoppingJs/entry/src/ohosTest/config.json b/ShoppingJs/entry/src/ohosTest/config.json new file mode 100644 index 0000000000000000000000000000000000000000..f33f058d051a9fd7605b0cc92668f52e2646ef42 --- /dev/null +++ b/ShoppingJs/entry/src/ohosTest/config.json @@ -0,0 +1,52 @@ +{ + "app": { + "bundleName": "com.huawei.codelab", + "vendor": "huawei", + "version": { + "code": 1, + "name": "1.0" + }, + "apiVersion": { + "compatible": 3, + "target": 4, + "releaseType": "Beta1" + } + }, + "deviceConfig": {}, + "module": { + "package": "com.huawei.codelab", + "name": "testModule", + "deviceType": [ + "phone" + ], + "distro": { + "deliveryWithInstall": true, + "moduleName": "entry_test", + "moduleType": "feature" + }, + "abilities": [ + { + "name": "decc.testkit.runner.JsEntryAbility", + "description": "Test Entry Ability", + "icon": "$media:icon", + "label": "TestAbility", + "launchType": "standard", + "orientation": "landscape", + "visible": true, + "type": "page" + } + ], + "js": [ + { + "pages": [ + "pages/index/index" + ], + "name": "default", + "window": { + "designWidth": 750, + "autoDesignWidth": false + } + } + ] + } +} \ No newline at end of file diff --git a/ShoppingJs/entry/src/ohosTest/js/default/app.js b/ShoppingJs/entry/src/ohosTest/js/default/app.js new file mode 100644 index 0000000000000000000000000000000000000000..d8dfc5237086ae1af0e29ab1a8b1fd40f13a9b7a --- /dev/null +++ b/ShoppingJs/entry/src/ohosTest/js/default/app.js @@ -0,0 +1,6 @@ +export default { + onCreate() { + }, + onDestroy() { + } +}; diff --git a/ShoppingJs/entry/src/ohosTest/js/default/common/logo.png b/ShoppingJs/entry/src/ohosTest/js/default/common/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..8ff99801a568aba384af790d7af48f83d9cf1603 Binary files /dev/null and b/ShoppingJs/entry/src/ohosTest/js/default/common/logo.png differ diff --git a/ShoppingJs/entry/src/ohosTest/js/default/i18n/en-US.json b/ShoppingJs/entry/src/ohosTest/js/default/i18n/en-US.json new file mode 100644 index 0000000000000000000000000000000000000000..55561b83737c3c31d082fbfa11e5fc987a351104 --- /dev/null +++ b/ShoppingJs/entry/src/ohosTest/js/default/i18n/en-US.json @@ -0,0 +1,8 @@ +{ + "strings": { + "hello": "Hello", + "world": "World" + }, + "Files": { + } +} \ No newline at end of file diff --git a/ShoppingJs/entry/src/ohosTest/js/default/i18n/zh-CN.json b/ShoppingJs/entry/src/ohosTest/js/default/i18n/zh-CN.json new file mode 100644 index 0000000000000000000000000000000000000000..cce1af06761a42add0cac1a0567aa3237eda8cb4 --- /dev/null +++ b/ShoppingJs/entry/src/ohosTest/js/default/i18n/zh-CN.json @@ -0,0 +1,8 @@ +{ + "strings": { + "hello": "您好", + "world": "世界" + }, + "Files": { + } +} \ No newline at end of file diff --git a/ShoppingJs/entry/src/ohosTest/js/default/pages/index/index.css b/ShoppingJs/entry/src/ohosTest/js/default/pages/index/index.css new file mode 100644 index 0000000000000000000000000000000000000000..6fda792753f2e15f22b529c7b90a82185b2770bf --- /dev/null +++ b/ShoppingJs/entry/src/ohosTest/js/default/pages/index/index.css @@ -0,0 +1,9 @@ +.container { + flex-direction: column; + justify-content: center; + align-items: center; +} + +.title { + font-size: 100px; +} diff --git a/ShoppingJs/entry/src/ohosTest/js/default/pages/index/index.hml b/ShoppingJs/entry/src/ohosTest/js/default/pages/index/index.hml new file mode 100644 index 0000000000000000000000000000000000000000..1c26cf73deab18390844a53a103458b10ed11e93 --- /dev/null +++ b/ShoppingJs/entry/src/ohosTest/js/default/pages/index/index.hml @@ -0,0 +1,5 @@ +
+ + {{ $t('strings.hello') }} {{title}} + +
diff --git a/ShoppingJs/entry/src/ohosTest/js/default/pages/index/index.js b/ShoppingJs/entry/src/ohosTest/js/default/pages/index/index.js new file mode 100644 index 0000000000000000000000000000000000000000..8e45a632a42592c80d81362b27d42976e5e00b51 --- /dev/null +++ b/ShoppingJs/entry/src/ohosTest/js/default/pages/index/index.js @@ -0,0 +1,40 @@ +import file from '@system.file' +import app from '@system.app' +import device from '@system.device' +import router from '@system.router' + +import {Core, ExpectExtend, ReportExtend, InstrumentLog} from 'deccjsunit/index' + +export default { + data: { + title: "" + }, + onInit() { + this.title = this.$t('strings.world'); + }, + onShow() { + const core = Core.getInstance() + const expectExtend = new ExpectExtend({ + 'id': 'extend' + }) + const reportExtend = new ReportExtend(file) + const instrumentLog = new InstrumentLog({ + 'id': 'report' + }) + core.addService('expect', expectExtend) + core.addService('report', reportExtend) + core.addService('report', instrumentLog) + core.init() + core.subscribeEvent('spec', instrumentLog) + core.subscribeEvent('suite', instrumentLog) + core.subscribeEvent('task', instrumentLog) + + const configService = core.getDefaultService('config') + configService.setConfig(this) + + require('../../../test/List.test') + core.execute() + }, + onReady() { + }, +} \ No newline at end of file diff --git a/ShoppingJs/entry/src/ohosTest/js/test/ExampleJsunit.test.js b/ShoppingJs/entry/src/ohosTest/js/test/ExampleJsunit.test.js new file mode 100644 index 0000000000000000000000000000000000000000..79c59740af9424a45643da52bcf4c963d831852c --- /dev/null +++ b/ShoppingJs/entry/src/ohosTest/js/test/ExampleJsunit.test.js @@ -0,0 +1,11 @@ +import app from '@system.app' + +import {describe, beforeAll, beforeEach, afterEach, afterAll, it, expect} from 'deccjsunit/index' + +describe('appInfoTest', function () { + it('app_info_test_001', 0, function () { + var info = app.getInfo() + expect(info.versionName).assertEqual('1.0') + expect(info.versionCode).assertEqual('3') + }) +}) \ No newline at end of file diff --git a/ShoppingJs/entry/src/ohosTest/js/test/List.test.js b/ShoppingJs/entry/src/ohosTest/js/test/List.test.js new file mode 100644 index 0000000000000000000000000000000000000000..305eb5bb254cd4802afaffebe68fca2f9436159f --- /dev/null +++ b/ShoppingJs/entry/src/ohosTest/js/test/List.test.js @@ -0,0 +1 @@ +require('./ExampleJsunit.test.js') \ No newline at end of file diff --git a/ShoppingJs/entry/src/ohosTest/resources/base/element/string.json b/ShoppingJs/entry/src/ohosTest/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..40be19fe0d5538f04c7fc10f65c183585828b0cd --- /dev/null +++ b/ShoppingJs/entry/src/ohosTest/resources/base/element/string.json @@ -0,0 +1,12 @@ +{ + "string": [ + { + "name": "app_name", + "value": "codelab" + }, + { + "name": "mainability_description", + "value": "hap sample empty page" + } + ] +} diff --git a/ShoppingJs/entry/src/ohosTest/resources/base/media/icon.png b/ShoppingJs/entry/src/ohosTest/resources/base/media/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ce307a8827bd75456441ceb57d530e4c8d45d36c Binary files /dev/null and b/ShoppingJs/entry/src/ohosTest/resources/base/media/icon.png differ diff --git a/ShoppingJs/gradle/wrapper/gradle-wrapper.jar b/ShoppingJs/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..490fda8577df6c95960ba7077c43220e5bb2c0d9 Binary files /dev/null and b/ShoppingJs/gradle/wrapper/gradle-wrapper.jar differ diff --git a/ShoppingJs/gradle/wrapper/gradle-wrapper.properties b/ShoppingJs/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000000000000000000000000000000000000..6623300bebd011bc5f7991f4f9c389e2f67b14ac --- /dev/null +++ b/ShoppingJs/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-all.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/ShoppingJs/settings.gradle b/ShoppingJs/settings.gradle new file mode 100644 index 0000000000000000000000000000000000000000..4773db73233a570c2d0c01a22e75321acfbf7a07 --- /dev/null +++ b/ShoppingJs/settings.gradle @@ -0,0 +1 @@ +include ':entry' diff --git a/VoiceCamera/LICENSE b/VoiceCamera/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..eb44b24a0ff113ed4b008408a3406f792bde2adb --- /dev/null +++ b/VoiceCamera/LICENSE @@ -0,0 +1,78 @@ + Copyright (c) 2021 Huawei Device Co., Ltd. All rights reserved. + + 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. + +Apache License, Version 2.0 +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + +2. Grant of Copyright License. + +Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. + +Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. + +You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: +1.You must give any other recipients of the Work or Derivative Works a copy of this License; and +2.You must cause any modified files to carry prominent notices stating that You changed the files; and +3.You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and +4.If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + +You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + +5. Submission of Contributions. + +Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + +6. Trademarks. + +This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. + +Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. + +In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. + +While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS \ No newline at end of file diff --git a/VoiceCamera/README.md b/VoiceCamera/README.md new file mode 100644 index 0000000000000000000000000000000000000000..ebcc642995fb7097d1d0267f543baf425d340f3a --- /dev/null +++ b/VoiceCamera/README.md @@ -0,0 +1,10 @@ +# Distributed Voice Camera + +What is it? +In this article, Codelab uses the HarmonyOS distributed file system and AI voice recognition function to develop a distributed voice camera. +Using this camera application, different devices under the same distributed network can see the photos taken by the main device in real time. +This effectively solves the pain point of the need to run back and forth to pass the mobile phone when taking pictures of each other. +In addition, the main device also supports a voice-controlled camera function, allowing you to control the camera from a distance. + +Licensing +Please see LICENSE for more info. \ No newline at end of file diff --git a/VoiceCamera/RELEASE-NOTES.md b/VoiceCamera/RELEASE-NOTES.md new file mode 100644 index 0000000000000000000000000000000000000000..ea23a13e3d4407d06cba3aa7155a23e5081b50b4 --- /dev/null +++ b/VoiceCamera/RELEASE-NOTES.md @@ -0,0 +1,2 @@ +1.0.0 +1.Initial version \ No newline at end of file diff --git a/VoiceCamera/build.gradle b/VoiceCamera/build.gradle new file mode 100644 index 0000000000000000000000000000000000000000..14d3cf79a4c787374f9646244fe5ee1f5bca73d6 --- /dev/null +++ b/VoiceCamera/build.gradle @@ -0,0 +1,36 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +apply plugin: 'com.huawei.ohos.app' + +ohos { + compileSdkVersion 4 + defaultConfig { + compatibleSdkVersion 3 + } +} + +buildscript { + repositories { + maven { + url 'https://mirrors.huaweicloud.com/repository/maven/' + } + maven { + url 'https://developer.huawei.com/repo/' + } + jcenter() + } + dependencies { + classpath 'com.huawei.ohos:hap:2.4.0.1' + } +} + +allprojects { + repositories { + maven { + url 'https://mirrors.huaweicloud.com/repository/maven/' + } + maven { + url 'https://developer.huawei.com/repo/' + } + jcenter() + } +} diff --git a/VoiceCamera/entry/.gitignore b/VoiceCamera/entry/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..796b96d1c402326528b4ba3c12ee9d92d0e212e9 --- /dev/null +++ b/VoiceCamera/entry/.gitignore @@ -0,0 +1 @@ +/build diff --git a/VoiceCamera/entry/build.gradle b/VoiceCamera/entry/build.gradle new file mode 100644 index 0000000000000000000000000000000000000000..b85af1f41fc0154be26557fd984be37a79e1d2a6 --- /dev/null +++ b/VoiceCamera/entry/build.gradle @@ -0,0 +1,13 @@ +apply plugin: 'com.huawei.ohos.hap' +ohos { + compileSdkVersion 4 + defaultConfig { + compatibleSdkVersion 3 + } +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar', '*.har']) + testCompile'junit:junit:4.12' + implementation('com.alibaba:fastjson:1.2.4') +} \ No newline at end of file diff --git a/VoiceCamera/entry/src/main/config.json b/VoiceCamera/entry/src/main/config.json new file mode 100644 index 0000000000000000000000000000000000000000..418f76726a1a581faf0d6cfc429a908a9b0e33ba --- /dev/null +++ b/VoiceCamera/entry/src/main/config.json @@ -0,0 +1,95 @@ +{ + "app": { + "bundleName": "com.huawei.codedemo", + "vendor": "huawei", + "version": { + "code": 1, + "name": "1.0" + }, + "apiVersion": { + "compatible": 3, + "target": 4 + } + }, + "deviceConfig": {}, + "module": { + "package": "com.huawei.camerademo", + "name": ".MyApplication", + "deviceType": [ + "phone" + ], + "metaData": { + "customizeData": [ + { + "name": "hwc-theme", + "value": "androidhwext:style/Theme.Emui.NoTitleBar", + "extra": "" + } + ] + }, + "distro": { + "deliveryWithInstall": true, + "moduleName": "entry", + "moduleType": "entry" + }, + "abilities": [ + { + "skills": [ + { + "entities": [ + "entity.system.home" + ], + "actions": [ + "action.system.home" + ] + } + ], + "orientation": "portrait", + "name": "com.huawei.camerademo.MainAbility", + "icon": "$media:icon", + "description": "$string:mainability_description", + "label": "CameraDemo", + "type": "page", + "launchType": "standard" + }, + { + "orientation": "portrait", + "name": "com.huawei.camerademo.ImageAbility", + "icon": "$media:icon", + "description": "$string:imageability_description", + "label": "Picture", + "type": "page", + "launchType": "standard" + } + ], + "reqPermissions": [ + { + "name": "ohos.permission.CAMERA" + }, + { + "name": "ohos.permission.WRITE_USER_STORAGE" + }, + { + "name": "ohos.permission.MICROPHONE" + }, + { + "name": "ohos.permission.READ_USER_STORAGE" + }, + { + "name": "ohos.permission.DISTRIBUTED_DATASYNC" + }, + { + "name": "ohos.permission.DISTRIBUTED_DEVICE_STATE_CHANGE" + }, + { + "name": "ohos.permission.GET_DISTRIBUTED_DEVICE_INFO" + }, + { + "name": "ohos.permission.GET_BUNDLE_INFO" + }, + { + "name": "ohos.permission.ACCESS_SERVICE" + } + ] + } +} \ No newline at end of file diff --git a/VoiceCamera/entry/src/main/java/com/huawei/camerademo/ImageAbility.java b/VoiceCamera/entry/src/main/java/com/huawei/camerademo/ImageAbility.java new file mode 100644 index 0000000000000000000000000000000000000000..6eed6235ecefa074b62a85b15426df7113de4cdd --- /dev/null +++ b/VoiceCamera/entry/src/main/java/com/huawei/camerademo/ImageAbility.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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.huawei.camerademo; + +import com.huawei.camerademo.slice.ImageAbilitySlice; + +import ohos.aafwk.ability.Ability; +import ohos.aafwk.content.Intent; + +/** + * ImageAbility + * + * @since 2021-3-8 + */ +public class ImageAbility extends Ability { + @Override + public void onStart(Intent intent) { + super.onStart(intent); + super.setMainRoute(ImageAbilitySlice.class.getName()); + } +} diff --git a/VoiceCamera/entry/src/main/java/com/huawei/camerademo/MainAbility.java b/VoiceCamera/entry/src/main/java/com/huawei/camerademo/MainAbility.java new file mode 100644 index 0000000000000000000000000000000000000000..4df2fd64aa2a9be91f1d16945855de8f669d0cb6 --- /dev/null +++ b/VoiceCamera/entry/src/main/java/com/huawei/camerademo/MainAbility.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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.huawei.camerademo; + +import static com.huawei.camerademo.utils.PermissionBridge.EVENT_PERMISSION_DENIED; +import static com.huawei.camerademo.utils.PermissionBridge.EVENT_PERMISSION_GRANTED; + +import static ohos.bundle.IBundleManager.PERMISSION_GRANTED; + +import com.huawei.camerademo.slice.MainAbilitySlice; +import com.huawei.camerademo.utils.PermissionBridge; + +import ohos.aafwk.ability.Ability; +import ohos.aafwk.content.Intent; +import ohos.agp.utils.Color; +import ohos.security.SystemPermission; + +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + +/** + * MainAbility + * + * @since 2021-3-8 + */ +public class MainAbility extends Ability { + private static final int PERMISSION_REQUEST_CODE = 1; + + @Override + public void onStart(Intent intent) { + super.onStart(intent); + super.setMainRoute(MainAbilitySlice.class.getName()); + requestCameraPermission(); + getWindow().setStatusBarColor(Color.BLACK.getValue()); + } + + private void requestCameraPermission() { + List permissions = + new LinkedList( + Arrays.asList( + SystemPermission.WRITE_USER_STORAGE, + SystemPermission.READ_USER_STORAGE, + SystemPermission.CAMERA, + SystemPermission.DISTRIBUTED_DATASYNC, + SystemPermission.MICROPHONE)); + permissions.removeIf( + permission -> + verifySelfPermission(permission) == PERMISSION_GRANTED || !canRequestPermission(permission)); + + if (!permissions.isEmpty()) { + requestPermissionsFromUser(permissions.toArray(new String[permissions.size()]), PERMISSION_REQUEST_CODE); + } else { + PermissionBridge.getHandler().sendEvent(EVENT_PERMISSION_GRANTED); + } + } + + @Override + public void onRequestPermissionsFromUserResult(int requestCode, String[] permissions, int[] grantResults) { + if (requestCode != PERMISSION_REQUEST_CODE) { + return; + } + for (int grantResult : grantResults) { + if (grantResult != PERMISSION_GRANTED) { + PermissionBridge.getHandler().sendEvent(EVENT_PERMISSION_DENIED); + terminateAbility(); + return; + } + } + PermissionBridge.getHandler().sendEvent(EVENT_PERMISSION_GRANTED); + } +} diff --git a/VoiceCamera/entry/src/main/java/com/huawei/camerademo/MyApplication.java b/VoiceCamera/entry/src/main/java/com/huawei/camerademo/MyApplication.java new file mode 100644 index 0000000000000000000000000000000000000000..0530063385a2e2d92677a68ae2f062f35e621ca6 --- /dev/null +++ b/VoiceCamera/entry/src/main/java/com/huawei/camerademo/MyApplication.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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.huawei.camerademo; + +import ohos.aafwk.ability.AbilityPackage; + +/** + * MyApplication + * + * @since 2021-3-8 + */ +public class MyApplication extends AbilityPackage { + @Override + public void onInitialize() { + super.onInitialize(); + } +} diff --git a/VoiceCamera/entry/src/main/java/com/huawei/camerademo/listener/MyAsrListener.java b/VoiceCamera/entry/src/main/java/com/huawei/camerademo/listener/MyAsrListener.java new file mode 100644 index 0000000000000000000000000000000000000000..66f16dab1ba5c39b60c96fa6e4c289ce7a0070bb --- /dev/null +++ b/VoiceCamera/entry/src/main/java/com/huawei/camerademo/listener/MyAsrListener.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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.huawei.camerademo.listener; + +import ohos.ai.asr.AsrListener; +import ohos.utils.PacMap; + +/** + * MyAsrListener + * + * @since 2021-3-8 + */ +public class MyAsrListener implements AsrListener { + @Override + public void onInit(PacMap pacMap) {} + + @Override + public void onBeginningOfSpeech() {} + + @Override + public void onRmsChanged(float var1) {} + + @Override + public void onBufferReceived(byte[] bytes) {} + + @Override + public void onEndOfSpeech() {} + + @Override + public void onError(int var1) {} + + @Override + public void onResults(PacMap pacMap) {} + + @Override + public void onIntermediateResults(PacMap pacMap) {} + + @Override + public void onEnd() {} + + @Override + public void onEvent(int var1, PacMap pacMap) {} + + @Override + public void onAudioStart() {} + + @Override + public void onAudioEnd() {} +} diff --git a/VoiceCamera/entry/src/main/java/com/huawei/camerademo/slice/ImageAbilitySlice.java b/VoiceCamera/entry/src/main/java/com/huawei/camerademo/slice/ImageAbilitySlice.java new file mode 100644 index 0000000000000000000000000000000000000000..bebf2baa32c0f5c5e9295fa9d3a9f235daa04910 --- /dev/null +++ b/VoiceCamera/entry/src/main/java/com/huawei/camerademo/slice/ImageAbilitySlice.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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.huawei.camerademo.slice; + +import static com.huawei.camerademo.utils.CameraUtil.CAMERA_IMAGE_FILE_PATH; + +import com.huawei.camerademo.ResourceTable; +import com.huawei.camerademo.utils.CameraUtil; +import com.huawei.camerademo.utils.DistributeFileUtil; + +import ohos.aafwk.ability.AbilitySlice; +import ohos.aafwk.content.Intent; +import ohos.agp.components.Image; +import ohos.hiviewdfx.HiLog; +import ohos.hiviewdfx.HiLogLabel; +import ohos.media.image.PixelMap; + +/** + * ImageAbilitySlice + * + * @since 2021-3-8 + */ +public class ImageAbilitySlice extends AbilitySlice { + static final HiLogLabel TAG = new HiLogLabel(HiLog.LOG_APP, 0x00201, ImageAbilitySlice.class.getName()); + private Image image; + + @Override + public void onStart(Intent intent) { + super.onStart(intent); + super.setUIContent(ResourceTable.Layout_ability_image); + initView(intent); + } + + private void initView(Intent intent) { + HiLog.info(TAG, "initView"); + if (findComponentById(ResourceTable.Id_image) instanceof Image) { + image = (Image) findComponentById(ResourceTable.Id_image); + } + if (intent.getStringParam(CAMERA_IMAGE_FILE_PATH) != null + && !intent.getStringParam(CAMERA_IMAGE_FILE_PATH).isEmpty()) { + setImage(intent.getStringParam(CAMERA_IMAGE_FILE_PATH)); + } else { + setDisImage(intent); + } + } + + /** + * read image form distribute + * + * @param intent intent + */ + private void setDisImage(Intent intent) { + String filePath = intent.getStringParam("filePath"); + if (filePath != null && !filePath.isEmpty()) { + image.setPixelMap(DistributeFileUtil.readToDistributedDir(this, filePath)); + } + } + + private void setImage(String picPath) { + if (picPath != null && !picPath.isEmpty()) { + PixelMap pixelMap = CameraUtil.getPixelMap(null, picPath, 1); + image.setPixelMap(pixelMap); + } + } +} diff --git a/VoiceCamera/entry/src/main/java/com/huawei/camerademo/slice/MainAbilitySlice.java b/VoiceCamera/entry/src/main/java/com/huawei/camerademo/slice/MainAbilitySlice.java new file mode 100644 index 0000000000000000000000000000000000000000..fd8edd82231b80b53a17b9b8b7ebd9e3729d662e --- /dev/null +++ b/VoiceCamera/entry/src/main/java/com/huawei/camerademo/slice/MainAbilitySlice.java @@ -0,0 +1,522 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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.huawei.camerademo.slice; + +import static com.huawei.camerademo.utils.CameraUtil.CAMERA_IMAGE_FILE_PATH; + +import static ohos.media.camera.device.Camera.FrameConfigType.FRAME_CONFIG_PICTURE; +import static ohos.media.camera.device.Camera.FrameConfigType.FRAME_CONFIG_PREVIEW; + +import com.huawei.camerademo.ImageAbility; +import com.huawei.camerademo.ResourceTable; +import com.huawei.camerademo.listener.MyAsrListener; +import com.huawei.camerademo.utils.CameraUtil; +import com.huawei.camerademo.utils.DistributeFileUtil; +import com.huawei.camerademo.utils.MyDrawTask; +import com.huawei.camerademo.utils.PermissionBridge; + +import com.alibaba.fastjson.JSONObject; + +import ohos.aafwk.ability.AbilitySlice; +import ohos.aafwk.content.Intent; +import ohos.aafwk.content.Operation; +import ohos.agp.components.Component; +import ohos.agp.components.ComponentContainer; +import ohos.agp.components.DirectionalLayout; +import ohos.agp.components.Image; +import ohos.agp.components.surfaceprovider.SurfaceProvider; +import ohos.agp.graphics.Surface; +import ohos.agp.graphics.SurfaceOps; +import ohos.ai.asr.AsrClient; +import ohos.ai.asr.AsrIntent; +import ohos.ai.asr.AsrListener; +import ohos.ai.asr.util.AsrResultKey; +import ohos.app.Environment; +import ohos.app.dispatcher.TaskDispatcher; +import ohos.eventhandler.EventHandler; +import ohos.eventhandler.EventRunner; +import ohos.eventhandler.InnerEvent; +import ohos.hiviewdfx.HiLog; +import ohos.hiviewdfx.HiLogLabel; +import ohos.media.audio.AudioCapturer; +import ohos.media.audio.AudioCapturerInfo; +import ohos.media.audio.AudioStreamInfo; +import ohos.media.camera.CameraKit; +import ohos.media.camera.device.Camera; +import ohos.media.camera.device.CameraConfig; +import ohos.media.camera.device.CameraStateCallback; +import ohos.media.camera.device.FrameConfig; +import ohos.media.image.ImageReceiver; +import ohos.media.image.common.ImageFormat; +import ohos.utils.PacMap; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +/** + * MainAbilitySlice + * + * @since 2021-3-8 + */ +public class MainAbilitySlice extends AbilitySlice implements PermissionBridge.OnPermissionStateListener { + private static final HiLogLabel TAG = new HiLogLabel(3, 0xD001100, "MainAbilitySlice"); + + private static final int EVENT_IMAGESAVING_PROMTING = 0x0000024; + + private static final int SCREEN_WIDTH = 1080; + + private static final int SCREEN_HEIGHT = 2340; + + private static final int IMAGE_RCV_CAPACITY = 9; + + private static final String IMG_FILE_PREFIX = "IMG_"; + + private static final String IMG_FILE_TYPE = ".png"; + + private static final Map COMMAND_MAP = new HashMap<>(); + + private static AsrClient asrClient; + + static { + COMMAND_MAP.put("拍照", true); + COMMAND_MAP.put("茄子", true); + } + + private Surface previewSurface; + + private SurfaceProvider surfaceProvider; + + private boolean isCameraRear; + + private Camera cameraDevice; + + private EventHandler creamEventHandler; + + private ImageReceiver imageReceiver; + + private Image takePictureImage; + + private Image switchCameraImage; + + private File targetFile; + + private Image smallImage; + + private byte[] bytes; + + private boolean isRecord = false; + + private ThreadPoolExecutor poolExecutor; + + private AudioCapturer audioCapturer; + + private boolean recognizeOver; + + private String fileName; + + @Override + public void onStart(Intent intent) { + HiLog.info(TAG, "onStart"); + super.onStart(intent); + super.setUIContent(ResourceTable.Layout_ability_main); + new PermissionBridge().setOnPermissionStateListener(this); + } + + private void initSurface() { + surfaceProvider = new SurfaceProvider(this); + DirectionalLayout.LayoutConfig params = + new DirectionalLayout.LayoutConfig( + ComponentContainer.LayoutConfig.MATCH_PARENT, ComponentContainer.LayoutConfig.MATCH_PARENT); + + surfaceProvider.setLayoutConfig(params); + surfaceProvider.pinToZTop(true); + + surfaceProvider.getSurfaceOps().get().addCallback(new SurfaceCallBack()); + Component surfaceContainer = findComponentById(ResourceTable.Id_surface_container); + if (surfaceContainer instanceof ComponentContainer) { + ((ComponentContainer) surfaceContainer).addComponent(surfaceProvider); + } + } + + private void initControlComponents() { + if (findComponentById(ResourceTable.Id_tack_picture_btn) instanceof Image) { + takePictureImage = (Image) findComponentById(ResourceTable.Id_tack_picture_btn); + } + takePictureImage.setClickedListener(this::takePicture); + + if (findComponentById(ResourceTable.Id_switch_camera_btn) instanceof Image) { + switchCameraImage = (Image) findComponentById(ResourceTable.Id_switch_camera_btn); + MyDrawTask drawTask = new MyDrawTask(switchCameraImage); + switchCameraImage.addDrawTask(drawTask); + } + switchCameraImage.setClickedListener(component -> switchClicked()); + + if (findComponentById(ResourceTable.Id_small_pic) instanceof Image) { + smallImage = (Image) findComponentById(ResourceTable.Id_small_pic); + } + smallImage.setClickedListener(component -> showBigPic()); + } + + private void openAudio() { + HiLog.info(TAG, "openAudio"); + + if (!isRecord) { + asrClient.startListening(setStartIntent()); + isRecord = true; + poolExecutor.submit(new AudioCaptureRunnable()); + } + } + + private void takePicture(Component component) { + HiLog.info(TAG, "takePicture"); + if (!takePictureImage.isEnabled()) { + HiLog.info(TAG, "takePicture return"); + return; + } + if (cameraDevice == null || imageReceiver == null) { + return; + } + FrameConfig.Builder framePictureConfigBuilder = cameraDevice.getFrameConfigBuilder(FRAME_CONFIG_PICTURE); + framePictureConfigBuilder.addSurface(imageReceiver.getRecevingSurface()); + FrameConfig pictureFrameConfig = framePictureConfigBuilder.build(); + cameraDevice.triggerSingleCapture(pictureFrameConfig); + } + + private void switchClicked() { + if (!takePictureImage.isEnabled()) { + return; + } + takePictureImage.setEnabled(false); + isCameraRear = !isCameraRear; + CameraUtil.isCameraRear = isCameraRear; + openCamera(); + } + + private void openCamera() { + imageReceiver = ImageReceiver.create(SCREEN_WIDTH, SCREEN_HEIGHT, ImageFormat.JPEG, IMAGE_RCV_CAPACITY); + imageReceiver.setImageArrivalListener(this::saveImage); + + CameraKit cameraKit = CameraKit.getInstance(getApplicationContext()); + String[] cameraList = cameraKit.getCameraIds(); + String cameraId = cameraList.length > 1 && isCameraRear ? cameraList[1] : cameraList[0]; + CameraStateCallbackImpl cameraStateCallback = new CameraStateCallbackImpl(); + cameraKit.createCamera(cameraId, cameraStateCallback, creamEventHandler); + } + + private void saveImage(ImageReceiver receiver) { + fileName = IMG_FILE_PREFIX + System.currentTimeMillis() + IMG_FILE_TYPE; + targetFile = new File(getExternalFilesDir(Environment.DIRECTORY_PICTURES), fileName); + try { + HiLog.info(TAG, "filePath is " + targetFile.getCanonicalPath()); + } catch (IOException e) { + HiLog.error(TAG, "filePath is error"); + } + + ohos.media.image.Image image = receiver.readNextImage(); + if (image == null) { + return; + } + ohos.media.image.Image.Component component = image.getComponent(ImageFormat.ComponentType.JPEG); + bytes = new byte[component.remaining()]; + component.read(bytes); + try (FileOutputStream output = new FileOutputStream(targetFile)) { + output.write(bytes); + output.flush(); + handler.sendEvent(EVENT_IMAGESAVING_PROMTING); + DistributeFileUtil.copyPicToDistributedDir(MainAbilitySlice.this, targetFile, fileName); + } catch (IOException e) { + HiLog.info(TAG, "IOException, Save image failed"); + } + } + + private void releaseCamera() { + if (cameraDevice != null) { + cameraDevice.release(); + cameraDevice = null; + } + + if (imageReceiver != null) { + imageReceiver.release(); + imageReceiver = null; + } + + if (creamEventHandler != null) { + creamEventHandler.removeAllEvent(); + creamEventHandler = null; + } + } + + @Override + protected void onStop() { + super.onStop(); + releaseCamera(); + } + + @Override + public void onPermissionGranted() { + getWindow().setTransparent(true); + initSurface(); + initAudioCapturer(); + initControlComponents(); + initASRClient(); + creamEventHandler = new EventHandler(EventRunner.create("CameraBackground")); + } + + private void initAudioCapturer() { + poolExecutor = + new ThreadPoolExecutor( + 3, + 3, + 3, + TimeUnit.SECONDS, + new LinkedBlockingQueue<>(6), + new ThreadPoolExecutor.DiscardOldestPolicy()); + + AudioStreamInfo audioStreamInfo = + new AudioStreamInfo.Builder() + .encodingFormat(AudioStreamInfo.EncodingFormat.ENCODING_PCM_16BIT) + .channelMask(AudioStreamInfo.ChannelMask.CHANNEL_IN_MONO) + .sampleRate(16000) + .build(); + + AudioCapturerInfo audioCapturerInfo = new AudioCapturerInfo.Builder().audioStreamInfo(audioStreamInfo).build(); + + audioCapturer = new AudioCapturer(audioCapturerInfo); + } + + private boolean recognizeWords(String result) { + JSONObject jsonObject = JSONObject.parseObject(result); + JSONObject resultObject = new JSONObject(); + if (jsonObject.getJSONArray("result").get(0) instanceof JSONObject) { + resultObject = (JSONObject) jsonObject.getJSONArray("result").get(0); + } + String resultWord = resultObject.getString("ori_word").replace(" ", ""); + boolean command = COMMAND_MAP.getOrDefault(resultWord, false); + HiLog.info(TAG, "======" + resultWord + "===" + command); + return command; + } + + private void initListener() { + AsrListener asrListener = + new MyAsrListener() { + @Override + public void onInit(PacMap params) { + super.onInit(params); + openAudio(); + HiLog.info(TAG, "======onInit======"); + } + + @Override + public void onError(int error) { + super.onError(error); + HiLog.info(TAG, "======error:" + error); + } + + @Override + public void onIntermediateResults(PacMap pacMap) { + super.onIntermediateResults(pacMap); + HiLog.info(TAG, "======onIntermediateResults:"); + String result = pacMap.getString(AsrResultKey.RESULTS_INTERMEDIATE); + boolean recognizeResult = recognizeWords(result); + + if (recognizeResult && !recognizeOver) { + recognizeOver = true; + takePicture(new Component(getContext())); + asrClient.stopListening(); + } + } + + @Override + public void onResults(PacMap results) { + super.onResults(results); + HiLog.info(TAG, "======onResults:"); + + recognizeOver = false; + asrClient.startListening(setStartIntent()); + } + + @Override + public void onEnd() { + super.onEnd(); + HiLog.info(TAG, "======onEnd:"); + + recognizeOver = false; + asrClient.stopListening(); + asrClient.startListening(setStartIntent()); + } + }; + // Initializes asr. + if (asrClient != null) { + asrClient.init(setInitIntent(), asrListener); + } + } + + private void initASRClient() { + asrClient = AsrClient.createAsrClient(this).orElse(null); + TaskDispatcher taskDispatcher = getAbility().getMainTaskDispatcher(); + taskDispatcher.asyncDispatch( + new Runnable() { + @Override + public void run() { + initListener(); + } + }); + } + + @Override + public void onPermissionDenied() {} + + class CameraStateCallbackImpl extends CameraStateCallback { + CameraStateCallbackImpl() {} + + @Override + public void onCreated(Camera camera) { + previewSurface = surfaceProvider.getSurfaceOps().get().getSurface(); + if (previewSurface == null) { + HiLog.info(TAG, "create camera filed, preview surface is null"); + return; + } + + try { + Thread.sleep(200); + } catch (InterruptedException exception) { + HiLog.info(TAG, "Waiting to be interrupted"); + } + + CameraConfig.Builder cameraConfigBuilder = camera.getCameraConfigBuilder(); + cameraConfigBuilder.addSurface(previewSurface); + cameraConfigBuilder.addSurface(imageReceiver.getRecevingSurface()); + camera.configure(cameraConfigBuilder.build()); + cameraDevice = camera; + + enableImageGroup(); + } + + @Override + public void onConfigured(Camera camera) { + FrameConfig.Builder framePreviewConfigBuilder = camera.getFrameConfigBuilder(FRAME_CONFIG_PREVIEW); + framePreviewConfigBuilder.addSurface(previewSurface); + try { + // 启动循环帧捕获 + camera.triggerLoopingCapture(framePreviewConfigBuilder.build()); + } catch (IllegalArgumentException e) { + HiLog.error(TAG, "Argument Exception"); + } catch (IllegalStateException e) { + HiLog.error(TAG, "State Exception"); + } + } + + private void enableImageGroup() { + takePictureImage.setEnabled(true); + switchCameraImage.setEnabled(true); + } + } + + class SurfaceCallBack implements SurfaceOps.Callback { + @Override + public void surfaceCreated(SurfaceOps callbackSurfaceOps) { + if (callbackSurfaceOps != null) { + callbackSurfaceOps.setFixedSize(surfaceProvider.getHeight(), surfaceProvider.getWidth()); + } + openCamera(); + } + + @Override + public void surfaceChanged(SurfaceOps callbackSurfaceOps, int format, int width, int height) {} + + @Override + public void surfaceDestroyed(SurfaceOps callbackSurfaceOps) {} + } + + private EventHandler handler = + new EventHandler(EventRunner.current()) { + @Override + protected void processEvent(InnerEvent event) { + switch (event.eventId) { + case EVENT_IMAGESAVING_PROMTING: + HiLog.info(TAG, "EVENT_IMAGESAVING_PROMTING"); + MyDrawTask drawTask = new MyDrawTask(smallImage); + smallImage.addDrawTask(drawTask); + drawTask.putPixelMap(CameraUtil.getPixelMap(bytes, "", 1)); + smallImage.setEnabled(true); + break; + default: + break; + } + } + }; + + /** + * showBigPic + */ + private void showBigPic() { + Intent intent = new Intent(); + Operation operation = + new Intent.OperationBuilder() + .withBundleName(getBundleName()) + .withAbilityName(ImageAbility.class.getName()) + .build(); + intent.setOperation(operation); + if (targetFile != null) { + try { + intent.setParam(CAMERA_IMAGE_FILE_PATH, targetFile.getCanonicalPath()); + } catch (IOException e) { + HiLog.error(TAG, "filePath is error"); + } + } + startAbility(intent); + } + + private AsrIntent setInitIntent() { + AsrIntent initIntent = new AsrIntent(); + initIntent.setAudioSourceType(AsrIntent.AsrAudioSrcType.ASR_SRC_TYPE_PCM); + initIntent.setEngineType(AsrIntent.AsrEngineType.ASR_ENGINE_TYPE_LOCAL); + return initIntent; + } + + private AsrIntent setStartIntent() { + AsrIntent asrIntent = new AsrIntent(); + asrIntent.setVadEndWaitMs(2000); + asrIntent.setVadFrontWaitMs(4800); + asrIntent.setTimeoutThresholdMs(20000); + return asrIntent; + } + + /** + * Obtains PCM audio streams and sends them to the ASR engine during recording. + */ + private class AudioCaptureRunnable implements Runnable { + @Override + public void run() { + byte[] buffers = new byte[1280]; + audioCapturer.start(); + while (isRecord) { + int ret = audioCapturer.read(buffers, 0, 1280); + if (ret <= 0) { + HiLog.error(TAG, "======Error read data"); + } else { + asrClient.writePcm(buffers, buffers.length); + } + } + } + } +} diff --git a/VoiceCamera/entry/src/main/java/com/huawei/camerademo/utils/CameraUtil.java b/VoiceCamera/entry/src/main/java/com/huawei/camerademo/utils/CameraUtil.java new file mode 100644 index 0000000000000000000000000000000000000000..01b0d755a41f6db3266926ca1d0bcf38066ed722 --- /dev/null +++ b/VoiceCamera/entry/src/main/java/com/huawei/camerademo/utils/CameraUtil.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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.huawei.camerademo.utils; + +import ohos.media.image.ImageSource; +import ohos.media.image.PixelMap; +import ohos.media.image.common.PixelFormat; +import ohos.media.image.common.Size; + +/** + * CameraUtil + * + * @since 2021-3-8 + */ +public class CameraUtil { + /** + * camera image file path + */ + public static final String CAMERA_IMAGE_FILE_PATH = "camera_image_file_path"; + + /** + * is camera rear + */ + public static boolean isCameraRear = false; + + /** + * Obtains the PixelMap of an image. + * + * @param imageBytes Image byte stream + * @param imagePath Image path + * @param rotateCount angle of rotation + * @return PixelMap + */ + public static PixelMap getPixelMap(byte[] imageBytes, String imagePath, int rotateCount) { + ImageSource.SourceOptions sourceOptions = new ImageSource.SourceOptions(); + sourceOptions.formatHint = "image/png"; + ImageSource imageSource = null; + if (imagePath != null && !imagePath.isEmpty()) { + imageSource = ImageSource.create(imagePath, sourceOptions); + } else if (imageBytes != null) { + imageSource = ImageSource.create(imageBytes, sourceOptions); + } else { + return null; + } + ImageSource.DecodingOptions decodingOpts = new ImageSource.DecodingOptions(); + decodingOpts.desiredSize = new Size(0, 0); + decodingOpts.rotateDegrees = (isCameraRear ? 270 : 90) * rotateCount; + decodingOpts.desiredPixelFormat = PixelFormat.ARGB_8888; + return imageSource.createPixelmap(decodingOpts); + } +} diff --git a/VoiceCamera/entry/src/main/java/com/huawei/camerademo/utils/DistributeFileUtil.java b/VoiceCamera/entry/src/main/java/com/huawei/camerademo/utils/DistributeFileUtil.java new file mode 100644 index 0000000000000000000000000000000000000000..7cf29bcdf9815aab02b74667b92b62ed6ae2733c --- /dev/null +++ b/VoiceCamera/entry/src/main/java/com/huawei/camerademo/utils/DistributeFileUtil.java @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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.huawei.camerademo.utils; + +import com.huawei.camerademo.ImageAbility; + +import ohos.aafwk.content.Intent; +import ohos.aafwk.content.Operation; +import ohos.agp.window.dialog.ToastDialog; +import ohos.app.Context; +import ohos.distributedschedule.interwork.DeviceInfo; +import ohos.distributedschedule.interwork.DeviceManager; +import ohos.media.image.ImageSource; +import ohos.media.image.PixelMap; +import ohos.media.image.SourceDataMalformedException; +import ohos.media.image.common.PixelFormat; +import ohos.media.image.common.Rect; +import ohos.media.image.common.Size; + +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.List; + +/** + * DistributeFileUtil + * + * @since 2021-03-8 + */ +public class DistributeFileUtil { + private static final String TAG = DistributeFileUtil.class.getSimpleName(); + private static final int CACHE_SIZE = 1024; + private static final int IO_END_LEN = -1; + private Context context; + + private static File getDisFile(Context context, String fileName) { + File distributedDir = context.getDistributedDir(); + File distributedFile; + if (distributedDir == null) { + showTip(context, "分布式文件服务异常,目录为空!"); + return null; + } else { + distributedFile = new File(distributedDir, fileName); + } + return distributedFile; + } + + /** + * Copying Images to the Distributed File Service Address + * + * @param context context + * @param sourceFile file + * @param fileName filename + * @throws IOException Signals that an I/O exception of some sort has occurred + */ + public static void copyPicToDistributedDir(Context context, File sourceFile, String fileName) { + InputStream in = null; + OutputStream out = null; + File disPath = getDisFile(context, fileName); + if (disPath == null) { + return; + } + try { + in = new FileInputStream(sourceFile); + out = new FileOutputStream(disPath); + byte[] buffer = new byte[CACHE_SIZE]; + int len; + while ((len = in.read(buffer)) != IO_END_LEN) { + out.write(buffer, 0, len); + } + // 拷贝成功,启动远程FA + startRemoteFas(context, disPath.getCanonicalPath()); + } catch (IOException e) { + LogUtil.error(TAG, "copy error occur while copy"); + showTip(context, "分布式文件服务异常,拷贝异常!"); + } finally { + try { + if (in != null) { + in.close(); + } + } catch (IOException e) { + LogUtil.error(TAG, "io close exception"); + } + try { + if (out != null) { + out.close(); + } + } catch (IOException e) { + LogUtil.error(TAG, "io close exception"); + } + } + } + + /** + * Reads the distributed file service address image. + * + * @param context context + * @param filePath filePath + * @return Provides images in forms of pixel matrices + * @throws SourceDataMalformedException Provides the exception for unsupported image format + */ + public static PixelMap readToDistributedDir(Context context, String filePath) { + File disPath = new File(filePath); + if (disPath == null) { + return null; + } + try { + ImageSource.SourceOptions srcOpts = new ImageSource.SourceOptions(); + ImageSource imageSource = ImageSource.create(disPath, srcOpts); + if (imageSource != null) { + LogUtil.info(TAG, "imageSource != null ok ok ok"); + showTip(context, "imageSource != null ok ok ok"); + ImageSource.DecodingOptions decodingOpts = new ImageSource.DecodingOptions(); + decodingOpts.desiredSize = new Size(0, 0); + decodingOpts.desiredRegion = new Rect(0, 0, 0, 0); + decodingOpts.desiredPixelFormat = PixelFormat.ARGB_8888; + decodingOpts.rotateDegrees = 90; + return imageSource.createPixelmap(decodingOpts); + } else { + showTip(context, "imageSource == null error error error"); + } + } catch (SourceDataMalformedException e) { + LogUtil.error(TAG, "readToDistributedDir SourceDataMalformedException "); + } + + return null; + } + + /** + * Deleting a Distributed File Service Address + * + * @param context context + * @param fileName fileName + */ + private void deleteDistributedDir(Context context, String fileName) { + File disPath = getDisFile(context, fileName); + if (disPath != null && disPath.exists() && disPath.isFile()) { + boolean result = disPath.delete(); + showTip(context, "delete :" + (result ? "success" : "fail")); + } else { + showTip(context, "No pictures exists in the distributedDir"); + } + } + + private static void showTip(Context context, String msg) { + context.getUITaskDispatcher() + .delayDispatch( + new Runnable() { + @Override + public void run() { + ToastDialog toastDialog = new ToastDialog(context); + toastDialog.setAutoClosable(false); + toastDialog.setContentText(msg); + toastDialog.show(); + } + }, + 0); + } + + /** + * Start Remote + * + * @param context context + * @param filePath filePath + */ + private static void startRemoteFas(Context context, String filePath) { + List deviceInfos = + DeviceManager.getDeviceList(ohos.distributedschedule.interwork.DeviceInfo.FLAG_GET_ONLINE_DEVICE); + if (deviceInfos == null || deviceInfos.size() < 1) { + return; + } + Intent[] intents = new Intent[deviceInfos.size()]; + for (int i = 0; i < deviceInfos.size(); i++) { + Intent intent = new Intent(); + intent.setParam("filePath", filePath); + Operation operation = + new Intent.OperationBuilder() + .withDeviceId(deviceInfos.get(i).getDeviceId()) + .withBundleName(context.getBundleName()) + .withAbilityName(ImageAbility.class.getName()) + .withFlags(Intent.FLAG_ABILITYSLICE_MULTI_DEVICE) + .build(); + intent.setOperation(operation); + intents[i] = intent; + } + context.startAbilities(intents); + } +} diff --git a/VoiceCamera/entry/src/main/java/com/huawei/camerademo/utils/LogUtil.java b/VoiceCamera/entry/src/main/java/com/huawei/camerademo/utils/LogUtil.java new file mode 100644 index 0000000000000000000000000000000000000000..2bda444e7b5dd13c67a20d8709cda1f90b0962e1 --- /dev/null +++ b/VoiceCamera/entry/src/main/java/com/huawei/camerademo/utils/LogUtil.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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.huawei.camerademo.utils; + +import ohos.hiviewdfx.HiLog; +import ohos.hiviewdfx.HiLogLabel; + +/** + * Log Util + * + * @since 2021-03-8 + */ +public class LogUtil { + private static final String TAG_LOG = "LogUtil"; + + private static final HiLogLabel LABEL_LOG = new HiLogLabel(0, 0, LogUtil.TAG_LOG); + + private static final String LOG_FORMAT = "%{public}s: %{public}s"; + + private LogUtil() {} + + /** + * Print info log + * + * @param tag log tag + * @param msg log message + */ + public static void info(String tag, String msg) { + HiLog.info(LABEL_LOG, LOG_FORMAT, tag, msg); + } + + /** + * Print info log + * + * @param tag log tag + * @param msg log message + */ + public static void error(String tag, String msg) { + HiLog.info(LABEL_LOG, LOG_FORMAT, tag, msg); + } +} diff --git a/VoiceCamera/entry/src/main/java/com/huawei/camerademo/utils/MyDrawTask.java b/VoiceCamera/entry/src/main/java/com/huawei/camerademo/utils/MyDrawTask.java new file mode 100644 index 0000000000000000000000000000000000000000..46e899043540aa681ba2d88ca2a8aa680e1cf4cf --- /dev/null +++ b/VoiceCamera/entry/src/main/java/com/huawei/camerademo/utils/MyDrawTask.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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.huawei.camerademo.utils; + +import ohos.agp.components.Component; +import ohos.agp.render.Canvas; +import ohos.agp.render.Paint; +import ohos.agp.render.PixelMapHolder; +import ohos.agp.utils.Color; +import ohos.agp.utils.RectFloat; +import ohos.media.image.PixelMap; + +/** + * MyDrawTask + * + * @since 2021-3-8 + */ +public class MyDrawTask implements Component.DrawTask { + private static final int HALF = 2; + private static final int STROKE_WIDTH = 3; + private static final int MAX_SIZE = 66; + private Component component; + private PixelMap pixelMap; + private RectFloat rectSrc; + + public MyDrawTask(Component component) { + this.component = component; + } + + /** + * putPixelMap + * + * @param pixelMap Provides images in forms of pixel matrices + */ + public void putPixelMap(PixelMap pixelMap) { + if (pixelMap != null) { + this.pixelMap = pixelMap; + int halfWidth = pixelMap.getImageInfo().size.width / HALF; + int halfHeight = pixelMap.getImageInfo().size.height / HALF; + int center = Math.min(halfWidth, halfHeight); + rectSrc = new RectFloat(halfWidth - center, halfHeight - center, halfWidth + center, halfHeight + center); + component.invalidate(); + } + } + + @Override + public void onDraw(Component component, Canvas canvas) { + int halfWidth = component.getWidth() / HALF; + int halfHeight = component.getHeight() / HALF; + int center = Math.min(halfWidth, halfHeight); + if (center > MAX_SIZE) { + center = MAX_SIZE; + } + + int radius = center - STROKE_WIDTH / HALF; + if (pixelMap != null) { + canvas.drawPixelMapHolderCircleShape(new PixelMapHolder(pixelMap), rectSrc, halfWidth, halfHeight, radius); + } + + Paint roundPaint = new Paint(); + roundPaint.setAntiAlias(true); + roundPaint.setStyle(Paint.Style.STROKE_STYLE); + roundPaint.setStrokeWidth(STROKE_WIDTH); + roundPaint.setStrokeCap(Paint.StrokeCap.ROUND_CAP); + roundPaint.setColor(Color.WHITE); + canvas.drawCircle(halfWidth, halfHeight, radius, roundPaint); + } +} diff --git a/VoiceCamera/entry/src/main/java/com/huawei/camerademo/utils/PermissionBridge.java b/VoiceCamera/entry/src/main/java/com/huawei/camerademo/utils/PermissionBridge.java new file mode 100644 index 0000000000000000000000000000000000000000..7afb81d7fc0da25223cdccc1e11ce19428fecda7 --- /dev/null +++ b/VoiceCamera/entry/src/main/java/com/huawei/camerademo/utils/PermissionBridge.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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.huawei.camerademo.utils; + +import ohos.eventhandler.EventHandler; +import ohos.eventhandler.EventRunner; +import ohos.eventhandler.InnerEvent; + +/** + * PermissionBridge + * + * @since 2021-3-8 + */ +public class PermissionBridge { + /** + * permission handler granted + */ + public static final int EVENT_PERMISSION_GRANTED = 0x0000023; + + /** + * permission handler denied + */ + public static final int EVENT_PERMISSION_DENIED = 0x0000024; + + private static final String TAG = PermissionBridge.class.getSimpleName(); + + private static OnPermissionStateListener onPermissionStateListener; + + private static EventHandler handler = + new EventHandler(EventRunner.current()) { + @Override + protected void processEvent(InnerEvent event) { + switch (event.eventId) { + case EVENT_PERMISSION_GRANTED: + onPermissionStateListener.onPermissionGranted(); + break; + case EVENT_PERMISSION_DENIED: + onPermissionStateListener.onPermissionDenied(); + break; + default: + LogUtil.info(TAG, "EventHandler Undefined Event"); + break; + } + } + }; + + /** + * setOnPermissionStateListener + * + * @param permissionStateListener OnPermissionStateListener + */ + public void setOnPermissionStateListener(OnPermissionStateListener permissionStateListener) { + onPermissionStateListener = permissionStateListener; + } + + /** + * OnPermissionStateListener + * + */ + public interface OnPermissionStateListener { + void onPermissionGranted(); + + void onPermissionDenied(); + } + + /** + * getHandler + * + * @return EventHandler + */ + public static EventHandler getHandler() { + return handler; + } +} diff --git a/VoiceCamera/entry/src/main/resources/base/element/string.json b/VoiceCamera/entry/src/main/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..dfddb7cb95e8574b5465c387791067ce033ba968 --- /dev/null +++ b/VoiceCamera/entry/src/main/resources/base/element/string.json @@ -0,0 +1,16 @@ +{ + "string": [ + { + "name": "app_name", + "value": "CameraDemo" + }, + { + "name": "mainability_description", + "value": "Java_Phone_Empty Feature Ability" + }, + { + "name": "imageability_description", + "value": "Java_Tv_Empty Feature Ability" + } + ] +} \ No newline at end of file diff --git a/VoiceCamera/entry/src/main/resources/base/graphic/background_ability_image.xml b/VoiceCamera/entry/src/main/resources/base/graphic/background_ability_image.xml new file mode 100644 index 0000000000000000000000000000000000000000..90a82183b4b7d43c4a010873bfc5015701a7e061 --- /dev/null +++ b/VoiceCamera/entry/src/main/resources/base/graphic/background_ability_image.xml @@ -0,0 +1,18 @@ + + + + + \ No newline at end of file diff --git a/VoiceCamera/entry/src/main/resources/base/graphic/background_ability_main.xml b/VoiceCamera/entry/src/main/resources/base/graphic/background_ability_main.xml new file mode 100644 index 0000000000000000000000000000000000000000..44fb1387798e43f7b7fdf3f4886d8213bcc2618b --- /dev/null +++ b/VoiceCamera/entry/src/main/resources/base/graphic/background_ability_main.xml @@ -0,0 +1,20 @@ + + + + + \ No newline at end of file diff --git a/VoiceCamera/entry/src/main/resources/base/graphic/background_button.xml b/VoiceCamera/entry/src/main/resources/base/graphic/background_button.xml new file mode 100644 index 0000000000000000000000000000000000000000..c1e11b6d497a11fb5e5693f94cf345832925583f --- /dev/null +++ b/VoiceCamera/entry/src/main/resources/base/graphic/background_button.xml @@ -0,0 +1,22 @@ + + + + + + \ No newline at end of file diff --git a/VoiceCamera/entry/src/main/resources/base/layout/ability_image.xml b/VoiceCamera/entry/src/main/resources/base/layout/ability_image.xml new file mode 100644 index 0000000000000000000000000000000000000000..2a642598e3d7b29ae5551a42abb62cd460ac52af --- /dev/null +++ b/VoiceCamera/entry/src/main/resources/base/layout/ability_image.xml @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/VoiceCamera/entry/src/main/resources/base/layout/ability_main.xml b/VoiceCamera/entry/src/main/resources/base/layout/ability_main.xml new file mode 100644 index 0000000000000000000000000000000000000000..2d1bb0690d7ef7d653deb88c2ad3c2cb6a5c0e3b --- /dev/null +++ b/VoiceCamera/entry/src/main/resources/base/layout/ability_main.xml @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/VoiceCamera/entry/src/main/resources/base/media/bg_image.png b/VoiceCamera/entry/src/main/resources/base/media/bg_image.png new file mode 100644 index 0000000000000000000000000000000000000000..7c028464ed3f2d6ff75fb33b81eaf0ed00c75e87 Binary files /dev/null and b/VoiceCamera/entry/src/main/resources/base/media/bg_image.png differ diff --git a/VoiceCamera/entry/src/main/resources/base/media/ic_camera_back.png b/VoiceCamera/entry/src/main/resources/base/media/ic_camera_back.png new file mode 100644 index 0000000000000000000000000000000000000000..85453cfd5c185dc0f0d9898ae06c8d779d4b2bd2 Binary files /dev/null and b/VoiceCamera/entry/src/main/resources/base/media/ic_camera_back.png differ diff --git a/VoiceCamera/entry/src/main/resources/base/media/ic_camera_foot.png b/VoiceCamera/entry/src/main/resources/base/media/ic_camera_foot.png new file mode 100644 index 0000000000000000000000000000000000000000..47a0eaf8f1860e0966ff426c0f14320178651024 Binary files /dev/null and b/VoiceCamera/entry/src/main/resources/base/media/ic_camera_foot.png differ diff --git a/VoiceCamera/entry/src/main/resources/base/media/ic_camera_head.png b/VoiceCamera/entry/src/main/resources/base/media/ic_camera_head.png new file mode 100644 index 0000000000000000000000000000000000000000..063c47d64be1db57c7b128dd65d18c72fe6c0e56 Binary files /dev/null and b/VoiceCamera/entry/src/main/resources/base/media/ic_camera_head.png differ diff --git a/VoiceCamera/entry/src/main/resources/base/media/ic_camera_photo.png b/VoiceCamera/entry/src/main/resources/base/media/ic_camera_photo.png new file mode 100644 index 0000000000000000000000000000000000000000..cf6e9e8b7cc9023f2bfb6ce10bb5c0d95625d6d5 Binary files /dev/null and b/VoiceCamera/entry/src/main/resources/base/media/ic_camera_photo.png differ diff --git a/VoiceCamera/entry/src/main/resources/base/media/ic_camera_switch.png b/VoiceCamera/entry/src/main/resources/base/media/ic_camera_switch.png new file mode 100644 index 0000000000000000000000000000000000000000..92eff2321b1e2ff15bef1b4016b582d9fbb8a74d Binary files /dev/null and b/VoiceCamera/entry/src/main/resources/base/media/ic_camera_switch.png differ diff --git a/VoiceCamera/entry/src/main/resources/base/media/ic_camera_thumbnail.png b/VoiceCamera/entry/src/main/resources/base/media/ic_camera_thumbnail.png new file mode 100644 index 0000000000000000000000000000000000000000..0d7a9c3c29ca4fc7b683c3ac9c77c1f45bbaf44d Binary files /dev/null and b/VoiceCamera/entry/src/main/resources/base/media/ic_camera_thumbnail.png differ diff --git a/VoiceCamera/entry/src/main/resources/base/media/ic_image_foot.png b/VoiceCamera/entry/src/main/resources/base/media/ic_image_foot.png new file mode 100644 index 0000000000000000000000000000000000000000..57d2c54ed0f94fed6c8b844d638642b42249fb3e Binary files /dev/null and b/VoiceCamera/entry/src/main/resources/base/media/ic_image_foot.png differ diff --git a/VoiceCamera/entry/src/main/resources/base/media/ic_image_left.png b/VoiceCamera/entry/src/main/resources/base/media/ic_image_left.png new file mode 100644 index 0000000000000000000000000000000000000000..3392dec285b862ff560c8176151773a8fc767e08 Binary files /dev/null and b/VoiceCamera/entry/src/main/resources/base/media/ic_image_left.png differ diff --git a/VoiceCamera/entry/src/main/resources/base/media/ic_image_right.png b/VoiceCamera/entry/src/main/resources/base/media/ic_image_right.png new file mode 100644 index 0000000000000000000000000000000000000000..950381b0938adcdcf3295f05fb15c53175cfcd8d Binary files /dev/null and b/VoiceCamera/entry/src/main/resources/base/media/ic_image_right.png differ diff --git a/VoiceCamera/entry/src/main/resources/base/media/icon.png b/VoiceCamera/entry/src/main/resources/base/media/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ce307a8827bd75456441ceb57d530e4c8d45d36c Binary files /dev/null and b/VoiceCamera/entry/src/main/resources/base/media/icon.png differ diff --git a/VoiceCamera/entry/src/test/java/com/huawei/camerademo/ExampleTest.java b/VoiceCamera/entry/src/test/java/com/huawei/camerademo/ExampleTest.java new file mode 100644 index 0000000000000000000000000000000000000000..0832d47fb007d6fefd10470e5ba454e2c13d2185 --- /dev/null +++ b/VoiceCamera/entry/src/test/java/com/huawei/camerademo/ExampleTest.java @@ -0,0 +1,9 @@ +package com.huawei.camerademo; + +import org.junit.Test; + +public class ExampleTest { + @Test + public void onStart() { + } +} diff --git a/VoiceCamera/gradle/wrapper/gradle-wrapper.jar b/VoiceCamera/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..490fda8577df6c95960ba7077c43220e5bb2c0d9 Binary files /dev/null and b/VoiceCamera/gradle/wrapper/gradle-wrapper.jar differ diff --git a/VoiceCamera/gradle/wrapper/gradle-wrapper.properties b/VoiceCamera/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000000000000000000000000000000000000..6623300bebd011bc5f7991f4f9c389e2f67b14ac --- /dev/null +++ b/VoiceCamera/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-all.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/VoiceCamera/settings.gradle b/VoiceCamera/settings.gradle new file mode 100644 index 0000000000000000000000000000000000000000..4773db73233a570c2d0c01a22e75321acfbf7a07 --- /dev/null +++ b/VoiceCamera/settings.gradle @@ -0,0 +1 @@ +include ':entry'