简介

HMS Core Drive SDK(简称Drive)允许您创建使用华为云空间服务的应用程序,华为云空间服务可以为您的应用提供云端存储功能,让用户在使用您开发的应用时产生的文件保存到云盘,也可以下载和同步在云盘中的所有文件,包括照片、视频、以及文档等。同时云盘为各类数据提供了全方位的安全保障,让用户更安全、便捷地管理数据。
当前HUAWEI Drive Kit的核心能力包括文件的上传、下载、搜索、评论和回复,以及云端文件变化查询、推送通知功能等。

您将建立什么

在这个Codelab中,您将创建出一款可以使用华为云空间服务功能的Android应用程序,可以通过华为HMS Core Drive SDK方便快捷管理、编辑云盘上的文件,也可以在云盘上保存照片、绘图、录音、视频和各类设计作品。用户可以在其他智能手机、平板电脑或计算机上使用华为云盘中的文件。

您将会学到什么

硬件要求

软件要求

集成HUAWEI HMS Core能力,需要完成以下准备工作

具体操作,请按照《HUAWEI HMS Core集成准备》中详细说明来完成。

开通相关服务

单击"我的项目 > 项目设置 > API管理",进入服务管理菜单。

打开"Account Kit"和"Drive Kit"服务开关。

添加配置文件

添加当前应用的agconnect-services.json配置文件

  1. 打开华为开发者联盟 AppGallery Connect应用管理中之前创建的应用,并选择"我的项目 > 项目设置 > 常规",单击数据存储位置设置按钮。
  2. 根据应用实际情况,设置对应数据存储位置,单击确认按钮。
  3. 下载应用中的"agconnect-services.json"。
  4. 将下载的"agconnect-services.json"文件移至Android Studio开发工程App的根目录下。

添加SDK依赖

1.打开应用级的build.gradle文件。

2.在"dependencies "中添加如下编译依赖。

dependencies { implementation 'com.huawei.hms:drive:5.0.0.301' implementation 'com.huawei.hms:hwid:4.0.0.300' }

3.重新打开修改完的build.gradle文件,右上方出现Sync Now链接。单击"Sync Now"等待同步完成。

4.多语言设置。

● 如果您的应用不需要设置只支持某些特定语言,则请忽略本步骤。应用将默认支持所有HMS Core SDK支持的语言。

● 如果您的应用需要设置只支持某些特定语言,则可通过本步骤配置。

打开应用级根目录的build.gradle文件。

android > defaultConfig中新增resConfigs配置,en(英语) 和 zh-rCN(简体中文)为必须配置的语种,配置格式如下:

android { defaultConfig { ... resConfigs "en", "zh-rCN", "需要支持的其他语言" } }

配置混淆脚本

开发者编译APK前需要配置不要混淆HMS Core SDK,避免功能异常。

1.打开Android工程的混淆配置文件app/proguard-rules.pro。

2.加入排除HMS SDK的混淆配置。

-ignorewarning -keepattributes *Annotation* -keepattributes Exceptions -keepattributes InnerClasses -keepattributes Signature -keepattributes SourceFile,LineNumberTable -keep class com.hianalytics.android.**{*;} -keep class com.huawei.updatesdk.**{*;} -keep class com.huawei.hms.**{*;} -keep class com.huawei.cloud.services.drive.**{*;} -keep class com.huawei.cloud.base.json.JsonError{*;} -keep class com.huawei.cloud.base.json.JsonErrorContainer{*;}

3.如果开发者使用了AndResGuard,需要在混淆配置文件中加入AndResGuard允许清单。

"R.string.hms*", "R.string.connect_server_fail_prompt_toast", "R.string.getting_message_fail_prompt_toast", "R.string.no_available_network_prompt_toast", "R.string.third_app_*", "R.string.upsdk_*", "R.layout.hms*", "R.layout.upsdk_*", "R.drawable.upsdk*", "R.color.upsdk*", "R.dimen.upsdk*", "R.style.upsdk*", "R.string.agc*"

本节所示代码包含一些未定义的全局变量和方法,您可以下载样例代码查看其含义。

登录华为帐号

处理LOGIN按钮事件

通过HMS Core SDK登录时,需要设置Drive Scope,用于获取访问Drive接口的权限:

