定制Android App的OA UI

前提条件

已完成账号及用户SDK的开发,详细请参见账号及用户SDK

定制项

Android App的OA UI定制项如下。

App界面图示

可定制内容

图示

修改原生元素

  • 显示手机区号(图示中②)

  • 修改登录和注册按钮的颜色(图示中③)

  • 修改现有控件的事件

  • 修改登录超限提示信息样式(图示中④)

新增元素

  • 新增控件和单击事件

  • 新增邮箱登录方式(图示中①)

说明

Android不支持客户端修改失败提示信息,且不支持对现有控件增加自定义事件。您仅可以修改现有控件的事件或新增控件来自定义事件。

显示手机区号

登录和注册页面中的手机区号默认不显示,如果您需要显示手机区号,如+86,请根据以下内容来操作。

在App工程中添加以下代码,并通过设置supportForeignMobileNumbers参数的值来控制手机区号的显示和隐藏。

//登录页显示手机区号
OpenAccountUIConfigs.AccountPasswordLoginFlow.supportForeignMobileNumbers=true; //true为显示,false为隐藏
//忘记密码页显示手机区号
OpenAccountUIConfigs.MobileResetPasswordLoginFlow.supportForeignMobileNumbers=true; //true为显示,false为隐藏

修改登录和注册按钮的颜色

登录和注册按钮默认为浅灰色,如果您需要修改App登录和注册按钮的颜色,请根据以下步骤来操作。

  1. 在代码工程的styles.xml中新增一个style。

  2. 修改style中登录和注册按钮的颜色,其对应的参数为ali_sdk_openaccount_attrs_next_step_bg

    <style name="NewLogin" parent="@style/Login">
            //修改item属性的值
            <item name="ali_sdk_openaccount_attrs_next_step_bg">@color/color_1FC88B</item>
    </style>
  3. 修改AndroidManifest中的activity的主题配置,使设置的颜色生效。

    //AndroidManifest中的activity的主题配置修改
    <activity
                android:name="com.aliyun.iot.ilop.demo.page.login3rd.OALoginActivity"
                android:configChanges="orientation|screenSize|keyboardHidden|locale|layoutDirection|keyboard"
                android:theme="@style/NewLogin" />

修改现有控件的事件

如果您需要修改现有控件的事件,只需重新设置控件事件即可。以重写登录界面中的登录事件为例,即重新定义下图所示内容。

重写控件的事件

修改登录超限提示信息样式

