首页 > 其他分享 >【HMS Core】School Diary应用集成多个HMS Core服务,更好的体验华为生态系统

【HMS Core】School Diary应用集成多个HMS Core服务,更好的体验华为生态系统

时间:2022-11-23 09:45:43浏览次数:69  
标签:Core School String void HMS tag query new public

​一、介绍

总览

通过建立本次的School Diary应用,您可以更好地体验华为生态系统的组成部分,包括认证服务、云存储和云数据库等Serverless服务。此外您还可以了解到如何使用账号服务集成应用登录功能。老师和学生两种角色的匹配过程是本应用的一大特色。该过程涉及二维码扫描、用户头像保存以及在数据库中匹配细节等环节。

您将建立什么

在本次Codelab中,您将建立一个集成认证服务、统一扫码服务、云数据库和云存储等服务接口的School Diary项目,创建一款端到端的用于为学生和老师处理学校作业任务的应用。老师们可以创建、分发、批改和关闭作业任务。学生们可以查看任务,并上传图片提交作业。

您将学会什么

  • 使用认证服务登录应用。

  • 扫描二维码并匹配老师和学生。

  • 使用云数据库操作让老师们能够创建、分发、批改和关闭作业任务。

  • 使用云存储服务让学生们能够上传和更新作业图片。

 

二、您需要什么

Duration: 2:00

开发环境

  • 一部安装Window10操作系统的台式电脑或笔记本。

  • 一部装有HMS Core (APK) 5.0.0.300或以上版本的华为手机。

  • 已通过验证华为账号

 

三、能力接入准备

Duration: 10:00

在接入所需的SDK前,您需要完成以下准备:

  • 在AppGallery Connect上创建一个应用。

  • 创建一个安卓项目。

  • 生成签名证书。

  • 生成签名证书指纹。

  • 配置指纹。

  • 添加应用包名并保存配置文件。

  • 在项目级build.gradle文件中添加AppGallery Connect插件和Maven仓。

  • 在Android Studio中配置签名证书。

详情请参见AppGallery Connect上开通API服务

您需要首先注册成为一名开发者才能进行以上操作。

 

四、开通服务

Duration: 4:00

在接入相关SDK前,您需要在AppGallery Connect控制台上开启所需权限。操作如下:

1、登录AppGallery Connect 点击“项目设置”中“API管理”页签,开通如下服务的API权限。

  • 认证服务(华为账号认证方式)

  • 云数据库

  • 云存储

  • 统一扫码服务

cke_53356.png

说明:以上API权限默认已开通。如未开通,请手动开通。

2、在弹出页面设置数据处理位置。

cke_36635.png

五、集成服务

Duration: 4:00

您需要集成云数据库SDK到您的Android Studio项目中。

1、登录AppGallery Connect并点击“我的项目”。

2、选择项目,在应用下拉列表中选择需要集成SDK的应用。

3、选择“项目设置”,进入“常规”页面。在“应用”区域,点击下载“agconnect-services.json”文件。

cke_50925.png

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

cke_59966.png

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

cke_67727.png

cke_70388.png

6、在buildscript > dependencies中配置AppGallery Connect插件地址。

buildscript { 
    dependencies { 
        classpath 'com.huawei.agconnect:agcp:<version>'
    } 
}

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:<version>'
implementation 'com.huawei.hms:hwid:<version>'
implementation 'com.huawei.hms:scan:<version>'
implementation "com.huawei.agconnect:agconnect-storage:<version>"
 implementation 'com.huawei.agconnect:agconnect-cloud-database:<version>'

六、设计UI

Duration: 5:00

为您的应用设计如下UI。

  • 老师和学生的登录界面UI。

  • 老师和学生的匹配界面UI。

  • 老师和学生的作业管理界面UI。

学生登录界面UI

cke_156281.pngcke_159195.png

cke_164423.pngcke_167964.pngcke_186271.pngcke_180705.pngcke_261274.pngcke_206018.pngcke_210032.png

老师登录界面UI

cke_278723.pngcke_282548.pngcke_287492.png

cke_304939.pngcke_297907.pngcke_309734.png

 

cke_315202.png

 

七、前提准备

Duration: 5:00

认证服务

本次将使用华为账号登录方式。因此,您需要在AppGallery Connect上开启认证服务的华为账号认证方式。否则,登录将失败。

  1. 登录AppGallery Connect并点击“我的项目”。

  2. 找到并点击项目。

  3. 点击“构建”>“认证服务”。如果您首次使用认证服务,请点击“立即开通”。

  4. 选择“认证方式”页签,在“操作”列中选择“华为账号”。

cke_353127.png

云数据库

操作云数据,需要您先开通该服务,然后创建存储区和有着所需字段的云数据库对象。

1、登录AppGallery Connect并点击“我的项目”。

2、点击您的项目。

3、选择“构建”>“云数据库”。如果您首次使用云数据库,请点击“立即开通”。

4、在弹出的页面设置数据处理位置。

5、点击“新增”打开“新增对象类型”页面。

cke_369547.png

6、设置“对象类型名”为“TaskItem”,点击“下一步”。

7、单击“新增字段”添加如下字段,点击“下一步”。

字段

类型

主键

非空

加密

默认值

TaskID

String

TaskName

String

TaskDescription

String

CreatedDate

Date

DueDate

Date

CreadtedBy

String

StudentID

String

Status

Integer

SubmittedDate

Date

group_id

String

AttachmentUrl

Text

8、单击“下一步”,添加索引。

9、设置角色和对应权限。

cke_455576.png

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

11、按照上述操作添加Loginmapping和UserData对象类型。

Loginmapping

字段

类型

主键

非空

加密

默认值

StudentID

String

 

 

StudentName

String

 

 

 

 

StudentEmail

String

 

 

 

 

TeacherID

String

 

 

TeacherName

String

 

 

 

 

TeacherEmail

String

 

 

UserType

Integer

 

 

 

 

MappedDate

Date

 

 

 

 

 

UserData

 

字段

类型

主键

非空

加密

默认值

UserID

String

 

 

 

 

UserType

String

 

 

 

 

UserName

String

 

 

 

 

TeacherId

String

 

 

 

 

 

12、单击“导出”。

cke_565871.png

13、选择导出文件格式。此处选择“java格式”,选择java文件类型为“android”,输入包名称,单击“确定”。对象类型文件会以zip形式导出至本地。

14、提取压缩包中的文件至项目的model包里。

cke_642919.png

15、选择“存储区”页签。

16、单击“新增”,进入创建存储区页面。

cke_693846.png

云存储

使用云存储服务,您需要首先启用它,并在开始编程前完成下述步骤。

1、云存储开通后,创建一个存储实例并赋名。单击“下一步”。

cke_729598.png

2、制定安全策略来设置用户是否需要经过验证才能访问存储。

cke_769420.png

3、完成上述步骤后您就可以使用云存储服务了。

统一扫码服务

