一、介绍
总览
通过建立本次的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权限。
-
认证服务(华为账号认证方式)
-
云数据库
-
云存储
-
统一扫码服务
说明:以上API权限默认已开通。如未开通,请手动开通。
2、在弹出页面设置数据处理位置。
五、集成服务
Duration: 4:00
您需要集成云数据库SDK到您的Android Studio项目中。
1、登录AppGallery Connect并点击“我的项目”。
2、选择项目,在应用下拉列表中选择需要集成SDK的应用。
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:<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
老师登录界面UI
七、前提准备
Duration: 5:00
认证服务
本次将使用华为账号登录方式。因此,您需要在AppGallery Connect上开启认证服务的华为账号认证方式。否则,登录将失败。
-
登录AppGallery Connect并点击“我的项目”。
-
找到并点击项目。
-
点击“构建”>“认证服务”。如果您首次使用认证服务,请点击“立即开通”。
-
选择“认证方式”页签,在“操作”列中选择“华为账号”。
云数据库
操作云数据,需要您先开通该服务,然后创建存储区和有着所需字段的云数据库对象。
1、登录AppGallery Connect并点击“我的项目”。
2、点击您的项目。
3、选择“构建”>“云数据库”。如果您首次使用云数据库,请点击“立即开通”。
4、在弹出的页面设置数据处理位置。
5、点击“新增”打开“新增对象类型”页面。
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、设置角色和对应权限。
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、单击“导出”。
13、选择导出文件格式。此处选择“java格式”,选择java文件类型为“android”,输入包名称,单击“确定”。对象类型文件会以zip形式导出至本地。
14、提取压缩包中的文件至项目的model包里。
15、选择“存储区”页签。
16、单击“新增”,进入创建存储区页面。
云存储
使用云存储服务,您需要首先启用它,并在开始编程前完成下述步骤。
1、云存储开通后,创建一个存储实例并赋名。单击“下一步”。
2、制定安全策略来设置用户是否需要经过验证才能访问存储。
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, 单击运行按钮在手机或模拟器上运行应用。点击登录按钮登录应用。
2、成功登录后,选择用户类型。
3、如果用户是学生,则需要扫码匹配老师。
4、老师的二维码在其个人主页下展示。
5、匹配成功后显示主页。
-
学生主页
-
老师主页
6、老师们点击主页“+”按钮创建任务。
7、学生和老师的主页都可以展示创建完成的任务。
8、学生可以在主页点击任务并更新任务状态。
9、老师可以查看任务状态和学生的历史任务。
十、恭喜您
祝贺您,您已成功构建一款School Diary应用并学会了:
-
在AppGallery Connect上配置云数据库和云存储。
-
使用Android Studio集成多个HMS Core服务并构建一款School Diary应用。
十一、参考
参考如下文档获取更多信息:
-
Auth Service
-
Cloud DB
-
Cloud Storage
-
Scan Kit
点击此处下载源码。
声明:本codelab实现多个HMS Core服务在单个项目中的集成,供您参考。您需要验证确保相关开源代码的安全合法合规。
欲了解更多更全技术文章,欢迎访问https://developer.huawei.com/consumer/cn/forum/?ha_source=zzh