Duration: 3:00
总览
通过构建本次的SplitBill应用,您可以更好地了解华为生态的组成部分,包括认证服务、云存储和云数据库等Serverless服务。此外您还可以了解如何使用近距离数据通信服务的Nearby Connection功能分享文件。无需使用现金,SplitBill应用能够实现用户与其他任意用户共同支付账单。
您将建立什么
在本次的Codelab中,您将建立一款SplitBill应用,并使用到华为认证服务、Network Kit、近距离数据通信服务、云数据库和云存储的接口。该应用为用户提供端到端的群收款服务。使用该应用,多位用户可以组成一个群,共同完成账单支付。
您将学到什么
使用认证服务登录应用(华为账号和手机号认证方式)
创建群。
添加成员到群。
添加账单。
上传用户头像至云存储和在云数据库中更新头像。
使用近距离通信服务分享账单文件。
Duration: 2:00
开发环境
设备要求
一部用于测试的安卓手机或模拟器。
Duration: 10:00
在集成SDK之前,您需要完成以下准备:
在AppGallery Connect上创建应用。
创建一个安卓项目。
生成签名证书。
生成签名证书指纹。
配置签名证书指纹。
添加应用包名,保存配置文件。
在项目级build.gradle文件中添加AppGallery Connect插件和Maven仓。
在Android Studio中配置签名证书。
详情请见HUWEI HMS Core集成准备。
说明:在上述准备工作前您需要注册成为一名开发者。
Duration: 4:00
首先,您需要在AppGallery Connect上启用HMS Core的相关服务。启用前,请完成以下准备工作:
1、登录AppGallery Connect,点击“项目设置”中“API管理”页签,开通如下服务的API权限。
认证服务(华为账号认证方式)
云数据库
云存储
近距离通信服务
Network Kit




说明:以上API权限默认已开通。如未开通,请手动开通。
2、在弹出的页面设置数据处理位置。

Duration: 4:00
您需要将云数据库SDK集成到您的Android Studio项目中。
1、登录AppGallery Connect,点击“我的项目”。
2、点击您的项目,在顶部的应用下拉列表中选择应用。
3、点击“项目设置”>“常规”。在“应用”区域,下载agconnect-services.json文件。

4、将agconnect-services.json文件复制到您的项目中。

5、在Android Studio中,打开项目级build.gradle文件,前往allprojects > repositories,在buildscript的repositories部分配置Maven仓地址。


6、在buildscript的dependencies部分配置AppGallery Connect插件。
- buildscript {
- dependencies {
- classpath 'com.huawei.agconnect:agcp:
' - }
- }
7、在应用级build.gradle文件中添加AppGallery Connect插件。
apply plugin: 'com.huawei.agconnect'
8、(可选)在Application类中的onCreate方法里添加初始化代码。
- if (AGConnectInstance.getInstance() == null) {
- AGConnectInstance.initialize(getApplicationContext());
- }
9、在应用级build.gradle文件里的dependencies部分添加所需依赖路径。
- implementation 'com.huawei.agconnect:agconnect-auth:
' - implementation 'com.huawei.hms:hwid:
' - implementation 'com.huawei.hms:nearby:
' - implementation"com.huawei.agconnect:agconnect-storage:
" - implementation'com.huawei.agconnect:agconnect-cloud-database:
'
Duration: 5:00
创建以下界面:登录、建群、群列表、添加账单、账单和收支标签页、账单详情、账单分享详情和收到的账单文件列表。
用户登录界面

群列表和建群界面




Duration: 5:00
认证服务
认证服务SDK能够让您快速便捷地在您的应用上实现用户注册和登录功能。
登录AppGallery Connect,点击“我的项目”。
点击您的项目。
选择“构建”,单击“认证服务”。若您首次使用认证服务,点击“立即开通”。
单击“认证方式”,在“操作”栏中启用华为账号认证方式。

Cloud DB
使用云数据库服务,您需要启用云数据库,创建存储区和云数据库对象所需字段。
1、登录AppGallery Connect,点击“我的项目”。
2、点击您的项目。
3、选择“构建”,点击“云数据库”。若您首次使用云数据库,点击“立即开通”。
4、在弹出页面设置数据处理位置。
5、单击“新增”,进入创建对象类型页面。

6、设置对象类型,单击“下一步”。
7、单击“+新增字段”添加字段,单击“下一步”。
8、添加所需索引。
9、设置各角色权限。

10、单击“确定”。返回对象类型列表,查看已创建的对象类型。
11、单击“导出”。

12、选择文件格式,此处选择文件类型为Java,文件格式为Android。输入包名,点击“确定”。对象类型文件将被作为zip文件下载
13、提取zip中的文件至项目的model包里。
1)点击“存储区”页签。
2)单击“新增”,进入创建存储区页面。

云存储
使用云存储服务,您需要启用云存储,并在开发前完成下述操作。
1、启用云存储后,创建存储实例,单击“下一步”。
2、创建安全策略,控制是否允许未经认证的用户访问存储。单击“完成”。