private void driveLogin() { AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES); List<Scope> scopeList = new ArrayList<>(); scopeList.add(new Scope(DriveScopes.SCOPE_DRIVE)); //所有权限,除了应用文件夹权限 scopeList.add(new Scope(DriveScopes.SCOPE_DRIVE_READONLY)); // 只允许查看文件内容和元数据 scopeList.add(new Scope(DriveScopes.SCOPE_DRIVE_FILE)); //允许查看和管理文件 scopeList.add(new Scope(DriveScopes.SCOPE_DRIVE_METADATA)); //允许查看和管理文件元数据,但不包括文件实体 scopeList.add(new Scope(DriveScopes.SCOPE_DRIVE_METADATA_READONLY)); //只允许查看文件元数据,但不包括文件实体 scopeList.add(new Scope(DriveScopes.SCOPE_DRIVE_APPDATA)); //支持APP数据上传和存储 scopeList.add(HuaweiIdAuthAPIManager.HUAWEIID_BASE_SCOPE); // 帐号基本权限 HuaweiIdAuthParams authParams = new HuaweiIdAuthParamsHelper(DEFAULT_AUTH_REQUEST_PARAM) .setAccessToken() .setIdToken() .setScopeList(scopeList) .createParams(); // 调用帐号接口, 获取信息 HuaweiIdAuthService client = HuaweiIdAuthManager.getService(this, authParams); startActivityForResult(client.getSignInIntent(), REQUEST_SIGN_IN_LOGIN); }

不同的DriveScope对应不同的权限, 第三方开发者可以根据自己的业务需要申请对应权限,API的使用请参见《华为帐号服务开发者文档》。如果用户未登录华为帐号,HMS Core SDK会引导用户登录。