登录App时,如果输入登录密码的次数达到上限,App会限制继续操作,并提示重置密码。如果您需要修改该失败提示对话框的样式,只需修改toastUtils.alert()即可。

  1. 重写toastUtils.alert()的代码。

    //OALoginActivity中的LoginTask
    protected class LoginTask extends TaskWithDialog<Void, Void, Result<LoginResult>> {
            private String loginId;
            private String password;
            private String sig;
            private String nocToken;
            private String cSessionId;
    
            public LoginTask(Activity activity, String loginId, String password, String sig, String nocToken, String cSessionId) {
                super(activity);
                this.loginId = loginId;
                this.password = password;
                this.sig = sig;
                this.nocToken = nocToken;
                this.cSessionId = cSessionId;
            }
    
            protected Result<LoginResult> asyncExecute(Void... params) {
                Map<String, Object> loginRequest = new HashMap();
                if (this.loginId != null) {
                    loginRequest.put("loginId", this.loginId);
                }
    
                if (this.password != null) {
                    try {
                        String rsaKey = RSAKey.getRsaPubkey();
                        if (TextUtils.isEmpty(rsaKey)) {
                            return null;
                        }
    
                        loginRequest.put("password", Rsa.encrypt(this.password, rsaKey));
                    } catch (Exception var4) {
                        return null;
                    }
                }
    
                LoginActivity.this.hideSoftInputForHw();
                Result result;
                if (!CommonUtils.isNetworkAvailable()) {
                    if (ConfigManager.getInstance().isSupportOfflineLogin()) {
                        return LoginActivity.this.tryOfflineLogin(this.loginId, this.password);
                    } else {
                        result = new Result();
                        result.code = 10014;
                        result.message = MessageUtils.getMessageContent(10014, new Object[0]);
                        return result;
                    }
                } else {
                    if (this.sig != null) {
                        loginRequest.put("sig", this.sig);
                    }
    
                    if (!TextUtils.isEmpty(this.cSessionId)) {
                        loginRequest.put("csessionid", this.cSessionId);
                    }
    
                    if (!TextUtils.isEmpty(this.nocToken)) {
                        loginRequest.put("nctoken", this.nocToken);
                    }
    
                    result = OpenAccountUtils.toLoginResult(RpcUtils.pureInvokeWithRiskControlInfo("loginRequest", loginRequest, "login"));
                    return ConfigManager.getInstance().isSupportOfflineLogin() && result.code == 10019 ? LoginActivity.this.tryOfflineLogin(this.loginId, this.password) : result;
                }
            }
    
            protected void doWhenException(Throwable t) {
                this.executorService.postUITask(new Runnable() {
                    public void run() {
                        ToastUtils.toastSystemError(LoginTask.this.context);
                    }
                });
            }
    
            protected void onPostExecute(Result<LoginResult> result) {
                this.dismissProgressDialog();
                super.onPostExecute(result);
    
                try {
                    if (result == null) {
                        if (ConfigManager.getInstance().isSupportOfflineLogin()) {
                            ToastUtils.toastNetworkError(this.context);
                        } else {
                            ToastUtils.toastSystemError(this.context);
                        }
                    } else {
                        android.net.Uri.Builder builder;
                        Intent h5Intent;
                        String accountName;
                        switch(result.code) {
                        case 1:
                            if (result.data != null && ((LoginResult)result.data).loginSuccessResult != null) {
                                SessionData sessionData = OpenAccountUtils.createSessionDataFromLoginSuccessResult(((LoginResult)result.data).loginSuccessResult);
                                if (sessionData.scenario == null) {
                                    sessionData.scenario = 1;
                                }
    
                                LoginActivity.this.sessionManagerService.updateSession(sessionData);
                                accountName = ((LoginResult)result.data).userInputName;
                                if (TextUtils.isEmpty(accountName)) {
                                    accountName = this.loginId;
                                }
    
                                if (ConfigManager.getInstance().isSupportOfflineLogin()) {
                                    OpenAccountSDK.getSqliteUtil().saveToSqlite(this.loginId, this.password);
                                }
    
                                boolean isExist = LoginActivity.this.loginIdEdit.saveInputHistory(accountName);
                                if (AccountPasswordLoginFlow.showTipAlertAfterLogin && !isExist) {
                                    String message = ResourceUtils.getString(LoginActivity.this.getApplicationContext(), "ali_sdk_openaccount_dynamic_text_alert_msg_after_login");
                                    LoginActivity.this.showTipDialog(String.format(message, this.loginId));
                                } else {
                                    LoginActivity.this.loginSuccess();
                                }
    
                                return;
                            }
                            break;
                        case 2:
                            SessionData sessionData1 = OpenAccountUtils.createSessionDataFromLoginSuccessResult(((LoginResult)result.data).loginSuccessResult);
                            if (sessionData1.scenario == null) {
                                sessionData1.scenario = 1;
                            }
    
                            LoginActivity.this.sessionManagerService.updateSession(sessionData1);
                            LoginActivity.this.loginSuccess();
                            break;
                        case 4037:
                            if (AccountPasswordLoginFlow.showAlertForPwdErrorToManyTimes) {
                                String postive = LoginActivity.this.getResources().getString(string.ali_sdk_openaccount_text_confirm);
                                accountName = LoginActivity.this.getResources().getString(string.ali_sdk_openaccount_text_reset_password);
                                final ToastUtils toastUtils = new ToastUtils();
                                android.content.DialogInterface.OnClickListener postiveListener = new android.content.DialogInterface.OnClickListener() {
                                    public void onClick(DialogInterface dialog, int which) {
                                        toastUtils.dismissAlertDialog(LoginActivity.this);
                                    }
                                };
                                android.content.DialogInterface.OnClickListener negativeListener = new android.content.DialogInterface.OnClickListener() {
                                    public void onClick(DialogInterface dialog, int which) {
                                        LoginActivity.this.forgetPassword((View)null);
                                    }
                                };
            //重写toastUtils类的alert()方法
                                toastUtils.alert(LoginActivity.this, "", result.message, postive, postiveListener, accountName, negativeListener);
                            } else {
                                ToastUtils.toast(this.context, result.message, result.code);
                            }
                            break;
                        case 26053:
                            if (result.data != null && ((LoginResult)result.data).checkCodeResult != null && !TextUtils.isEmpty(((LoginResult)result.data).checkCodeResult.clientVerifyData)) {
                                builder = Uri.parse(((LoginResult)result.data).checkCodeResult.clientVerifyData).buildUpon();
                                builder.appendQueryParameter("callback", "https://www.alipay.com/webviewbridge");
                                h5Intent = new Intent(LoginActivity.this, LoginDoubleCheckWebActivity.class);
                                h5Intent.putExtra("url", builder.toString());
                                h5Intent.putExtra("title", result.message);
                                h5Intent.putExtra("callback", "https://www.alipay.com/webviewbridge");
                                LoginActivity.this.startActivityForResult(h5Intent, RequestCode.NO_CAPTCHA_REQUEST_CODE);
                                return;
                            }
                            break;
                        case 26152:
                            if (result.data != null && ((LoginResult)result.data).checkCodeResult != null && !TextUtils.isEmpty(((LoginResult)result.data).checkCodeResult.clientVerifyData)) {
                                builder = Uri.parse(((LoginResult)result.data).checkCodeResult.clientVerifyData).buildUpon();
                                builder.appendQueryParameter("callback", "https://www.alipay.com/webviewbridge");
                                h5Intent = new Intent(LoginActivity.this, LoginIVWebActivity.class);
                                h5Intent.putExtra("url", builder.toString());
                                h5Intent.putExtra("title", result.message);
                                h5Intent.putExtra("callback", "https://www.alipay.com/webviewbridge");
                                LoginActivity.this.startActivityForResult(h5Intent, RequestCode.RISK_IV_REQUEST_CODE);
                            }
                            break;
                        default:
                            if (TextUtils.equals(result.type, "CALLBACK") && LoginActivity.this.getLoginCallback() != null) {
                                LoginActivity.this.getLoginCallback().onFailure(result.code, result.message);
                                return;
                            }
    
                            LoginActivity.this.onPwdLoginFail(result.code, result.message);
                        }
                    }
                } catch (Throwable var8) {
                    AliSDKLogger.e("oa", "after post execute error", var8);
                    ToastUtils.toastSystemError(this.context);
                }
    
            }
        }
  2. 定制弹出的对话框,并显示result.message的错误信息。

    toastUtils.alert(LoginActivity.this, "", result.message, postive, postiveListener, accountName, negativeListener);