统一扫码服务属于HMS Core服务,您无需在AppGallery Connect上进行配置。

八、实现功能

Duration: 15:00

完成前提准备后,在您的应用中使用认证服务、云数据库、云存储和统一扫码服务。

1、前往登录界面,输入如下代码实现带有华为账号登录按钮的登录功能。

binding.loginButton.setOnClickListener(view -> {
     showProgressDialog("Login..." );
     HuaweiIdAuthParamsHelper huaweiIdAuthParamsHelper = new HuaweiIdAuthParamsHelper(HuaweiIdAuthParams.DEFAULT_AUTH_REQUEST_PARAM);
     List<Scope> scopeList = new ArrayList<>();
     scopeList.add(new Scope(HwIDConstant.SCOPE.ACCOUNT_BASEPROFILE));
     huaweiIdAuthParamsHelper.setScopeList(scopeList);
     HuaweiIdAuthParams authParams = huaweiIdAuthParamsHelper.setAccessToken().createParams();
     HuaweiIdAuthService service = HuaweiIdAuthManager.getService(LoginActivity.this, authParams);
     startActivityForResult(service.getSignInIntent(), REQUEST_CODE_SIGN_IN);
 });

2、实现onActivityResult。

@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
     super.onActivityResult(requestCode, resultCode, data);
     if (requestCode == REQUEST_CODE_SIGN_IN) {
         Task<AuthHuaweiId> authHuaweiIdTask = HuaweiIdAuthManager.parseAuthResultFromIntent(data);
         if (authHuaweiIdTask.isSuccessful()) {
             AuthHuaweiId huaweiAccount = authHuaweiIdTask.getResult();
             AGConnectAuthCredential credential = HwIdAuthProvider.credentialWithToken(huaweiAccount.getAccessToken());
             AGConnectAuth.getInstance()
                     .signIn(credential)
                     .addOnSuccessListener(signInResult -> {
                         hideDialog();
                         AGConnectUser user = signInResult.getUser();
                         validateLogin();
                     })
                     .addOnFailureListener(e -> {
                         hideDialog();
                         Log.e(getString(R.string.SIGN_IN_FAILED_TAG), e.getLocalizedMessage());
                     });

         } else {
             Log.e(getString(R.string.SIGN_IN_FAILED_TAG), getString(R.string.SIGN_IN_FAILED_MSG));
             hideDialog();
         }
     }
 }

3、创建wrapper类用于云数据库存储区初始化和操作对象,例如注入新用户和认证存在用户等。

public class CloudDBZoneWrapper {

     private static final String TAG = "CloudDBZoneWrapper";
     private static final String CLOUD_DB_NAME = "SchoolDB";
     private AGConnectCloudDB mCloudDB;
     private CloudDBZone mCloudDBZone;
     private CloudDBZoneConfig mConfig;
     private UiTaskCallBack mUiTaskCallBack = UiTaskCallBack.DEFAULT;
     private UiStudentCallBack mUiStudentCallBack = UiStudentCallBack.DEFAULT;

     public CloudDBZoneWrapper() {
         SchoolDiaryApplication.setRegionRoutePolicy(
                 AGConnectInstance.getInstance().getOptions().getRoutePolicy());
         mCloudDB = AGConnectCloudDB.getInstance();
     }

     /**
      *设置存储位置
      */
     public void setStorageLocation(Context context) {
         if (mCloudDBZone != null) {
             closeCloudDBZone();
         }
         AGConnectOptionsBuilder builder = new AGConnectOptionsBuilder()
                 .setRoutePolicy(SchoolDiaryApplication.getRegionRoutePolicy());
         AGConnectInstance instance = AGConnectInstance.buildInstance(builder.build(context));
         mCloudDB = AGConnectCloudDB.getInstance(instance, AGConnectAuth.getInstance());
     }

     /**
      *在Application中初始化AGConnectCloudDB
      * @param context application context
      */
     public static void initAGConnectCloudDB(Context context) {
         AGConnectCloudDB.initialize(context);
     }

     /**
      *调用AGConnectCloudDB.createObjectType初始化schema
      */
     public void createObjectType() {
         try {
             mCloudDB.createObjectType(ObjectTypeInfoHelper.getObjectTypeInfo());
         } catch (AGConnectCloudDBException e) {
             Log.w(TAG, "createObjectType: " + e.getMessage());
         }
     }

     /**
      * 打开存储区
      */
     public void openCloudDBZoneV2(DBZoneListener listener) {
         mConfig = new CloudDBZoneConfig(CLOUD_DB_NAME, CloudDBZoneConfig.CloudDBZoneSyncProperty.CLOUDDBZONE_CLOUD_CACHE,
                 CloudDBZoneConfig.CloudDBZoneAccessProperty.CLOUDDBZONE_PUBLIC);
         mConfig.setPersistenceEnabled(true);
         Task<CloudDBZone> openDBZoneTask = mCloudDB.openCloudDBZone2(mConfig, true);
         openDBZoneTask.addOnSuccessListener(cloudDBZone -> {
             if (null != listener) {
                 listener.getCloudDbZone(cloudDBZone);
             }
             mCloudDBZone = cloudDBZone;
         }).addOnFailureListener(e -> Log.w(TAG, "Open cloudDBZone failed for " + e.getMessage()));
     }

     /**
      *调用AGConnectCloudDB.closeCloudDBZone接口
      */
     public void closeCloudDBZone() {
         try {
             mCloudDB.closeCloudDBZone(mCloudDBZone);
         } catch (AGConnectCloudDBException e) {
             Log.w(TAG, "closeCloudDBZone: " + e.getMessage());
         }
     }

     /**
      * 添加更新任务列表的回调
      * @param uiTaskCallBack 更新任务列表的回调
      */
     public void addTaskCallBacks(UiTaskCallBack uiTaskCallBack) {
         this.mUiTaskCallBack = uiTaskCallBack;
     }

     /**
      * 添加更新用户列表的回调
      */
     public void addStudentCallBacks(UiStudentCallBack uiStudentCallBack) {
         this.mUiStudentCallBack = uiStudentCallBack;
     }


     /**
      *查询TaskItems
      * @param query 查询条件
      */
     public void queryTasks(CloudDBZoneQuery<TaskItem> query, int tag) {
         if (mCloudDBZone == null) {
             Log.w(TAG, "CloudDBZone is null, try re-open it");
             return;
         }
         Task<CloudDBZoneSnapshot<TaskItem>> queryTask = mCloudDBZone.executeQuery(query,
                 CloudDBZoneQuery.CloudDBZoneQueryPolicy.POLICY_QUERY_FROM_CLOUD_ONLY);
         queryTask
                 .addOnSuccessListener(snapshot -> processQueryResult(snapshot, tag))
                 .addOnFailureListener(e -> mUiTaskCallBack.updateUiOnError("DB Query Error, Something went wrong!"));
     }