private DriveCredential.AccessMethod refreshAT = new DriveCredential.AccessMethod() { // 此处做简单处理,正式使用请参考华为云空间服务开发者指南-客户端开发-存储鉴权信息章节 @Override public String refreshToken() { return accessToken; } }; // 帐号信息获取时的异常流程, 在该函数获取并保存拿到相关的accessToken和unionID @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); Log.i(TAG, "onActivityResult, requestCode = " + requestCode + ", resultCode = " + resultCode); if (requestCode == REQUEST_SIGN_IN_LOGIN) { Task<AuthHuaweiId> authHuaweiIdTask = HuaweiIdAuthManager. parseAuthResultFromIntent(data); if (authHuaweiIdTask.isSuccessful()) { AuthHuaweiId huaweiAccount = authHuaweiIdTask.getResult(); accessToken = huaweiAccount.getAccessToken(); unionId = huaweiAccount.getUnionId(); int returnCode = init(unionId, accessToken, refreshAT); if (DriveCode.SUCCESS == returnCode) { showTips("login ok"); } else if (DriveCode.SERVICE_URL_NOT_ENABLED == returnCode) { showTips("drive is not enabled"); } else { showTips("login error"); } } else { Log.d(TAG, "onActivityResult, signIn failed: " + ((ApiException) authHuaweiIdTask.getException()).getStatusCode()); Toast.makeText(getApplicationContext(), "onActivityResult, signIn failed.", Toast.LENGTH_LONG).show(); } } } /** * 通过上下文context和华为账号信息(unionId,countrycode,accessToken)初始化drive。 * 当accessToken失效时,注册一个AccessMethod去获取一个新的accessToken。 * * @param unionID unionID from HwID * @param at access token * @param refreshAT a callback to refresh AT */ public int init(String unionID, String at, DriveCredential.AccessMethod refreshAT) { if (StringUtils.isNullOrEmpty(unionID) || StringUtils.isNullOrEmpty(at)) { return DriveCode.ERROR; } DriveCredential.Builder builder = new DriveCredential.Builder(unionID, refreshAT); mCredential = builder.build().setAccessToken(at); return DriveCode.SUCCESS; }

调用Files.create接口创建目录及上传文件

接口说明

接口名:

public Create create(File content) throws java.io.IOException public Create create(File content, AbstractInputStreamContent mediaContent) throws java.io.IOException

接口描述:
用于创建文件和文件夹, 具体接口使用请参见《华为云空间服务开发者文档》

设置读写权限

app/src/main/AndroidManifest.xml中添加手机存储的读写权限。

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_MEDIA_STORAGE" tools:ignore="ProtectedPermissions" />

在MainActivity.java 中添加权限。

private static String[] PERMISSIONS_STORAGE = { Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA};

在onCreate函数中添加申请权限的代码片段。

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { requestPermissions(PERMISSIONS_STORAGE, 1); }

处理CREATE FOLDER AND UPLOAD FILE按钮事件

该事件会在用户云盘根目录创建一个新目录文件夹, 并将输入框中指定目录下的文件上传至该新建目录。注意,代码中含有一些全局变量与函数,具体含义可以下载示例代码后查看。

private void uploadFiles() { new Thread(new Runnable() { @Override public void run() { try { if (accessToken == null) { showTips("please click 'Login'."); return; } if (StringUtils.isNullOrEmpty(uploadFileName.getText().toString())) { showTips("please input upload file name above."); return; } java.io.File fileObject = new java.io.File("/sdcard/" + uploadFileName.getText()); if (!fileObject.exists()) { showTips("the input file does not exist."); return; } Drive drive = buildDrive(); Map<String, String> appProperties = new HashMap<>(); appProperties.put("appProperties", "property"); // 创建文件夹 File file = new File(); file.setFileName("somepath" + System.currentTimeMillis()) .setMimeType("application/vnd.huawei-apps.folder") .setAppSettings(appProperties); if (isApplicationData.isEnabled()) { file.setParentFolder(Collections.singletonList("applicationData")); } directoryCreated = drive.files().create(file).execute(); // 上传文件 File content = new File() .setFileName(fileObject.getName()) .setMimeType(mimeType(fileObject)) .setParentFolder(Collections.singletonList(directoryCreated.getId())); drive.files() .create(content, new FileContent("image/jpeg", fileObject)) .setFields("*") .execute(); showTips("upload success"); } catch (Exception ex) { Log.d(TAG, "upload error " + ex.toString()); showTips("upload error " + ex.toString()); } } }).start(); } private Drive buildDrive() { Drive service = new Drive.Builder(mCredential, this).build(); return service; } private String mimeType(java.io.File file) { if (file != null && file.exists() && file.getName().contains(".")) { String fileName = file.getName(); String suffix = fileName.substring(fileName.lastIndexOf(".")); if (MIME_TYPE_MAP.keySet().contains(suffix)) { return MIME_TYPE_MAP.get(suffix); } } return "*/*"; }

创建成功后可以通过文件管理登录云盘查看新上传的文件。

如果勾选了applicationData 则会在应用程序数据文件夹进行相应操作。应用程序数据文件夹对普通用户是不可见的,适合存储应用程序特定的数据。

调用Files.list接口根据文件名称查询文件详细信息

接口说明

public List list() throws java.io.IOException

处理QUERY FILE按钮事件

在输入框中输入文件的名称即可查询该文件的详细信息。如果勾选了applicationData 则会在应用程序数据文件夹进行查询操作。

private void queryFiles() { new Thread(new Runnable() { @Override public void run() { try { if (accessToken == null) { showTips("please click 'Login'."); return; } String containers = ""; String queryFile = "fileName = '" + searchFileName.getText() + "' and mimeType != 'application/vnd.huawei-apps.folder'"; if (isApplicationData.isChecked()) { containers = "applicationData"; queryFile = "'applicationData' in parentFolder and ".concat(queryFile); } Drive drive = buildDrive(); Drive.Files.List request = drive.files().list(); FileList files; while (true) { files = request .setQueryParam(queryFile) .setPageSize(10) .setOrderBy("fileName") .setFields("category,nextCursor,files(id,fileName,size)") .setContainers(containers) .execute(); if (files == null || files.getFiles().size() > 0) { break; } if (!StringUtils.isNullOrEmpty(files.getNextCursor())) { request.setCursor(files.getNextCursor()); } else { break; } } String text = ""; if (files != null && files.getFiles().size() > 0) { fileSearched = files.getFiles().get(0); text = fileSearched.toString(); } else { text = "empty"; } final String finalText = text; MainActivity.this.runOnUiThread(new Runnable() { @Override public void run() { queryResult.setText(finalText); } }); showTips("query ok"); } catch (Exception ex) { Log.d(TAG, "query error " + ex.toString()); showTips("query error " + ex.toString()); } } }).start(); }

本Demo中展示的文件详细信息包括文件名称(fileName),文件ID(id),文件大小(size)。如果存在同名文件,本Demo中只展示其中一个文件的详情。文件拥有的详细信息请参见《华为云空间服务API参考》

调用Files.Get接口下载文件

接口说明

public Get get(String fileId) throws java.io.IOException

处理DOWNLOAD THE FILE按钮事件

单击DOWNLOAD THE FILE按钮,即可下载QUERY FILE中查询的文件。

private void downloadFiles() { new Thread(new Runnable() { @Override public void run() { try { if (accessToken == null) { showTips("please click 'Login'."); return; } if (fileSearched == null) { showTips("please click 'QUERY FILE'."); return; } Drive drive = buildDrive(); File content = new File(); Drive.Files.Get request = drive.files().get(fileSearched.getId()); content.setFileName(fileSearched.getFileName()) .setId(fileSearched.getId()); MediaHttpDownloader downloader = request.getMediaHttpDownloader(); downloader.setContentRange(0, fileSearched.getSize() - 1); String filePath = "/storage/emulated/0/Huawei/Drive/DownLoad/Demo_" + fileSearched.getFileName(); request.executeContentAndDownloadTo(new FileOutputStream(new java.io.File(filePath))); showTips("download to " + filePath); } catch (Exception ex) { Log.d(TAG, "download error " + ex.toString()); showTips("download error " + ex.toString()); } } }).start(); }

文件下载成功后,可以在内部存储/Huawei/Drive/DownLoad目录下查看该下载文件。

调用Comments.Create接口评论文件

接口说明

public Create create(String fileId, Comment content) throws java.io.IOException

处理COMMENT THE FILE按钮事件

在Comment输入框中输入评论内容,即可评论QUERY FILE中查询的文件。

private void createComment() { new Thread(new Runnable() { @Override public void run() { try { if (accessToken == null) { showTips("please click 'Login'."); return; } if (fileSearched == null) { showTips("please click 'QUERY FILE'."); return; } if (StringUtils.isNullOrEmpty(commentText.getText().toString())) { showTips("please input comment above."); return; } Drive drive = buildDrive(); Comment comment = new Comment(); comment.setDescription(commentText.getText().toString()); mComment = drive.comments() .create(fileSearched.getId(), comment) .setFields("*") .execute(); if (mComment != null && mComment.getId() != null) { Log.i(TAG, "Add comment success"); showTips("Add comment success"); } else { Log.e(TAG, "Add comment failed"); showTips("Add comment failed"); } } catch (Exception ex) { Log.d(TAG, "Add comment error " + ex.toString()); showTips("Add comment error"); } } }).start(); }

评论成功之后,可以通过QUERY COMMENTS查看该文件的所有评论。

调用Comments.List接口查看文件的评论

接口说明

public List list(String fileId) throws java.io.IOException

处理QUERY COMMENTS按钮事件

通过该事件,即可查看QUERY FILE中查询文件的所有评论。

private void queryComment() { new Thread(new Runnable() { @Override public void run() { try { if (accessToken == null) { showTips("please click 'Login'."); return; } if (fileSearched == null) { showTips("please click 'QUERY FILE'."); return; } Drive drive = buildDrive(); CommentList response= drive.comments() .list(fileSearched.getId()) .setFields("comments(id,description,replies(description))") .execute(); final String text = response.getComments().toString(); MainActivity.this.runOnUiThread(new Runnable() { @Override public void run() { commentList.setText(text); } }); } catch (Exception ex) { Log.d(TAG, "query comments error " + ex.toString()); showTips("query comments error"); } } }).start(); }

查询得到的该文件评论如下图展示,本Demo中展示的评论信息包括评论内容(description),评论ID(id),回复内容(replies/description)。

调用Replies.Create接口回复评论

接口说明

public Create create(String fileId, String commentId, Reply content) throws java.io.IOException

处理REPLY THE COMMENT按钮事件

在Reply输入框中输入回复内容,即可回复COMMENT THE FILE中创建的评论。

private void createReply() { new Thread(new Runnable() { @Override public void run() { try { if (accessToken == null) { showTips("please click 'Login'."); return; } if (fileSearched == null) { showTips("please click 'QUERY FILE'."); return; } if (mComment == null) { showTips("please click 'COMMENT THE FILE'."); return; } if (StringUtils.isNullOrEmpty(replyText.getText().toString())) { showTips("please input comment above."); return; } Drive drive = buildDrive(); Reply reply = new Reply(); reply.setDescription(replyText.getText().toString()); mReply = drive.replies() .create(fileSearched.getId(), mComment.getId(), reply) .setFields("*") .execute(); if (mReply != null && mReply.getId() != null) { Log.i(TAG, "Add reply success"); showTips("Add reply success"); } else { Log.e(TAG, "Add reply failed"); showTips("Add reply failed"); } } catch (Exception ex) { Log.d(TAG, "Add reply", ex); showTips("Add reply error"); } } }).start(); }

回复成功之后,可以通过QUERY REPLIES查看该评论的所有回复。

调用Replies.List接口查看评论的回复

接口说明

public List list(String fileId, String commentId) throws java.io.IOException

处理QUERY REPLIES按钮事件

通过该事件,即可查看COMMENT THE FILE中创建的评论的所有回复。

private void queryReply() { new Thread(new Runnable() { @Override public void run() { try { if (accessToken == null) { showTips("please click 'Login'."); return; } if (fileSearched == null) { showTips("please click 'QUERY FILE'."); return; } if (mComment == null) { showTips("please click 'COMMENT THE FILE'."); return; } Drive drive = buildDrive(); ReplyList response = drive.replies() .list(fileSearched.getId(), mComment.getId()) .setFields("replies(id,description)") .execute(); final String text = response.getReplies().toString(); MainActivity.this.runOnUiThread(new Runnable() { @Override public void run() { replyList.setText(text); } }); } catch (Exception ex) { Log.d(TAG, "query Reply error " + ex.toString()); showTips("query reply error"); } } }).start(); }

查询得到的该评论的回复如下图展示,本Demo中展示的回复信息包括回复内容(description),回复ID(id)。

调用HistoryVersion.List接口查看文件的历史版本

接口说明

public List list(String fileId) throws java.io.IOException

处理LIST HISTORYVERSION按钮事件

通过该事件,即可查看QUERY FILE中查询文件的所有历史版本。

private void queryHistoryVersion() { new Thread(new Runnable() { @Override public void run() { try { if (accessToken == null) { showTips("please click 'Login'."); return; } if (fileSearched == null) { showTips("please click 'QUERY FILE'."); return; } Drive drive = buildDrive(); HistoryVersionList response = drive.historyVersions() .list(fileSearched.getId()) .setFields("historyVersions(id,sha256)") .execute(); final String text = response.getHistoryVersions().toString(); MainActivity.this.runOnUiThread(new Runnable() { @Override public void run() { historyVersionList.setText(text); } }); } catch (Exception ex) { Log.d(TAG, "query historyVersion error " + ex.toString()); showTips("query historyVersion error"); } } }).start(); }

查询得到的该文件的历史版本信息如下图展示,本Demo中展示的文件历史版本信息包括版本ID(id),版本sha256(sha256)。

Drive服务示例代码运行效果

在Android Studio中,单击"运行"。然后,选择您的设备作为目标,单击"确定"以在设备上启动示例应用程序。程序运行界面如下:

下载Drive服务示例代码

您可以直接下载Drive服务示例代码,进行Drive服务开发体验。

源码下载

祝贺您,您已经成功地构建了您的第一个Drive服务应用程序!

您学习了如何使用Drive SDK的相关知识,并学习了如何创建属于您自己的Drive服务应用程序。您学习了如何在Android Studio中运行Drive Demo程序。

现在,您知道了创建华为云空间服务应用的所有关键操作。

接下来您可以

看看这些代码片段...
com/huawei/www/driveapplication/MainActivity.java

您可以阅读下面链接,了解更多相关的信息。

相关文档

挑战赛模板

已复制代码