3、完成上述操作后,您即可使用云存储服务。
Duration: 15:00
完成准备工作后,集成认证服务、云数据库、云存储、Network Kit和近距离通信服务到您的应用中。
在已设计好的登录页面添加如下代码,实现华为账号登录按钮。
- activityAuthBinding.tvGetcode.setOnClickListener(this);
- activityAuthBinding.btnHuaweiId.setOnClickListener(this);
- HuaweiIdAuthParamsHelper huaweiIdAuthParamsHelper = new HuaweiIdAuthParamsHelper(HuaweiIdAuthParams.DEFAULT_AUTH_REQUEST_PARAM);
- scopeList = new ArrayList<>();
- scopeList.add(new Scope(HwIDConstant.SCOPE.ACCOUNT_BASEPROFILE));
- scopeList.add(new Scope(HwIDConstant.SCOPE.SCOPE_ACCOUNT_EMAIL));
- scopeList.add(new Scope(HwIDConstant.SCOPE.SCOPE_MOBILE_NUMBER));
- scopeList.add(new Scope(HwIDConstant.SCOPE.SCOPE_ACCOUNT_PROFILE));
- huaweiIdAuthParamsHelper.setScopeList(scopeList);
- HuaweiIdAuthParams authParams = huaweiIdAuthParamsHelper.setAccessToken().setMobileNumber().createParams();
- service = HuaweiIdAuthManager.getService(this, authParams);
-
- Button Click:
- loginViewModel.signInWithHuaweiId(AuthActivity.this, service).observe(AuthActivity.this, new Observer<SignInResult>() {
- @Override
- public void onChanged(SignInResult user) {
- activityAuthBinding.authProgressBar.setVisibility(View.VISIBLE);
- progress();
- loginSuccess (user);
- }
- });
实现OnActivityResult。
- huaweiSignIn.launch(service.getSignInIntent());
- ActivityResultLauncher<Intent> huaweiSignIn = AuthActivity.authActivity.registerForActivityResult(
- new ActivityResultContracts.StartActivityForResult(),
- new ActivityResultCallback<ActivityResult>() {
- @Override
- public void onActivityResult(ActivityResult result) {
-
- if (result.getResultCode() == Activity.RESULT_OK) {
-
- Task<AuthAccount> authAccountTask = AccountAuthManager.parseAuthResultFromIntent(result.getData());
- if (authAccountTask.isSuccessful()) {
- AuthAccount authAccount = authAccountTask.getResult();
-
- AGConnectAuthCredential credential = HwIdAuthProvider.credentialWithToken(authAccount.getAccessToken());
- AGConnectAuth.getInstance().signIn(credential).addOnSuccessListener(new OnSuccessListener<SignInResult>() {
- @Override
- public void onSuccess(SignInResult signInResult) {
- // onSuccess
- Common.showToast("log in", AuthActivity.authActivity);
- authenticatedUserMutableLiveData.setValue(signInResult);
-
- }
- })
- .addOnFailureListener(new OnFailureListener() {
- @Override
- public void onFailure(Exception e) {
- Common.showToast("fail", AuthActivity.authActivity);
- }
- });
- }
- }
- }
- });
创建wrapper类,用于初始化云数据库存储区,插入新用户和验证已知用户等数据库操作。
- public class CloudDBZoneWrapper { private static final String TAG = "CloudDBZoneWrapper"; private static final String DB_NAME = "SplitBillSampleApp"; private AGConnectCloudDB mCloudDB; private CloudDBZone mCloudDBZone; private CloudDBZoneConfig mConfig; /**
- SplitBillApplication
- * Get instance of Cloud DB zone wrapper to initiate cloud DB * * @return mCloudDBZoneWrapper */ public CloudDBZoneWrapper getCloudDBZoneWrapper() { if (mCloudDBZoneWrapper != null) { return mCloudDBZoneWrapper; } mCloudDBZoneWrapper = new CloudDBZoneWrapper(); return mCloudDBZoneWrapper; }
-
- /** * Get CloudDB task to open AGConnectCloudDB */ public Task<CloudDBZone> openCloudDBZoneV2() { mConfig = new CloudDBZoneConfig(DB_NAME, CloudDBZoneConfig.CloudDBZoneSyncProperty.CLOUDDBZONE_CLOUD_CACHE, CloudDBZoneConfig.CloudDBZoneAccessProperty.CLOUDDBZONE_PUBLIC); mConfig.setPersistenceEnabled(true); Task<CloudDBZone> openDBZoneTask = mCloudDB.openCloudDBZone2(mConfig, true); return openDBZoneTask; }
- /**
- * 调用AGConnectCloudDB.closeCloudDBZone
- */
- public void closeCloudDBZone() {
- try {
- mRegister.remove();
- mCloudDB.closeCloudDBZone(mCloudDBZone);
- } catch (AGConnectCloudDBException e) {
- Log.w(TAG, "CloudDBZone: " + e.getMessage());
- }
- }
-
- /**
- * 云数据库中插入好友数据
- */
- public MutableLiveData<Boolean> upsertExpenseData(Friends friends) {
- if (mCloudDBZone == null) {
- Log.w(TAG, "CloudDBZone is null, try re-open it");
- }
-
- Task<Integer> upsertTask = mCloudDBZone.executeUpsert(friends);
- upsertTask.addOnSuccessListener(new OnSuccessListener<Integer>() {
- @Override
- public void onSuccess(Integer cloudDBZoneResult) {
- friendsUpdateSuccess.postValue(true);
- }
- }).addOnFailureListener(new OnFailureListener() {
- @Override
- public void onFailure(Exception e) {
- friendsUpdateSuccess.postValue(false);
- }
- });
- return friendsUpdateSuccess;
- }
- /**
- * 云数据库中获取好友列表
- */
-
- public MutableLiveData<Integer> getFriendsIdLiveData() {
- CloudDBZoneQuery<Friends> snapshotQuery = CloudDBZoneQuery.where(Friends.class);
- Task<Long> countQueryTask = mCloudDBZone.executeCountQuery(snapshotQuery, FriendsEditFields.CONTACT_ID,
- CloudDBZoneQuery.CloudDBZoneQueryPolicy.POLICY_QUERY_FROM_CLOUD_ONLY);
- countQueryTask.addOnSuccessListener(new OnSuccessListener<Long>() {
- @Override
- public void onSuccess(Long aLong) {
- Log.i(TAG, "The total number of groups is " + aLong);
- friendsIdLiveData.postValue((int) (aLong + 1));
- }
- }).addOnFailureListener(new OnFailureListener() {
- @Override
- public void onFailure(Exception e) {
- Log.w(TAG, "Count query is failed: " + Log.getStackTraceString(e));
- }
- });
-
- return friendsIdLiveData;
- }
- /**
- * 云数据库中获取好友列表
-
- * 从数据库中添加,获取群或账单数据使用相似方法。
- */
- private void insertGroupDataInDB(Group group){
- groupViewModel.upsertGroupData(group).observe(getActivity(), new Observer<Boolean>() {
- @Override
- public void onChanged(Boolean aBoolean) {
- if (aBoolean.equals(true)) {
- progressStats = true;
- Toast.makeText(getContext(), getString(R.string.add_group_success) + " " + group.getName(), Toast.LENGTH_LONG).show();
- Navigation.findNavController(fragmentCreateGroupBinding.getRoot()).navigate(R.id.navigation_activity);
- } else {
- Toast.makeText(getContext(), getString(R.string.add_group_failed), Toast.LENGTH_LONG).show();
- progressStats = false;
- }
- }
- });
- }
-
-
-
-
- 添加账单数据:
- private void addExpenseData() {
- Expense expense = new Expense();
- String strExpenseName = expenseName.getText().toString();
- String strPaidBy = spinner.getSelectedItem().toString();
- String strExpenseAmount = expenseDesc.getText().toString();
- expense.setId(0);
- expense.setName(strExpenseName);
-
- expense.setAmount(Float.parseFloat((strExpenseAmount) + ".00"));
- expense.setPaid_user_id(0);
- expense.setStatus(1);
- expenseViewModel.upsertExpenseData(expense).observe(getActivity(), new Observer<Boolean>() {
- @Override
- public void onChanged(Boolean aBoolean) {
- if (aBoolean.equals(true)) {
- Toast.makeText(getContext(), getString(R.string.add_expense_success) + " " + strExpenseName, Toast.LENGTH_LONG).show();
- //TODO : back key
-
- } else {
- Toast.makeText(getContext(), getString(R.string.add_expense_failed), Toast.LENGTH_LONG).show();
- }
- }
- });
- }
数据插入完成后,接着使用近距离通信服务提供的类。
- NearbyAgent:
- /*
- * 华为技术有限公司版权所有 保留一切权利,
- 授权于Apache License 2.0版本(以下简称“许可证”)。
- 使用此许可证时,须遵循其规定。
- 您可以通过以下途径获取许可证的副本:
- http://www.apache.org/licenses/LICENSE-2.0
- 除非得到适用法律或书面同意,
- 许可证许可范围内所提供的软件均按“现状”提供,
- 而不做任何明示或暗示的保证。
- 关于许可证中特定语言的权限和限制,
- 请参见许可证。
- */
-
- package com.huawei.codelabs.splitbill.ui.main.helper;
-
- import android.Manifest;
- import android.app.Dialog;
- import android.content.Context;
- import android.content.Intent;
- import android.graphics.Bitmap;
- import android.graphics.Canvas;
- import android.graphics.Color;
- import android.graphics.Paint;
- import android.graphics.Typeface;
- import android.graphics.pdf.PdfDocument;
- import android.net.Uri;
- import android.os.Environment;
- import android.util.Log;
- import android.view.View;
-
-
- import androidx.core.app.ActivityCompat;
- import androidx.documentfile.provider.DocumentFile;
- import androidx.fragment.app.FragmentActivity;
- import androidx.recyclerview.widget.LinearLayoutManager;
-
- import com.huawei.codelabs.splitbill.R;
- import com.huawei.codelabs.splitbill.databinding.FragmentAccountBinding;
- import com.huawei.codelabs.splitbill.databinding.FragmentFileDetailsBinding;
- import com.huawei.codelabs.splitbill.databinding.FragmentSendExpenseDetailsBinding;
- import com.huawei.codelabs.splitbill.ui.main.activities.MainActivity;
- import com.huawei.codelabs.splitbill.ui.main.adapter.DeviceAdapter;
- import com.huawei.codelabs.splitbill.ui.main.adapter.FilesAdapter;
- import com.huawei.codelabs.splitbill.ui.main.adapter.FriendsListAdapter;
- import com.huawei.codelabs.splitbill.ui.main.models.Device;
- import com.huawei.codelabs.splitbill.ui.main.models.Files;
- import com.huawei.hms.hmsscankit.ScanUtil;
- import com.huawei.hms.hmsscankit.WriterException;
- import com.huawei.hms.ml.scan.HmsBuildBitmapOption;
- import com.huawei.hms.ml.scan.HmsScan;
- import com.huawei.hms.ml.scan.HmsScanAnalyzerOptions;
- import com.huawei.hms.nearby.Nearby;
- import com.huawei.hms.nearby.StatusCode;
- import com.huawei.hms.nearby.discovery.BroadcastOption;
- import com.huawei.hms.nearby.discovery.ConnectCallback;
- import com.huawei.hms.nearby.discovery.ConnectInfo;
- import com.huawei.hms.nearby.discovery.ConnectResult;
- import com.huawei.hms.nearby.discovery.DiscoveryEngine;
- import com.huawei.hms.nearby.discovery.Policy;
- import com.huawei.hms.nearby.discovery.ScanEndpointCallback;
- import com.huawei.hms.nearby.discovery.ScanEndpointInfo;
- import com.huawei.hms.nearby.discovery.ScanOption;
- import com.huawei.hms.nearby.transfer.Data;
- import com.huawei.hms.nearby.transfer.DataCallback;
- import com.huawei.hms.nearby.transfer.TransferEngine;
- import com.huawei.hms.nearby.transfer.TransferStateUpdate;
-
- import java.io.File;
- import java.io.FileNotFoundException;
- import java.io.FileOutputStream;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.OutputStream;
- import java.nio.charset.StandardCharsets;
- import java.util.ArrayList;
- import java.util.List;
-
- import static java.nio.charset.StandardCharsets.UTF_8;
-
- public class NearbyAgent {
- public static final int REQUEST_CODE_SCAN_ONE = 0X01;
- private static final String[] REQUIRED_PERMISSIONS =
- new String[]{Manifest.permission.ACCESS_COARSE_LOCATION,
- Manifest.permission.ACCESS_FINE_LOCATION,
- Manifest.permission.ACCESS_WIFI_STATE,
- Manifest.permission.CHANGE_WIFI_STATE,
- Manifest.permission.BLUETOOTH,
- Manifest.permission.BLUETOOTH_ADMIN,
- Manifest.permission.READ_EXTERNAL_STORAGE,
- Manifest.permission.WRITE_EXTERNAL_STORAGE,
- Manifest.permission.CAMERA};
- private static final int REQUEST_CODE_REQUIRED_PERMISSIONS = 1;
- private final String TAG = "Nearby_Agent";
- private final String mFileServiceId = "NearbyAgentFileService";
- private final String mEndpointName = android.os.Build.DEVICE;
- int lineYAxis = 350;
- FragmentAccountBinding fragmentAccountBinding;
- FragmentSendExpenseDetailsBinding fragmentSendExpenseDetailsBinding;
- FragmentFileDetailsBinding fragmentFileDetailsBinding;
- ArrayList<Files> filesArrayList;
- FilesAdapter groupAdapter;
- private MainActivity mContext = null;
- private TransferEngine mTransferEngine = null;
- private DiscoveryEngine mDiscoveryEngine = null;
- private List<File> mFiles = new ArrayList<>();
- private String mRemoteEndpointId;
- private String mRemoteEndpointName;
- private String mScanInfo;
- private final ScanEndpointCallback mDiscCb =
- new ScanEndpointCallback() {
- @Override
- public void onFound(String endpointId, ScanEndpointInfo discoveryEndpointInfo) {
- if (discoveryEndpointInfo.getName().equals(mScanInfo)) {
-
- Log.d(TAG, "Found endpoint:" + discoveryEndpointInfo.getName() + ". Connecting.");
- mDiscoveryEngine.requestConnect(mEndpointName, endpointId, mConnCbRcver);
- }
- }
-
- @Override
- public void onLost(String endpointId) {
- Log.d(TAG, "Lost endpoint.");
- }
- };
- private String mRcvedFilename = null;
- private Bitmap mResultImage;
- private String mFileName;
- private long mStartTime = 0;
- private float mSpeed = 60;
- private String mSpeedStr = "60";
- private boolean isTransfer = false;
- private final DataCallback mDataCbSender =
- new DataCallback() {
- @Override
- public void onReceived(String endpointId, Data data) {
- if (data.getType() == Data.Type.BYTES) {
- String msg = new String(data.asBytes(), UTF_8);
- if (msg.equals("Receive Success")) {
- Log.d(TAG, "Received ACK. Send next.");
- sendOneFile();
- }
- }
- }
-
- @Override
- public void onTransferUpdate(String string, TransferStateUpdate update) {
- if (update.getStatus() == TransferStateUpdate.Status.TRANSFER_STATE_SUCCESS) {
- } else if (update.getStatus() == TransferStateUpdate.Status.TRANSFER_STATE_IN_PROGRESS) {
- showProgressSpeedSender(update);
- } else if (update.getStatus() == TransferStateUpdate.Status.TRANSFER_STATE_FAILURE) {
- Log.d(TAG, "Transfer failed.");
- } else {
- Log.d(TAG, "Transfer cancelled.");
- }
- }
- };
- private final ConnectCallback mConnCbRcver =
- new ConnectCallback() {
- @Override
- public void onEstablish(String endpointId, ConnectInfo connectionInfo) {
- Log.d(TAG, "Accept connection.");
- mRemoteEndpointName = connectionInfo.getEndpointName();
- mRemoteEndpointId = endpointId;
- mDiscoveryEngine.acceptConnect(endpointId, mDataCbRcver);
- }
-
- @Override
- public void onResult(String endpointId, ConnectResult result) {
- if (result.getStatus().getStatusCode() == StatusCode.STATUS_SUCCESS) {
- Log.d(TAG, "Connection Established. Stop Discovery.");
- mDiscoveryEngine.stopBroadcasting();
- mDiscoveryEngine.stopScan();
- fragmentFileDetailsBinding.tvMainDesc.setText("Connected.");
- }
- }
-
- @Override
- public void onDisconnected(String endpointId) {
- Log.d(TAG, "Disconnected.");
- if (isTransfer == true) {
- fragmentSendExpenseDetailsBinding.tvMainDesc.setVisibility(View.GONE);
- fragmentSendExpenseDetailsBinding.tvMainDesc.setText("Connection lost.");
- }
- }
- };
- private Data incomingFile = null;
- private final DataCallback mDataCbRcver =
- new DataCallback() {
- @Override
- public void onReceived(String endpointId, Data data) {
- if (data.getType() == Data.Type.BYTES) {
- String msg = new String(data.asBytes(), UTF_8);
- mRcvedFilename = msg;
- Log.d(TAG, "received filename: " + mRcvedFilename);
- isTransfer = true;
- fragmentFileDetailsBinding.tvMainDesc.setText(new StringBuilder("Receiving file ").append(mRcvedFilename).append(" from ").append(mRemoteEndpointName + ".").toString());
- fragmentFileDetailsBinding.pbMainDownload.setVisibility(View.VISIBLE);
- } else if (data.getType() == Data.Type.FILE) {
- incomingFile = data;
- } else {
- Log.d(TAG, "received stream. ");
- }
- }
-
- public File getLastModified(String directoryFilePath)
- {
- File directory = new File(directoryFilePath);
- File[] files = directory.listFiles(File::isFile);
- long lastModifiedTime = Long.MIN_VALUE;
- File chosenFile = null;
-
- if (files != null)
- {
- for (File file : files)
- {
- if (file.lastModified() > lastModifiedTime)
- {
- chosenFile = file;
- lastModifiedTime = file.lastModified();
- }
- }
- }
-
- return chosenFile;
- }
- @Override
- public void onTransferUpdate(String string, TransferStateUpdate update) {
- if (update.getStatus() == TransferStateUpdate.Status.TRANSFER_STATE_SUCCESS) {
- } else if (update.getStatus() == TransferStateUpdate.Status.TRANSFER_STATE_IN_PROGRESS) {
- showProgressSpeedReceiver(update);
- if (update.getBytesTransferred() == update.getTotalBytes()) {
- Log.d(TAG, "File transfer done. Rename File.");
- renameFile();
- Log.d(TAG, "Send Ack.");
- fragmentFileDetailsBinding.tvMainDesc.setText(new StringBuilder("Transfer success. Speed: ").append(mSpeedStr).append("MB/s. \nView the File at /Sdcard/Download/Nearby"));
- mTransferEngine.sendData(mRemoteEndpointId, Data.fromBytes("Receive Success".getBytes(StandardCharsets.UTF_8)));
- isTransfer = false;
- Files files = new Files();
- File file= getLastModified(Environment.getExternalStorageDirectory().getPath() + Constants.DOWNLOAD_PATH);
- files.setFileName(file.getName());
- files.setFilePath(new File(file.getAbsolutePath()));
- filesArrayList.add(files);
- groupAdapter.notifyDataSetChanged();
- }
- } else if (update.getStatus() == TransferStateUpdate.Status.TRANSFER_STATE_FAILURE) {
- Log.d(TAG, "Transfer failed.");
- } else {
- Log.d(TAG, "Transfer cancelled.");
- }
- }
- };
- private ArrayList<Device> deviceList;
- private final ConnectCallback mConnCbSender =
- new ConnectCallback() {
- @Override
- public void onEstablish(String endpointId, ConnectInfo connectionInfo) {
- Log.d(TAG, "Accept connection.");
- mDiscoveryEngine.acceptConnect(endpointId, mDataCbSender);
- fragmentSendExpenseDetailsBinding.rcDevice.setHasFixedSize(true);
- fragmentSendExpenseDetailsBinding.rcDevice.setLayoutManager(new LinearLayoutManager(mContext));
- Device device = new Device();
- device.setDeviceName(connectionInfo.getEndpointName());
- deviceList.add(device);
- DeviceAdapter deviceAdapter = new DeviceAdapter(deviceList);
- fragmentSendExpenseDetailsBinding.rcDevice.setAdapter(deviceAdapter);
- deviceAdapter.notifyDataSetChanged();
- mRemoteEndpointName = connectionInfo.getEndpointName();
- mRemoteEndpointId = endpointId;
- }
-
- @Override
- public void onResult(String endpointId, ConnectResult result) {
- if (result.getStatus().getStatusCode() == StatusCode.STATUS_SUCCESS) {
- Log.d(TAG, "Connection Established. Stop discovery. Start to send file.");
- mDiscoveryEngine.stopScan();
- mDiscoveryEngine.stopBroadcasting();
- sendOneFile();
- fragmentSendExpenseDetailsBinding.barcodeImage.setVisibility(View.GONE);
- fragmentSendExpenseDetailsBinding.tvMainDesc.setText(new StringBuilder("MB/s. \nView the File at /Sdcard/Download/Nearby").append(mFileName).append(" to ").append(mRemoteEndpointName).append("."));
- fragmentSendExpenseDetailsBinding.pbMainDownload.setVisibility(View.GONE);
- }
- }
-
- @Override
- public void onDisconnected(String endpointId) {
- Log.d(TAG, "Disconnected.");
- if (isTransfer == true) {
- fragmentSendExpenseDetailsBinding.pbMainDownload.setVisibility(View.GONE);
- fragmentSendExpenseDetailsBinding.tvMainDesc.setText("Connection lost.");
- }
- }
- };
-
- public NearbyAgent(MainActivity context) {
- mContext = context;
- mDiscoveryEngine = Nearby.getDiscoveryEngine(context);
- deviceList = new ArrayList<>();
- mTransferEngine = Nearby.getTransferEngine(context);
- if (context instanceof MainActivity) {
- ActivityCompat.requestPermissions(context, REQUIRED_PERMISSIONS, REQUEST_CODE_REQUIRED_PERMISSIONS);
- }
- }
-
- public NearbyAgent(MainActivity context, FragmentAccountBinding fragmentAccountBinding) {
- this.fragmentAccountBinding = fragmentAccountBinding;
- this.mContext = context;
- mDiscoveryEngine = Nearby.getDiscoveryEngine(context);
- mTransferEngine = Nearby.getTransferEngine(context);
- if (context instanceof MainActivity) {
- ActivityCompat.requestPermissions(context, REQUIRED_PERMISSIONS, REQUEST_CODE_REQUIRED_PERMISSIONS);
- }
-
-
- }
-
- public static String getFileRealNameFromUri(Context context, Uri fileUri) {
- if (context == null || fileUri == null) {
- return Constants.UnknownFile;
- }
- DocumentFile documentFile = DocumentFile.fromSingleUri(context, fileUri);
- if (documentFile == null) {
- return Constants.UnknownFile;
- }
- return documentFile.getName();
- }
-
- private void showProgressSpeedSender(TransferStateUpdate update) {
- long transferredBytes = update.getBytesTransferred();
- long totalBytes = update.getTotalBytes();
- long curTime = System.currentTimeMillis();
- Log.d(TAG, "Transfer in progress. Transferred Bytes: "
- + transferredBytes + " Total Bytes: " + totalBytes);
- fragmentSendExpenseDetailsBinding.pbMainDownload.setProgress((int) (transferredBytes * 100 / totalBytes));
- if (mStartTime == 0) {
- mStartTime = curTime;
- }
- if (curTime != mStartTime) {
- mSpeed = ((float) transferredBytes) / ((float) (curTime - mStartTime)) / 1000;
- java.text.DecimalFormat myformat = new java.text.DecimalFormat("0.00");
- mSpeedStr = myformat.format(mSpeed);
- fragmentSendExpenseDetailsBinding.tvMainDesc.setText(new StringBuilder("Transfer in Progress. Speed: ").append(mSpeedStr).append("MB/s."));
- }
- if (transferredBytes == totalBytes) {
- mStartTime = 0;
- }
- }
-
- public void loadScanCode(Bitmap mResultImage) {
-
- fragmentSendExpenseDetailsBinding.barcodeImage.setVisibility(View.VISIBLE);
- fragmentSendExpenseDetailsBinding.barcodeImage.setImageBitmap(mResultImage);
- }
-
- public File createPdf(List
friendsUIList, Bitmap scaledImageBitmap, FragmentActivity activity ) { - //创建一个新的document。
- PdfDocument document = new PdfDocument();
- //创建页面描述。
- PdfDocument.PageInfo pageInfo = new PdfDocument.PageInfo.Builder(Constants.PAGEWIDTH, Constants.PAGEHEIGHT, 1).create();
- //开启一个页面。
- PdfDocument.Page page = document.startPage(pageInfo);
- Canvas canvas = page.getCanvas();
- Paint paint = new Paint();
- canvas.drawBitmap(scaledImageBitmap, 0, 0, paint);
- paint.setTextAlign(Paint.Align.CENTER);
- paint.setTextSize(50);
- paint.setColor(activity.getResources().getColor(android.R.color.black));
- paint.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.NORMAL));
- canvas.drawText("Invoice" + System.currentTimeMillis(), Constants.PAGEWIDTH / 2, 260, paint);
- paint.setStrokeWidth(2f);
-
- canvas.drawLine(Constants.PAGEWIDTH / 2, 300, 550, 300, paint);
- canvas.drawText("Amount" + " " + "Participants", Constants.PAGEWIDTH / 2, 400, paint);
- lineYAxis = 450;
- for (FriendsListAdapter.FriendsUI friendsUI : friendsUIList) {
- canvas.drawText(friendsUI.getFriendsName() + " " + friendsUI.getAmount() + "", Constants.PAGEWIDTH / 2, Constants.LINEYAXIS + 50, paint);
- lineYAxis = lineYAxis + 50;
- }
-
- document.finishPage(page);
-
- pageInfo = new PdfDocument.PageInfo.Builder(Constants.PAGEWIDTH, Constants.PAGEHEIGHT, 2).create();
- page = document.startPage(pageInfo);
- document.finishPage(page);
- //写入document内容。
- String directory_path = Environment.getExternalStorageDirectory().getPath() + Constants.CREATEINVOICE;
- File file = new File(directory_path);
- if (!file.exists()) {
- file.mkdirs();
- }
- String targetPdf = new StringBuilder().append(directory_path).append("invoice").append(System.currentTimeMillis()).append(".pdf").toString();
- File filePath = new File(targetPdf);
- try {
- document.writeTo(new FileOutputStream(filePath));
-
- } catch (IOException e) {
- Log.e("main", "error " + e.toString());
-
- }
- //关闭document。
- document.close();
- return filePath;
- }
-
- public void sendFile(File file, FragmentSendExpenseDetailsBinding fragmentSendExpenseDetailsBinding) {
- this.fragmentSendExpenseDetailsBinding = fragmentSendExpenseDetailsBinding;
-
- init();
- mFiles.add(file);
- sendFilesInner();
- }
-
- public void sendFiles(List
files ) { - init();
- mFiles = files;
- sendFilesInner();
- }
-
- public void sendFolder(File folder) {
- init();
- File[] subFile = folder.listFiles();
- for (int i = 0; i < subFile.length; i++) {
- if (!subFile[i].isDirectory()) {
- mFiles.add(subFile[i]);
- Log.d(TAG, "Travel folder: " + subFile[i].getName());
- }
- }
- sendFilesInner();
- }
-
- private void sendFilesInner() {
-
- /* 生成bitmap */
- try {
- //生成barcode。
- HmsBuildBitmapOption options = new HmsBuildBitmapOption.Creator().setBitmapMargin(1).setBitmapColor(Color.BLACK).setBitmapBackgroundColor(Color.WHITE).create();
- mResultImage = ScanUtil.buildBitmap(mEndpointName, HmsScan.QRCODE_SCAN_TYPE, Constants.BARCODE_SIZE, Constants.BARCODE_SIZE, options);
- loadScanCode(mResultImage);
- } catch (WriterException e) {
- Log.e(TAG, e.toString());
- }
- /* 开始广播 */
- BroadcastOption.Builder advBuilder = new BroadcastOption.Builder();
- advBuilder.setPolicy(Policy.POLICY_P2P);
- mDiscoveryEngine.startBroadcasting(mEndpointName, mFileServiceId, mConnCbSender, advBuilder.build());
- Log.d(TAG, "Start Broadcasting.");
-
- }
-
- public void receiveFile(FragmentFileDetailsBinding fragmentFileDetailsBinding, ArrayList
filesArrayList, FilesAdapter groupAdapter ) { - /* 扫描bitmap */
- this.fragmentFileDetailsBinding = fragmentFileDetailsBinding;
- this.filesArrayList=filesArrayList;
- this.groupAdapter=groupAdapter;
- Log.d("TAG", "start");
- init();
- HmsScanAnalyzerOptions options = new HmsScanAnalyzerOptions.Creator().setHmsScanTypes(HmsScan.QRCODE_SCAN_TYPE, HmsScan.DATAMATRIX_SCAN_TYPE).create();
- ScanUtil.startScan(mContext, REQUEST_CODE_SCAN_ONE, options);
- Log.d("TAG", "Sent");
- }
-
- public void onScanResult(Intent data) {
- if (data == null) {
- Log.d("TAG", "fail");
- fragmentFileDetailsBinding.tvMainDesc.setText("Scan Failed.");
- return;
- }
- /* 保存设备名称*/
- HmsScan obj = data.getParcelableExtra(ScanUtil.RESULT);
- mScanInfo = obj.getOriginalValue();
- /* 开始扫描*/
- ScanOption.Builder scanBuilder = new ScanOption.Builder();
- scanBuilder.setPolicy(Policy.POLICY_P2P);
- mDiscoveryEngine.startScan(mFileServiceId, mDiscCb, scanBuilder.build());
- Log.d(TAG, "Start Scan.");
- fragmentFileDetailsBinding.tvMainDesc.setText(new StringBuilder().append("Connecting to ").append(mScanInfo).append("..."));
- }
-
- private void sendOneFile() {
- Data filenameMsg = null;
- Data filePayload = null;
- isTransfer = true;
- Log.d(TAG, "Left " + mFiles.size() + " Files to send.");
- if (mFiles.isEmpty()) {
- Log.d(TAG, "All Files Done. Disconnect");
- fragmentSendExpenseDetailsBinding.tvMainDesc.setText(R.string.all_files_sent);
- fragmentSendExpenseDetailsBinding.pbMainDownload.setVisibility(View.GONE);
- fragmentSendExpenseDetailsBinding.tvHeading.setVisibility(View.GONE);
- mDiscoveryEngine.disconnectAll();
- isTransfer = false;
- return;
- }
- try {
- mFileName = mFiles.get(0).getName();
- filePayload = Data.fromFile(mFiles.get(0));
- mFiles.remove(0);
- } catch (FileNotFoundException e) {
- Log.e(TAG, "File not found", e);
- return;
- }
- filenameMsg = Data.fromBytes(mFileName.getBytes(StandardCharsets.UTF_8));
- Log.d(TAG, "Send filename: " + mFileName);
- mTransferEngine.sendData(mRemoteEndpointId, filenameMsg);
- Log.d(TAG, "Send Payload.");
- mTransferEngine.sendData(mRemoteEndpointId, filePayload);
- }
-
- private void renameFile() {
- if (incomingFile == null) {
- Log.d(TAG, "incomingFile is null");
- return;
- }
- File rawFile = incomingFile.asFile().asJavaFile();
- Log.d(TAG, "raw file: " + rawFile.getAbsolutePath());
- File targetFileName = new File(rawFile.getParentFile(), mRcvedFilename);
- Log.d(TAG, "rename to : " + targetFileName.getAbsolutePath());
- Uri uri = incomingFile.asFile().asUri();
- if (uri == null) {
- boolean result = rawFile.renameTo(targetFileName);
- if (!result) {
- Log.e(TAG, "rename failed");
- } else {
- Log.e(TAG, "rename Succeeded ");
- }
- } else {
- try {
- openStream(uri, targetFileName);
- } catch (IOException e) {
- Log.e(TAG, e.toString());
- } finally {
- delFile(uri, rawFile);
- }
- }
- }
-
- private void openStream(Uri uri, File targetFileName) throws IOException {
- InputStream in = mContext.getContentResolver().openInputStream(uri);
- Log.e(TAG, "open input stream successfuly");
- try {
- copyStream(in, new FileOutputStream(targetFileName));
- Log.e(TAG, "copyStream successfuly");
- } catch (IOException e) {
- Log.e(TAG, e.toString());
- } finally {
- in.close();
- }
- }
-
- private void copyStream(InputStream in, OutputStream out) throws IOException {
- try {
- byte[] buffer = new byte[1024];
- int read;
- while ((read = in.read(buffer)) != -1) {
- out.write(buffer, 0, read);
- }
- out.flush();
- } finally {
- out.close();
- }
- }
-
- private void delFile(Uri uri, File payloadfile) {
- //删除源文件。
- mContext.getContentResolver().delete(uri, null, null);
- if (!payloadfile.exists()) {
- Log.e(TAG, "delete original file by uri successfully");
- } else {
- Log.e(TAG, "delete original file by uri failed and try to delete it by File delete");
- payloadfile.delete();
- if (payloadfile.exists()) {
- Log.e(TAG, "fail to delete original file");
- } else {
- Log.e(TAG, "delete original file successfully");
- }
- }
- }
-
- private void showProgressSpeedReceiver(TransferStateUpdate update) {
- long transferredBytes = update.getBytesTransferred();
- long totalBytes = update.getTotalBytes();
- long curTime = System.currentTimeMillis();
- Log.d(TAG, "Transfer in progress. Transferred Bytes: "
- + transferredBytes + " Total Bytes: " + totalBytes);
- fragmentFileDetailsBinding.pbMainDownload.setProgress((int) (transferredBytes * 100 / totalBytes));
- if (mStartTime == 0) {
- mStartTime = curTime;
- }
- if (curTime != mStartTime) {
- mSpeed = ((float) transferredBytes) / ((float) (curTime - mStartTime)) / 1000;
- java.text.DecimalFormat myformat = new java.text.DecimalFormat("0.00");
- mSpeedStr = myformat.format(mSpeed);
- fragmentFileDetailsBinding.tvMainDesc.setText(new StringBuilder().append("Transfer in Progress. Speed: ").append(mSpeedStr).append("MB/s."));
- }
- if (transferredBytes == totalBytes) {
- mStartTime = 0;
- }
- }
-
- private void init() {
- if (fragmentSendExpenseDetailsBinding != null) {
- fragmentSendExpenseDetailsBinding.pbMainDownload.setProgress(0);
- fragmentSendExpenseDetailsBinding.pbMainDownload.setVisibility(View.GONE);
- fragmentSendExpenseDetailsBinding.tvMainDesc.setText("");
- fragmentSendExpenseDetailsBinding.barcodeImage.setVisibility(View.GONE);
- }
- mDiscoveryEngine.disconnectAll();
- mDiscoveryEngine.stopScan();
- mDiscoveryEngine.stopBroadcasting();
- mFiles.clear();
- }
- }
- 初始化用于MainActivity的NearByAgent类:
- nearbyAgent = new NearbyAgent(this);
-
- 在SendExpenseDetailsFragment中发送文件码:
-
- ((MainActivity) getActivity()).nearbyAgent.sendFile(new File(getArguments().getString("mValues")), fragmentSendExpenseDetailsBinding);
-
-
-
- 在FileDetailsFragment中接收文件:
- ((MainActivity) getActivity()).nearbyAgent.receiveFile(fragmentFileDetailsBinding, filesArrayList, groupAdapter);
-
- 在MainActivity的onActivityResult中获取结果。
- @Override
- public void onActivityResult(int requestCode, int resultCode, Intent data) {
- switch (requestCode) {
- case NearbyAgent.REQUEST_CODE_SCAN_ONE:
- Log.d("data:", "1");
- nearbyAgent.onScanResult(data);
- break;
- default:
- break;
- }
- super.onActivityResult(requestCode, resultCode, data);
- }
-
-
- 使用近距离数据通信服务下载数据:
-
-
- /**
- AccountRepository类
- * 初始化Network Kit
- */
- @Override
- protected void initManager() {
- //下载manager。
- downloadManager = new DownloadManager.Builder("downloadManager")
- .build(context);
-
- callback = new FileRequestCallback() {
- @Override
- public GetRequest onStart(GetRequest request) {
- return request;
- }
-
- @Override
- public void onProgress(GetRequest request, Progress progress) {
- Log.i(TAG, "onProgress:" + progress);
- }
-
- @Override
- public void onSuccess(Response
response ) { - String filePath = "";
- if (response.getContent() != null) {
- filePath = response.getContent().getAbsolutePath();
- }
- Log.i(TAG, "onSuccess" + " for " + filePath);
- }
-
- @Override
- public void onException(GetRequest getRequest, NetworkException e, Response
response ) { - if (e instanceof Exception) {
- String errorMsg = "download exception for paused or canceled";
- Log.w(TAG, errorMsg);
- } else {
- String errorMsg = "download exception for request:" + getRequest.getId() +
- "\n\ndetail : " + e.getMessage();
- if (e.getCause() != null) {
- errorMsg += " , cause : " +
- e.getCause().getMessage();
- }
- Log.e(TAG, errorMsg);
- }
- }
- };
- }
-
-
-
- @Override
- public void download() {
- imageDownload(context);
- }
-
- private void imageDownload(Context context) {
- if (downloadManager == null) {
- Log.e(TAG, "can not download without init");
- return;
- }
-
- String downloadFilePath = context.getObbDir().getPath() + File.separator + "acc111.jpg";
- getRequest = DownloadManager.newGetRequestBuilder()
- .filePath(downloadFilePath)
- .url(Common.getUrlRequest())
- .build();
- Result result = downloadManager.start(getRequest, callback);
- checkResult(result);
- }
1、启动Android Studio,点击运行按钮,在手机或模拟器上运行您的应用。点击登录按钮登录您的应用。


2、登录成功后,展示群界面。点击“+“图标新建群。


3、群在创建完成后将被插入到云数据库中,用户进入主界面,显示最新群列表。
4、在列表中点击一个群,打开群详情界面。

5、点击任意一条列表中的账单或收支数据,进入详情界面。


6、点击分享按钮,将账单详情以文件形式发送给附近的好友。该操作使用近距离数据通信服务,无需使用到手机流量和wifi。

7、查看收到的账单文件。

8、点击接收按钮接收文件。

祝贺您,您已成功构建一款SplitBill应用并学会了:
在AppGallery Connect中配置云数据库和云存储。
在Android Studio中集成多个HMS Core服务并构建一款SplitBill应用。
参考如下文档获取更多信息:
点击此处下载源码。
声明:本codelab实现多个HMS Core服务在单个项目中的集成,供您参考。您需要验证确保相关开源代码的安全合法合规。
欲了解更多更全技术文章,欢迎访问https://developer.huawei.com/consumer/cn/forum/?ha_source=zzh