     /**
      *查询UserData及状态
      * @param query 查询条件
      */
     public void queryUserData(CloudDBZoneQuery<UserData> query, int tag) {
         if (mCloudDBZone == null) {
             Log.w(TAG, "CloudDBZone is null, try re-open it");
             return;
         }
         Task<CloudDBZoneSnapshot<UserData>> queryTask = mCloudDBZone.executeQuery(query,
                 CloudDBZoneQuery.CloudDBZoneQueryPolicy.POLICY_QUERY_FROM_CLOUD_ONLY);
         queryTask.addOnSuccessListener(snapshot -> processUsersListResult(snapshot, tag))
                 .addOnFailureListener(e -> mUiStudentCallBack.updateStudentUiOnError("DB Query Error, Something went wrong!"));
     }

     /**
      *处理UserData和获取查询到的结果
      */
     private void processUsersListResult(CloudDBZoneSnapshot<UserData> snapshot, int tag) {
         CloudDBZoneObjectList<UserData> taskItemCursor = snapshot.getSnapshotObjects();
         List<UserData> studentItemList = new ArrayList<>();
         try {
             while (taskItemCursor.hasNext()) {
                 UserData studentItem = taskItemCursor.next();
                 studentItemList.add(studentItem);
             }
         } catch (AGConnectCloudDBException e) {
             mUiTaskCallBack.updateUiOnError("DB Upsert Error, Something went wrong!");
         } finally {
             snapshot.release();
         }
         mUiStudentCallBack.onStudentAddOrQuery(studentItemList, tag);
     }

     /**
      *处理TaskItem和获取查询到的结果
      */
     private void processQueryResult(CloudDBZoneSnapshot<TaskItem> snapshot, int tag) {
         CloudDBZoneObjectList<TaskItem> taskItemCursor = snapshot.getSnapshotObjects();
         List<TaskItem> taskItemList = new ArrayList<>();
         try {
             while (taskItemCursor.hasNext()) {
                 TaskItem taskItem = taskItemCursor.next();
                 taskItemList.add(taskItem);
             }
         } catch (AGConnectCloudDBException e) {
             mUiTaskCallBack.updateUiOnError("DB Upsert Error, Something went wrong!");
         } finally {
             snapshot.release();
         }
         mUiTaskCallBack.onAddOrQuery(taskItemList, tag);
     }

     /**
      *向上插入单个TaskItem
      * @param taskItem 本地添加或修改的TaskItem
      */
     public void upsertTaskItem(TaskItem taskItem, int tag) {
         if (mCloudDBZone == null) {
             Log.w(TAG, "CloudDBZone is null, try re-open it");
             return;
         }
         Task<Integer> upsertTask = mCloudDBZone.executeUpsert(taskItem);
         upsertTask.addOnSuccessListener(cloudDBZoneResult -> {
             mUiTaskCallBack.onRefresh(tag);
         }).addOnFailureListener(e -> {
             mUiTaskCallBack.updateUiOnError("DB Upsert Error, Something went wrong!");
         });
     }

     /**
      *向上插入大量TaskItem
      * @param taskItem 本地添加或修改的TaskItem
      */
     public void upsertTaskItems(List<TaskItem> taskItem, int tag) {
         if (mCloudDBZone == null) {
             Log.w(TAG, "CloudDBZone is null, try re-open it");
             return;
         }
         Task<Integer> upsertTask = mCloudDBZone.executeUpsert(taskItem);
         upsertTask.addOnSuccessListener(cloudDBZoneResult -> {
             mUiTaskCallBack.onRefresh(tag);
         }).addOnFailureListener(e -> {
             mUiTaskCallBack.updateUiOnError("DB Upsert Error, Something went wrong!");
             e.printStackTrace();
         });
     }

     /**
      *删除TaskItem
      * @param taskItemList 用户选择的任务
      */
     public void deleteTaskItems(List<TaskItem> taskItemList) {
         if (mCloudDBZone == null) {
             Log.w(TAG, "CloudDBZone is null, try re-open it");
             return;
         }
         Task<Integer> deleteTask = mCloudDBZone.executeDelete(taskItemList);
         if (deleteTask.getException() != null) {
             mUiTaskCallBack.updateUiOnError("DB Deletion Error, Something went wrong!");
             return;
         }
         
     }

 }

4、该wrapper类包含一些在后续部分可复用的方法。

初始化wrapper,打开存储区,使用CloudDBZoneQuery接口检验用户是否是新用户。

/**
  * 初始化云数据库和数据库操作用于获取使用ID登录的用户
 
  */
private void initCloudDB() {
     mCloudDBZoneWrapper = new CloudDBZoneWrapper();
     mHandler = new Handler(Looper.getMainLooper());
     mHandler.post(() -> {
         if (null != AGConnectAuth.getInstance().getCurrentUser()) {
             mCloudDBZoneWrapper.createObjectType();
             mCloudDBZoneWrapper.openCloudDBZoneV2(mCloudDBZone -> {
                 this.mCloudDBZone = mCloudDBZone;
                 queryUserDetails();
             });
         }
     });
 }

5、存储区初始化成功后,在onInit中执行查询操作验证用户。

AGConnectUser user = AGConnectAuth.getInstance().getCurrentUser();
new Handler().post(() -> {
     mCloudDBZoneWrapper
             .queryUserData(CloudDBZoneQuery.where(UserData.class)
             .equalTo("UserID", user.getUid()), 1);
 });

6、在wrapper对象中添加监听器获取查询结果。已注册用户使用任意设备登录应用时,在本地的Shared Preference记录用户类型和匹配状态后,将用户引导至对应的界面。

mCloudDBZoneWrapper.addStudentCallBacks(new UiStudentCallBack() {
     @Override
     public void onStudentAddOrQuery(List<UserData> studentItemList, int tag) {
         hideDialog();
         if (studentItemList.size() > 0) {
             UserData currentUser = studentItemList.get(0);
             int userType = Integer.parseInt(currentUser.getUserType());
             PrefUtil.getInstance(LoginActivity.this).setInt("USER_TYPE", userType);
             PrefUtil.getInstance(LoginActivity.this).setBool("IS_MAPPED", true);

             Intent i;
             if (userType == Constants.USER_STUDENT || userType == Constants.USER_TEACHER)
                 i = new Intent(LoginActivity.this, HomeActivity.class);
             else
                 i = new Intent(LoginActivity.this, UserSelectionActivity.class);
             startActivity(i);
             finish();
         } else {
             Intent i = new Intent(LoginActivity.this, UserSelectionActivity.class);
             startActivity(i);
             finish();
         }
     }

     @Override
     public void updateStudentUiOnError(String errorMessage) {
         hideDialog();
         showToast(errorMessage);
     }
 });

7、在页面的onDestroy方法中关闭存储区,否则可能导致错误发生。

若登录用户为新用户,引导至UserSelectionActivty获取用户类型。在选择用户类型后,将用户信息插入到云数据库中(在调用插入接口前需要初始化wrapper)。

/**
  * 向云数据库中插入用户类型
  */