新增控件和单击事件

当您需要在App中新增一个控件,并添加相应的单击事件时,可以根据以下步骤来操作。

  1. 在界面上新增一个控件,并在重写的xml文件中添加控件信息。

    添加控件

  2. 在activity中添加该控件的单击事件。

    添加事件activity

新增邮箱登录方式

如果您需要App支持通过邮箱方式登录,您需要开发邮箱相关的功能,包括邮箱登录(图示中①)、邮箱注册(图示中②)、邮箱重置密码(图示中③)等。

邮箱注册功能

  1. 在App登录页面增加邮箱登录的方式。

    Android账号及用户SDK的OALoginActivity已默认支持手机号登录和邮箱登录。此时您只需要将手机区号隐藏,并修改登录提示即可。

    1. 隐藏手机区号。

      //隐藏手机区号
       OpenAccountUIConfigs.AccountPasswordLoginFlow.supportForeignMobileNumbers = false;
    2. 修改邮箱登录信息。

      //修改登录提示信息
      this.loginIdEdit.getEditText().setHint("请输入手机号或邮箱账号");
  2. 新增邮箱注册功能。

    1. 在登录页面增加邮箱注册按钮。

      请参见本文档中新增控件和单击事件来实现。

    2. 定义注册的回调函数。

      //注册的回调函数
       private EmailRegisterCallback getEmailRegisterCallback() {
              return new EmailRegisterCallback() {
      
                  @Override
                  public void onSuccess(OpenAccountSession session) {
                      //注册成功
                  }
      
                  @Override
                  public void onFailure(int code, String message) {
                      //注册失败
                  }
      
                  @Override
                  public void onEmailSent(String email) {
      
                  }
      
              };
          }
    3. 跳转到邮箱注册界面。

      //跳转到邮箱注册界面
      OpenAccountUIService openAccountUIService = OpenAccountSDK.getService(OpenAccountUIService.class);
      openAccountUIService.showEmailRegister(OALoginActivity.this, getEmailRegisterCallback());
  3. 新增邮箱重置密码功能。

    通过邮箱方式登录App时,如果忘记了邮箱注册的密码,需要通过邮箱重置密码的功能来找回密码。

    1. 在登录页面增加邮箱找回按钮。

      请参见本文档中新增控件和单击事件来实现。

    2. 定义邮箱密码找回的回调方法。

         private EmailResetPasswordCallback getEmailResetPasswordCallback() {
              return new EmailResetPasswordCallback() {
      
                  @Override
                  public void onSuccess(OpenAccountSession session) {
                      //修改成功
                  }
      
                  @Override
                  public void onFailure(int code, String message) {
                     //修改失败
                  }
      
                  @Override
                  public void onEmailSent(String email) {
                      //发送验证码邮件
                  }
      
              };
          }
    3. 跳转到修改密码界面。

      OpenAccountUIService openAccountUIService = (OpenAccountUIService) OpenAccountSDK.getService(OpenAccountUIService.class);
      openAccountUIService.showEmailResetPassword(this, this.getEmailResetPasswordCallback());