public void insertUserType() {
     if (mCloudDBZone == null) {
         Log.e(TAG, "CloudDBZone is null, try re-open it");
         return;
     }
     showProgressDialog("Loading...");
     AGConnectUser user = AGConnectAuth.getInstance().getCurrentUser();
     UserData userData = new UserData();
     userData.setUserID(user.getUid());
     userData.setUserName(user.getDisplayName());
     userData.setUserType(String.valueOf(Constants.USER_TEACHER));

     Task<Integer> upsertTask = mCloudDBZone.executeUpsert(userData);
     upsertTask.addOnSuccessListener(cloudDBZoneResult -> {

         hideDialog();
         Toast.makeText(UserSelectionActivity.this, "TeacherMapActivity_user_insert_success " + cloudDBZoneResult + " records", Toast.LENGTH_SHORT).show();

         // 保存匹配状态和当前登录的用户类型。
         PrefUtil.getInstance(UserSelectionActivity.this).setInt("USER_TYPE", Constants.USER_TEACHER);
         PrefUtil.getInstance(UserSelectionActivity.this).setBool("IS_MAPPED", true);

         Intent i = new Intent(UserSelectionActivity.this, HomeActivity.class);
         startActivity(i);
         finish();
     });
     upsertTask.addOnFailureListener(e -> {
         hideDialog();
         Log.e(TAG, e.getMessage());
         Toast.makeText(UserSelectionActivity.this, "insert_failed " + e.getLocalizedMessage() + " records", Toast.LENGTH_SHORT).show();
     });
 }

用户信息成功插入后,若用户为学生则引导用户至StudentMapActivity。若用户为老师,直接引导至HomeActivity。

实现老师用户功能

老师的主页面包括两个Fragment。其一是TaskListFragment,其二是StudentListFragment。

  • TaskListFragment是老师创建的任务列表。

  • StudentListFragment是老师的学生列表。

1、制作创建作业的按钮UI。基于UI编写代码获取作业名称,作业描述以及提交作业的最晚日期。老师发布作业后,获取老师账户下匹配的学生列表,为每一位学生创建作业任务并将任务信息插入至云数据库。

/**
  * 收集学生列表,创建任务
  *
 
  * 
 为所有学生创建任务。
  * @param data
  */
public void upsertTaskItem(Intent data) {
     mCloudDBZoneWrapper.addStudentCallBacks(new UiStudentCallBack() {
         @Override
         public void onStudentAddOrQuery(List<UserData> studentItemList, int tag) {
             createAndInsertTaskList(data, studentItemList);
         }

         @Override
         public void updateStudentUiOnError(String errorMessage) {
             Toast.makeText(getActivity(), errorMessage, Toast.LENGTH_SHORT).show();
         }
     });
     new Handler(Looper.getMainLooper()).post(() -> {
         mCloudDBZoneWrapper.queryUserData(CloudDBZoneQuery.where(UserData.class)
                 .equalTo("TeacherId", user.getUid())
                 .and()
                 .equalTo("UserType", String.valueOf(Constants.USER_STUDENT)), 2);
     });
}

/**
  *根据{@link CreateTaskActivity}获取的信息创建TaskItem的记录
  * 通过 wrapper类插入老师创建的TaskItem
  * 将创建一个具有共同ID的任务组
  * 和为每一个学生分发送一份具有唯一ID的作业任务。
  * @param taskData
  * @param studentsList
  */
private void createAndInsertTaskList(Intent taskData, List<UserData> studentsList) {
     List<TaskItem> taskItemList = new ArrayList();
     Date cDate = new Date();
     String taskGroupId = String.valueOf(UUID.randomUUID()); // unique for each Task
     String date = "";
     for (int ind = 0; ind < studentsList.size(); ind++) {
         TaskItem task = new TaskItem();
         task.setTaskID(String.valueOf(UUID.randomUUID()));//unique for each student
         task.setGroup_id(taskGroupId);

         task.setTaskName(taskData.getStringExtra("task_name"));
         task.setTaskDescription(taskData.getStringExtra("task_desc"));
         task.setStatus(Constants.STATUS_NEW);

         task.setStudentID(studentsList.get(ind).getUserID());
         task.setCreadtedBy(user.getUid());

         date = taskData.getStringExtra("due_date");
         task.setDueDate(UserUtil.localToUTCDate(date));
         task.setCreatedDate(cDate);
         taskItemList.add(task);
     }
     mCloudDBZoneWrapper.upsertTaskItems(taskItemList, 0);
 }
@Override
public void onRefresh(int tag) {
     generateTaskListQuery(index);
 }

2、插入数据后,向数据库查询作业列表,展示列表在TaskListFragment。每当进入该Fragment时和作业更新后都应当查询一次作业列表,这样主页就能一直展示最新信息。

/**
  *创建查询今日TaskItem列表的Query
  * 根据登录用户为老师或学生展示不同的列表
  * 根据参数不同展示当前任务或历史任务
  * @param inputValue
  */
private void generateTaskListQuery(int inputValue) {
     binding.progressBar.setVisibility(View.VISIBLE);

     CloudDBZoneQuery<TaskItem> query;
     Date date = UserUtil.getCurrentDateTimeAsUTC();
     date.setHours(0);
     date.setMinutes(0);
     date.setSeconds(0);

     if (inputValue == Constants.TASK_ITEM) {
         query = CloudDBZoneQuery.where(TaskItem.class)
                 .greaterThanOrEqualTo("DueDate", date)
                 .and().notEqualTo("Status", STATUS_CLOSED);

         if (userType == Constants.USER_TEACHER)
             query = query.and().equalTo("CreadtedBy", user.getUid());

         if (userType == Constants.USER_STUDENT)
             query = query.and().equalTo("StudentID", user.getUid());

         getTaskListFromDB(query);

     } else if (inputValue == Constants.TASK_HISTORY_ITEM) {
         query = CloudDBZoneQuery.where(TaskItem.class)
                 .lessThanOrEqualTo("DueDate", date)
                 .and().equalTo("StudentID", user.getUid());
         getTaskListFromDB(query);
     } else {
         query = CloudDBZoneQuery.where(TaskItem.class);
         getTaskListFromDB(query);
     }
}

/**
  *调用在wrapper类中定义的方法查询TaskItem
 
  * @param query
  */
private void getTaskListFromDB(CloudDBZoneQuery<TaskItem> query) {
     new Handler(Looper.getMainLooper()).postDelayed(() -> {
         mCloudDBZoneWrapper.queryTasks(query, 1);
     }, 500);
 }
@Override
public void onAddOrQuery(List<TaskItem> taskItemList, int tag) {
     taskItemsList.clear();
     HashMap<String, TaskItem> tempMap = new HashMap<>();
     for (TaskItem taskItem : taskItemList) {
         if (!tempMap.containsKey(taskItem.getGroup_id())) {
             taskItemsList.add(taskItem);
             tempMap.put(taskItem.getGroup_id(), taskItem);
         }
     }
     taskAdapter.updateList(taskItemsList);
     binding.progressBar.setVisibility(View.GONE);
 }
@Override
public void updateUiOnError(String errorMessage) {
     Toast.makeText(getActivity(), errorMessage, Toast.LENGTH_SHORT).show();
 }

3、老师点击任务时,打开TaskSummaryActivity页面。该页面展示作业信息、分配到作业任务的学生列表以及未提交和已提交的作业数量。老师们还可以在该页面上根据状态或需要关闭作业。

/**
  * 更新当前的任务状态为关闭
  */
private void closeCurrentTask() {
     if (taskItems.size() > 0) {
         List<TaskItem> closeTaskItems = new ArrayList<>();
         for (int i = 0; i < taskItems.size(); i++) {
             TaskItem taskItem = taskItems.get(i).getTaskItem();
             taskItem.setStatus(Constants.STATUS_CLOSED);
             closeTaskItems.add(taskItem);
         }
         new Handler(Looper.getMainLooper()).post(() -> {
             mCloudDBZoneWrapper.upsertTaskItems(closeTaskItems, 3);
         });
     }
 }
 
/**
  *从数据库中获取任务列表
  * @param groupId
  */
private void getSubmittedTaskList(String groupId) {
     new Handler(Looper.getMainLooper()).post(() -> {
         mCloudDBZoneWrapper.queryTasks(CloudDBZoneQuery.where(TaskItem.class).equalTo("group_id", groupId), 1);
     });
 }

@Override
public void onAddOrQuery(List<TaskItem> taskItemList, int tag) {
     taskItems.clear();
     for (int ind = 0; ind < taskItemList.size(); ind++) {
         UserAndTask userAndTask = new UserAndTask();
         userAndTask.setTaskItem(taskItemList.get(ind));
         taskItems.add(userAndTask);
     }
     getStudentList();
 }
 
/**
  *从数据库获取学生列表
  */
private void getStudentList() {
     new Handler().post(() -> {
         mCloudDBZoneWrapper.queryUserData(
                 CloudDBZoneQuery.where(UserData.class)
                         .equalTo("UserType", String.valueOf(Constants.USER_STUDENT))
                         .and()
                         .equalTo("TeacherId", user.getUid()),
                  2);
     });
 }
@Override
public void onStudentAddOrQuery(List<UserData> studentItemList, int tag) {
     int pendingCount = 0, submittedCount = 0, evaluatedCount = 0;
     for (int ind = 0; ind < taskItems.size(); ind++) {
         //获取计数
         int tStatus = taskItems.get(ind).getTaskItem().getStatus();
         pendingCount += (tStatus == Constants.STATUS_NEW) ? 1 : 0;
         submittedCount += (tStatus == Constants.STATUS_SUBMITTED) ? 1 : 0;
         evaluatedCount += (tStatus == Constants.STATUS_EVALUATED) ? 1 : 0;
         //匹配任务和学生
         for (int jnd = 0; jnd < studentItemList.size(); jnd++) {
             if (taskItems.get(ind).getTaskItem().getStudentID().equals(studentItemList.get(jnd).getUserID())) {
                 taskItems.get(ind).setUserData(studentItemList.get(jnd));
             }
         }
     }
     taskSumListAdapter.updateList(taskItems);
     displayTaskDetails(taskItems.get(0).getTaskItem(), pendingCount, submittedCount, evaluatedCount);
     hideDialog();
 }

4、学生上传的作业可以被老师批改。老师只需点击某位学生的作业即可。点击作业后打开TaskDetailActivtiy。该页面处理老师的作业批改和学生的作业提交。添加如下代码实现作业批改。

binding.btnValidateTask.setOnClickListener(v -> {
     if (validatePreValidation(taskItem)) {
         updateValidateStatus(taskItem);
     }
 });
/**
  * 验证作业任务是否可以被批改
  */
private boolean validatePreValidation(TaskItem taskParam) {
     if (taskParam.getStatus() == Constants.STATUS_NEW) {
         showToast("Not submitted, Cannot evaluate");
         return false;
     } else if (taskParam.getStatus() == Constants.STATUS_EVALUATED) {
         showToast("Task already evaluated");
         return false;
     } else if (taskParam.getStatus() == Constants.STATUS_CLOSED) {
         showToast("Task Closed, Cannot evaluate");
         return false;
     } else if (taskParam.getAttachmentUrl() == null || taskParam.getAttachmentUrl().get().isEmpty()) {
         showToast("No Attachment, Cannot evaluate");
         return false;
     } else {
         return true;
     }
 }
/**
  *当老师批改作业时更新作业状态
  *更新老师批时的作业状态
  */
private void updateValidateStatus(TaskItem taskParam) {
     showProgressDialog("Updating Task status..");
     taskParam.setStatus(Constants.STATUS_EVALUATED);
     new Handler(Looper.getMainLooper()).post(() -> {
         mCloudDBZoneWrapper.upsertTaskItem(taskParam, VALIDATE);
     });
 }
@Override
public void onRefresh(int tag) {
     hideDialog();
     if (tag == VALIDATE || tag == SUBMIT) {
         String msg = tag == VALIDATE ? "Task Validated." : "Task Submitted.";
         showAlertDialog(msg, () -> {
             HomeActivity.NEED_UPDATE = true;
             finish();
         });
     } else if (tag == ATTACHMENT) {
         showToast("File uploaded Successfully");
     }
 }

5、StudentsListFragment仅展示HomeActivity第二个页签的学生列表。执行数据库查询操作获取学生列表。

/**
  * 执行查询操作获取学生列表
    * @param inputValue
  * @param teacherId
  */
private void getStudentList(int inputValue, String teacherId) {
     binding.progressBar.setVisibility(View.VISIBLE);
     CloudDBZoneQuery<UserData> query;
     if (inputValue == Constants.STUDENT_ITEM) {
         query = CloudDBZoneQuery.where(UserData.class)
                 .equalTo("TeacherId", teacherId)
                 .and()
                 .equalTo("UserType", String.valueOf(Constants.USER_STUDENT));
         getListData(query);
     } else {
         query = CloudDBZoneQuery.where(UserData.class);
         getListData(query);
     }
}

/**
  *调用wrapper类方法获取UserData
  * @param query
  */
private void getListData(CloudDBZoneQuery<UserData> query) {
     new Handler().postDelayed(() -> {
         mCloudDBZoneWrapper.queryUserData(query, 1);
     }, 300);
}


/**
  数据库监听器方法
  *OnResult方法实现检索学生列表
  * @param studentItemList
  * @param tag
  */
@Override
public void onStudentAddOrQuery(List<UserData> studentItemList, int tag) {
     studentAdapter.updateList(studentItemList);
     binding.progressBar.setVisibility(View.GONE);
}

/**
  *数据库监听器方法
  *OnError方法实现检索学生列表
  * @param errorMessage
  */