更多UI定制参考

如果您还需定制更多的UI,例如修改更多原生元素,您可以根据以下内容来自行实现。

  • 修改更多组件的样式

    账号及用户SDK开放了所有组件的样式,在SDK的deali_sdk_openaccount_styles.xml里定义了可以直接覆盖的所有样式。您可以继承对应的style再修改AndroidManifest.xml中activity的配置。详细操作请参见本文档中修改登录和注册按钮的颜色来实现。

  • 修改OA界面布局

    控件或Activity通过LayoutMapping类来加载layout文件。建议您不要修改现有控件的ID,直接新增控件来加载自定义的layout文件。

    com.alibaba.sdk.android.openaccount.ui.LayoutMapping.put(控件(或activity).class,R.layout.xxx); 

    您可以在下图所示目录下查找所需的layout文件。

    gradle

  • 定制OA模块界面

    如果您需要自定义整个界面,建议您集成原先界面的activity后,再替换SDK中整个界面的内容。您可以参照以下示例代码来实现。

    //以登录界面为例
    //1.继承LoginActivity,重写登录界面为OALoginActivity
       public class OALoginActivity extends LoginActivity{}
    
    //2.在 OALoginActivity中重写getLayoutName方法,将布局文件的名称返回
    @Override
        protected String getLayoutName() {
            //替换成自己的布局文件名称
            return "ali_sdk_openaccount_login2";
        }
    
    //3.在账号及用户SDK初始化后,将登录界面替换到SDK中
    OALoginAdapter adapter = (OALoginAdapter) LoginBusiness.getLoginAdapter();
    adapter.setDefaultLoginClass(OALoginActivity.class);
    说明

    在重写界面时,需将原界面中的控件全部添加上,且保持ID不变。不需要的控件隐藏处理,请勿删除,否则可能出现找不到控件的错误。

如果您还需定制除原生元素以外的内容,可以参考以下信息来实现。