@Override
public void updateStudentUiOnError(String errorMessage) {
     Toast.makeText(getActivity(), errorMessage, Toast.LENGTH_SHORT).show();
 }

6、若老师想查看学生历史作业任务,可以点击列表中的某位学生,然后打开StudentDetailActivity页面。在该页面中执行数据库查询操作获取列表。

/**
  *添加UserData数据库操作监听器
  *通过wrapper类获取学生列表
  */
public void queryUserDetails() {
     mCloudDBZoneWrapper.addStudentCallBacks(new UiStudentCallBack() {
         @Override
         public void onStudentAddOrQuery(List<UserData> studentItemList, int tag) {
             hideDialog();
             if (studentItemList.size() > 0) {
                 UserData currentUser = studentItemList.get(0);
                 binding.txtStudentName.setText(currentUser.getUserName());
                 binding.txtStudentDetail.setText("My Student");
                 getTaskList(currentUser.getUserID());
             }
         }

         @Override
         public void updateStudentUiOnError(String errorMessage) {
             hideDialog();
             showToast(errorMessage);
         }
     });
     new Handler().post(() -> {
         mCloudDBZoneWrapper.queryUserData(CloudDBZoneQuery.where(UserData.class).equalTo("UserID", studentId), 1);
     });
}

/**
  *创建用于获取特定学生数据的Query
  * @param studentId
  */
private void getTaskList(String studentId) {
     CloudDBZoneQuery<TaskItem> query;
     query = CloudDBZoneQuery.where(TaskItem.class).equalTo("StudentID", studentId);
     getListData(query);
}

/**
  *
  * @param query
  */
private void getListData(CloudDBZoneQuery<TaskItem> query) {
     new Handler().post(() -> {
         mCloudDBZoneWrapper.queryTasks(query, 1);
     });
 }

/**
  *数据库监听器方法
  *OnResult方法实现检索TaskItem列表
  * @param taskItemList
  * @param tag
  */
@Override
public void onAddOrQuery(List<TaskItem> taskItemList, int tag) {
     hideDialog();
     taskListAdapter.updateList(taskItemList);
}

/**
  *数据库监听器方法
  *onError实现检索TaskItem列表
  * @param errorMessage
  */
@Override
public void updateUiOnError(String errorMessage) {
     hideDialog();
     showToast(errorMessage);
 }

7、老师信息和二维码将展示在TeachersProfileActivity页面。添加下述代码生成老师的二维码。

/**
  *初始化View,生成和展示带有老师信息的二维码
  *学生通过扫描该二维码可以匹配该老师
  * @param savedInstanceState
  */
@Override
protected void onPostCreate(@Nullable Bundle savedInstanceState) {
     super.onPostCreate(savedInstanceState);

     AGConnectUser user = AGConnectAuth.getInstance().getCurrentUser();
     String content = "{\"TeacherID\":\"" + user.getUid() + "\"," +
             "\"TeacherName\":\"" + user.getDisplayName() + "\"," +
             "\"EmailID\":\"" + user.getEmail() + "\"}";

     binding.txtTeacherName.setText(user.getDisplayName());
     binding.txtTeacherId.setText((user.getEmail() == null) ? "" : user.getEmail());

     int type = HmsScan.QRCODE_SCAN_TYPE;
     int width = 400;
     int height = 400;

     HmsBuildBitmapOption options = new HmsBuildBitmapOption.Creator().setBitmapMargin(3).create();
     try {
         //若HmsBuildBitmapOption对象未构建,将options设置为null。
         qrBitmap = ScanUtil.buildBitmap(content, type, width, height, options);
         ((ImageView) findViewById(R.id.img_teacher_qr)).setImageBitmap(qrBitmap);
     } catch (WriterException e) {
         Log.w("buildBitmap", e);
     }
 }

实现学生用户功能

在StudentMapActivty页面,添加二维码扫描功能。通过统一扫码服务,学生可以扫码匹配老师。

初始化统一扫码服务扫描页面,实现onActivityResult方法。老师的二维码包含一串由统一扫码服务根据老师登录信息生成的JSON数据。

使用Intent中的结果数据在该Activity中执行下述数据库操作。

1、创建Loginmapping云数据库对象。

2、创建UserData云数据对象。

/**
  *开启相机二维码扫描
  */
private void initScanQR() {
     HmsScanAnalyzerOptions options = new HmsScanAnalyzerOptions.Creator()
             .setHmsScanTypes(HmsScan.QRCODE_SCAN_TYPE, HmsScan.DATAMATRIX_SCAN_TYPE)
             .create();
     ScanUtil.startScan(StudentMapActivity.this, REQUEST_CODE, options);
 }
 
/**
  *二维码扫描后
  *传入老师二维码中的JSON对象
  * @param requestCode
  * @param resultCode
  * @param data
  */
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
     super.onActivityResult(requestCode, resultCode, data);
     if (resultCode != RESULT_OK || data == null) {
         return;
     }
     if (requestCode == REQUEST_CODE) {
         //传入扫描的图片并返回结果。
         HmsScan obj = data.getParcelableExtra(ScanUtil.RESULT);
         if (obj != null && !TextUtils.isEmpty(((HmsScan) obj).getOriginalValue())) {
             try {
                 /*二维码中的老师信息以JSON格式返回。*/
                 JSONObject jsonObject = new JSONObject(obj.getOriginalValue());
                 initCloudDB(jsonObject);

             } catch (JSONException e) {
                 Log.e(TAG, e.getMessage());
             }
         } else {
             Log.e("Error", "Scanned result (null) not available");
         }
     } else {
         Log.e("Error", "Scanned result not available");
     }

 }
 
/**
  *初始化wrapper类。初始化完成后,调用数据库插入操作方法。
  * @param jsonObject
  */
private void initCloudDB(JSONObject jsonObject) {
     mCloudDBZoneWrapper = new CloudDBZoneWrapper();
     mHandler = new Handler(Looper.getMainLooper());
     mHandler.post(() -> {
         if (null != AGConnectAuth.getInstance().getCurrentUser()) {
             mCloudDBZoneWrapper.createObjectType();
             mCloudDBZoneWrapper.openCloudDBZoneV2(mCloudDBZone1 -> {
                 this.mCloudDBZone = mCloudDBZone1;
                 try {
                     upsertTeacherDetails(jsonObject);
                 } catch (JSONException e) {
                     e.printStackTrace();
                 }
             });
         }
     });
 }
/**
  *向云数据库中的LoginMapping插入老师和学生的匹配记录
 
  * @param jsonObject
  * @throws JSONException
  */
public void upsertTeacherDetails(JSONObject jsonObject) throws JSONException {
     if (mCloudDBZone == null) {
         Log.e(TAG, "CloudDBZone is null, try re-open it");
         return;
     }
     showProgressDialog("Loading...");

     AGConnectUser user = AGConnectAuth.getInstance().getCurrentUser();
     String teacherId = jsonObject.getString("TeacherID");

     Loginmapping loginmapping = new Loginmapping();
     loginmapping.setStudentID(user.getUid());
     loginmapping.setTeacherID(teacherId);
     loginmapping.setStudentName(user.getDisplayName());
     loginmapping.setStudentEmail(user.getEmail());
     loginmapping.setTeacherEmail(jsonObject.getString("EmailID"));
     loginmapping.setTeacherName(jsonObject.getString("TeacherName"));
     //loginmapping.setUserType(1);
     Date date = new Date();
     loginmapping.setMappedDate(date);

     Task<Integer> upsertTask = mCloudDBZone.executeUpsert(loginmapping);
     upsertTask.addOnSuccessListener(cloudDBZoneResult -> {
         insertUserType(teacherId);
     }).addOnFailureListener(e -> {
         hideDialog();
         Log.e("TAG", "insert_failed " + e.getLocalizedMessage() + " records");
     });
 }
/**
  *插入匹配后的学生记录
  * @param teacherId
  */
public void insertUserType(String teacherId) {
     if (mCloudDBZone == null) {
         Log.e(TAG, "CloudDBZone is null, try re-open it");
         return;
     }

     AGConnectUser user = AGConnectAuth.getInstance().getCurrentUser();
     UserData userData = new UserData();
     userData.setUserID(user.getUid());
     userData.setUserName(user.getDisplayName());
     userData.setUserType(String.valueOf(Constants.USER_STUDENT));
     userData.setTeacherId(teacherId);

     Task<Integer> upsertTask = mCloudDBZone.executeUpsert(userData);
     upsertTask.addOnSuccessListener(cloudDBZoneResult -> {
         hideDialog();
         Toast.makeText(StudentMapActivity.this, "Student Registered and Mapped.", Toast.LENGTH_SHORT).show();
         if (!initFrom.equals("StudentProfileActivity")) {
             PrefUtil.getInstance(this).setInt("USER_TYPE", Constants.USER_STUDENT);
             PrefUtil.getInstance(this).setBool("IS_MAPPED", true);
             startActivity(new Intent(StudentMapActivity.this, HomeActivity.class));
         }
         finish();
     });

     upsertTask.addOnFailureListener(e -> {
         hideDialog();
         Log.e(TAG, "insert_failed " + e.getLocalizedMessage() + " records");
     });
 }

3、匹配成功后,引导学生至HomeActivtiy。该页面包括两部分。复用TaskListFragment作为“Current Task”和“Task History”页签

  • Current Task页签列举需要完成的作业。

  • Task History列举所有历史作业。

/**
  *创建获取今日TaskItem列表的Query
  * 基于登录用户,展示不同列表
  *根据参数不同展示当前TaskItem列表或历史任务
  * @param inputValue
  */
private void generateTaskListQuery(int inputValue) {
     binding.progressBar.setVisibility(View.VISIBLE);

     CloudDBZoneQuery<TaskItem> query;
     Date date = UserUtil.getCurrentDateTimeAsUTC();
     date.setHours(0);
     date.setMinutes(0);
     date.setSeconds(0);

     if (inputValue == Constants.TASK_ITEM) {
         query = CloudDBZoneQuery.where(TaskItem.class)
                 .greaterThanOrEqualTo("DueDate", date)
                 .and().notEqualTo("Status", STATUS_CLOSED);

         if (userType == Constants.USER_TEACHER)
             query = query.and().equalTo("CreadtedBy", user.getUid());

         if (userType == Constants.USER_STUDENT)
             query = query.and().equalTo("StudentID", user.getUid());

         getTaskListFromDB(query);

     } else if (inputValue == Constants.TASK_HISTORY_ITEM) {
         query = CloudDBZoneQuery.where(TaskItem.class)
                 .lessThanOrEqualTo("DueDate", date)
                 .and().equalTo("StudentID", user.getUid());
         getTaskListFromDB(query);
     } else {
         query = CloudDBZoneQuery.where(TaskItem.class);
         getTaskListFromDB(query);
     }
}
private void getTaskListFromDB(CloudDBZoneQuery<TaskItem> query) {
     new Handler(Looper.getMainLooper()).postDelayed(() -> {
         mCloudDBZoneWrapper.queryTasks(query, 1);
     }, 500);
 }
@Override
public void onAddOrQuery(List<TaskItem> taskItemList, int tag) {
     taskItemsList.clear();
     HashMap<String, TaskItem> tempMap = new HashMap<>();
     for (TaskItem taskItem : taskItemList) {
         if (!tempMap.containsKey(taskItem.getGroup_id())) {
             taskItemsList.add(taskItem);
             tempMap.put(taskItem.getGroup_id(), taskItem);
         }
     }
     taskAdapter.updateList(taskItemsList);
     binding.progressBar.setVisibility(View.GONE);
 }

4、学生点击HomeActivity上对应的作业任务打开TaskDetailActivity页面后,上传作业图片。实现下述步骤。

1)从相册中挑选照片,上传照片至云存储,并获取上传照片的URL地址。

binding.btnUpload.setOnClickListener(view -> uploadFile(view));
/**
  *检查初始化后的云存储
  *调用图片挑选方法
  */
public void uploadFile(View view) {
     if (mAGCStorageManagement == null) {
         initAGCStorageManagement();
     }
     pickImageFromGallery();
}

/**
  *初始化从设备相册挑选相册功能
  */
private void pickImageFromGallery() {
     Uri filePath = Uri.parse(Environment.getExternalStorageDirectory().getAbsolutePath());
     //Uri filePath = Uri.parse("/storage/");
     Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
     intent.setDataAndType(filePath, "image/*");
     startActivityForResult(intent, PICKFILE_REQUEST_CODE);
 }
/**
  * 获取图片的挑选结果并转成bitmap
  *将结果传入云存储的上传方法中
  */
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
     super.onActivityResult(requestCode, resultCode, data);
     if (requestCode == PICKFILE_REQUEST_CODE && resultCode == RESULT_OK && null != data) {
         imageUri = data.getData();
         ImageView imageView = new ImageView(TaskDetailActivity.this);
         imageView.setImageURI(imageUri);
         imageView.invalidate();
         BitmapDrawable drawable = (BitmapDrawable) imageView.getDrawable();
         uploadImageToCloud(drawable.getBitmap());
     }
}

/**
  *为图片名称生成随机字串
  *开启图片上传异步方法
  */
private void uploadImageToCloud(Bitmap bitmap) {
     showProgressDialog("Uploading... ");
     final String randomKey = UUID.randomUUID().toString();
     FileFromBitmap fileFromBitmap = new FileFromBitmap(bitmap, randomKey, TaskDetailActivity.this);
     fileFromBitmap.execute();
 }
/**
  *图片上传AsyncTask类
  *doInBackground:通过相册路径获取文件
  *onPostExecute:上传至云存储
  *获取上传的文件URL用于展示或查看
  */
class FileFromBitmap extends AsyncTask<Void, Integer, File> {
     Context context;
     Bitmap bitmap;
     String fileName;

     public FileFromBitmap(Bitmap bitmap, String fileName, Context context) {
         this.bitmap = bitmap;
         this.context = context;
         this.fileName = fileName;
     }

     @Override
     protected void onPreExecute() {
         super.onPreExecute();
     }

     @Override
     protected File doInBackground(Void... voids) {
         File fileBackGround = new File(Environment.getExternalStorageDirectory().getAbsolutePath(), fileName);
         ByteArrayOutputStream bos = new ByteArrayOutputStream();
         bitmap.compress(Bitmap.CompressFormat.JPEG, 100, bos);
         byte[] bitmapdata = bos.toByteArray();
         FileOutputStream fos = null;
         try {
             fos = new FileOutputStream(fileBackGround);
         } catch (FileNotFoundException e) {
             e.printStackTrace();
         }
         try {
             fos.write(bitmapdata);
             fos.flush();
             fos.close();
         } catch (IOException e) {
             e.printStackTrace();
         }
         return fileBackGround;
     }

     @Override
     protected void onPostExecute(File file) {
         if (!file.exists()) {
             return;
         }
         StorageReference storageReference = mAGCStorageManagement.getStorageReference(file.getPath());
         //成功则上传文件和获取URL
         UploadTask uploadTask = storageReference.putFile(file);
         Task<Uri> urlTask = uploadTask.continueWithTask(task -> {
             if (!task.isSuccessful()) {
                 throw task.getException();
             }
             return storageReference.getDownloadUrl();
         });
         //上传完成后发送URL
         urlTask.addOnCompleteListener(task -> {
             if (task.isSuccessful()) {
                 Uri downloadUri = task.getResult();
                 System.out.println("Upload " + downloadUri);
                 hideDialog();
                 if (downloadUri != null) {
                     String photoStringLink = downloadUri.toString(); //此处存储下载地址。
                     addImageInRecyclerView(photoStringLink);
                 }
             }
         });
         uploadTask.addOnSuccessListener(uploadResult -> hideDialog())
                 .addOnFailureListener(e -> hideDialog());
     }
 }

2)串联所有上传的图片URL并更新相应任务的URL。

/**
  *当学生上传附件时更新附件URL
  *需使用wrapper类的upsertTaskItem方法
  */
private void updateAttachURL(String uploadUrL) {
     showProgressDialog("Updating your attachments..");

     String str = taskItem.getAttachmentUrl() == null ? "" : taskItem.getAttachmentUrl().get();
     str += (str.isEmpty()) ? uploadUrL : ", " + uploadUrL;
     taskItem.setAttachmentUrl(new Text(str)); // 此处为附件URL字符串,多个地址用逗号隔开.

     new Handler(Looper.getMainLooper()).postDelayed(() -> {
         mCloudDBZoneWrapper.upsertTaskItem(taskItem, ATTACHMENT);
     }, 500);
 }
@Override
public void onRefresh(int tag) {
     hideDialog();
     if (tag == VALIDATE || tag == SUBMIT) {
         String msg = tag == VALIDATE ? "Task Validated." : "Task Submitted.";
         showAlertDialog(msg, () -> {
             HomeActivity.NEED_UPDATE = true;
             finish();
         });
     } else if (tag == ATTACHMENT) {
         showToast("File uploaded Successfully");
     }
 }

3)任务中的URL更新后,接下来提交任务。提交完成后,任务状态变为“SUBMITTED”。

binding.btnSubmitTask.setOnClickListener(v -> {
     if (submitPreValidation(taskItem)) {
         updateSubmissionStatus(taskItem);
     }
 });
/**
  *验证任务数据是否能够被学生提交
  */
private boolean submitPreValidation(TaskItem taskParam) {
     if (taskParam.getStatus() == Constants.STATUS_SUBMITTED) {
         showToast("Task Already submitted");
         return false;
     } else if (taskParam.getStatus() == Constants.STATUS_EVALUATED) {
         showToast("Task evaluated, Cannot Submit");
         return false;
     } else if (taskParam.getStatus() == Constants.STATUS_CLOSED) {
         showToast("Task Closed, Cannot Submit");
         return false;
     } else if (taskParam.getAttachmentUrl() == null || taskParam.getAttachmentUrl().get().isEmpty()) {
         showToast("No Attachment, Cannot submit");
         return false;
     } else {
         return true;
     }
 }
 
/**
  *学生提交作业时更新任务状态
  *需使用wrapper类的upsertTaskItem方法
  */
private void updateSubmissionStatus(TaskItem task_Item) {
     showProgressDialog("Updating your attachments..");
     task_Item.setStatus(Constants.STATUS_SUBMITTED);
     new Handler(Looper.getMainLooper()).post(() -> {
         mCloudDBZoneWrapper.upsertTaskItem(task_Item, SUBMIT);
         Toast.makeText(this, "Url Updated successfully", Toast.LENGTH_SHORT).show();
     });
 }
 
@Override
public void onRefresh(int tag) {
     hideDialog();
     if (tag == VALIDATE || tag == SUBMIT) {
         String msg = tag == VALIDATE ? "Task Validated." : "Task Submitted.";
         showAlertDialog(msg, () -> {
             HomeActivity.NEED_UPDATE = true;
             finish();
         });
     } else if (tag == ATTACHMENT) {
         showToast("File uploaded Successfully");
     }
 }

九、打包与测试

1、启用Androd Studio, 单击运行按钮在手机或模拟器上运行应用。点击登录按钮登录应用。

cke_2160757.pngcke_2191947.png

2、成功登录后,选择用户类型。

cke_2218850.png

3、如果用户是学生,则需要扫码匹配老师。

cke_2288975.png

4、老师的二维码在其个人主页下展示。

cke_2368717.png

5、匹配成功后显示主页。

  • 学生主页

cke_2537726.png

  • 老师主页

cke_2637171.png

6、老师们点击主页“+”按钮创建任务。

cke_2732057.pngcke_2798355.png

7、学生和老师的主页都可以展示创建完成的任务。

cke_2880527.png

8、学生可以在主页点击任务并更新任务状态。

cke_2975191.pngcke_3042525.png

9、老师可以查看任务状态和学生的历史任务。

cke_3137009.pngcke_3170147.png

十、恭喜您

祝贺您,您已成功构建一款School Diary应用并学会了:

  • AppGallery Connect上配置云数据库和云存储。

  • 使用Android Studio集成多个HMS Core服务并构建一款School Diary应用。

十一、参考

参考如下文档获取更多信息:

点击此处下载源码。

声明:本codelab实现多个HMS Core服务在单个项目中的集成,供您参考。您需要验证确保相关开源代码的安全合法合规。

 

欲了解更多更全技术文章,欢迎访问https://developer.huawei.com/consumer/cn/forum/?ha_source=zzh

标签:Core,School,String,void,HMS,tag,query,new,public
From: https://www.cnblogs.com/developer-huawei/p/16917268.html

相关文章