From 3d926d3d60a6945b26893ae7bc9bc7ffd935f50d Mon Sep 17 00:00:00 2001
From: fengjignqi <15192412330@163.com>
Date: Thu, 24 Sep 2026 17:23:47 +0800
Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E8=AF=AD=E8=A8=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
app/src/main/AndroidManifest.xml | 10 +
.../java/com/juhabi/birds/MyApplication.kt | 4 +-
.../com/juhabi/birds/base/BaseActivity.kt | 52 +++-
.../com/juhabi/birds/base/BaseFragment.kt | 52 +++-
.../birds/component/picker/DateTimePicker.kt | 36 ++-
.../birds/custom/CustomRefreshHeader.java | 6 +-
.../ConstraintLayoutDemoActivity.kt | 14 +-
.../demo/recycler/RecyclerViewDemoActivity.kt | 25 +-
.../recycler/adapter/RecyclerDemoAdapter.java | 6 +-
.../juhabi/birds/device/BindDeviceActivity.kt | 10 +-
.../birds/device/DeviceDetailActivity.kt | 18 +-
.../com/juhabi/birds/device/DeviceFragment.kt | 46 ++-
.../birds/device/adapter/DeviceAdapter.kt | 78 +++--
.../com/juhabi/birds/device/mvp/DeviceImpl.kt | 29 +-
.../birds/device/mvp/DevicePresenter.kt | 18 +-
.../com/juhabi/birds/dialog/LoadingDialog.kt | 3 +-
.../juhabi/birds/login/ForgetPsdActivity.kt | 62 +++-
.../com/juhabi/birds/login/LoginActivity.kt | 115 ++++++-
.../juhabi/birds/login/RegisterActivity.kt | 62 +++-
.../com/juhabi/birds/login/mvp/LoginImpl.kt | 33 +-
.../juhabi/birds/login/mvp/LoginPresenter.kt | 38 ++-
.../juhabi/birds/mine/MineCenterActivity.kt | 73 +++--
.../com/juhabi/birds/mine/MineFragment.kt | 95 ++++--
.../juhabi/birds/mine/ModifyPsdActivity.kt | 24 +-
.../juhabi/birds/mine/UpdateUserAccount.kt | 72 +++--
.../com/juhabi/birds/mine/WebviewActivity.kt | 18 +-
.../com/juhabi/birds/mine/mvp/MineImpl.kt | 80 +++--
.../juhabi/birds/mine/mvp/MinePresenter.kt | 48 +--
.../juhabi/birds/util/LubanCompressUtil.kt | 5 +-
.../birds/util/SharedPreferencesUtil.kt | 4 +-
.../juhabi/birds/util/data/IDCardValidate.kt | 20 +-
.../juhabi/birds/util/file/PreviewFileUtil.kt | 16 +-
.../birds/util/manager/ActivityManager.kt | 4 +-
.../birds/util/manager/UserInfoManager.kt | 14 +-
.../juhabi/birds/utils/AppUpdateManager.kt | 37 ++-
app/src/main/res/drawable/select_radio.xml | 5 +
.../activity_constraint_layout_demo.xml | 38 +--
app/src/main/res/layout/activity_login.xml | 42 ++-
.../main/res/layout/activity_mine_center.xml | 2 +-
.../main/res/layout/bind_device_layout.xml | 14 +-
.../main/res/layout/device_detail_layout.xml | 4 +-
app/src/main/res/layout/device_item.xml | 29 +-
app/src/main/res/layout/dialog_input_text.xml | 4 +-
app/src/main/res/layout/dialog_loading.xml | 2 +-
app/src/main/res/layout/forget_psd_layout.xml | 26 +-
app/src/main/res/layout/fragment_home.xml | 4 +-
app/src/main/res/layout/fragment_plan.xml | 2 +-
.../include_activity_back_gray_layout.xml | 14 +-
.../include_activity_back_greenbg_layout.xml | 24 +-
.../include_activity_back_whitebg_layout.xml | 24 +-
.../layout/include_title_history_layout.xml | 2 +-
.../res/layout/include_title_home_layout.xml | 2 +-
.../res/layout/include_title_mine_layout.xml | 2 +-
.../res/layout/include_title_plan_layout.xml | 2 +-
.../main/res/layout/mine_center_layout.xml | 38 +--
app/src/main/res/layout/modify_psd_layout.xml | 44 ++-
.../res/layout/permission_confirm_dialog.xml | 8 +-
app/src/main/res/layout/popup_add_device.xml | 8 +-
.../main/res/layout/popup_device_action.xml | 4 +-
app/src/main/res/layout/register_layout.xml | 22 +-
.../res/layout/update_user_phone_layout.xml | 12 +-
.../res/mipmap-xhdpi/icon_language_china.png | Bin 0 -> 671 bytes
.../res/mipmap-xhdpi/icon_language_en.png | Bin 0 -> 1283 bytes
.../res/mipmap-xhdpi/icon_language_st.png | Bin 0 -> 951 bytes
.../mipmap-xhdpi/icon_login_user_uncheck.png | Bin 0 -> 524 bytes
.../mipmap-xxhdpi/icon_login_user_uncheck.png | Bin 0 -> 976 bytes
.../icon_login_user_uncheck.png | Bin 0 -> 1239 bytes
app/src/main/res/values-ar/strings.xml | 292 ++++++++++++++++++
app/src/main/res/values-en/strings.xml | 292 ++++++++++++++++++
app/src/main/res/values/strings.xml | 235 +++++++++++++-
app/src/main/res/values/styles.xml | 14 +
71 files changed, 1896 insertions(+), 542 deletions(-)
create mode 100644 app/src/main/res/drawable/select_radio.xml
create mode 100644 app/src/main/res/mipmap-xhdpi/icon_language_china.png
create mode 100644 app/src/main/res/mipmap-xhdpi/icon_language_en.png
create mode 100644 app/src/main/res/mipmap-xhdpi/icon_language_st.png
create mode 100644 app/src/main/res/mipmap-xhdpi/icon_login_user_uncheck.png
create mode 100644 app/src/main/res/mipmap-xxhdpi/icon_login_user_uncheck.png
create mode 100644 app/src/main/res/mipmap-xxxhdpi/icon_login_user_uncheck.png
create mode 100644 app/src/main/res/values-ar/strings.xml
create mode 100644 app/src/main/res/values-en/strings.xml
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index e1da272..f9f3b2c 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -20,11 +20,21 @@
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
+ android:localeConfig="@xml/locales_config"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:usesCleartextTraffic="true"
android:theme="@style/Theme.KotMvpProject">
+
+
+
+
diff --git a/app/src/main/java/com/juhabi/birds/MyApplication.kt b/app/src/main/java/com/juhabi/birds/MyApplication.kt
index 0968d2b..c09f031 100644
--- a/app/src/main/java/com/juhabi/birds/MyApplication.kt
+++ b/app/src/main/java/com/juhabi/birds/MyApplication.kt
@@ -29,7 +29,9 @@ class MyApplication : Application(), Application.ActivityLifecycleCallbacks {
instance = this
- if (!isMainProcess()) return
+ if (!isMainProcess()) {
+ return
+ }
registerActivityLifecycleCallbacks(this)
initialize()
diff --git a/app/src/main/java/com/juhabi/birds/base/BaseActivity.kt b/app/src/main/java/com/juhabi/birds/base/BaseActivity.kt
index e041a30..ba54f1b 100644
--- a/app/src/main/java/com/juhabi/birds/base/BaseActivity.kt
+++ b/app/src/main/java/com/juhabi/birds/base/BaseActivity.kt
@@ -105,8 +105,10 @@ abstract class BaseActivity> :
}
/** 显示加载弹窗;按返回键取消时关闭当前 Activity。 */
- fun showLoadingDialog(message: String = "加载中...") {
- if (isFinishing || isDestroyed || loadingDialog?.isShowing == true) return
+ fun showLoadingDialog(message: String = mContext.getString(R.string.i18n_043)) { //加载中...
+ if (isFinishing || isDestroyed || loadingDialog?.isShowing == true) {
+ return
+ }
val dialog = LoadingDialog(this, message).apply {
setCanceledOnTouchOutside(false)
setCancelable(true)
@@ -210,7 +212,12 @@ abstract class BaseActivity> :
/** 使用默认说明文案申请一个或多个权限。 */
protected open fun requestPermission(vararg permissions: String) {
- getMorePermissionRequst(permissions.toList().toTypedArray(), "需要权限", "请允许应用使用相关权限", 0)
+ getMorePermissionRequst(
+ permissions.toList().toTypedArray(),
+ mContext.getString(R.string.i18n_068),
+ mContext.getString(R.string.i18n_069),
+ 0
+ ) //需要权限 / 请允许应用使用相关权限
}
/** 权限被拒绝后的兼容回调,子类可重写。 */
@@ -224,10 +231,10 @@ abstract class BaseActivity> :
protected open fun onPermissionsDenied(permissions: Array, permanentlyDenied: Boolean) {
noAllowPermission()
if (permanentlyDenied && !isFinishing) {
- AlertDialog.Builder(this).setTitle("权限被拒绝")
- .setMessage("请在应用设置中开启所需权限")
- .setNegativeButton("取消", null)
- .setPositiveButton("去设置") { _, _ -> openAppPermissionSettings() }
+ AlertDialog.Builder(this).setTitle(mContext.getString(R.string.i18n_070)) //权限被拒绝
+ .setMessage(mContext.getString(R.string.i18n_071)) //请在应用设置中开启所需权限
+ .setNegativeButton(mContext.getString(R.string.i18n_041), null) //取消
+ .setPositiveButton(mContext.getString(R.string.i18n_072)) { _, _ -> openAppPermissionSettings() } //去设置
.show()
}
}
@@ -252,8 +259,8 @@ abstract class BaseActivity> :
fun showDialogtext(
title: String,
content: String,
- okText: String = "确定",
- cancelText: String = "取消",
+ okText: String = mContext.getString(R.string.i18n_027), //确定
+ cancelText: String = mContext.getString(R.string.i18n_041), //取消
click: OnDialogClick? = null,
touchOutsideCancel: Boolean = true
) {
@@ -298,8 +305,8 @@ abstract class BaseActivity> :
tip: String = "",
content: String = "",
touchOutsideCancel: Boolean = true,
- cancelText: String = "取消",
- confirmText: String = "确定",
+ cancelText: String = mContext.getString(R.string.i18n_041), //取消
+ confirmText: String = mContext.getString(R.string.i18n_027), //确定
click: OnDialogClick? = null
) {
val view = layoutInflater.inflate(R.layout.dialog_input_text, null)
@@ -324,7 +331,7 @@ abstract class BaseActivity> :
setOnClickListener {
val value = input.text.toString().trim()
if (value.isEmpty()) {
- input.error = hint.ifBlank { "请输入内容" }
+ input.error = hint.ifBlank { mContext.getString(R.string.i18n_073) } //请输入内容
return@setOnClickListener
}
dialog.dismiss()
@@ -377,8 +384,14 @@ abstract class BaseActivity> :
fun showPermissionDialog(icon: Drawable?, showIcon: Boolean, title: String, content: String) {
showPermissionDialog(title, content)
permissionDialog?.findViewById(R.id.permissionIcon)?.apply {
- visibility = if (showIcon) android.view.View.VISIBLE else android.view.View.GONE
- if (icon != null) setImageDrawable(icon)
+ visibility = if (showIcon) {
+ android.view.View.VISIBLE
+ } else {
+ android.view.View.GONE
+ }
+ if (icon != null) {
+ setImageDrawable(icon)
+ }
}
}
@@ -388,8 +401,14 @@ abstract class BaseActivity> :
view.findViewById(R.id.permissionTitle).text = title
view.findViewById(R.id.permissionContent).text = content
val dialog = AlertDialog.Builder(this).setView(view).create()
- view.findViewById(R.id.permissionCancel).setOnClickListener { dialog.dismiss(); onCancel() }
- view.findViewById(R.id.permissionAllow).setOnClickListener { dialog.dismiss(); onAllow() }
+ view.findViewById(R.id.permissionCancel).setOnClickListener {
+ dialog.dismiss()
+ onCancel()
+ }
+ view.findViewById(R.id.permissionAllow).setOnClickListener {
+ dialog.dismiss()
+ onAllow()
+ }
dialog.setOnShowListener {
dialog.window?.apply {
// 动态权限确认弹窗固定左右各 50dp,避免使用 AlertDialog 默认宽度。
@@ -426,3 +445,4 @@ abstract class BaseActivity> :
super.onDestroy()
}
}
+
diff --git a/app/src/main/java/com/juhabi/birds/base/BaseFragment.kt b/app/src/main/java/com/juhabi/birds/base/BaseFragment.kt
index b092c14..98d59f5 100644
--- a/app/src/main/java/com/juhabi/birds/base/BaseFragment.kt
+++ b/app/src/main/java/com/juhabi/birds/base/BaseFragment.kt
@@ -99,9 +99,11 @@ abstract class BaseFragment> : Fragment(), I
}
/** 在 Fragment 已附加时显示加载弹窗。 */
- fun showLoadingDialog(message: String = "加载中...") {
+ fun showLoadingDialog(message: String = mContext.getString(R.string.i18n_043)) { //加载中...
val currentContext = context ?: return
- if (!isAdded || loadingDialog?.isShowing == true) return
+ if (!isAdded || loadingDialog?.isShowing == true) {
+ return
+ }
val dialog = LoadingDialog(currentContext, message).apply {
setCanceledOnTouchOutside(false)
setCancelable(true)
@@ -210,7 +212,12 @@ abstract class BaseFragment> : Fragment(), I
/** 使用默认说明文案申请一个或多个权限。 */
protected open fun requestPermission(vararg permissions: String) {
- getMorePermissionRequst(permissions.toList().toTypedArray(), "需要权限", "请允许应用使用相关权限", 0)
+ getMorePermissionRequst(
+ permissions.toList().toTypedArray(),
+ mContext.getString(R.string.i18n_068),
+ mContext.getString(R.string.i18n_069),
+ 0
+ ) //需要权限 / 请允许应用使用相关权限
}
/** 权限被拒绝后的兼容回调,子类可重写。 */
@@ -224,10 +231,10 @@ abstract class BaseFragment> : Fragment(), I
protected open fun onPermissionsDenied(permissions: Array, permanentlyDenied: Boolean) {
noAllowPermission()
if (permanentlyDenied && isAdded) {
- AlertDialog.Builder(requireContext()).setTitle("权限被拒绝")
- .setMessage("请在应用设置中开启所需权限")
- .setNegativeButton("取消", null)
- .setPositiveButton("去设置") { _, _ -> openAppPermissionSettings() }
+ AlertDialog.Builder(requireContext()).setTitle(mContext.getString(R.string.i18n_070)) //权限被拒绝
+ .setMessage(mContext.getString(R.string.i18n_071)) //请在应用设置中开启所需权限
+ .setNegativeButton(mContext.getString(R.string.i18n_041), null) //取消
+ .setPositiveButton(mContext.getString(R.string.i18n_072)) { _, _ -> openAppPermissionSettings() } //去设置
.show()
}
}
@@ -252,8 +259,8 @@ abstract class BaseFragment> : Fragment(), I
fun showDialogtext(
title: String,
content: String,
- okText: String = "确定",
- cancelText: String = "取消",
+ okText: String = mContext.getString(R.string.i18n_027), //确定
+ cancelText: String = mContext.getString(R.string.i18n_041), //取消
click: OnDialogClick? = null,
touchOutsideCancel: Boolean = true
) {
@@ -298,8 +305,8 @@ abstract class BaseFragment> : Fragment(), I
tip: String = "",
content: String = "",
touchOutsideCancel: Boolean = true,
- cancelText: String = "取消",
- confirmText: String = "确定",
+ cancelText: String = mContext.getString(R.string.i18n_041), //取消
+ confirmText: String = mContext.getString(R.string.i18n_027), //确定
click: OnDialogClick? = null
) {
val view = layoutInflater.inflate(R.layout.dialog_input_text, null)
@@ -324,7 +331,7 @@ abstract class BaseFragment> : Fragment(), I
setOnClickListener {
val value = input.text.toString().trim()
if (value.isEmpty()) {
- input.error = hint.ifBlank { "请输入内容" }
+ input.error = hint.ifBlank { mContext.getString(R.string.i18n_073) } //请输入内容
return@setOnClickListener
}
dialog.dismiss()
@@ -376,8 +383,14 @@ abstract class BaseFragment> : Fragment(), I
fun showPermissionDialog(icon: Drawable?, showIcon: Boolean, title: String, content: String) {
showPermissionDialog(title, content)
permissionDialog?.findViewById(R.id.permissionIcon)?.apply {
- visibility = if (showIcon) View.VISIBLE else View.GONE
- if (icon != null) setImageDrawable(icon)
+ visibility = if (showIcon) {
+ View.VISIBLE
+ } else {
+ View.GONE
+ }
+ if (icon != null) {
+ setImageDrawable(icon)
+ }
}
}
/** 在系统权限申请框弹出前,显示自定义权限说明弹窗。 */
@@ -386,8 +399,14 @@ abstract class BaseFragment> : Fragment(), I
view.findViewById(R.id.permissionTitle).text = title
view.findViewById(R.id.permissionContent).text = content
val dialog = AlertDialog.Builder(requireContext()).setView(view).create()
- view.findViewById(R.id.permissionCancel).setOnClickListener { dialog.dismiss(); onCancel() }
- view.findViewById(R.id.permissionAllow).setOnClickListener { dialog.dismiss(); onAllow() }
+ view.findViewById(R.id.permissionCancel).setOnClickListener {
+ dialog.dismiss()
+ onCancel()
+ }
+ view.findViewById(R.id.permissionAllow).setOnClickListener {
+ dialog.dismiss()
+ onAllow()
+ }
dialog.setOnShowListener {
dialog.window?.apply {
// 动态权限确认弹窗固定左右各 50dp,避免使用 AlertDialog 默认宽度。
@@ -421,3 +440,4 @@ abstract class BaseFragment> : Fragment(), I
super.onDestroyView()
}
}
+
diff --git a/app/src/main/java/com/juhabi/birds/component/picker/DateTimePicker.kt b/app/src/main/java/com/juhabi/birds/component/picker/DateTimePicker.kt
index 9346cad..63b2bb9 100644
--- a/app/src/main/java/com/juhabi/birds/component/picker/DateTimePicker.kt
+++ b/app/src/main/java/com/juhabi/birds/component/picker/DateTimePicker.kt
@@ -37,10 +37,17 @@ class DateTimePicker private constructor(
}
.setType(mode.visibleItems)
.setDate(selectedTime)
- .setTitleText(mode.title)
- .setSubmitText("确定")
- .setCancelText("取消")
- .setLabel("年", "月", "日", "时", "分", "秒")
+ .setTitleText(context.getString(mode.titleRes))
+ .setSubmitText(context.getString(com.juhabi.birds.R.string.i18n_027)) //确定
+ .setCancelText(context.getString(com.juhabi.birds.R.string.i18n_041)) //取消
+ .setLabel(
+ context.getString(com.juhabi.birds.R.string.i18n_198),
+ context.getString(com.juhabi.birds.R.string.i18n_199),
+ context.getString(com.juhabi.birds.R.string.i18n_200),
+ context.getString(com.juhabi.birds.R.string.i18n_201),
+ context.getString(com.juhabi.birds.R.string.i18n_202),
+ context.getString(com.juhabi.birds.R.string.i18n_203)
+ ) //年 / 月 / 日 / 时 / 分 / 秒
.setSubmitColor(ContextCompat.getColor(context, R.color.bottom_nav_selected))
.setCancelColor(ContextCompat.getColor(context, R.color.bottom_nav_unselected))
.setTextColorCenter(ContextCompat.getColor(context, R.color.bottom_nav_selected))
@@ -57,22 +64,22 @@ class DateTimePicker private constructor(
enum class Mode(
internal val visibleItems: BooleanArray,
internal val pattern: String,
- internal val title: String
+ internal val titleRes: Int
) {
DATE(
booleanArrayOf(true, true, true, false, false, false),
"yyyy-MM-dd",
- "选择日期"
+ R.string.picker_select_date //选择日期
),
TIME(
booleanArrayOf(false, false, false, true, true, true),
"HH:mm:ss",
- "选择时间"
+ R.string.picker_select_time //选择时间
),
DATE_TIME(
booleanArrayOf(true, true, true, true, true, true),
"yyyy-MM-dd HH:mm:ss",
- "选择日期时间"
+ R.string.picker_select_date_time //选择日期时间
);
internal fun format(date: Date): String {
@@ -113,7 +120,7 @@ class DateTimePicker private constructor(
fun setTimeRange(startTimeInMillis: Long, endTimeInMillis: Long): Builder {
return apply {
- require(startTimeInMillis <= endTimeInMillis) { "开始时间不能晚于结束时间" }
+ require(startTimeInMillis <= endTimeInMillis) { context.getString(com.juhabi.birds.R.string.i18n_204) } //开始时间不能晚于结束时间
startTime = Calendar.getInstance().apply { timeInMillis = startTimeInMillis }
endTime = Calendar.getInstance().apply { timeInMillis = endTimeInMillis }
}
@@ -126,13 +133,17 @@ class DateTimePicker private constructor(
}
fun build(): DateTimePicker {
- val listener = requireNotNull(onSelected) { "必须设置选择结果监听器" }
+ val listener = requireNotNull(onSelected) { context.getString(com.juhabi.birds.R.string.i18n_205) } //必须设置选择结果监听器
val effectiveSelectedTime = selectedTime.clone() as Calendar
startTime?.let { start ->
- if (effectiveSelectedTime.before(start)) effectiveSelectedTime.timeInMillis = start.timeInMillis
+ if (effectiveSelectedTime.before(start)) {
+ effectiveSelectedTime.timeInMillis = start.timeInMillis
+ }
}
endTime?.let { end ->
- if (effectiveSelectedTime.after(end)) effectiveSelectedTime.timeInMillis = end.timeInMillis
+ if (effectiveSelectedTime.after(end)) {
+ effectiveSelectedTime.timeInMillis = end.timeInMillis
+ }
}
return DateTimePicker(
context = context,
@@ -145,3 +156,4 @@ class DateTimePicker private constructor(
}
}
}
+
diff --git a/app/src/main/java/com/juhabi/birds/custom/CustomRefreshHeader.java b/app/src/main/java/com/juhabi/birds/custom/CustomRefreshHeader.java
index da37f73..dd28515 100644
--- a/app/src/main/java/com/juhabi/birds/custom/CustomRefreshHeader.java
+++ b/app/src/main/java/com/juhabi/birds/custom/CustomRefreshHeader.java
@@ -96,7 +96,9 @@ public class CustomRefreshHeader extends RelativeLayout implements RefreshHeader
* 平滑动画过渡回正常大小
*/
private void animateToNormalScale() {
- if (ivRefresh == null) return;
+ if (ivRefresh == null) {
+ return;
+ }
float startScale = mCurrentScale;
ValueAnimator scaleAnimator = ValueAnimator.ofFloat(startScale, 1f);
@@ -175,4 +177,4 @@ public class CustomRefreshHeader extends RelativeLayout implements RefreshHeader
animateToNormalScale();
}
}
-}
\ No newline at end of file
+}
diff --git a/app/src/main/java/com/juhabi/birds/demo/constraint/ConstraintLayoutDemoActivity.kt b/app/src/main/java/com/juhabi/birds/demo/constraint/ConstraintLayoutDemoActivity.kt
index 3d178be..6e97ec8 100644
--- a/app/src/main/java/com/juhabi/birds/demo/constraint/ConstraintLayoutDemoActivity.kt
+++ b/app/src/main/java/com/juhabi/birds/demo/constraint/ConstraintLayoutDemoActivity.kt
@@ -1,5 +1,6 @@
package com.juhabi.birds.demo.constraint
+import com.juhabi.birds.R
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@@ -22,8 +23,17 @@ class ConstraintLayoutDemoActivity : BaseActivityNoPresenter
val item = adapter.data[position]
val message = when (view.id) {
- R.id.titleTextView -> "点击了标题:${item.title}"
- R.id.idTextView -> "点击了编号:${item.id}"
- R.id.statusTextView -> "点击了状态:${item.status}"
- R.id.timeTextView -> "点击了更新时间:${item.updateTime}"
+ R.id.titleTextView -> getString(R.string.demo_clicked_title, item.title) //点击了标题:%1$s
+ R.id.idTextView -> getString(R.string.demo_clicked_id, item.id) //点击了编号:%1$d
+ R.id.statusTextView -> getString(R.string.demo_clicked_status, item.status) //点击了状态:%1$s
+ R.id.timeTextView -> getString(R.string.demo_clicked_update_time, item.updateTime) //点击了更新时间:%1$s
else -> return@setOnItemChildClickListener
}
android.widget.Toast.makeText(this, message, android.widget.Toast.LENGTH_SHORT).show()
@@ -42,18 +42,19 @@ class RecyclerViewDemoActivity : BaseActivityNoPresenter {
return listOf(
- RecyclerDemoItem(1001, "设备巡检记录", "已完成", "2026-07-23 09:30:00"),
- RecyclerDemoItem(1002, "自动浇水计划", "执行中", "2026-07-23 10:15:20"),
- RecyclerDemoItem(1003, "传感器数据同步", "待处理", "2026-07-23 11:08:45"),
- RecyclerDemoItem(1004, "系统配置备份", "已完成", "2026-07-22 18:42:10"),
- RecyclerDemoItem(1005, "固件升级任务", "失败", "2026-07-22 16:20:35"),
- RecyclerDemoItem(1006, "用户操作日志", "已完成", "2026-07-22 14:05:00")
+ RecyclerDemoItem(1001, mContext.getString(R.string.i18n_188), mContext.getString(R.string.i18n_189), "2026-07-23 09:30:00"), //设备巡检记录 / 已完成
+ RecyclerDemoItem(1002, mContext.getString(R.string.i18n_190), mContext.getString(R.string.i18n_191), "2026-07-23 10:15:20"), //自动浇水计划 / 执行中
+ RecyclerDemoItem(1003, mContext.getString(R.string.i18n_192), mContext.getString(R.string.i18n_193), "2026-07-23 11:08:45"), //传感器数据同步 / 待处理
+ RecyclerDemoItem(1004, mContext.getString(R.string.i18n_194), mContext.getString(R.string.i18n_189), "2026-07-22 18:42:10"), //系统配置备份 / 已完成
+ RecyclerDemoItem(1005, mContext.getString(R.string.i18n_195), mContext.getString(R.string.i18n_196), "2026-07-22 16:20:35"), //固件升级任务 / 失败
+ RecyclerDemoItem(1006, mContext.getString(R.string.i18n_197), mContext.getString(R.string.i18n_189), "2026-07-22 14:05:00") //用户操作日志 / 已完成
)
}
}
+
diff --git a/app/src/main/java/com/juhabi/birds/demo/recycler/adapter/RecyclerDemoAdapter.java b/app/src/main/java/com/juhabi/birds/demo/recycler/adapter/RecyclerDemoAdapter.java
index 0073ba7..eaade8e 100644
--- a/app/src/main/java/com/juhabi/birds/demo/recycler/adapter/RecyclerDemoAdapter.java
+++ b/app/src/main/java/com/juhabi/birds/demo/recycler/adapter/RecyclerDemoAdapter.java
@@ -33,11 +33,11 @@ public class RecyclerDemoAdapter extends BaseQuickAdapter
}
override fun createPresenter(): DevicePresenter? {
- return DevicePresenter(DeviceImpl())
+ return DevicePresenter(DeviceImpl(mContext), mContext)
}
override fun initWindows() {
@@ -35,7 +35,7 @@ class BindDeviceActivity: BaseActivity
}
override fun initView(savedInstanceState: Bundle?) {
- binding.includedTitle.tvTitle.setText(R.string.bind_device)
+ binding.includedTitle.tvTitle.setText(R.string.bind_device) //绑定设备
listOf(
binding.tvBind,
binding.includedTitle.imgBack,
@@ -67,11 +67,11 @@ class BindDeviceActivity: BaseActivity
?.trim()
.orEmpty()
if (imei.isBlank()) {
- showMessage("请输入设备编码")
+ showMessage(mContext.getString(R.string.i18n_022)) //请输入设备编码
return
}
if (name.isBlank()) {
- showMessage("请输入设备名称")
+ showMessage(mContext.getString(R.string.i18n_024)) //请输入设备名称
return
}
presenter?.bindDevice(imei, name)
@@ -92,3 +92,5 @@ class BindDeviceActivity: BaseActivity
}
+
+
diff --git a/app/src/main/java/com/juhabi/birds/device/DeviceDetailActivity.kt b/app/src/main/java/com/juhabi/birds/device/DeviceDetailActivity.kt
index 34e3c4f..f66b162 100644
--- a/app/src/main/java/com/juhabi/birds/device/DeviceDetailActivity.kt
+++ b/app/src/main/java/com/juhabi/birds/device/DeviceDetailActivity.kt
@@ -33,7 +33,7 @@ class DeviceDetailActivity :
}
override fun createPresenter(): DevicePresenter? {
- return DevicePresenter(DeviceImpl())
+ return DevicePresenter(DeviceImpl(mContext), mContext)
}
override fun initWindows() {
@@ -48,10 +48,10 @@ class DeviceDetailActivity :
override fun initView(savedInstanceState: Bundle?) {
device = intent.getSerializableExtra(EXTRA_DEVICE) as? DeviceEntity
- binding.includeTitle.tvTitle.text = device?.name ?: "设备详情"
+ binding.includeTitle.tvTitle.text = device?.name ?: mContext.getString(R.string.i18n_074) //设备详情
val deviceNo = device?.deviceNo
if (deviceNo.isNullOrBlank()) {
- showError("设备信息无效")
+ showError(mContext.getString(R.string.i18n_075)) //设备信息无效
} else {
presenter?.getDeviceDetail(deviceNo)
}
@@ -75,7 +75,7 @@ class DeviceDetailActivity :
R.id.img_switch_ctrl ->{
val currentDevice = device ?: return
if (currentDevice.status == "0") {
- showMessage("设备已经离线")
+ showMessage(mContext.getString(R.string.i18n_076)) //设备已经离线
return
}
@@ -102,7 +102,7 @@ class DeviceDetailActivity :
override fun getDeviceDetailSuccess(device: DeviceEntity) {
this.device = device
binding.includeTitle.tvTitle.text = device.name
- binding.tvBind.text = "设备编号:${device.deviceNo}"
+ binding.tvBind.text = getString(R.string.device_number_value, device.deviceNo) //设备编号:%1$s
updateCoverControlState(device.hoodCoverOn == HOOD_COVER_ON)
}
@@ -124,7 +124,11 @@ class DeviceDetailActivity :
private fun updateCoverControlState(coverOn: Boolean) {
binding.switchCtrl.isChecked = coverOn
binding.imgSwitchCtrl.setImageResource(
- if (coverOn) R.mipmap.icon_switch_on else R.mipmap.icon_switch_off
+ if (coverOn) {
+ R.mipmap.icon_switch_on
+ } else {
+ R.mipmap.icon_switch_off
+ }
)
}
@@ -134,3 +138,5 @@ class DeviceDetailActivity :
}
+
+
diff --git a/app/src/main/java/com/juhabi/birds/device/DeviceFragment.kt b/app/src/main/java/com/juhabi/birds/device/DeviceFragment.kt
index 0c93de4..df6b896 100644
--- a/app/src/main/java/com/juhabi/birds/device/DeviceFragment.kt
+++ b/app/src/main/java/com/juhabi/birds/device/DeviceFragment.kt
@@ -35,7 +35,7 @@ class DeviceFragment : BaseFragment(),
}
override fun createPresenter(): DevicePresenter {
- return DevicePresenter(DeviceImpl())
+ return DevicePresenter(DeviceImpl(mContext), mContext)
}
override fun initView(savedInstanceState: Bundle?) {
@@ -59,11 +59,11 @@ class DeviceFragment : BaseFragment(),
view,
onRename = {
showEditDialog(
- title = "重命名设备",
- label = "设备名称",
+ title = mContext.getString(R.string.i18n_077), //重命名设备
+ label = mContext.getString(R.string.i18n_023), //设备名称
hint = device.name,
- tip = "最多支持20个字符,支持中英文、数字和字符。",
- confirmText = "确认修改",
+ tip = mContext.getString(R.string.i18n_078), //最多支持20个字符,支持中英文、数字和字符。
+ confirmText = mContext.getString(R.string.i18n_042), //确认修改
click = object : OnDialogClick {
override fun onCancelClick() {
}
@@ -76,10 +76,10 @@ class DeviceFragment : BaseFragment(),
},
onUnbind = {//解绑设备
showDialogtext(
- "提示",
- "确定要解绑该设备?",
- "确定",
- "取消",
+ mContext.getString(R.string.i18n_063), //提示
+ mContext.getString(R.string.i18n_079), //确定要解绑该设备?
+ mContext.getString(R.string.i18n_027), //确定
+ mContext.getString(R.string.i18n_041), //取消
object : OnDialogClick {
override fun onCancelClick() {
}
@@ -98,7 +98,7 @@ class DeviceFragment : BaseFragment(),
val device = deviceAdapter.data[position]
// 没有经纬度时只展示空态,不要把空坐标传给地图
if (device.lat.isNullOrBlank() || device.lng.isNullOrBlank()) {
- showMessage("暂无定位")
+ showMessage(mContext.getString(R.string.i18n_080)) //暂无定位
return@setOnItemChildClickListener
}
// 地图页后续接入,这里先预留点击入口
@@ -136,8 +136,8 @@ class DeviceFragment : BaseFragment(),
// 扫一扫:先申请相机权限,授权后再打开扫码页
getMorePermissionRequst(
scanPermissions(),
- "相机权限",
- "扫一扫功能需要使用相机权限",
+ mContext.getString(R.string.i18n_081), //相机权限
+ mContext.getString(R.string.i18n_082), //扫一扫功能需要使用相机权限
REQUEST_CAMERA_AND_ALBUM
)
}
@@ -157,7 +157,11 @@ class DeviceFragment : BaseFragment(),
binding.refresh.finishLoadMore()
binding.refresh.setNoMoreData(devices.size < PAGE_SIZE)
binding.relNoMore.visibility =
- if (deviceAdapter.data.isEmpty()) View.VISIBLE else View.GONE
+ if (deviceAdapter.data.isEmpty()) {
+ View.VISIBLE
+ } else {
+ View.GONE
+ }
}
override fun bindDeviceSuccess(message: String) {
@@ -180,11 +184,17 @@ class DeviceFragment : BaseFragment(),
deviceAdapter.data.removeAt(removedPosition)
deviceAdapter.notifyItemRemoved(removedPosition)
binding.relNoMore.visibility =
- if (deviceAdapter.data.isEmpty()) View.VISIBLE else View.GONE
+ if (deviceAdapter.data.isEmpty()) {
+ View.VISIBLE
+ } else {
+ View.GONE
+ }
}
// 等待 RecyclerView 删除动画完成后再刷新,避免刷新结果立即覆盖动画。
binding.root.postDelayed({
- if (!isAdded) return@postDelayed
+ if (!isAdded) {
+ return@postDelayed
+ }
currentPage = FIRST_PAGE
requestDeviceList()
}, REMOVE_ANIMATION_DELAY_MS)
@@ -265,7 +275,7 @@ class DeviceFragment : BaseFragment(),
?.joinToString("\n")
.orEmpty()
if (content.isBlank()) {
- ToastUtil.showShort(mContext, "未识别到内容")
+ ToastUtil.showShort(mContext, mContext.getString(R.string.i18n_083)) //未识别到内容
} else {
ToastUtil.showShort(mContext, content)
}
@@ -276,7 +286,7 @@ class DeviceFragment : BaseFragment(),
)
ToastUtil.showShort(
mContext,
- resultError.takeUnless { it.isNullOrBlank() } ?: "扫码失败"
+ resultError.takeUnless { it.isNullOrBlank() } ?: mContext.getString(R.string.i18n_084) //扫码失败
)
}
else -> {
@@ -288,3 +298,5 @@ class DeviceFragment : BaseFragment(),
}
}
+
+
diff --git a/app/src/main/java/com/juhabi/birds/device/adapter/DeviceAdapter.kt b/app/src/main/java/com/juhabi/birds/device/adapter/DeviceAdapter.kt
index b22917f..1b5e98a 100644
--- a/app/src/main/java/com/juhabi/birds/device/adapter/DeviceAdapter.kt
+++ b/app/src/main/java/com/juhabi/birds/device/adapter/DeviceAdapter.kt
@@ -2,6 +2,8 @@ package com.juhabi.birds.device.adapter
import android.widget.ProgressBar
import androidx.core.content.ContextCompat
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.ClipDrawable
import com.chad.library.adapter.base.BaseQuickAdapter
import com.chad.library.adapter.base.viewholder.BaseViewHolder
import com.juhabi.birds.R
@@ -9,18 +11,20 @@ import com.juhabi.birds.device.entity.DeviceEntity
/** 临时设备列表适配器,后续可根据设计稿替换 item 展示。 */
class DeviceAdapter : BaseQuickAdapter(R.layout.device_item) {
+ private val progressDrawables = mutableMapOf()
+
init {
addChildClickViewIds(R.id.img_more)
}
override fun convert(holder: BaseViewHolder, item: DeviceEntity) {
//设备名字
- holder.setText(R.id.tv_dev_name, item.name.ifBlank { "未命名设备" })
+ holder.setText(R.id.tv_dev_name, item.name.ifBlank { context.getString(com.juhabi.birds.R.string.i18n_170) }) //未命名设备
//在线状态
if (item.status == "1") {
- holder.setText(R.id.tv_dev_online_status, "在线")
+ holder.setText(R.id.tv_dev_online_status, context.getString(com.juhabi.birds.R.string.i18n_030)) //在线
} else {
- holder.setText(R.id.tv_dev_online_status, "离线")
+ holder.setText(R.id.tv_dev_online_status, context.getString(com.juhabi.birds.R.string.i18n_171)) //离线
}
val statusRes = if (item.status == "1") {
R.drawable.shape_device_online_bg
@@ -41,19 +45,19 @@ class DeviceAdapter : BaseQuickAdapter(R.layout.de
val wifiTextColor: Int
if (item.signalLevel == 0) {
imgResource = R.mipmap.icon_dev_wifi_offline
- signalText = "离线"
+ signalText = context.getString(com.juhabi.birds.R.string.i18n_171) //离线
wifiTextColor = R.color.color_999999
} else if (item.signalLevel == 1) {
imgResource = R.mipmap.icon_dev_wifi_low
- signalText = "弱"
+ signalText = context.getString(com.juhabi.birds.R.string.i18n_172) //弱
wifiTextColor = R.color.color_e34d59
} else if (item.signalLevel == 2) {
imgResource = R.mipmap.icon_dev_wifi_mid
- signalText = "一般"
+ signalText = context.getString(com.juhabi.birds.R.string.i18n_173) //一般
wifiTextColor = R.color.color_ff942a
} else {
imgResource = R.mipmap.icon_dev_wifi_hight
- signalText = "强"
+ signalText = context.getString(com.juhabi.birds.R.string.i18n_033) //强
wifiTextColor = R.color.color_zhu
}
@@ -62,15 +66,23 @@ class DeviceAdapter : BaseQuickAdapter(R.layout.de
holder.setTextColor(R.id.tv_signal_text,wifiTextColor)
holder.setText(
R.id.tv_head_foot_connect_status,
- if (item.hoodConnected == "0") "断开" else "已连接"
+ if (item.hoodConnected == "0") {
+ context.getString(com.juhabi.birds.R.string.i18n_174)
+ } else {
+ context.getString(com.juhabi.birds.R.string.i18n_035)
+ } //断开 / 已连接
)
}else{
holder.setImageResource(R.id.img_signal, R.mipmap.icon_dev_wifi_offline)
- holder.setText(R.id.tv_signal_text, "离线")
+ holder.setText(R.id.tv_signal_text, context.getString(com.juhabi.birds.R.string.i18n_171)) //离线
holder.setTextColor(R.id.tv_signal_text, R.color.color_999999)
holder.setText(
R.id.tv_head_foot_connect_status,
- if (item.hoodConnected == "0") "断开" else "已连接"
+ if (item.hoodConnected == "0") {
+ context.getString(com.juhabi.birds.R.string.i18n_174)
+ } else {
+ context.getString(com.juhabi.birds.R.string.i18n_035)
+ } //断开 / 已连接
)
}
@@ -123,20 +135,15 @@ class DeviceAdapter : BaseQuickAdapter(R.layout.de
} else {
R.drawable.progress_offline_bg
}
- // mutate 避免两条进度条共用同一份离线 drawable 状态
- progressHead.progressDrawable = ContextCompat.getDrawable(
- context,
- headProgressRes
- )?.mutate()
- progressFoot.progressDrawable = ContextCompat.getDrawable(
- context,
- footProgressRes
- )?.mutate()
- // 替换 drawable 后进度会被重置,需要重新写入
+ // 每个进度条使用独立 drawable 状态,避免 RecyclerView 复用时 clip 进度互相影响。
+ progressHead.progressDrawable = getProgressDrawable(headProgressRes)
+ progressFoot.progressDrawable = getProgressDrawable(footProgressRes)
progressHead.max = 100
progressHead.progress = headBattery
progressFoot.max = 100
progressFoot.progress = footBattery
+ setProgressDrawableLevel(progressHead, headBattery)
+ setProgressDrawableLevel(progressFoot, footBattery)
//tv_location_detail 显示地址
holder.setText(R.id.tv_location_detail, item.address)
@@ -158,8 +165,37 @@ class DeviceAdapter : BaseQuickAdapter(R.layout.de
}
}
+ private fun getProgressDrawable(resourceId: Int): Drawable? {
+ val constantState = progressDrawables.getOrPut(resourceId) {
+ ContextCompat.getDrawable(context, resourceId)?.constantState
+ }
+ return constantState?.newDrawable(context.resources)?.mutate()
+ }
+
+ private fun setProgressDrawableLevel(progressBar: ProgressBar, progress: Int) {
+ val drawable = progressBar.progressDrawable ?: return
+ val clipDrawable = drawable.findClipDrawable() ?: return
+ clipDrawable.level = (progress * MAX_DRAWABLE_LEVEL / progressBar.max)
+ .coerceIn(0, MAX_DRAWABLE_LEVEL)
+ drawable.invalidateSelf()
+ }
+
+ private fun Drawable.findClipDrawable(): ClipDrawable? {
+ if (this is ClipDrawable) {
+ return this
+ }
+ if (this is android.graphics.drawable.LayerDrawable) {
+ for (index in 0 until numberOfLayers) {
+ getDrawable(index).findClipDrawable()?.let { return it }
+ }
+ }
+ return null
+ }
+
private companion object {
const val BATTERY_LOW_MAX = 30
const val BATTERY_MID_MAX = 70
+ const val MAX_DRAWABLE_LEVEL = 10_000
}
-}
\ No newline at end of file
+}
+
diff --git a/app/src/main/java/com/juhabi/birds/device/mvp/DeviceImpl.kt b/app/src/main/java/com/juhabi/birds/device/mvp/DeviceImpl.kt
index e17c390..0e78ed7 100644
--- a/app/src/main/java/com/juhabi/birds/device/mvp/DeviceImpl.kt
+++ b/app/src/main/java/com/juhabi/birds/device/mvp/DeviceImpl.kt
@@ -1,7 +1,9 @@
package com.juhabi.birds.device.mvp
+import android.content.Context
import android.os.Handler
import android.os.Looper
+import com.juhabi.birds.R
import com.juhabi.birds.device.entity.DeviceEntity
import com.juhabi.birds.network.ApiClient
import com.juhabi.birds.network.BaseBean
@@ -11,7 +13,7 @@ import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
-class DeviceImpl : DeviceContract.DataModel {
+class DeviceImpl(private val mContext: Context) : DeviceContract.DataModel {
private val mainHandler = Handler(Looper.getMainLooper())
private var deviceListCall: Call>>? = null
private var deviceDetailCall: Call>? = null
@@ -42,7 +44,7 @@ class DeviceImpl : DeviceContract.DataModel {
callback.success(devices)
} else {
callback.fail(
- bean?.message?.ifBlank { null } ?: "获取设备列表失败"
+ bean?.message?.ifBlank { null } ?: mContext.getString(R.string.i18n_175) //获取设备列表失败
)
}
}
@@ -54,7 +56,7 @@ class DeviceImpl : DeviceContract.DataModel {
) {
if (!call.isCanceled) {
mainHandler.post {
- callback.fail(error.message ?: "网络请求失败")
+ callback.fail(error.message ?: mContext.getString(R.string.i18n_150)) //网络请求失败
}
}
}
@@ -86,7 +88,7 @@ class DeviceImpl : DeviceContract.DataModel {
callback.success(device)
} else {
callback.fail(
- bean?.message?.ifBlank { null } ?: "获取设备详情失败"
+ bean?.message?.ifBlank { null } ?: mContext.getString(R.string.i18n_176) //获取设备详情失败
)
}
}
@@ -98,7 +100,7 @@ class DeviceImpl : DeviceContract.DataModel {
) {
if (!call.isCanceled) {
mainHandler.post {
- callback.fail(error.message ?: "网络请求失败")
+ callback.fail(error.message ?: mContext.getString(R.string.i18n_150)) //网络请求失败
}
}
}
@@ -130,7 +132,7 @@ class DeviceImpl : DeviceContract.DataModel {
if (response.isSuccessful && (bean?.code == 0 || bean?.code == 200)) {
callback.success(bean)
} else {
- callback.fail(bean?.message?.ifBlank { null } ?: "控制眼罩失败")
+ callback.fail(bean?.message?.ifBlank { null } ?: mContext.getString(R.string.i18n_177)) //控制眼罩失败
}
}
}
@@ -138,7 +140,7 @@ class DeviceImpl : DeviceContract.DataModel {
override fun onFailure(call: Call>, error: Throwable) {
if (!call.isCanceled) {
mainHandler.post {
- callback.fail(error.message ?: "网络请求失败")
+ callback.fail(error.message ?: mContext.getString(R.string.i18n_150)) //网络请求失败
}
}
}
@@ -169,7 +171,7 @@ class DeviceImpl : DeviceContract.DataModel {
callback.success(bean)
} else {
callback.fail(
- bean?.message?.ifBlank { null } ?: "绑定设备失败"
+ bean?.message?.ifBlank { null } ?: mContext.getString(R.string.i18n_178) //绑定设备失败
)
}
}
@@ -178,7 +180,7 @@ class DeviceImpl : DeviceContract.DataModel {
override fun onFailure(call: Call>, error: Throwable) {
if (!call.isCanceled) {
mainHandler.post {
- callback.fail(error.message ?: "网络请求失败")
+ callback.fail(error.message ?: mContext.getString(R.string.i18n_150)) //网络请求失败
}
}
}
@@ -206,7 +208,7 @@ class DeviceImpl : DeviceContract.DataModel {
if (response.isSuccessful && (bean?.code == 0 || bean?.code == 200)) {
callback.success(bean)
} else {
- callback.fail(bean?.message?.ifBlank { null } ?: "设备重命名失败")
+ callback.fail(bean?.message?.ifBlank { null } ?: mContext.getString(R.string.i18n_179)) //设备重命名失败
}
}
}
@@ -214,7 +216,7 @@ class DeviceImpl : DeviceContract.DataModel {
override fun onFailure(call: Call>, error: Throwable) {
if (!call.isCanceled) {
mainHandler.post {
- callback.fail(error.message ?: "网络请求失败")
+ callback.fail(error.message ?: mContext.getString(R.string.i18n_150)) //网络请求失败
}
}
}
@@ -241,7 +243,7 @@ class DeviceImpl : DeviceContract.DataModel {
if (response.isSuccessful && (bean?.code == 0 || bean?.code == 200)) {
callback.success(bean)
} else {
- callback.fail(bean?.message?.ifBlank { null } ?: "设备解绑失败")
+ callback.fail(bean?.message?.ifBlank { null } ?: mContext.getString(R.string.i18n_180)) //设备解绑失败
}
}
}
@@ -249,7 +251,7 @@ class DeviceImpl : DeviceContract.DataModel {
override fun onFailure(call: Call>, error: Throwable) {
if (!call.isCanceled) {
mainHandler.post {
- callback.fail(error.message ?: "网络请求失败")
+ callback.fail(error.message ?: mContext.getString(R.string.i18n_150)) //网络请求失败
}
}
}
@@ -274,3 +276,4 @@ class DeviceImpl : DeviceContract.DataModel {
mainHandler.removeCallbacksAndMessages(null)
}
}
+
diff --git a/app/src/main/java/com/juhabi/birds/device/mvp/DevicePresenter.kt b/app/src/main/java/com/juhabi/birds/device/mvp/DevicePresenter.kt
index e8fc9f1..e3c4ae0 100644
--- a/app/src/main/java/com/juhabi/birds/device/mvp/DevicePresenter.kt
+++ b/app/src/main/java/com/juhabi/birds/device/mvp/DevicePresenter.kt
@@ -1,12 +1,15 @@
package com.juhabi.birds.device.mvp
+import android.content.Context
+import com.juhabi.birds.R
import com.juhabi.birds.base.BasePresenter
import com.juhabi.birds.device.entity.DeviceEntity
import com.juhabi.birds.network.BaseBean
import org.json.JSONObject
class DevicePresenter(
- private val deviceImpl: DeviceImpl
+ private val deviceImpl: DeviceImpl,
+ private val mContext: Context
) : BasePresenter() {
/** 获取当前账号已经绑定的设备列表。 */
@@ -65,7 +68,7 @@ class DevicePresenter(
override fun success(data: BaseBean) {
view?.apply {
hideLoading()
- controlEyeSuccess(data.message.ifBlank { "眼罩状态已更新" })
+ controlEyeSuccess(data.message.ifBlank { mContext.getString(R.string.i18n_181) }) //眼罩状态已更新
}
}
@@ -90,7 +93,7 @@ class DevicePresenter(
override fun success(data: BaseBean) {
view?.apply {
hideLoading()
- bindDeviceSuccess(data.message.ifBlank { "设备绑定成功" })
+ bindDeviceSuccess(data.message.ifBlank { mContext.getString(R.string.i18n_182) }) //设备绑定成功
}
}
@@ -108,7 +111,7 @@ class DevicePresenter(
val currentView = view ?: return
val trimmedName = name.trim()
if (deviceNo.isBlank() || trimmedName.isBlank()) {
- currentView.showError("设备编号和名称不能为空")
+ currentView.showError(mContext.getString(R.string.i18n_183)) //设备编号和名称不能为空
return
}
currentView.showLoading()
@@ -120,7 +123,7 @@ class DevicePresenter(
override fun success(data: BaseBean) {
view?.apply {
hideLoading()
- renameDeviceSuccess(data.message.ifBlank { "设备重命名成功" })
+ renameDeviceSuccess(data.message.ifBlank { mContext.getString(R.string.i18n_184) }) //设备重命名成功
}
}
@@ -137,7 +140,7 @@ class DevicePresenter(
fun unbindDevice(deviceNo: String) {
val currentView = view ?: return
if (deviceNo.isBlank()) {
- currentView.showError("设备编号不能为空")
+ currentView.showError(mContext.getString(R.string.i18n_185)) //设备编号不能为空
return
}
currentView.showLoading()
@@ -145,7 +148,7 @@ class DevicePresenter(
override fun success(data: BaseBean) {
view?.apply {
hideLoading()
- unbindDeviceSuccess(data.message.ifBlank { "设备解绑成功" })
+ unbindDeviceSuccess(data.message.ifBlank { mContext.getString(R.string.i18n_186) }) //设备解绑成功
}
}
@@ -164,3 +167,4 @@ class DevicePresenter(
super.onDestroy()
}
}
+
diff --git a/app/src/main/java/com/juhabi/birds/dialog/LoadingDialog.kt b/app/src/main/java/com/juhabi/birds/dialog/LoadingDialog.kt
index adb555b..7768a3a 100644
--- a/app/src/main/java/com/juhabi/birds/dialog/LoadingDialog.kt
+++ b/app/src/main/java/com/juhabi/birds/dialog/LoadingDialog.kt
@@ -13,7 +13,7 @@ import com.juhabi.birds.R
class LoadingDialog(
context: Context,
- private val message: String = "加载中..."
+ private val message: String = context.getString(com.juhabi.birds.R.string.i18n_043) //加载中...
) : Dialog(context, R.style.LoadingDialogTheme) {
override fun onCreate(savedInstanceState: Bundle?) {
@@ -39,3 +39,4 @@ class LoadingDialog(
}
}
}
+
diff --git a/app/src/main/java/com/juhabi/birds/login/ForgetPsdActivity.kt b/app/src/main/java/com/juhabi/birds/login/ForgetPsdActivity.kt
index 6bb35e4..0a9e77c 100644
--- a/app/src/main/java/com/juhabi/birds/login/ForgetPsdActivity.kt
+++ b/app/src/main/java/com/juhabi/birds/login/ForgetPsdActivity.kt
@@ -33,7 +33,7 @@ class ForgetPsdActivity : BaseActivity()
}
override fun createPresenter(): LoginPresenter? {
- return LoginPresenter(LoginImpl())
+ return LoginPresenter(LoginImpl(mContext), mContext)
}
override fun initWindows() {
@@ -46,7 +46,7 @@ class ForgetPsdActivity : BaseActivity()
}
override fun initView(savedInstanceState: Bundle?) {
- binding.includedTitle.tvTitle.setText(R.string.forget_password_title)
+ binding.includedTitle.tvTitle.setText(R.string.forget_password_title) //忘记密码
listOf(
binding.includedTitle.imgBack,
binding.getCodeButton,
@@ -67,14 +67,30 @@ class ForgetPsdActivity : BaseActivity()
R.id.tv_change_login_type ->{
resetByPhone = !resetByPhone
- binding.linearCountry.visibility = if (resetByPhone) View.VISIBLE else View.GONE
+ binding.linearCountry.visibility = if (resetByPhone) {
+ View.VISIBLE
+ } else {
+ View.GONE
+ }
binding.imgLoginType.setImageResource(
- if (resetByPhone) R.mipmap.icon_login_phone else R.mipmap.icon_login_email
+ if (resetByPhone) {
+ R.mipmap.icon_login_phone
+ } else {
+ R.mipmap.icon_login_email
+ }
)
- binding.tvChangeLoginType.text = if (resetByPhone) "邮箱" else "手机"
+ binding.tvChangeLoginType.text = if (resetByPhone) {
+ mContext.getString(R.string.i18n_018)
+ } else {
+ mContext.getString(R.string.i18n_085)
+ } //邮箱 / 手机
binding.phoneEditText.apply {
text?.clear()
- hint = if (resetByPhone) "请输入手机号" else "请输入邮箱"
+ hint = if (resetByPhone) {
+ mContext.getString(R.string.i18n_044)
+ } else {
+ mContext.getString(R.string.i18n_086)
+ } //请输入手机号 / 请输入邮箱
inputType = if (resetByPhone) {
InputType.TYPE_CLASS_PHONE
} else {
@@ -86,7 +102,12 @@ class ForgetPsdActivity : BaseActivity()
R.id.getCodeButton -> {//获取验证码
val account = binding.phoneEditText.text.toString().trim()
if (account.isBlank()) {
- ToastUtil.showShort(mContext, if (resetByPhone) "手机号不能为空" else "邮箱不能为空")
+ val accountError = if (resetByPhone) {
+ mContext.getString(R.string.i18n_087)
+ } else {
+ mContext.getString(R.string.i18n_088)
+ } //手机号不能为空 / 邮箱不能为空
+ ToastUtil.showShort(mContext, accountError)
return
}
if (resetByPhone) {
@@ -113,29 +134,34 @@ class ForgetPsdActivity : BaseActivity()
val confirmPassword = binding.confirmPasswordEditText.text.toString()
if (account.isBlank()) {
- ToastUtil.showShort(mContext, if (resetByPhone) "手机号不能为空" else "邮箱不能为空")
+ val accountError = if (resetByPhone) {
+ mContext.getString(R.string.i18n_087)
+ } else {
+ mContext.getString(R.string.i18n_088)
+ } //手机号不能为空 / 邮箱不能为空
+ ToastUtil.showShort(mContext, accountError)
return
}
if (code.isBlank()) {
- ToastUtil.showShort(mContext, "验证码不能为空")
+ ToastUtil.showShort(mContext, mContext.getString(R.string.i18n_089)) //验证码不能为空
return
}
if (password.isBlank()) {
- ToastUtil.showShort(mContext, "密码不能为空")
+ ToastUtil.showShort(mContext, mContext.getString(R.string.i18n_090)) //密码不能为空
return
}
if (confirmPassword.isBlank()) {
- ToastUtil.showShort(mContext, "请再次输入密码")
+ ToastUtil.showShort(mContext, mContext.getString(R.string.i18n_091)) //请再次输入密码
return
}
if (password != confirmPassword) {
- ToastUtil.showShort(mContext, "两次输入的密码不一致")
+ ToastUtil.showShort(mContext, mContext.getString(R.string.i18n_092)) //两次输入的密码不一致
return
}
if (!isValidPassword(password)) {
ToastUtil.showShort(
mContext,
- "密码需为8-20位,且包含大小写字母和数字"
+ mContext.getString(R.string.i18n_093) //密码需为8-20位,且包含大小写字母和数字
)
return
}
@@ -173,13 +199,13 @@ class ForgetPsdActivity : BaseActivity()
isEnabled = false
isClickable = false
setBackgroundResource(R.drawable.shape_register_code_disabled_bg)
- text = getString(R.string.register_code_countdown, CODE_COUNTDOWN_SECONDS)
+ text = getString(R.string.register_code_countdown, CODE_COUNTDOWN_SECONDS) //%1$d秒后重新获取
}
codeCountDownTimer = object : CountDownTimer(CODE_COUNTDOWN_MILLIS, 1_000L) {
override fun onTick(millisUntilFinished: Long) {
val secondsRemaining = (millisUntilFinished + 999L) / 1_000L
binding.getCodeButton.text = getString(
- R.string.register_code_countdown,
+ R.string.register_code_countdown, //%1$d秒后重新获取
secondsRemaining
)
}
@@ -190,7 +216,7 @@ class ForgetPsdActivity : BaseActivity()
isEnabled = true
isClickable = true
setBackgroundResource(R.drawable.shape_register_code_bg)
- setText(R.string.register_get_code)
+ setText(R.string.register_get_code) //获取验证码
}
}
}.start()
@@ -201,7 +227,7 @@ class ForgetPsdActivity : BaseActivity()
override fun onForgetPsdSuccess(message: String) {
hideLoading()
- ToastUtil.showShort(mContext, message.ifBlank { "密码修改成功" })
+ ToastUtil.showShort(mContext, message.ifBlank { mContext.getString(R.string.i18n_094) }) //密码修改成功
finish()
}
@@ -243,3 +269,5 @@ class ForgetPsdActivity : BaseActivity()
const val PASSWORD_MAX_LENGTH = 20
}
}
+
+
diff --git a/app/src/main/java/com/juhabi/birds/login/LoginActivity.kt b/app/src/main/java/com/juhabi/birds/login/LoginActivity.kt
index 3240712..2a1538b 100644
--- a/app/src/main/java/com/juhabi/birds/login/LoginActivity.kt
+++ b/app/src/main/java/com/juhabi/birds/login/LoginActivity.kt
@@ -3,9 +3,16 @@ package com.juhabi.birds.login
import android.content.Intent
import android.os.Bundle
import android.os.CountDownTimer
+import android.graphics.Color
import android.text.InputType
+import android.text.SpannableString
+import android.text.Spanned
+import android.text.TextPaint
+import android.text.method.LinkMovementMethod
+import android.text.style.ClickableSpan
import android.view.LayoutInflater
import android.view.View
+import androidx.core.content.ContextCompat
import com.gyf.immersionbar.ImmersionBar
import com.juhabi.birds.AppConfig
import com.juhabi.birds.R
@@ -16,7 +23,7 @@ import com.juhabi.birds.login.mvp.LoginContract
import com.juhabi.birds.login.mvp.LoginImpl
import com.juhabi.birds.login.mvp.LoginPresenter
import com.juhabi.birds.main.MainActivity
-import com.juhabi.birds.mine.MineCenterActivity
+import com.juhabi.birds.mine.WebviewActivity
import com.juhabi.birds.network.TokenManager
import com.juhabi.birds.util.SharedPreferencesUtil
import com.juhabi.birds.util.ToastUtil
@@ -39,7 +46,7 @@ class LoginActivity : BaseActivity(),
}
override fun createPresenter(): LoginPresenter {
- return LoginPresenter(LoginImpl())
+ return LoginPresenter(LoginImpl(mContext), mContext)
}
override fun initWindows() {
@@ -61,6 +68,7 @@ class LoginActivity : BaseActivity(),
).forEach { button ->
button.setOnClickListener(this)
}
+ setupAgreementText()
updateLoginModeText()
}
@@ -84,13 +92,25 @@ class LoginActivity : BaseActivity(),
// 切换手机号和邮箱登录,并同步输入框图标、提示和输入类型。
loginByPhone = !loginByPhone
binding.imgLoginType.setImageResource(
- if (loginByPhone) R.mipmap.icon_login_phone else R.mipmap.icon_login_email
+ if (loginByPhone) {
+ R.mipmap.icon_login_phone
+ } else {
+ R.mipmap.icon_login_email
+ }
)
- binding.tvChangeLoginType.text = if (loginByPhone) "邮箱" else "手机"
+ binding.tvChangeLoginType.text = if (loginByPhone) {
+ mContext.getString(R.string.i18n_018)
+ } else {
+ mContext.getString(R.string.i18n_085)
+ } //邮箱 / 手机
// 账号类型切换后,同步更新密码/验证码登录切换提示。
updateLoginModeText()
binding.accountEditText.apply {
- hint = if (loginByPhone) "请输入手机号" else "请输入邮箱"
+ hint = if (loginByPhone) {
+ mContext.getString(R.string.i18n_044)
+ } else {
+ mContext.getString(R.string.i18n_086)
+ } //请输入手机号 / 请输入邮箱
inputType = if (loginByPhone) {
InputType.TYPE_CLASS_PHONE
} else {
@@ -104,7 +124,12 @@ class LoginActivity : BaseActivity(),
// 根据当前账号类型选择手机或邮箱验证码接口。
val account = binding.accountEditText.text.toString().trim()
if (account.isBlank()) {
- ToastUtil.showShort(mContext, if (loginByPhone) "手机号不能为空" else "邮箱不能为空")
+ val accountError = if (loginByPhone) {
+ mContext.getString(R.string.i18n_087)
+ } else {
+ mContext.getString(R.string.i18n_088)
+ } //手机号不能为空 / 邮箱不能为空
+ ToastUtil.showShort(mContext, accountError)
return
}
if (loginByPhone) {
@@ -127,17 +152,21 @@ class LoginActivity : BaseActivity(),
}
R.id.loginButton -> {//登录
+ if (!binding.checkboxRead.isChecked) {
+ ToastUtil.showShort(mContext, mContext.getString(R.string.i18n_095)) //请先阅读并同意用户协议和隐私政策
+ return
+ }
// 账号类型和登录方式组合成四种登录请求。
val account = binding.accountEditText.text.toString().trim()
if (account.isBlank()) {
- ToastUtil.showShort(mContext, "账号不能为空")
+ ToastUtil.showShort(mContext, mContext.getString(R.string.i18n_096)) //账号不能为空
return
}
if (loginByPassword) {
val password = binding.passwordEditText.text.toString()
if (password.isBlank()) {
- ToastUtil.showShort(mContext, "密码不能为空")
+ ToastUtil.showShort(mContext, mContext.getString(R.string.i18n_090)) //密码不能为空
return
}
if (loginByPhone) {
@@ -148,7 +177,7 @@ class LoginActivity : BaseActivity(),
} else {
val code = binding.codeEditText.text.toString().trim()
if (code.isBlank()) {
- ToastUtil.showShort(mContext, "验证码不能为空")
+ ToastUtil.showShort(mContext, mContext.getString(R.string.i18n_089)) //验证码不能为空
return
}
if (loginByPhone) {
@@ -174,12 +203,66 @@ class LoginActivity : BaseActivity(),
binding.passwordEditText.typeface = binding.accountEditText.typeface
}
+ private fun setupAgreementText() {
+ val content = getString(R.string.agree_user_policy) //我已阅读并同意《用户协议》《隐私政策》
+ val text = SpannableString(content)
+ addClickableSpan(text, content, getString(R.string.user_agreement)) { //《用户协议》
+ openAgreementPage(WebviewActivity.PAGE_AGREEMENT, AppConfig.USER_AGREEMENT_URL)
+ }
+ addClickableSpan(text, content, getString(R.string.privacy_policy)) { //《隐私政策》
+ openAgreementPage(WebviewActivity.PAGE_PRIVACY, AppConfig.PRIVACY_POLICY_URL)
+ }
+ binding.tvAgreementFull.apply {
+ this.text = text
+ movementMethod = LinkMovementMethod.getInstance()
+ highlightColor = Color.TRANSPARENT
+ }
+ binding.realtiveReadFinish.setOnClickListener {
+ binding.checkboxRead.isChecked = !binding.checkboxRead.isChecked
+ }
+ }
+
+ private fun addClickableSpan(
+ spannable: SpannableString,
+ content: String,
+ target: String,
+ action: () -> Unit
+ ) {
+ val start = content.indexOf(target)
+ if (start < 0) {
+ return
+ }
+ spannable.setSpan(object : ClickableSpan() {
+ override fun onClick(widget: View) = action()
+
+ override fun updateDrawState(ds: TextPaint) {
+ ds.color = ContextCompat.getColor(this@LoginActivity, R.color.color_white)
+ ds.isUnderlineText = false
+ }
+ }, start, start + target.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
+ }
+
+ private fun openAgreementPage(page: Int, url: String) {
+ goTo(WebviewActivity::class.java, Bundle().apply {
+ putInt(WebviewActivity.EXTRA_PAGE, page)
+ putString(WebviewActivity.EXTRA_URL, url)
+ })
+ }
+
/** 根据账号类型和当前登录方式,更新登录方式切换按钮文案。 */
private fun updateLoginModeText() {
binding.tvChangeCode.text = if (loginByPassword) {
- if (loginByPhone) "切换手机号验证码登录" else "切换邮箱验证码登录"
+ if (loginByPhone) {
+ getString(R.string.register_change_code_login)
+ } else {
+ mContext.getString(R.string.i18n_097)
+ } //切换手机号验证码登录 / 切换邮箱验证码登录
} else {
- if (loginByPhone) "切换手机号密码登录" else "切换邮箱密码登录"
+ if (loginByPhone) {
+ mContext.getString(R.string.i18n_098)
+ } else {
+ mContext.getString(R.string.i18n_099)
+ } //切换手机号密码登录 / 切换邮箱密码登录
}
}
@@ -191,13 +274,13 @@ class LoginActivity : BaseActivity(),
isEnabled = false
isClickable = false
setBackgroundResource(R.drawable.shape_register_code_disabled_bg)
- text = getString(R.string.register_code_countdown, CODE_COUNTDOWN_SECONDS)
+ text = getString(R.string.register_code_countdown, CODE_COUNTDOWN_SECONDS) //%1$d秒后重新获取
}
codeCountDownTimer = object : CountDownTimer(CODE_COUNTDOWN_MILLIS, 1_000L) {
override fun onTick(millisUntilFinished: Long) {
val secondsRemaining = (millisUntilFinished + 999L) / 1_000L
binding.getCodeButton.text = getString(
- R.string.register_code_countdown,
+ R.string.register_code_countdown, //%1$d秒后重新获取
secondsRemaining
)
}
@@ -208,7 +291,7 @@ class LoginActivity : BaseActivity(),
isEnabled = true
isClickable = true
setBackgroundResource(R.drawable.shape_register_code_bg)
- setText(R.string.register_get_code)
+ setText(R.string.register_get_code) //获取验证码
}
}
}.start()
@@ -238,7 +321,7 @@ class LoginActivity : BaseActivity(),
AppConfig.USER_LOGIN = token.isNotBlank()
SharedPreferencesUtil.putBoolean(this, KEY_USER_LOGIN, AppConfig.USER_LOGIN)
- ToastUtil.showShort(mContext, "登录成功")
+ ToastUtil.showShort(mContext, mContext.getString(R.string.i18n_100)) //登录成功
startActivity(Intent(this, MainActivity::class.java))
finish()
}
@@ -260,3 +343,5 @@ class LoginActivity : BaseActivity(),
const val KEY_USER_LOGIN = "user_login"
}
}
+
+
diff --git a/app/src/main/java/com/juhabi/birds/login/RegisterActivity.kt b/app/src/main/java/com/juhabi/birds/login/RegisterActivity.kt
index 7a104f0..01f0207 100644
--- a/app/src/main/java/com/juhabi/birds/login/RegisterActivity.kt
+++ b/app/src/main/java/com/juhabi/birds/login/RegisterActivity.kt
@@ -38,7 +38,7 @@ class RegisterActivity : BaseActivity(),
}
override fun createPresenter(): LoginPresenter? {
- return LoginPresenter(LoginImpl())
+ return LoginPresenter(LoginImpl(mContext), mContext)
}
override fun initWindows() {
@@ -51,7 +51,7 @@ class RegisterActivity : BaseActivity(),
}
override fun initView(savedInstanceState: Bundle?) {
- binding.includedTitle.tvTitle.setText(R.string.login_register)
+ binding.includedTitle.tvTitle.setText(R.string.login_register) //手机注册
listOf(
binding.passwordVisibilityButton,
binding.getCodeButton,
@@ -73,14 +73,30 @@ class RegisterActivity : BaseActivity(),
R.id.tv_change_login_type ->{
registerByPhone = !registerByPhone
- binding.linearCountry.visibility = if (registerByPhone) View.VISIBLE else View.GONE
+ binding.linearCountry.visibility = if (registerByPhone) {
+ View.VISIBLE
+ } else {
+ View.GONE
+ }
binding.imgLoginType.setImageResource(
- if (registerByPhone) R.mipmap.icon_login_phone else R.mipmap.icon_login_email
+ if (registerByPhone) {
+ R.mipmap.icon_login_phone
+ } else {
+ R.mipmap.icon_login_email
+ }
)
- binding.tvChangeLoginType.text = if (registerByPhone) "邮箱" else "手机"
+ binding.tvChangeLoginType.text = if (registerByPhone) {
+ mContext.getString(R.string.i18n_018)
+ } else {
+ mContext.getString(R.string.i18n_085)
+ } //邮箱 / 手机
binding.phoneEditText.apply {
text?.clear()
- hint = if (registerByPhone) "请输入手机号" else "请输入邮箱"
+ hint = if (registerByPhone) {
+ mContext.getString(R.string.i18n_044)
+ } else {
+ mContext.getString(R.string.i18n_086)
+ } //请输入手机号 / 请输入邮箱
inputType = if (registerByPhone) {
InputType.TYPE_CLASS_PHONE
} else {
@@ -94,7 +110,12 @@ class RegisterActivity : BaseActivity(),
R.id.getCodeButton -> {//获取验证码
val account = binding.phoneEditText.text.toString().trim()
if (account.isBlank()) {
- ToastUtil.showShort(mContext, if (registerByPhone) "手机号不能为空" else "邮箱不能为空")
+ val accountError = if (registerByPhone) {
+ mContext.getString(R.string.i18n_087)
+ } else {
+ mContext.getString(R.string.i18n_088)
+ } //手机号不能为空 / 邮箱不能为空
+ ToastUtil.showShort(mContext, accountError)
return
}
if (registerByPhone) {
@@ -111,29 +132,34 @@ class RegisterActivity : BaseActivity(),
val confirmPassword = binding.confirmPasswordEditText.text.toString()
if (account.isBlank()) {
- ToastUtil.showShort(mContext, if (registerByPhone) "手机号不能为空" else "邮箱不能为空")
+ val accountError = if (registerByPhone) {
+ mContext.getString(R.string.i18n_087)
+ } else {
+ mContext.getString(R.string.i18n_088)
+ } //手机号不能为空 / 邮箱不能为空
+ ToastUtil.showShort(mContext, accountError)
return
}
if (code.isBlank()) {
- ToastUtil.showShort(mContext, "验证码不能为空")
+ ToastUtil.showShort(mContext, mContext.getString(R.string.i18n_089)) //验证码不能为空
return
}
if (password.isBlank()) {
- ToastUtil.showShort(mContext, "密码不能为空")
+ ToastUtil.showShort(mContext, mContext.getString(R.string.i18n_090)) //密码不能为空
return
}
if (confirmPassword.isBlank()) {
- ToastUtil.showShort(mContext, "请再次输入密码")
+ ToastUtil.showShort(mContext, mContext.getString(R.string.i18n_091)) //请再次输入密码
return
}
if (password != confirmPassword) {
- ToastUtil.showShort(mContext, "两次输入的密码不一致")
+ ToastUtil.showShort(mContext, mContext.getString(R.string.i18n_092)) //两次输入的密码不一致
return
}
if (!isValidPassword(password)) {
ToastUtil.showShort(
mContext,
- "密码需为8-20位,且包含大小写字母和数字"
+ mContext.getString(R.string.i18n_093) //密码需为8-20位,且包含大小写字母和数字
)
return
}
@@ -198,13 +224,13 @@ class RegisterActivity : BaseActivity(),
isEnabled = false
isClickable = false
setBackgroundResource(R.drawable.shape_register_code_disabled_bg)
- text = getString(R.string.register_code_countdown, CODE_COUNTDOWN_SECONDS)
+ text = getString(R.string.register_code_countdown, CODE_COUNTDOWN_SECONDS) //%1$d秒后重新获取
}
codeCountDownTimer = object : CountDownTimer(CODE_COUNTDOWN_MILLIS, 1_000L) {
override fun onTick(millisUntilFinished: Long) {
val secondsRemaining = (millisUntilFinished + 999L) / 1_000L
binding.getCodeButton.text = getString(
- R.string.register_code_countdown,
+ R.string.register_code_countdown, //%1$d秒后重新获取
secondsRemaining
)
}
@@ -215,7 +241,7 @@ class RegisterActivity : BaseActivity(),
isEnabled = true
isClickable = true
setBackgroundResource(R.drawable.shape_register_code_bg)
- setText(R.string.register_get_code)
+ setText(R.string.register_get_code) //获取验证码
}
}
}.start()
@@ -223,7 +249,7 @@ class RegisterActivity : BaseActivity(),
override fun onRegisterSuccess(message: String) {
hideLoading()
- ToastUtil.showShort(mContext, message.ifBlank { "注册成功" })
+ ToastUtil.showShort(mContext, message.ifBlank { mContext.getString(R.string.i18n_101) }) //注册成功
finish()
}
@@ -266,3 +292,5 @@ class RegisterActivity : BaseActivity(),
const val PASSWORD_MAX_LENGTH = 20
}
}
+
+
diff --git a/app/src/main/java/com/juhabi/birds/login/mvp/LoginImpl.kt b/app/src/main/java/com/juhabi/birds/login/mvp/LoginImpl.kt
index ca87619..e57f188 100644
--- a/app/src/main/java/com/juhabi/birds/login/mvp/LoginImpl.kt
+++ b/app/src/main/java/com/juhabi/birds/login/mvp/LoginImpl.kt
@@ -1,7 +1,9 @@
package com.juhabi.birds.login.mvp
+import android.content.Context
import android.os.Handler
import android.os.Looper
+import com.juhabi.birds.R
import com.juhabi.birds.login.model.UserEntity
import com.juhabi.birds.login.model.SendCodeEntity
import com.juhabi.birds.network.ApiClient
@@ -12,7 +14,7 @@ import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
-class LoginImpl : LoginContract.DataModel {
+class LoginImpl(private val mContext: Context) : LoginContract.DataModel {
private val mainHandler = Handler(Looper.getMainLooper())
private var loginCall: Call>? = null
@@ -63,7 +65,7 @@ class LoginImpl : LoginContract.DataModel {
if (response.isSuccessful && bean != null) {
callback.success(bean)
} else {
- callback.fail("接口响应异常:${response.code()}")
+ callback.fail(mContext.getString(R.string.api_response_error, response.code())) //接口响应异常:%1$d
}
}
}
@@ -75,7 +77,7 @@ class LoginImpl : LoginContract.DataModel {
calls -= call
if (!call.isCanceled) {
mainHandler.post {
- callback.fail(error.message ?: "网络请求失败")
+ callback.fail(error.message ?: mContext.getString(R.string.i18n_150)) //网络请求失败
}
}
}
@@ -142,7 +144,7 @@ class LoginImpl : LoginContract.DataModel {
if (response.isSuccessful && bean != null) {
callback.success(bean)
} else {
- callback.fail("接口响应异常:${response.code()}")
+ callback.fail(mContext.getString(R.string.api_response_error, response.code())) //接口响应异常:%1$d
}
}
}
@@ -151,7 +153,7 @@ class LoginImpl : LoginContract.DataModel {
calls -= call
if (!call.isCanceled) {
mainHandler.post {
- callback.fail(error.message ?: "网络请求失败")
+ callback.fail(error.message ?: mContext.getString(R.string.i18n_150)) //网络请求失败
}
}
}
@@ -178,7 +180,7 @@ class LoginImpl : LoginContract.DataModel {
if (response.isSuccessful && bean != null) {
callback.success(bean)
} else {
- callback.fail("接口响应异常:${response.code()}")
+ callback.fail(mContext.getString(R.string.api_response_error, response.code())) //接口响应异常:%1$d
}
}
}
@@ -187,7 +189,7 @@ class LoginImpl : LoginContract.DataModel {
calls -= call
if (!call.isCanceled) {
mainHandler.post {
- callback.fail(error.message ?: "网络请求失败")
+ callback.fail(error.message ?: mContext.getString(R.string.i18n_150)) //网络请求失败
}
}
}
@@ -214,7 +216,7 @@ class LoginImpl : LoginContract.DataModel {
if (response.isSuccessful && bean != null) {
callback.success(bean)
} else {
- callback.fail("接口响应异常:${response.code()}")
+ callback.fail(mContext.getString(R.string.api_response_error, response.code())) //接口响应异常:%1$d
}
}
}
@@ -223,7 +225,7 @@ class LoginImpl : LoginContract.DataModel {
calls -= call
if (!call.isCanceled) {
mainHandler.post {
- callback.fail(error.message ?: "网络请求失败")
+ callback.fail(error.message ?: mContext.getString(R.string.i18n_150)) //网络请求失败
}
}
}
@@ -250,7 +252,7 @@ class LoginImpl : LoginContract.DataModel {
if (response.isSuccessful && bean != null) {
callback.success(bean)
} else {
- callback.fail("接口响应异常:${response.code()}")
+ callback.fail(mContext.getString(R.string.api_response_error, response.code())) //接口响应异常:%1$d
}
}
}
@@ -259,7 +261,7 @@ class LoginImpl : LoginContract.DataModel {
calls -= call
if (!call.isCanceled) {
mainHandler.post {
- callback.fail(error.message ?: "网络请求失败")
+ callback.fail(error.message ?: mContext.getString(R.string.i18n_150)) //网络请求失败
}
}
}
@@ -286,7 +288,7 @@ class LoginImpl : LoginContract.DataModel {
if (response.isSuccessful && bean != null) {
callback.success(bean)
} else {
- callback.fail("接口响应异常:${response.code()}")
+ callback.fail(mContext.getString(R.string.api_response_error, response.code())) //接口响应异常:%1$d
}
}
}
@@ -295,7 +297,7 @@ class LoginImpl : LoginContract.DataModel {
calls -= call
if (!call.isCanceled) {
mainHandler.post {
- callback.fail(error.message ?: "网络请求失败")
+ callback.fail(error.message ?: mContext.getString(R.string.i18n_150)) //网络请求失败
}
}
}
@@ -318,7 +320,7 @@ class LoginImpl : LoginContract.DataModel {
if (response.isSuccessful && bean != null) {
callback.success(bean)
} else {
- callback.fail("接口响应异常:${response.code()}")
+ callback.fail(mContext.getString(R.string.api_response_error, response.code())) //接口响应异常:%1$d
}
}
}
@@ -326,7 +328,7 @@ class LoginImpl : LoginContract.DataModel {
override fun onFailure(call: Call>, error: Throwable) {
if (!call.isCanceled) {
mainHandler.post {
- callback.fail(error.message ?: "网络请求失败")
+ callback.fail(error.message ?: mContext.getString(R.string.i18n_150)) //网络请求失败
}
}
}
@@ -341,3 +343,4 @@ class LoginImpl : LoginContract.DataModel {
mainHandler.removeCallbacksAndMessages(null)
}
}
+
diff --git a/app/src/main/java/com/juhabi/birds/login/mvp/LoginPresenter.kt b/app/src/main/java/com/juhabi/birds/login/mvp/LoginPresenter.kt
index b723bc9..f541ab0 100644
--- a/app/src/main/java/com/juhabi/birds/login/mvp/LoginPresenter.kt
+++ b/app/src/main/java/com/juhabi/birds/login/mvp/LoginPresenter.kt
@@ -1,12 +1,17 @@
package com.juhabi.birds.login.mvp
+import android.content.Context
+import com.juhabi.birds.R
import com.juhabi.birds.base.BasePresenter
import com.juhabi.birds.login.model.UserEntity
import com.juhabi.birds.login.model.SendCodeEntity
import com.juhabi.birds.network.BaseBean
import org.json.JSONObject
-class LoginPresenter(private val loginImpl: LoginImpl) : BasePresenter() {
+class LoginPresenter(
+ private val loginImpl: LoginImpl,
+ private val mContext: Context
+) : BasePresenter() {
fun getPhoneSmsCode(phoneNumber: String, sendCodeSceneRegister: String) {
@@ -22,10 +27,10 @@ class LoginPresenter(private val loginImpl: LoginImpl) : BasePresenter {
getSinglePermissionRequst(
Manifest.permission.ACCESS_FINE_LOCATION,
- "定位权限",
- "定位功能需要获取您的位置信息",
+ mContext.getString(R.string.i18n_104), //定位权限
+ mContext.getString(R.string.i18n_105), //定位功能需要获取您的位置信息
REQUEST_LOCATION
)
}
R.id.normalDialogButton -> {
showDialogtext(
- "操作确认",
- "确定要执行这项操作吗?",
- "确定",
- "取消",
+ mContext.getString(R.string.i18n_106), //操作确认
+ mContext.getString(R.string.i18n_107), //确定要执行这项操作吗?
+ mContext.getString(R.string.i18n_027), //确定
+ mContext.getString(R.string.i18n_041), //取消
object : OnDialogClick {
override fun onCancelClick() {
- showMessage("已取消")
+ showMessage(mContext.getString(R.string.i18n_108)) //已取消
}
override fun onConfirmClick() {
- showMessage("已点击确定")
+ showMessage(mContext.getString(R.string.i18n_109)) //已点击确定
}
}
)
@@ -123,14 +123,14 @@ class MineCenterActivity : BaseActivity {
showEditDialog(
- title = "请输入内容",
- hint = "请输入测试内容",
+ title = mContext.getString(R.string.i18n_073), //请输入内容
+ hint = mContext.getString(R.string.i18n_110), //请输入测试内容
click = object : OnDialogClick {
override fun onCancelClick() {
- showMessage("已取消输入")
+ showMessage(mContext.getString(R.string.i18n_111)) //已取消输入
}
override fun onConfirmClick(content: String) {
- showMessage("输入内容:$content")
+ showMessage(getString(R.string.input_content_result, content)) //输入内容:%1$s
}
}
)
@@ -179,7 +179,7 @@ class MineCenterActivity : BaseActivity
selectedDateTime = result.timestamp
binding.dateTimeResultTextView.text = result.formattedText
- showMessage("已选择:${result.formattedText}")
+ showMessage(getString(R.string.selected_result, result.formattedText)) //已选择:%1$s
}
.build()
.show()
@@ -190,12 +190,27 @@ class MineCenterActivity : BaseActivity) {
- showMessage("权限获取成功:${permissions.joinToString { it.substringAfterLast('.') }}")
+ showMessage(getString(
+ R.string.permission_granted_value, //权限获取成功:%1$s
+ permissions.joinToString { it.substringAfterLast('.') }
+ ))
}
override fun onPermissionsDenied(permissions: Array, permanentlyDenied: Boolean) {
- showMessage("权限被拒绝:${permissions.joinToString { it.substringAfterLast('.') }}")
+ showMessage(getString(
+ R.string.permission_denied_value, //权限被拒绝:%1$s
+ permissions.joinToString { it.substringAfterLast('.') }
+ ))
super.onPermissionsDenied(permissions, permanentlyDenied)
}
@@ -243,4 +264,6 @@ class MineCenterActivity : BaseActivity(),
}
override fun createPresenter(): MinePresenter {
- return MinePresenter(MineImpl())
+ return MinePresenter(MineImpl(mContext), mContext)
}
override fun onResume() {
@@ -78,10 +82,10 @@ class MineFragment : BaseFragment(),
R.id.linear_qchc -> {
showDialogtext(
- "提示",
- "清除当前应用缓存?",
- "确定",
- "取消",
+ mContext.getString(R.string.i18n_063), //提示
+ mContext.getString(R.string.i18n_113), //清除当前应用缓存?
+ mContext.getString(R.string.i18n_027), //确定
+ mContext.getString(R.string.i18n_041), //取消
object : OnDialogClick {
override fun onCancelClick() {
}
@@ -93,15 +97,20 @@ class MineFragment : BaseFragment(),
)
}
+ R.id.linear_qhyy -> {
+ showLanguageDialog()
+ }
+
+
//注销账号
R.id.linear_zxzh -> {
showDialogtext(
- "确认注销账号?",
- "注销账号后,账号将被删除,所有设备" +
- "解绑、个人信息被清除,无法恢复,请谨慎操作?",
- "确认注销",
- "取消",
+ mContext.getString(R.string.i18n_114), //确认注销账号?
+ mContext.getString(R.string.i18n_115) + //注销账号后,账号将被删除,所有设备
+ mContext.getString(R.string.i18n_116), //解绑、个人信息被清除,无法恢复,请谨慎操作?
+ mContext.getString(R.string.i18n_117), //确认注销
+ mContext.getString(R.string.i18n_041), //取消
object : OnDialogClick {
override fun onCancelClick() {
}
@@ -118,10 +127,10 @@ class MineFragment : BaseFragment(),
//退出登录
R.id.tv_logout -> {
showDialogtext(
- "退出登录",
- "确定要退出当前账号吗?",
- "确定",
- "取消",
+ mContext.getString(R.string.i18n_061), //退出登录
+ mContext.getString(R.string.i18n_118), //确定要退出当前账号吗?
+ mContext.getString(R.string.i18n_027), //确定
+ mContext.getString(R.string.i18n_041), //取消
object : OnDialogClick {
override fun onCancelClick() {
}
@@ -138,6 +147,50 @@ class MineFragment : BaseFragment(),
}
}
+ private fun showLanguageDialog() {
+ val view = layoutInflater.inflate(R.layout.dialog_language, null)
+ val dialog = AlertDialog.Builder(requireContext(), R.style.LanguageDialogTheme)
+ .setView(view)
+ .create()
+
+ view.findViewById(R.id.languageChinese).setOnClickListener {
+ dialog.dismiss()
+ setAppLanguage("zh-CN")
+ }
+ view.findViewById(R.id.languageEnglish).setOnClickListener {
+ dialog.dismiss()
+ setAppLanguage("en")
+ }
+ view.findViewById(R.id.languageArabic).setOnClickListener {
+ dialog.dismiss()
+ setAppLanguage("ar")
+ }
+
+ dialog.setCanceledOnTouchOutside(true)
+ dialog.window?.apply {
+ setBackgroundDrawableResource(android.R.color.transparent)
+ setGravity(android.view.Gravity.BOTTOM)
+ attributes = attributes.apply {
+ height = WindowManager.LayoutParams.WRAP_CONTENT
+ windowAnimations = R.style.LanguageDialogAnimation
+ }
+ }
+ dialog.show()
+ // AlertDialog.show() 会按主题重新计算默认宽度,因此必须在 show 后覆盖为屏幕宽度。
+ dialog.window?.setLayout(
+ WindowManager.LayoutParams.MATCH_PARENT,
+ WindowManager.LayoutParams.WRAP_CONTENT
+ )
+ }
+
+ private fun setAppLanguage(languageTag: String) {
+ val currentTag = AppCompatDelegate.getApplicationLocales().toLanguageTags()
+ if (currentTag == languageTag) {
+ return
+ }
+ AppCompatDelegate.setApplicationLocales(LocaleListCompat.forLanguageTags(languageTag))
+ }
+
private fun openAgreementPage(page: Int, url: String) {
goTo(
WebviewActivity::class.java,
@@ -150,13 +203,17 @@ class MineFragment : BaseFragment(),
/** 将个人信息请求结果更新到个人中心页面。 */
override fun getUserInfoSuccess(userInfo: MineUserInfo) {
- val displayName = userInfo.userName.ifBlank { "未设置用户名" }
+ val displayName = userInfo.userName.ifBlank { mContext.getString(R.string.i18n_119) } //未设置用户名
val displayAccount = userInfo.nickName
?.ifBlank { null }
- ?: "未绑定手机号"
+ ?: mContext.getString(R.string.i18n_120) //未绑定手机号
binding.tvUserName.text = displayName
binding.tvUserAccount.text = displayAccount
- binding.tvChangeBindAccount.text = if (userInfo.phoneBound == true) "换绑手机号" else "换绑邮箱"
+ binding.tvChangeBindAccount.text = if (userInfo.phoneBound == true) {
+ mContext.getString(R.string.i18n_121)
+ } else {
+ mContext.getString(R.string.i18n_122)
+ } //换绑手机号 / 换绑邮箱
if (userInfo.avatar.isNullOrBlank()) {
binding.imgAvatar.setImageResource(R.mipmap.icon_mine_user_head_defaul)
@@ -226,7 +283,7 @@ class MineFragment : BaseFragment(),
activity?.runOnUiThread {
if (isAdded && view != null) {
binding.tvTotalCache.text = formatCacheSize(0L)
- showMessage("缓存清理成功")
+ showMessage(mContext.getString(R.string.i18n_123)) //缓存清理成功
}
}
}.start()
@@ -263,3 +320,5 @@ class MineFragment : BaseFragment(),
}
}
}
+
+
diff --git a/app/src/main/java/com/juhabi/birds/mine/ModifyPsdActivity.kt b/app/src/main/java/com/juhabi/birds/mine/ModifyPsdActivity.kt
index a2eb081..2c419c3 100644
--- a/app/src/main/java/com/juhabi/birds/mine/ModifyPsdActivity.kt
+++ b/app/src/main/java/com/juhabi/birds/mine/ModifyPsdActivity.kt
@@ -25,7 +25,7 @@ class ModifyPsdActivity : BaseActivity(),
}
override fun createPresenter(): MinePresenter? {
- return MinePresenter(MineImpl())
+ return MinePresenter(MineImpl(mContext), mContext)
}
override fun initWindows() {
@@ -39,7 +39,7 @@ class ModifyPsdActivity : BaseActivity(),
override fun initView(savedInstanceState: Bundle?) {
- binding.includedTitle.tvTitle.text = "修改密码"
+ binding.includedTitle.tvTitle.setText(R.string.modify_password) //修改密码
listOf(
binding.oldPsdVisibilityButton,
binding.passwordVisibilityButton,
@@ -64,27 +64,27 @@ class ModifyPsdActivity : BaseActivity(),
// 按顺序校验输入项,避免无效数据提交到服务端。
if (oldPassword.isBlank()) {
- ToastUtil.showShort(mContext, "当前密码不能为空")
+ ToastUtil.showShort(mContext, mContext.getString(R.string.i18n_124)) //当前密码不能为空
return
}
if (newPassword.isBlank()) {
- ToastUtil.showShort(mContext, "新密码不能为空")
+ ToastUtil.showShort(mContext, mContext.getString(R.string.i18n_125)) //新密码不能为空
return
}
if (confirmPassword.isBlank()) {
- ToastUtil.showShort(mContext, "请再次输入新密码")
+ ToastUtil.showShort(mContext, mContext.getString(R.string.i18n_046)) //请再次输入新密码
return
}
if (oldPassword == newPassword) {
- ToastUtil.showShort(mContext, "新密码不能与当前密码相同")
+ ToastUtil.showShort(mContext, mContext.getString(R.string.i18n_126)) //新密码不能与当前密码相同
return
}
if (newPassword != confirmPassword) {
- ToastUtil.showShort(mContext, "两次输入的新密码不一致")
+ ToastUtil.showShort(mContext, mContext.getString(R.string.i18n_127)) //两次输入的新密码不一致
return
}
if (!isValidPassword(newPassword)) {
- ToastUtil.showShort(mContext, "密码需为8-20位,且包含大小写字母和数字")
+ ToastUtil.showShort(mContext, mContext.getString(R.string.i18n_093)) //密码需为8-20位,且包含大小写字母和数字
return
}
@@ -104,13 +104,15 @@ class ModifyPsdActivity : BaseActivity(),
override fun onModifyPasswordSuccess(message: String) {
hideLoading()
- ToastUtil.showShort(mContext, message.ifBlank { "密码修改成功" })
+ ToastUtil.showShort(mContext, message.ifBlank { mContext.getString(R.string.i18n_094) }) //密码修改成功
finish()
}
/** 8-20 位,且同时包含大写、小写字母和数字。 */
private fun isValidPassword(password: String): Boolean {
- if (password.length !in PASSWORD_MIN_LENGTH..PASSWORD_MAX_LENGTH) return false
+ if (password.length !in PASSWORD_MIN_LENGTH..PASSWORD_MAX_LENGTH) {
+ return false
+ }
return password.any { it.isUpperCase() } &&
password.any { it.isLowerCase() } &&
password.any { it.isDigit() }
@@ -129,3 +131,5 @@ class ModifyPsdActivity : BaseActivity(),
}
+
+
diff --git a/app/src/main/java/com/juhabi/birds/mine/UpdateUserAccount.kt b/app/src/main/java/com/juhabi/birds/mine/UpdateUserAccount.kt
index 47c4801..22f1d83 100644
--- a/app/src/main/java/com/juhabi/birds/mine/UpdateUserAccount.kt
+++ b/app/src/main/java/com/juhabi/birds/mine/UpdateUserAccount.kt
@@ -31,7 +31,7 @@ class UpdateUserAccount : BaseActivity{
- if (userLoginByPhone) presenter?.getCurrentPhoneCode()
+ if (userLoginByPhone) {
+ presenter?.getCurrentPhoneCode()
+ }
else presenter?.getCurrentEmailCode()
}
@@ -79,12 +85,14 @@ class UpdateUserAccount : BaseActivity "隐私政策" to "privacy_policy.html"
- PAGE_AGREEMENT -> "用户协议" to "user_agreement.html"
+ PAGE_PRIVACY -> mContext.getString(R.string.i18n_134) to "privacy_policy.html" //隐私政策
+ PAGE_AGREEMENT -> mContext.getString(R.string.i18n_058) to "user_agreement.html" //用户协议
else -> null
}
if (page == null) {
- showMessage("页面参数无效")
+ showMessage(mContext.getString(R.string.i18n_135)) //页面参数无效
finish()
return
}
@@ -108,13 +109,15 @@ class WebviewActivity : BaseActivity>? = null
private var versionCall: Call>? = null
@@ -39,7 +41,7 @@ class MineImpl : MineContract.DataModel {
) {
mainHandler.post {
response.body()?.let(callback::success)
- ?: callback.fail("验证码发送失败")
+ ?: callback.fail(mContext.getString(R.string.i18n_149)) //验证码发送失败
}
}
@@ -48,7 +50,7 @@ class MineImpl : MineContract.DataModel {
error: Throwable
) {
if (!call.isCanceled) {
- mainHandler.post { callback.fail(error.message ?: "网络请求失败") }
+ mainHandler.post { callback.fail(error.message ?: mContext.getString(R.string.i18n_150)) } //网络请求失败
}
}
})
@@ -58,10 +60,12 @@ class MineImpl : MineContract.DataModel {
override fun getCurrentEmailCode(callback: MineContract.Callback>) {
ApiClient.apiService.getCurrentEmailCode(NetworkManager.getToken()).enqueue(object : Callback> {
override fun onResponse(call: Call>, response: Response>) {
- mainHandler.post { response.body()?.let(callback::success) ?: callback.fail("验证码发送失败") }
+ mainHandler.post { response.body()?.let(callback::success) ?: callback.fail(mContext.getString(R.string.i18n_149)) } //验证码发送失败
}
override fun onFailure(call: Call>, error: Throwable) {
- if (!call.isCanceled) mainHandler.post { callback.fail(error.message ?: "网络请求失败") }
+ if (!call.isCanceled) {
+ mainHandler.post { callback.fail(error.message ?: mContext.getString(R.string.i18n_150)) }
+ } //网络请求失败
}
})
}
@@ -81,7 +85,7 @@ class MineImpl : MineContract.DataModel {
) {
mainHandler.post {
response.body()?.let(callback::success)
- ?: callback.fail("验证码发送失败")
+ ?: callback.fail(mContext.getString(R.string.i18n_149)) //验证码发送失败
}
}
@@ -90,7 +94,7 @@ class MineImpl : MineContract.DataModel {
error: Throwable
) {
if (!call.isCanceled) {
- mainHandler.post { callback.fail(error.message ?: "网络请求失败") }
+ mainHandler.post { callback.fail(error.message ?: mContext.getString(R.string.i18n_150)) } //网络请求失败
}
}
})
@@ -98,12 +102,17 @@ class MineImpl : MineContract.DataModel {
}
override fun sendEmailCode(request: JSONObject, callback: MineContract.Callback>) {
- ApiClient.apiService.sendEmailCode(NetworkManager.getToken(), NetworkManager.toRequestBody(request)).enqueue(object : Callback> {
+ ApiClient.apiService.sendEmailCode(
+ NetworkManager.getToken(),
+ NetworkManager.toRequestBody(request)
+ ).enqueue(object : Callback> {
override fun onResponse(call: Call>, response: Response>) {
- mainHandler.post { response.body()?.let(callback::success) ?: callback.fail("验证码发送失败") }
+ mainHandler.post { response.body()?.let(callback::success) ?: callback.fail(mContext.getString(R.string.i18n_149)) } //验证码发送失败
}
override fun onFailure(call: Call>, error: Throwable) {
- if (!call.isCanceled) mainHandler.post { callback.fail(error.message ?: "网络请求失败") }
+ if (!call.isCanceled) {
+ mainHandler.post { callback.fail(error.message ?: mContext.getString(R.string.i18n_150)) }
+ } //网络请求失败
}
})
}
@@ -126,14 +135,14 @@ class MineImpl : MineContract.DataModel {
) {
mainHandler.post {
response.body()?.let(callback::success)
- ?: callback.fail("当前手机号验证失败")
+ ?: callback.fail(mContext.getString(R.string.i18n_151)) //当前手机号验证失败
}
}
override fun onFailure(call: Call>, error: Throwable) {
if (!call.isCanceled) {
mainHandler.post {
- callback.fail(error.message ?: "网络请求失败")
+ callback.fail(error.message ?: mContext.getString(R.string.i18n_150)) //网络请求失败
}
}
}
@@ -145,10 +154,12 @@ class MineImpl : MineContract.DataModel {
val body = JSONObject().apply { put("oldCode", oldCode.trim()) }
ApiClient.apiService.verifyOldEmail(NetworkManager.getToken(), NetworkManager.toRequestBody(body)).enqueue(object : Callback> {
override fun onResponse(call: Call>, response: Response>) {
- mainHandler.post { response.body()?.let(callback::success) ?: callback.fail("当前邮箱验证失败") }
+ mainHandler.post { response.body()?.let(callback::success) ?: callback.fail(mContext.getString(R.string.i18n_152)) } //当前邮箱验证失败
}
override fun onFailure(call: Call>, error: Throwable) {
- if (!call.isCanceled) mainHandler.post { callback.fail(error.message ?: "网络请求失败") }
+ if (!call.isCanceled) {
+ mainHandler.post { callback.fail(error.message ?: mContext.getString(R.string.i18n_150)) }
+ } //网络请求失败
}
})
}
@@ -176,14 +187,14 @@ class MineImpl : MineContract.DataModel {
) {
mainHandler.post {
response.body()?.let(callback::success)
- ?: callback.fail("手机号更换失败")
+ ?: callback.fail(mContext.getString(R.string.i18n_153)) //手机号更换失败
}
}
override fun onFailure(call: Call>, error: Throwable) {
if (!call.isCanceled) {
mainHandler.post {
- callback.fail(error.message ?: "网络请求失败")
+ callback.fail(error.message ?: mContext.getString(R.string.i18n_150)) //网络请求失败
}
}
}
@@ -192,13 +203,21 @@ class MineImpl : MineContract.DataModel {
}
override fun updateBindNewEmail(newEmail: String, code: String, callback: MineContract.Callback>) {
- val body = JSONObject().apply { put("newEmail", newEmail.trim()); put("code", code.trim()) }
- ApiClient.apiService.updateBindNewEmail(NetworkManager.getToken(), NetworkManager.toRequestBody(body)).enqueue(object : Callback> {
+ val body = JSONObject().apply {
+ put("newEmail", newEmail.trim())
+ put("code", code.trim())
+ }
+ ApiClient.apiService.updateBindNewEmail(
+ NetworkManager.getToken(),
+ NetworkManager.toRequestBody(body)
+ ).enqueue(object : Callback> {
override fun onResponse(call: Call>, response: Response>) {
- mainHandler.post { response.body()?.let(callback::success) ?: callback.fail("邮箱更换失败") }
+ mainHandler.post { response.body()?.let(callback::success) ?: callback.fail(mContext.getString(R.string.i18n_154)) } //邮箱更换失败
}
override fun onFailure(call: Call>, error: Throwable) {
- if (!call.isCanceled) mainHandler.post { callback.fail(error.message ?: "网络请求失败") }
+ if (!call.isCanceled) {
+ mainHandler.post { callback.fail(error.message ?: mContext.getString(R.string.i18n_150)) }
+ } //网络请求失败
}
})
}
@@ -215,14 +234,14 @@ class MineImpl : MineContract.DataModel {
) {
mainHandler.post {
response.body()?.let(callback::success)
- ?: callback.fail("退出登录失败")
+ ?: callback.fail(mContext.getString(R.string.i18n_155)) //退出登录失败
}
}
override fun onFailure(call: Call>, error: Throwable) {
if (!call.isCanceled) {
mainHandler.post {
- callback.fail(error.message ?: "网络请求失败")
+ callback.fail(error.message ?: mContext.getString(R.string.i18n_150)) //网络请求失败
}
}
}
@@ -242,14 +261,14 @@ class MineImpl : MineContract.DataModel {
) {
mainHandler.post {
response.body()?.let(callback::success)
- ?: callback.fail("注销账号失败")
+ ?: callback.fail(mContext.getString(R.string.i18n_156)) //注销账号失败
}
}
override fun onFailure(call: Call>, error: Throwable) {
if (!call.isCanceled) {
mainHandler.post {
- callback.fail(error.message ?: "网络请求失败")
+ callback.fail(error.message ?: mContext.getString(R.string.i18n_150)) //网络请求失败
}
}
}
@@ -276,7 +295,7 @@ class MineImpl : MineContract.DataModel {
if (response.isSuccessful && bean?.code == 200 && data != null) {
callback.success(data)
} else {
- callback.fail(bean?.message?.ifBlank { null } ?: "获取个人信息失败")
+ callback.fail(bean?.message?.ifBlank { null } ?: mContext.getString(R.string.i18n_157)) //获取个人信息失败
}
}
}
@@ -284,7 +303,7 @@ class MineImpl : MineContract.DataModel {
override fun onFailure(call: Call>, error: Throwable) {
if (!call.isCanceled) {
mainHandler.post {
- callback.fail(error.message ?: "网络请求失败")
+ callback.fail(error.message ?: mContext.getString(R.string.i18n_150)) //网络请求失败
}
}
}
@@ -315,7 +334,7 @@ class MineImpl : MineContract.DataModel {
} else {
callback.fail(
bean?.message?.ifBlank { null }
- ?: "版本检测失败"
+ ?: mContext.getString(R.string.i18n_158) //版本检测失败
)
}
}
@@ -328,7 +347,7 @@ class MineImpl : MineContract.DataModel {
if (!call.isCanceled) {
mainHandler.post {
callback.fail(
- error.message ?: "版本检测失败"
+ error.message ?: mContext.getString(R.string.i18n_158) //版本检测失败
)
}
}
@@ -363,14 +382,14 @@ class MineImpl : MineContract.DataModel {
if (response.isSuccessful && bean?.code == 200) {
callback.success(bean.message.orEmpty())
} else {
- callback.fail(bean?.message?.ifBlank { null } ?: "密码修改失败")
+ callback.fail(bean?.message?.ifBlank { null } ?: mContext.getString(R.string.i18n_159)) //密码修改失败
}
}
}
override fun onFailure(call: Call>, error: Throwable) {
if (!call.isCanceled) {
- mainHandler.post { callback.fail(error.message ?: "网络请求失败") }
+ mainHandler.post { callback.fail(error.message ?: mContext.getString(R.string.i18n_150)) } //网络请求失败
}
}
})
@@ -399,3 +418,4 @@ class MineImpl : MineContract.DataModel {
mainHandler.removeCallbacksAndMessages(null)
}
}
+
diff --git a/app/src/main/java/com/juhabi/birds/mine/mvp/MinePresenter.kt b/app/src/main/java/com/juhabi/birds/mine/mvp/MinePresenter.kt
index ad1c49f..a20996d 100644
--- a/app/src/main/java/com/juhabi/birds/mine/mvp/MinePresenter.kt
+++ b/app/src/main/java/com/juhabi/birds/mine/mvp/MinePresenter.kt
@@ -1,5 +1,7 @@
package com.juhabi.birds.mine.mvp
+import android.content.Context
+import com.juhabi.birds.R
import com.juhabi.birds.base.BasePresenter
import com.juhabi.birds.main.model.AppVersionInfo
import com.juhabi.birds.mine.model.MineUserInfo
@@ -7,7 +9,10 @@ import com.juhabi.birds.login.model.SendCodeEntity
import com.juhabi.birds.network.BaseBean
import org.json.JSONObject
-class MinePresenter(private val mineImpl: MineImpl) : BasePresenter() {
+class MinePresenter(
+ private val mineImpl: MineImpl,
+ private val mContext: Context
+) : BasePresenter() {
/** 通过登录态向当前绑定的手机号发送验证码,无需额外请求参数。 */
fun getCurrentPhoneCode() {
@@ -18,9 +23,9 @@ class MinePresenter(private val mineImpl: MineImpl) : BasePresenter return false
}
- if (!cardNumber.take(17).all(Char::isDigit)) return false
- if (cardNumber.last() != 'X' && !cardNumber.last().isDigit()) return false
- if (cardNumber.substring(0, 2) !in areaCodes) return false
- if (!hasValidBirthDate(cardNumber.substring(6, 14))) return false
+ if (!cardNumber.take(17).all(Char::isDigit)) {
+ return false
+ }
+ if (cardNumber.last() != 'X' && !cardNumber.last().isDigit()) {
+ return false
+ }
+ if (cardNumber.substring(0, 2) !in areaCodes) {
+ return false
+ }
+ if (!hasValidBirthDate(cardNumber.substring(6, 14))) {
+ return false
+ }
return cardNumber.last() == calculateVerifyCode(cardNumber)
}
private fun convertToNewCardNumber(oldCardNumber: String): String? {
- if (!oldCardNumber.all(Char::isDigit)) return null
+ if (!oldCardNumber.all(Char::isDigit)) {
+ return null
+ }
val firstSeventeen = buildString(17) {
append(oldCardNumber, 0, 6)
append("19")
diff --git a/app/src/main/java/com/juhabi/birds/util/file/PreviewFileUtil.kt b/app/src/main/java/com/juhabi/birds/util/file/PreviewFileUtil.kt
index a7c0b21..ab6e622 100644
--- a/app/src/main/java/com/juhabi/birds/util/file/PreviewFileUtil.kt
+++ b/app/src/main/java/com/juhabi/birds/util/file/PreviewFileUtil.kt
@@ -12,7 +12,9 @@ import java.io.File
object PreviewFileUtil {
@JvmStatic
fun previewFiles(context: Context, suffix: String?, fileUrl: String?): Boolean {
- if (fileUrl.isNullOrBlank()) return false
+ if (fileUrl.isNullOrBlank()) {
+ return false
+ }
val uri = when {
fileUrl.startsWith("content://", ignoreCase = true) -> Uri.parse(fileUrl)
fileUrl.startsWith("http://", ignoreCase = true) ||
@@ -31,11 +33,15 @@ object PreviewFileUtil {
val intent = Intent(Intent.ACTION_VIEW)
.setDataAndType(uri, resolvedMimeType)
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
- if (context !is Activity) intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ if (context !is Activity) {
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ }
return try {
context.startActivity(Intent.createChooser(intent, null).apply {
- if (context !is Activity) addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ if (context !is Activity) {
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ }
})
true
} catch (_: ActivityNotFoundException) {
@@ -44,7 +50,9 @@ object PreviewFileUtil {
}
private fun fileToContentUri(context: Context, file: File): Uri? {
- if (!file.exists() || !file.isFile) return null
+ if (!file.exists() || !file.isFile) {
+ return null
+ }
return try {
FileProvider.getUriForFile(context, "${context.packageName}.fileprovider", file)
} catch (_: IllegalArgumentException) {
diff --git a/app/src/main/java/com/juhabi/birds/util/manager/ActivityManager.kt b/app/src/main/java/com/juhabi/birds/util/manager/ActivityManager.kt
index 1ec63cb..f4fb17c 100644
--- a/app/src/main/java/com/juhabi/birds/util/manager/ActivityManager.kt
+++ b/app/src/main/java/com/juhabi/birds/util/manager/ActivityManager.kt
@@ -40,7 +40,9 @@ object ActivityManager {
@JvmStatic
fun removeActivityByTag(tag: String) {
- if (tag.isBlank()) return
+ if (tag.isBlank()) {
+ return
+ }
finishAndRemove { key, _ -> key.contains(tag) }
}
diff --git a/app/src/main/java/com/juhabi/birds/util/manager/UserInfoManager.kt b/app/src/main/java/com/juhabi/birds/util/manager/UserInfoManager.kt
index 4896049..b0507e5 100644
--- a/app/src/main/java/com/juhabi/birds/util/manager/UserInfoManager.kt
+++ b/app/src/main/java/com/juhabi/birds/util/manager/UserInfoManager.kt
@@ -41,9 +41,19 @@ object UserInfoManager {
@JvmStatic
fun setUserInfo(userInfo: MineUserInfo) {
val loginByPhone = userInfo.email.isNullOrBlank()
+ val normalizedEmail = if (loginByPhone) {
+ ""
+ } else {
+ userInfo.email
+ }
+ val normalizedPhoneNumber = if (loginByPhone) {
+ userInfo.phoneNumber
+ } else {
+ ""
+ }
val normalizedUserInfo = userInfo.copy(
- email = if (loginByPhone) "" else userInfo.email,
- phoneNumber = if (loginByPhone) userInfo.phoneNumber else "",
+ email = normalizedEmail,
+ phoneNumber = normalizedPhoneNumber,
userLoginByPhone = loginByPhone
)
setUserInfo(MyApplication.appContext, normalizedUserInfo)
diff --git a/app/src/main/java/com/juhabi/birds/utils/AppUpdateManager.kt b/app/src/main/java/com/juhabi/birds/utils/AppUpdateManager.kt
index 38c4f80..d96b73f 100644
--- a/app/src/main/java/com/juhabi/birds/utils/AppUpdateManager.kt
+++ b/app/src/main/java/com/juhabi/birds/utils/AppUpdateManager.kt
@@ -33,7 +33,7 @@ class AppUpdateManager(
versionInfo.versionName
}
if (!isNewerVersion(currentVersion, latestVersion)) {
- messageCallback("当前已是最新版本")
+ messageCallback(activity.getString(com.juhabi.birds.R.string.i18n_140)) //当前已是最新版本
return
}
showUpdateDialog(latestVersion, versionInfo)
@@ -43,7 +43,9 @@ class AppUpdateManager(
requestCode: Int,
grantResults: IntArray
): Boolean {
- if (requestCode != REQUEST_NOTIFICATIONS) return false
+ if (requestCode != REQUEST_NOTIFICATIONS) {
+ return false
+ }
val downloadUrl = pendingDownloadUrl
pendingDownloadUrl = null
@@ -52,7 +54,7 @@ class AppUpdateManager(
if (permissionGranted && !downloadUrl.isNullOrBlank()) {
downloadApk(downloadUrl)
} else {
- messageCallback("需要通知权限才能显示下载进度")
+ messageCallback(activity.getString(com.juhabi.birds.R.string.i18n_141)) //需要通知权限才能显示下载进度
}
return true
}
@@ -62,14 +64,14 @@ class AppUpdateManager(
versionInfo: AppVersionInfo
) {
AlertDialog.Builder(activity)
- .setTitle("发现新版本 $latestVersion")
+ .setTitle(activity.getString(com.juhabi.birds.R.string.update_new_version_title, latestVersion)) //发现新版本 %1$s
.setMessage(
versionInfo.releaseNotes.ifBlank {
- "是否立即下载更新?"
+ activity.getString(com.juhabi.birds.R.string.i18n_142) //是否立即下载更新?
}
)
- .setNegativeButton("取消", null)
- .setPositiveButton("下载") { _, _ ->
+ .setNegativeButton(activity.getString(com.juhabi.birds.R.string.i18n_041), null) //取消
+ .setPositiveButton(activity.getString(com.juhabi.birds.R.string.i18n_143)) { _, _ -> //下载
downloadApk(versionInfo.downloadUrl)
}
.setCancelable(!versionInfo.forceUpdate)
@@ -102,7 +104,7 @@ class AppUpdateManager(
private fun downloadApk(url: String) {
if (url.isBlank()) {
- messageCallback("下载地址为空")
+ messageCallback(activity.getString(com.juhabi.birds.R.string.i18n_144)) //下载地址为空
return
}
if (!hasNotificationPermission()) {
@@ -128,7 +130,7 @@ class AppUpdateManager(
}
}.onFailure { error ->
activity.runOnUiThread {
- messageCallback("下载失败:${error.message}")
+ messageCallback(activity.getString(com.juhabi.birds.R.string.update_download_failed, error.message)) //下载失败:%1$s
}
}
}
@@ -142,12 +144,14 @@ class AppUpdateManager(
}
private fun createDownloadChannel(manager: NotificationManager) {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
+ return
+ }
manager.createNotificationChannel(
NotificationChannel(
DOWNLOAD_CHANNEL_ID,
- "应用更新",
+ activity.getString(com.juhabi.birds.R.string.i18n_145), //应用更新
NotificationManager.IMPORTANCE_LOW
)
)
@@ -160,9 +164,11 @@ class AppUpdateManager(
val response = OkHttpClient().newCall(
Request.Builder().url(url).build()
).execute()
- if (!response.isSuccessful) error("HTTP ${response.code}")
+ if (!response.isSuccessful) {
+ error("HTTP ${response.code}")
+ }
- val body = response.body ?: error("下载响应为空")
+ val body = response.body ?: error(activity.getString(com.juhabi.birds.R.string.i18n_146)) //下载响应为空
val apkFile = File(
activity.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
UPDATE_APK_NAME
@@ -209,7 +215,7 @@ class AppUpdateManager(
DOWNLOAD_CHANNEL_ID
)
.setSmallIcon(android.R.drawable.stat_sys_download)
- .setContentTitle("正在下载更新")
+ .setContentTitle(activity.getString(com.juhabi.birds.R.string.i18n_147)) //正在下载更新
.setOnlyAlertOnce(true)
.setProgress(100, progress, false)
.build()
@@ -222,7 +228,7 @@ class AppUpdateManager(
DOWNLOAD_CHANNEL_ID
)
.setSmallIcon(android.R.drawable.stat_sys_download_done)
- .setContentTitle("下载完成")
+ .setContentTitle(activity.getString(com.juhabi.birds.R.string.i18n_148)) //下载完成
.build()
manager.notify(DOWNLOAD_NOTIFICATION_ID, notification)
}
@@ -248,3 +254,4 @@ class AppUpdateManager(
const val APK_MIME_TYPE = "application/vnd.android.package-archive"
}
}
+
diff --git a/app/src/main/res/drawable/select_radio.xml b/app/src/main/res/drawable/select_radio.xml
new file mode 100644
index 0000000..ebff77f
--- /dev/null
+++ b/app/src/main/res/drawable/select_radio.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_constraint_layout_demo.xml b/app/src/main/res/layout/activity_constraint_layout_demo.xml
index fcad020..0ee6253 100644
--- a/app/src/main/res/layout/activity_constraint_layout_demo.xml
+++ b/app/src/main/res/layout/activity_constraint_layout_demo.xml
@@ -11,9 +11,9 @@
android:layout_width="48dp"
android:layout_height="48dp"
android:background="?attr/selectableItemBackgroundBorderless"
- android:contentDescription="返回"
+ android:contentDescription="@string/back_button_description"
android:padding="14dp"
- android:src="@drawable/img_back_black"
+ android:src="@drawable/img_back_black_auto_mirrored"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
@@ -57,7 +57,7 @@
android:id="@+id/introText"
android:layout_width="0dp"
android:layout_height="wrap_content"
- android:text="下面用一个项目概览页演示常用约束。可以结合 XML 注释观察效果。"
+ android:text="@string/i18n_001"
android:textColor="#667085"
android:textSize="14sp"
app:layout_constraintEnd_toEndOf="parent"
@@ -101,7 +101,7 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="14dp"
- android:text="智能灌溉升级项目"
+ android:text="@string/i18n_002"
android:textColor="#18212F"
android:textSize="17sp"
android:textStyle="bold"
@@ -116,7 +116,7 @@
android:background="@drawable/bg_constraint_tag"
android:gravity="center"
android:paddingHorizontal="8dp"
- android:text="进行中"
+ android:text="@string/i18n_003"
android:textColor="#00866A"
android:textSize="12sp"
app:layout_constraintBaseline_toBaselineOf="@id/nameText"
@@ -129,7 +129,7 @@
android:layout_height="wrap_content"
android:layout_marginStart="14dp"
android:layout_marginTop="8dp"
- android:text="负责人:Android 学习者"
+ android:text="@string/i18n_004"
android:textColor="#667085"
android:textSize="13sp"
app:layout_constraintEnd_toEndOf="parent"
@@ -152,7 +152,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
- android:text="完成进度"
+ android:text="@string/i18n_005"
android:textColor="#667085"
android:textSize="12sp"
app:layout_constraintStart_toStartOf="parent"
@@ -175,7 +175,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
- android:text="项目详情"
+ android:text="@string/i18n_006"
android:textColor="#18212F"
android:textSize="18sp"
android:textStyle="bold"
@@ -186,7 +186,7 @@
android:id="@+id/toggleDetailsButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:text="隐藏详情"
+ android:text="@string/i18n_007"
android:textColor="#00866A"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/detailTitle"/>
@@ -196,7 +196,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="18dp"
- android:text="开始时间"
+ android:text="@string/i18n_008"
android:textColor="#667085"
android:textSize="14sp"
app:layout_constraintStart_toStartOf="parent"
@@ -207,7 +207,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="14dp"
- android:text="责任部门很长"
+ android:text="@string/i18n_009"
android:textColor="#667085"
android:textSize="14sp"
app:layout_constraintStart_toStartOf="parent"
@@ -238,7 +238,7 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
- android:text="移动研发组"
+ android:text="@string/i18n_010"
android:textColor="#344054"
android:textSize="14sp"
app:layout_constraintBaseline_toBaselineOf="@id/labelOwner"
@@ -257,7 +257,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="26dp"
- android:text="本周数据"
+ android:text="@string/i18n_011"
android:textColor="#18212F"
android:textSize="18sp"
android:textStyle="bold"
@@ -272,7 +272,7 @@
android:layout_marginTop="14dp"
android:background="#FFFFFF"
android:gravity="center"
- android:text="12\n任务"
+ android:text="@string/i18n_012"
android:textColor="#344054"
android:textSize="14sp"
app:layout_constraintEnd_toStartOf="@id/statTwo"
@@ -287,7 +287,7 @@
android:layout_marginStart="8dp"
android:background="#FFFFFF"
android:gravity="center"
- android:text="9\n完成"
+ android:text="@string/i18n_013"
android:textColor="#344054"
android:textSize="14sp"
app:layout_constraintEnd_toStartOf="@id/statThree"
@@ -301,7 +301,7 @@
android:layout_marginStart="8dp"
android:background="#FFFFFF"
android:gravity="center"
- android:text="3\n待处理"
+ android:text="@string/i18n_014"
android:textColor="#344054"
android:textSize="14sp"
app:layout_constraintEnd_toEndOf="parent"
@@ -313,7 +313,7 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
- android:text="16:9 比例区域"
+ android:text="@string/i18n_015"
android:textColor="#18212F"
android:textSize="16sp"
android:textStyle="bold"
@@ -329,7 +329,7 @@
android:layout_marginTop="12dp"
android:background="#DDEFEA"
android:gravity="center"
- android:text="宽度由两侧约束\n高度按 16:9 自动计算"
+ android:text="@string/i18n_016"
android:textColor="#26705F"
android:textSize="14sp"
app:layout_constraintDimensionRatio="16:9"
@@ -344,7 +344,7 @@
android:layout_marginBottom="24dp"
android:background="#FFF7E6"
android:padding="14dp"
- android:text="核心区别:LinearLayout 强调排列,RelativeLayout 强调相对规则;ConstraintLayout 用双向约束与辅助工具描述关系,复杂页面通常能减少嵌套层级。"
+ android:text="@string/i18n_017"
android:textColor="#7A4E00"
android:textSize="13sp"
app:layout_constraintBottom_toBottomOf="parent"
diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml
index c26492a..fcfc090 100644
--- a/app/src/main/res/layout/activity_login.xml
+++ b/app/src/main/res/layout/activity_login.xml
@@ -80,7 +80,7 @@
android:layout_height="match_parent"
android:layout_gravity="end"
android:gravity="center"
- android:text="邮箱"
+ android:text="@string/i18n_018"
android:textColor="@color/color_zhu"
android:textSize="13sp" />
@@ -149,7 +149,7 @@
android:layout_weight="1"
android:layout_marginStart="8dp"
android:background="@null"
- android:hint="请输入验证码"
+ android:hint="@string/i18n_019"
android:inputType="number"
android:maxLines="1"
android:textColorHint="@color/color_999999"
@@ -163,7 +163,7 @@
android:backgroundTint="@null"
android:stateListAnimator="@null"
android:gravity="center"
- android:text="获取验证码"
+ android:text="@string/register_get_code"
android:textAllCaps="false"
android:textColor="@color/color_white"
android:textSize="12sp" />
@@ -215,15 +215,37 @@
android:textColor="@color/color_zhu"
android:textSize="16sp" />
-
+ android:layout_marginTop="30dp"
+ android:layout_marginStart="20dp">
+
+
+
+
+
+ android:text="@string/i18n_020" />
diff --git a/app/src/main/res/layout/bind_device_layout.xml b/app/src/main/res/layout/bind_device_layout.xml
index 5d8c060..41491f6 100644
--- a/app/src/main/res/layout/bind_device_layout.xml
+++ b/app/src/main/res/layout/bind_device_layout.xml
@@ -30,7 +30,7 @@
android:layout_height="wrap_content"
android:textSize="15sp"
android:textColor="@color/color_333333"
- android:text="绑定"/>
+ android:text="@string/i18n_021"/>
+ android:hint="@string/i18n_022"/>
+ android:text="@string/i18n_023"/>
+ android:hint="@string/i18n_024"/>
+ android:text="@string/i18n_025"/>
@@ -112,7 +112,7 @@
android:layout_weight="7"
android:textSize="15sp"
android:textColor="@color/color_333333"
- android:text="IMEI码可在设备机身上的二维码标签旁边找到"/>
+ android:text="@string/i18n_026"/>
diff --git a/app/src/main/res/layout/device_detail_layout.xml b/app/src/main/res/layout/device_detail_layout.xml
index 7e548aa..1f922a9 100644
--- a/app/src/main/res/layout/device_detail_layout.xml
+++ b/app/src/main/res/layout/device_detail_layout.xml
@@ -103,7 +103,7 @@
android:layout_marginStart="10dp"
android:textSize="13sp"
android:textColor="@color/color_333333"
- android:text="眼罩开关"/>
+ android:text="@string/i18n_028"/>
+ android:text="@string/i18n_029"/>
+ android:text="@string/i18n_031"/>
+ android:text="@string/i18n_032"/>
+ android:text="@string/i18n_033"/>
@@ -147,7 +147,7 @@
android:layout_height="wrap_content"
android:textSize="13sp"
android:textColor="@color/color_black1"
- android:text="头套连接状态"/>
+ android:text="@string/i18n_034"/>
+ android:text="@string/i18n_035"/>
@@ -185,7 +185,7 @@
android:layout_height="wrap_content"
android:textSize="13sp"
android:textColor="@color/color_black1"
- android:text="头套设备电量"/>
+ android:text="@string/i18n_036"/>
+ android:text="@string/i18n_037"/>
+ android:text="@string/i18n_038"/>
+ android:text="@string/i18n_039"/>
+ android:text="@string/i18n_040"/>
-
\ No newline at end of file
+
diff --git a/app/src/main/res/layout/dialog_input_text.xml b/app/src/main/res/layout/dialog_input_text.xml
index efdcca0..3a41fd4 100644
--- a/app/src/main/res/layout/dialog_input_text.xml
+++ b/app/src/main/res/layout/dialog_input_text.xml
@@ -74,7 +74,7 @@
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
- android:text="取消"
+ android:text="@string/i18n_041"
android:textColor="@color/color_333333"
android:textSize="16sp" />
@@ -89,7 +89,7 @@
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
- android:text="确认修改"
+ android:text="@string/i18n_042"
android:textColor="@color/color_zhu"
android:textSize="16sp"
android:textStyle="bold" />
diff --git a/app/src/main/res/layout/dialog_loading.xml b/app/src/main/res/layout/dialog_loading.xml
index 97784e1..fc0187b 100644
--- a/app/src/main/res/layout/dialog_loading.xml
+++ b/app/src/main/res/layout/dialog_loading.xml
@@ -22,7 +22,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
- android:text="加载中..."
+ android:text="@string/i18n_043"
android:textColor="@color/bottom_nav_selected"
android:textSize="12sp" />
diff --git a/app/src/main/res/layout/forget_psd_layout.xml b/app/src/main/res/layout/forget_psd_layout.xml
index 44b87e3..a62af71 100644
--- a/app/src/main/res/layout/forget_psd_layout.xml
+++ b/app/src/main/res/layout/forget_psd_layout.xml
@@ -76,7 +76,7 @@
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@null"
- android:hint="请输入手机号"
+ android:hint="@string/i18n_044"
android:inputType="phone"
android:layout_marginStart="8dp"
android:maxLines="1"
@@ -89,7 +89,7 @@
android:layout_gravity="end"
android:layout_marginEnd="20dp"
android:gravity="center"
- android:text="邮箱"
+ android:text="@string/i18n_018"
android:textColor="@color/color_zhu"
android:textSize="13sp" />
@@ -117,7 +117,7 @@
android:layout_weight="1"
android:layout_marginStart="8dp"
android:background="@null"
- android:hint="请输入验证码"
+ android:hint="@string/i18n_019"
android:inputType="number"
android:maxLines="1"
android:textColorHint="@color/color_999999"
@@ -131,7 +131,7 @@
android:backgroundTint="@null"
android:stateListAnimator="@null"
android:gravity="center"
- android:text="获取验证码"
+ android:text="@string/register_get_code"
android:textAllCaps="false"
android:textColor="@color/color_white"
android:textSize="12sp" />
@@ -152,7 +152,7 @@
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@null"
- android:hint="请输入8-20位密码"
+ android:hint="@string/i18n_045"
android:inputType="textPassword"
android:textColorHint="@color/color_999999"
android:textSize="16sp" />
@@ -162,7 +162,7 @@
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@mipmap/icon_login_show"
- android:contentDescription="显示密码" />
+ android:contentDescription="@string/register_show_password" />
@@ -190,7 +190,7 @@
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@mipmap/icon_login_show"
- android:contentDescription="显示密码" />
+ android:contentDescription="@string/register_show_password" />
@@ -243,7 +243,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
- android:text="包含大小写字母和数字"
+ android:text="@string/i18n_049"
android:textColor="@color/white"
android:textSize="14sp" />
@@ -262,7 +262,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
- android:text="不能与当前密码相同"
+ android:text="@string/i18n_050"
android:textColor="@color/white"
android:textSize="14sp" />
@@ -280,7 +280,7 @@
android:background="@drawable/shape_login_field_bg"
android:backgroundTint="@null"
android:stateListAnimator="@null"
- android:text="确定"
+ android:text="@string/i18n_027"
android:textAllCaps="false"
android:textColor="@color/color_zhu"
android:textSize="17sp" />
diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml
index 3350226..4b0b26e 100644
--- a/app/src/main/res/layout/fragment_home.xml
+++ b/app/src/main/res/layout/fragment_home.xml
@@ -52,7 +52,8 @@
+ android:layout_height="wrap_content"
+ >
diff --git a/app/src/main/res/layout/fragment_plan.xml b/app/src/main/res/layout/fragment_plan.xml
index 65f4afb..9acf498 100644
--- a/app/src/main/res/layout/fragment_plan.xml
+++ b/app/src/main/res/layout/fragment_plan.xml
@@ -19,7 +19,7 @@
android:layout_marginTop="8dp"
android:contentDescription="@string/back_button_description"
android:padding="12dp"
- android:src="@drawable/img_back_black" />
+ android:src="@drawable/img_back_black_auto_mirrored" />
-
\ No newline at end of file
+
diff --git a/app/src/main/res/layout/include_activity_back_greenbg_layout.xml b/app/src/main/res/layout/include_activity_back_greenbg_layout.xml
index 1e945ac..d4eb7b7 100644
--- a/app/src/main/res/layout/include_activity_back_greenbg_layout.xml
+++ b/app/src/main/res/layout/include_activity_back_greenbg_layout.xml
@@ -12,8 +12,8 @@
android:layout_width="22dp"
android:layout_height="match_parent"
android:layout_centerVertical="true"
- android:src="@drawable/img_back_black"
- android:layout_marginLeft="10dp"/>
+ android:src="@drawable/img_back_black_auto_mirrored"
+ android:layout_marginStart="10dp"/>
diff --git a/app/src/main/res/layout/include_activity_back_whitebg_layout.xml b/app/src/main/res/layout/include_activity_back_whitebg_layout.xml
index 88d1815..c37b2b4 100644
--- a/app/src/main/res/layout/include_activity_back_whitebg_layout.xml
+++ b/app/src/main/res/layout/include_activity_back_whitebg_layout.xml
@@ -12,8 +12,8 @@
android:layout_width="22dp"
android:layout_height="match_parent"
android:layout_centerVertical="true"
- android:src="@drawable/img_back_black"
- android:layout_marginLeft="10dp"/>
+ android:src="@drawable/img_back_black_auto_mirrored"
+ android:layout_marginStart="10dp"/>
diff --git a/app/src/main/res/layout/include_title_history_layout.xml b/app/src/main/res/layout/include_title_history_layout.xml
index 372da75..ba45356 100644
--- a/app/src/main/res/layout/include_title_history_layout.xml
+++ b/app/src/main/res/layout/include_title_history_layout.xml
@@ -11,7 +11,7 @@
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:layout_marginStart="10dp"
- android:src="@drawable/img_back_black" />
+ android:src="@drawable/img_back_black_auto_mirrored" />
+ android:src="@drawable/img_back_black_auto_mirrored" />
+ android:src="@drawable/img_back_black_auto_mirrored" />
+ android:src="@drawable/img_back_black_auto_mirrored" />
+ android:text="@string/i18n_053"/>
@@ -83,13 +83,13 @@
android:textStyle="bold"
android:textSize="16sp"
android:textColor="@color/white"
- android:text="修改密码"/>
+ android:text="@string/modify_password"/>
+ android:src="@drawable/icon_mine_point_auto_mirrored"/>
+ android:text="@string/i18n_054"/>
+ android:src="@drawable/icon_mine_point_auto_mirrored"/>
+ android:text="@string/i18n_055"/>
+ android:src="@drawable/icon_mine_point_auto_mirrored"/>
+ android:text="@string/i18n_056"/>
+ android:src="@drawable/icon_mine_point_auto_mirrored"/>
+ android:text="@string/i18n_057"/>
+ android:src="@drawable/icon_mine_point_auto_mirrored"/>
+ android:text="@string/i18n_058"/>
+ android:src="@drawable/icon_mine_point_auto_mirrored"/>
+ android:text="@string/i18n_059"/>
+ android:src="@drawable/icon_mine_point_auto_mirrored"/>
+ android:text="@string/i18n_060"/>
+ android:src="@drawable/icon_mine_point_auto_mirrored"/>
-
\ No newline at end of file
+
diff --git a/app/src/main/res/layout/modify_psd_layout.xml b/app/src/main/res/layout/modify_psd_layout.xml
index 1f6f406..b5b077a 100644
--- a/app/src/main/res/layout/modify_psd_layout.xml
+++ b/app/src/main/res/layout/modify_psd_layout.xml
@@ -39,16 +39,20 @@
android:layout_marginTop="20dp"
android:gravity="center_vertical"
android:background="@drawable/shape_login_field_bg"
- android:paddingStart="44dp"
+ android:paddingStart="20dp"
android:paddingEnd="16dp">
-
+
@@ -66,16 +70,20 @@
android:layout_marginTop="20dp"
android:gravity="center_vertical"
android:background="@drawable/shape_login_field_bg"
- android:paddingStart="44dp"
+ android:paddingStart="20dp"
android:paddingEnd="16dp">
-
+
@@ -85,7 +93,7 @@
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@mipmap/icon_login_show"
- android:contentDescription="显示密码" />
+ android:contentDescription="@string/register_show_password" />
-
+
@@ -113,7 +125,7 @@
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@mipmap/icon_login_show"
- android:contentDescription="显示密码" />
+ android:contentDescription="@string/register_show_password" />
@@ -166,7 +178,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
- android:text="包含大小写字母和数字"
+ android:text="@string/i18n_049"
android:textColor="@color/white"
android:textSize="14sp" />
@@ -185,7 +197,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
- android:text="不能与当前密码相同"
+ android:text="@string/i18n_050"
android:textColor="@color/white"
android:textSize="14sp" />
@@ -203,7 +215,7 @@
android:background="@drawable/shape_login_field_bg"
android:backgroundTint="@null"
android:stateListAnimator="@null"
- android:text="确定"
+ android:text="@string/i18n_027"
android:textAllCaps="false"
android:textColor="@color/color_zhu"
android:textSize="17sp" />
diff --git a/app/src/main/res/layout/permission_confirm_dialog.xml b/app/src/main/res/layout/permission_confirm_dialog.xml
index 78a9190..06df714 100644
--- a/app/src/main/res/layout/permission_confirm_dialog.xml
+++ b/app/src/main/res/layout/permission_confirm_dialog.xml
@@ -13,7 +13,7 @@
android:layout_marginTop="28dp"
android:layout_marginHorizontal="24dp"
android:gravity="center"
- android:text="提示"
+ android:text="@string/i18n_063"
android:textColor="@color/color_333333"
android:textSize="18sp"
android:textStyle="bold" />
@@ -27,7 +27,7 @@
android:layout_marginHorizontal="24dp"
android:gravity="center"
android:lineSpacingExtra="4dp"
- android:text="内容内容"
+ android:text="@string/i18n_064"
android:textColor="@color/color_999999"
android:textSize="15sp" />
@@ -47,7 +47,7 @@
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
- android:text="取消"
+ android:text="@string/i18n_041"
android:textColor="@color/color_333333"
android:textSize="16sp" />
@@ -62,7 +62,7 @@
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
- android:text="确定"
+ android:text="@string/i18n_027"
android:textColor="@color/color_zhu"
android:textSize="16sp"
android:textStyle="bold" />
diff --git a/app/src/main/res/layout/popup_add_device.xml b/app/src/main/res/layout/popup_add_device.xml
index e4d959c..d212876 100644
--- a/app/src/main/res/layout/popup_add_device.xml
+++ b/app/src/main/res/layout/popup_add_device.xml
@@ -19,7 +19,7 @@
@@ -28,7 +28,7 @@
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:duplicateParentState="true"
- android:text="手动添加"
+ android:text="@string/i18n_065"
android:textColor="@color/popup_add_item_text"
android:textSize="16sp" />
@@ -47,7 +47,7 @@
@@ -56,7 +56,7 @@
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:duplicateParentState="true"
- android:text="扫一扫"
+ android:text="@string/i18n_066"
android:textColor="@color/popup_add_item_text"
android:textSize="16sp" />
diff --git a/app/src/main/res/layout/popup_device_action.xml b/app/src/main/res/layout/popup_device_action.xml
index 9b50f95..a891dd8 100644
--- a/app/src/main/res/layout/popup_device_action.xml
+++ b/app/src/main/res/layout/popup_device_action.xml
@@ -23,7 +23,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
- android:text="重命名"
+ android:text="@string/device_action_rename"
android:textColor="#FFFFFF"
android:textSize="16sp" />
@@ -46,7 +46,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
- android:text="解绑设备"
+ android:text="@string/device_action_unbind"
android:textColor="#FFFFFF"
android:textSize="16sp" />
diff --git a/app/src/main/res/layout/register_layout.xml b/app/src/main/res/layout/register_layout.xml
index ac092a3..0bb0b18 100644
--- a/app/src/main/res/layout/register_layout.xml
+++ b/app/src/main/res/layout/register_layout.xml
@@ -76,7 +76,7 @@
android:layout_height="match_parent"
android:layout_weight="2"
android:background="@null"
- android:hint="请输入手机号"
+ android:hint="@string/i18n_044"
android:inputType="phone"
android:layout_marginStart="8dp"
android:maxLines="1"
@@ -90,7 +90,7 @@
android:layout_gravity="end"
android:layout_marginEnd="20dp"
android:gravity="center"
- android:text="邮箱"
+ android:text="@string/i18n_018"
android:textColor="@color/color_zhu"
android:textSize="13sp" />
@@ -118,7 +118,7 @@
android:layout_weight="1"
android:layout_marginStart="8dp"
android:background="@null"
- android:hint="请输入验证码"
+ android:hint="@string/i18n_019"
android:inputType="number"
android:maxLines="1"
android:textColorHint="@color/color_999999"
@@ -153,7 +153,7 @@
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@null"
- android:hint="请输入8-20位密码"
+ android:hint="@string/i18n_045"
android:inputType="textPassword"
android:textColorHint="@color/color_999999"
android:textSize="16sp" />
@@ -163,7 +163,7 @@
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@mipmap/icon_login_show"
- android:contentDescription="显示密码" />
+ android:contentDescription="@string/register_show_password" />
@@ -191,7 +191,7 @@
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@mipmap/icon_login_show"
- android:contentDescription="显示密码" />
+ android:contentDescription="@string/register_show_password" />
@@ -244,7 +244,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
- android:text="包含大小写字母和数字"
+ android:text="@string/i18n_049"
android:textColor="@color/white"
android:textSize="14sp" />
@@ -262,7 +262,7 @@
android:background="@drawable/shape_login_field_bg"
android:backgroundTint="@null"
android:stateListAnimator="@null"
- android:text="确定"
+ android:text="@string/i18n_027"
android:textAllCaps="false"
android:textColor="@color/color_zhu"
android:textSize="17sp" />
diff --git a/app/src/main/res/layout/update_user_phone_layout.xml b/app/src/main/res/layout/update_user_phone_layout.xml
index cd258c9..1e6eaca 100644
--- a/app/src/main/res/layout/update_user_phone_layout.xml
+++ b/app/src/main/res/layout/update_user_phone_layout.xml
@@ -40,7 +40,7 @@
android:layout_height="wrap_content"
android:textSize="13sp"
android:textColor="@color/color_black1"
- android:text="当前绑定账号"/>
+ android:text="@string/i18n_067"/>
@@ -148,7 +148,7 @@
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@null"
- android:hint="请输入手机号"
+ android:hint="@string/i18n_044"
android:inputType="phone"
android:layout_marginStart="8dp"
android:maxLines="1"
@@ -181,7 +181,7 @@
android:layout_weight="1"
android:layout_marginStart="8dp"
android:background="@null"
- android:hint="请输入验证码"
+ android:hint="@string/i18n_019"
android:inputType="number"
android:maxLines="1"
android:textColorHint="@color/color_999999"
@@ -216,7 +216,7 @@
android:background="@drawable/shape_login_field_bg"
android:backgroundTint="@null"
android:stateListAnimator="@null"
- android:text="确认修改"
+ android:text="@string/i18n_042"
android:textAllCaps="false"
android:textColor="@color/color_zhu"
android:textSize="17sp" />
diff --git a/app/src/main/res/mipmap-xhdpi/icon_language_china.png b/app/src/main/res/mipmap-xhdpi/icon_language_china.png
new file mode 100644
index 0000000000000000000000000000000000000000..f68e91311615b489331fefe7aed5f2bae3c04693
GIT binary patch
literal 671
zcmeAS@N?(olHy`uVBq!ia0vp^9zbls!3HEZ&9QmSz`zvZ>Eakt!T4r|tsk=^kDI%o
zh^M3Qtq_q8<^!cg3>JJc4X+O{HdyIq2C1-m9OzETOnLj~jn?khqHe#QY_#3{aZg2B
zVA}ie)$-x94(j}fpVq*9>JjHbM#}|EjZU|$PcVvdh+Mkon9LM)XKwq`zcy>;>Gy35
z=ssDpUUp0UPc8?i>HOWEPOn%x)+JnCy545q=btt(|gb6pYQuFIK6!OSLbynnV1+H
zl63#w^9f3jnQoY^azEwd;%%`vzN}G9igYN_xzW>c)-$n3wNF(qTf)54MyZ`uCwcWL
zrbQX|Z1yxtJU`-k=FT0NlZO52NfVFg-cnNNN)whhzkR^GPrf@sOZ@!h_KtjITe#(6>3u;6<^Yq&0GaxLS2YRSYFAhbEFn!V)jHk*G5YSZ_G3as2!
z#ePK9?|Z??G?0B;Agi=R{?_MURV`+P5Khf`GS`+T@NwLA{k^f0P
zrVKBKlC^vu!h!pkgB-TJZZHviYOTQPvYVjLZI&fnGM7soSby=s;+b!S!-5I_SQ!}p
m{}Yi!-`Rd!*vVW&{{+=Cw_~Wd+etjz@
zR?K(*)*rK5uN*HmeWJgy+r%L^v2jqtVeaO?Z@w?LUp*=Re*Imk{X4#Lshoa(wS4mC
zYqjP{`s5;~qeuUjzux7uG>6V>1%tRbc6-+3!Oww}Yid35S
z$^|2=Fw3c{8rz6sSPgiqpcmeKyVOXUx|&w0M$U*qQjMIOckh+vnK!;D*M9h=AnBs0
zX853}W_qNZ)A=H#Or1t;=v2~y#hvYNK3nwx*#
zLrH6A1ht>$e-Je_HEmlpa~jV6KShuhBuzn0=cO7PgjETOMnvtan$A--k-u^&K{_C5
zpVYJq)yO++B|*^*DV`+x{sLvQ2v&7C!SH
zwpeXdlJ7uJBBNG45L9C&`3?jnGHTTWK{ZyA??6x@qgFi-RAVLin*3OBPWASi%35sC
zZV5}Zs!wg~u6&K>nK7-(N^*{g&D@7AmKLK{ZM-WPVqPiK0*B8#97pyrpQCMP=~*e_
z<}&7hZAfm$Hr}xHG0Axa>Xm|e*8;p}DfO^!zagJ(foEU7(!O(w_)2pk2{stb(I-b^
zdTlY2dRGE~$-4%e6$Li8xv+YtlYK+$;(7J!VxD6m$+y9mfBkvT@0&Iv$Q!o!r9=i3
zb%Y=WxbP{hv?P>d&)3J6jYU+KBy+%G!Th=dc8kluuB}awzUPw%hw
zv%;kWVR~%UNUUZ<)f7a@u@*mkRf6_SH37C;?m|sZ5rolwRZW+m_le(+8yI9FQ@fdX&V1cPN
zf8e&TxgAOFblq!H<80v8mpey&(YZ!&Pr!*vKLGK~#|B
zPSw!*2e~Z5O$d>*KFvi|jRb49
tiJBe*00960Lh5Gz00006NklrUqj~@U002ovPDHLkV1jWPV7veT
literal 0
HcmV?d00001
diff --git a/app/src/main/res/mipmap-xhdpi/icon_language_st.png b/app/src/main/res/mipmap-xhdpi/icon_language_st.png
new file mode 100644
index 0000000000000000000000000000000000000000..627651c1b46d80919f0731d77124e1f99ef8daf5
GIT binary patch
literal 951
zcmV;o14#UdP)HgXX!j$MRqRHn@19KBOT5E
z^~~|udA>Wnd2l|k&B5Ji{9VexAm~zhrfCo~2zxmMpfXNAAT?3%v5{xlswdpYs5>12z4yXydAyUJDr*#0>KE
z{fmVF_7=X)B<*TuvJl87w`%wiD^ESU0%Zhg=IH)oVikxnpaD+5d|VzuiSD>N&(8)M
zV8iI`uyW<8hny!5_{d6>5|l?qg9d>N%GUy6h9~8aK$oW$yr-j2BMo@?_6f$=9{O`DJ)DVW}OO7pL2W&7jAL7-~LkJuA0CCP!
z$F4#dL5B8-Xj+qgxRGfTaePb8{NK(dsWDV?==@;Y_??kQj1JBKezvpas&fXt3S|Uo
zbgQXlqvjWG8%K`8+M%*B21jP%sdG-N%MT$o%gqtzCo51!kVXvvX^am6>6H{Sflj6Lbht+A{}1EgLKPAZUe@
z)-?!f*;vtUCP6EWUYBbS)M0)QGzjW2&3V`kV{aK1uj6dS9Z3tAXW&4tXzeaPzWo<0$V#}Ke+%bka7t!(=?vv%y(uM
zW@_f>f6n)v|NY+g_>Yrf(&ndLuMff!+`$J_0(`*f&Hl(
zIOD7yy=*0oBl850so=mY<_oB&6KyVPaDg-ndbT1XDbo0I_Yqt>3_jU(L(J~P?7mqZ
zd7cg5hwtT*zSpHkuNm_O`d2gLj3;(HrUUH=&sk`52A1OHF>0QYc}oTkN5Xs5JSFoE
z8<;1cqsPGSP{*R?DVg`%z;YzaM9ot&Z`r{6NLc4PDn#FP3D0^M?hU-<<_o4^1$te2
z^qO&7byQelDvq-J=2LJ&7|FsjcnE(I)21!IgR09C%3&G#j&7*e!
O0000126oEP)M7bl@>S
zW557uXgUy7Rg+Fhw0eEt?QVK*uG?0flJ)Jq_TFp#>+ExPPqu=npMU9ex)8?05;zM_
zp%pp;`p&s#jIo9N$VPb!BU%8n5I6$&pbqxIVi*I8i@tNN8FQbt!{$O>{k#h#Z-KjT
z5az=O=+BIqN6;ZKR}I`;%PGuL7QqEVt6&j!9G(%@Vzs5CK2F99WF2hKz@}TWFLsnsCj>0DJ
zm37cxhK*27krqhf|Iu8778a|)oGbKdsHLXbOm$nPFi+z_+Qw7~7-+(E{Jn^|qr3%)
z$ui>8(9C2RsG*jcCDhplnVOk~fSKT_q>Fq-S8+-a_(4A?3%R8vdsfuMjjUm<$enr+
zHZVICv==mILL%zd%x^mYQl?!@q#h7M@Op
zIiP){spUxr{F@TrrvL(?w5czCzNb=Hno0trZqt7$)oz-K~NW
z+qFYkXkouF4&=4X5E0j&X5skftaXTrDjU}>vapl?9SgU|s*a8MNxO0Fb_+MfD$I@f
zNxO0FCJWccDy-zIl8TKJaf7BGsUE$03wvw$8D^H!kAtL5%_Tw9T6Gp~Wa2GolW7v&
zb!@j&(AM72G+MX~=K*xWDA)`WASqKzO|`Az=d-PVuZn{^RQUkf0-6In=}7v2vY@da
z5jE)|YuvV`J9fhR)Hxj(2E|Yj5^?|DLT&|_+d)mb$Qst_zTWZ#U2u(+O7O?`BIga!
zyV68N27JRoxm~rn_!>`JMeG8R>Zuh=Y0UyQ!w6GFvm-t
z8fvMjHcQ>s^Qto=p2n}3cO@@*&iRTK;dtGBM1K+SRp49je;A^_$~AZh#+qX;!$_^N
zpzSzAR$*rh0I#V2&VJQAe4fY|tnUK<0zT7wi4%S2T=PCBih?1R~Nx2y!7>xUo%vl0qfc!a|`oxsg&_7z9Eg
zB1F0qHySOpKoSDcFj5r7h5xR<-`w~6ocZq6bMCq4j`BX|{r7#(`^=qlW}F%64DfGs
zbhHcJ1fPPR!JptNxEnCGX3sUQ?G7-iGbw=#p9{WX&;@WDdWo(LFQv*#Mu(rTETdz}GhG&;3pLV;{}hBb$P
zM~Z9MS+ezDPPf}#1AYL%gDc=(z}T9-HLeBjaW7%RoN*{-HX0`51hUYhIY{$uP`4W-
zo52D^>;pH6`!d&peJox8|8V$Cy?Sdzzw);e-kZY$S!b)cp!5x*u`c~
zznp~GoayXbFlY$m2>e0#C%2d24`eCC6P=#4W>P-5ma<81T(*s&As4bh8vg8e=FV8)
zQ7(t@zp-?9pTW5tMrYu%?F=ndAqyn?9Qez^Rp$3(We`<`VSfboN_d@xt4s}Qsgg<{
zioLpTs45RWwnH$q)B1q68O4n4&1z>uXv(*`4_V*^6jzzTzOABmKAh#uG)*mP>WrY&
zoA}z)3BQ9Q8~E269DTc(wWHXuR&Ac*B{aNG;wtm1|5FvU`{7Jyr>SaMqQGUT68;EB
z)b~X=_DtEu%p(QSG6gO!ioX?kn`t?Fu>$883jQeaHq&zUdb;@GF=JanxFA%n@4#H;!R-SU$=X~CWub~IdR#X>FiuE7$=Y;xJ6?%a04!z
z-xD9~&oE>i==);sTHsrYGqA}a7hyJMt&Z8bXfRHoMHc!Xy^6Rz-IO|>YG29u#`FM#
z-v@7i=Yda&!q}R<18nk*tb5!`+AwFVikXdu$%F!Ju;CW)+0@r&{<19B;Z^@V3f}`y
z*=79QfUz}uu5m4`hRM0!8NjpFXr|<$0@DIE`@`|e?2&o_O|3z_9C9rT?nm0V^T@vj-Y0RBGWWQ5o?O7oT2F$$1SS?p&x0-GbAYeUKEwY4{sPy)y@0Vbd#-V<
zZ)CTy-@6ve`m_5000960hSs~Y00006Nkl
+
+ JUMHABI Saqr GPS Smart Falcon Hood
+ JUMHABI Saqr GPS Smart Falcon Hood
+ تسجيل الدخول
+ أدخل الحساب وكلمة المرور
+ رقم الهاتف
+ كلمة المرور
+ تسجيل الدخول
+ التسجيل برقم الهاتف
+ ربط الجهاز
+ أدخل الحساب
+ أدخل كلمة المرور
+ إظهار كلمة المرور
+ نسيت كلمة المرور؟
+ نسيت كلمة المرور
+ التبديل إلى الدخول برمز الهاتف
+ التبديل إلى الدخول بكلمة المرور
+ لقد قرأت ووافقت على اتفاقية المستخدم وسياسة الخصوصية وشروط التخصيص
+ لقد قرأت ووافقت على اتفاقية المستخدم وسياسة الخصوصية
+ اتفاقية المستخدم
+ سياسة الخصوصية
+ ليس لديك حساب؟ سجل الآن
+ إرسال الرمز
+ إعادة الإرسال بعد %1$d ثانية
+ لا مزيد من البيانات
+ أجهزتي
+ يتوفر إصدار جديد: %1$s
+ فشل التنزيل: %1$s
+ تم النقر على العنوان: %1$s
+ تم النقر على المعرف: %1$d
+ تم النقر على الحالة: %1$s
+ تم النقر على وقت التحديث: %1$s
+ تم النقر على: %1$s
+ النص المدخل: %1$s
+ المحدد: %1$s
+ الهاتف: %1$s
+ البريد الإلكتروني: %1$s
+ الحساب: %1$s
+ نوع المستخدم: %1$s
+ وقت التسجيل: %1$s
+ تم منح الإذن: %1$s
+ تم رفض الإذن: %1$s
+ معرف الجهاز: %1$s
+ خطأ في استجابة API: %1$d
+ غير مرتبط
+ غير متوفر
+ تعرض هذه الصفحة نظرة عامة على المشروع لتوضيح قيود التخطيط الشائعة. راجع تعليقات XML للتفاصيل.
+ ترقية الري الذكي
+ قيد التنفيذ
+ المسؤول: متعلم Android
+ التقدم
+ تفاصيل المشروع
+ إخفاء التفاصيل
+ وقت البدء
+ القسم المسؤول
+ فريق تطوير الجوال
+ هذا الأسبوع
+ 12
مهمة
+ 9
مكتملة
+ 3
معلقة
+ منطقة بنسبة 16:9
+ العرض مقيد من الجانبين
يحسب الارتفاع تلقائيا بنسبة 16:9
+ الفرق الأساسي: يركز LinearLayout على الترتيب، ويركز RelativeLayout على القواعد النسبية، بينما يستخدم ConstraintLayout قيودا ثنائية الاتجاه وأدوات مساعدة لوصف العلاقات، مما يقلل عادة تداخل التخطيطات في الشاشات المعقدة.
+ البريد الإلكتروني
+ أدخل رمز التحقق
+ مثال ConstraintLayout
+ ربط
+ أدخل رمز الجهاز
+ اسم الجهاز
+ أدخل اسم الجهاز
+ حتى 20 حرفا
+ يوجد رمز IMEI بجوار ملصق رمز QR على الجهاز.
+ موافق
+ مفتاح غطاء الصقر
+ الجهاز A
+ متصل
+ آخر تحديث
+ قوة الإشارة
+ قوية
+ اتصال الغطاء
+ متصل
+ بطارية الغطاء
+ بطارية حلقة الساق
+ الموقع الحالي
+ فوق متنزه طريق تيانموشان الوطني للغابات، حي شيهو، هانغتشو، تشجيانغ، على ارتفاع 236 مترا.
+ عرض الخريطة
+ إلغاء
+ تأكيد التغييرات
+ جار التحميل...
+ أدخل رقم الهاتف
+ أدخل كلمة مرور من 8 إلى 20 حرفا
+ أعد إدخال كلمة المرور الجديدة
+ متطلبات كلمة المرور
+ من 8 إلى 20 حرفا
+ تتضمن أحرفا كبيرة وصغيرة وأرقاما
+ يجب أن تختلف عن كلمة المرور الحالية
+ (الوقت المتبقي)
+ العنوان
+ اسم المستخدم
+ إعادة الربط
+ مسح ذاكرة التخزين المؤقت
+ معلومات الإصدار
+ اتفاقية الخصوصية
+ اتفاقية المستخدم
+ اللغة
+ حذف الحساب
+ تسجيل الخروج
+ أدخل كلمة المرور الحالية
+ تنبيه
+ المحتوى
+ إضافة يدوية
+ مسح رمز QR
+ الحساب المرتبط حاليا
+ الإذن مطلوب
+ اسمح للتطبيق باستخدام الأذونات المطلوبة
+ تم رفض الإذن
+ فعل الأذونات المطلوبة من الإعدادات
+ الانتقال إلى الإعدادات
+ أدخل المحتوى
+ تفاصيل الجهاز
+ معلومات الجهاز غير صالحة
+ الجهاز غير متصل
+ إعادة تسمية الجهاز
+ حتى 20 حرفا؛ يدعم الحروف والأرقام والرموز.
+ هل تريد إلغاء ربط هذا الجهاز؟
+ لا يتوفر موقع
+ إذن الكاميرا
+ يتطلب المسح إذن الكاميرا
+ لم يتم التعرف على محتوى
+ فشل المسح
+ الهاتف
+ أدخل البريد الإلكتروني
+ رقم الهاتف مطلوب
+ البريد الإلكتروني مطلوب
+ رمز التحقق مطلوب
+ كلمة المرور مطلوبة
+ أعد إدخال كلمة المرور
+ كلمتا المرور غير متطابقتين
+ يجب أن تتكون كلمة المرور من 8 إلى 20 حرفا وأن تتضمن أحرفا كبيرة وصغيرة وأرقاما
+ تم تغيير كلمة المرور
+ اقرأ ووافق على اتفاقية المستخدم وسياسة الخصوصية أولا
+ الحساب مطلوب
+ التبديل إلى الدخول برمز البريد الإلكتروني
+ التبديل إلى الدخول بالهاتف وكلمة المرور
+ التبديل إلى الدخول بالبريد وكلمة المرور
+ تم تسجيل الدخول
+ تم إنشاء الحساب
+ إذنا الكاميرا والميكروفون
+ يتطلب التقاط الصور وتسجيل الصوت إذني الكاميرا والميكروفون
+ إذن الموقع
+ تتطلب ميزات الموقع الوصول إلى موقعك
+ تأكيد الإجراء
+ هل تريد بالتأكيد تنفيذ هذا الإجراء؟
+ تم الإلغاء
+ تم التأكيد
+ أدخل نص الاختبار
+ تم إلغاء الإدخال
+ لم يتم تعيين اسم مستعار
+ هل تريد مسح ذاكرة التخزين المؤقت للتطبيق؟
+ هل تريد حذف حسابك؟
+ بعد الحذف، سيحذف حسابك وتلغى روابط جميع الأجهزة
+ وتمسح بياناتك الشخصية. لا يمكن التراجع عن ذلك، لذا تابع بحذر.
+ تأكيد الحذف
+ هل تريد تسجيل الخروج من الحساب الحالي؟
+ لم يتم تعيين اسم المستخدم
+ رقم الهاتف غير مرتبط
+ تغيير الهاتف المرتبط
+ تغيير البريد المرتبط
+ تم مسح ذاكرة التخزين المؤقت
+ كلمة المرور الحالية مطلوبة
+ كلمة المرور الجديدة مطلوبة
+ يجب أن تختلف كلمة المرور الجديدة عن الحالية
+ كلمتا المرور الجديدتان غير متطابقتين
+ تغيير الهاتف المرتبط
+ تغيير البريد المرتبط
+ رقم الهاتف الجديد مطلوب
+ البريد الإلكتروني الجديد مطلوب
+ أدخل رقم الهاتف الجديد
+ أدخل البريد الإلكتروني الجديد
+ سياسة الخصوصية
+ معلمات الصفحة غير صالحة
+ تعذر فتح هذا الرابط
+ فشل تحميل الصفحة. حاول لاحقا.
+ ملف الصورة غير موجود
+ فشل ضغط الصورة
+ لديك أحدث إصدار
+ يلزم إذن الإشعارات لإظهار تقدم التنزيل
+ هل تريد تنزيل التحديث الآن؟
+ تنزيل
+ رابط التنزيل فارغ
+ تحديث التطبيق
+ استجابة التنزيل فارغة
+ جار تنزيل التحديث
+ اكتمل التنزيل
+ فشل إرسال رمز التحقق
+ فشل طلب الشبكة
+ فشل التحقق من الهاتف
+ فشل التحقق من البريد الإلكتروني
+ فشل تغيير رقم الهاتف
+ فشل تغيير البريد الإلكتروني
+ فشل تسجيل الخروج
+ فشل حذف الحساب
+ فشل تحميل الملف الشخصي
+ فشل التحقق من التحديثات
+ فشل تغيير كلمة المرور
+ تم إرسال رمز التحقق
+ تم التحقق من الهاتف
+ تم التحقق من البريد الإلكتروني
+ تم تغيير رقم الهاتف
+ تم تغيير البريد الإلكتروني
+ تم تسجيل الخروج
+ تم حذف الحساب
+ خطأ في الخدمة
+ فشل تسجيل الدخول
+ فشل إنشاء الحساب
+ جهاز بلا اسم
+ غير متصل
+ ضعيفة
+ متوسطة
+ مفصول
+ فشل تحميل قائمة الأجهزة
+ فشل تحميل تفاصيل الجهاز
+ فشل التحكم في الغطاء
+ فشل ربط الجهاز
+ فشل تغيير اسم الجهاز
+ فشل إلغاء ربط الجهاز
+ تم تحديث حالة الغطاء
+ تم ربط الجهاز
+ معرف الجهاز واسمه مطلوبان
+ تم تغيير اسم الجهاز
+ معرف الجهاز مطلوب
+ تم إلغاء ربط الجهاز
+ إظهار التفاصيل
+ سجلات فحص الجهاز
+ مكتمل
+ خطة الري التلقائي
+ قيد التشغيل
+ مزامنة بيانات المستشعر
+ معلق
+ نسخ إعدادات النظام احتياطيا
+ مهمة ترقية البرنامج الثابت
+ فشل
+ سجل نشاط المستخدم
+ السنة
+ الشهر
+ اليوم
+ الساعة
+ الدقيقة
+ الثانية
+ لا يمكن أن يكون وقت البدء بعد وقت الانتهاء
+ يجب تعيين مستمع لنتيجة الاختيار
+ 中文
+ English
+ العربية
+ العربية غير متاحة حاليا
+ سيحفظ الرمز المميز تلقائيا بعد تسجيل الدخول
+ الانتقال إلى تسجيل الدخول
+ صورة رأس صفحة الخطط
+ رجوع
+ الملف الشخصي
+ إعادة التحميل
+ التحقق من التحديثات
+ اختبار إذني الكاميرا والميكروفون
+ اختبار إذن الموقع
+ اختبار مربع الحوار
+ اختبار مربع الإدخال
+ قائمة الأجهزة
+ الخطط
+ السجل
+ حسابي
+ الرئيسية
+ إعادة التسمية
+ إلغاء ربط الجهاز
+ إلغاء المسح
+ الخطط
+ السجل
+ حسابي
+ فتح مركز المكونات
+ تغيير كلمة المرور
+ اختر الدولة أو المنطقة
+ اختيار التاريخ والوقت
+ اختر التاريخ
+ اختر الوقت
+ اختر التاريخ والوقت
+ لم يتم الاختيار
+ مثال قائمة RecyclerView
+ مثال مكون القائمة
+ المعرف: %1$d
+ الحالة: %1$s
+ آخر تحديث: %1$s
+
diff --git a/app/src/main/res/values-en/strings.xml b/app/src/main/res/values-en/strings.xml
new file mode 100644
index 0000000..48f1deb
--- /dev/null
+++ b/app/src/main/res/values-en/strings.xml
@@ -0,0 +1,292 @@
+
+
+ JUMHABI Saqr GPS Smart Falcon Hood
+ JUMHABI Saqr GPS Smart Falcon Hood
+ Sign In
+ Enter your account and password
+ Phone Number
+ Password
+ Sign In
+ Sign Up with Phone
+ Bind Device
+ Enter account
+ Enter password
+ Show password
+ Forgot password?
+ Forgot Password
+ Switch to phone verification login
+ Switch to password login
+ I have read and agree to the User Agreement, Privacy Policy, and Customization Terms
+ I have read and agree to the User Agreement and Privacy Policy
+ User Agreement
+ Privacy Policy
+ No account? Sign up now
+ Get Code
+ Get again in %1$d seconds
+ No more data
+ My Devices
+ New version found: %1$s
+ Download failed: %1$s
+ Title clicked: %1$s
+ ID clicked: %1$d
+ Status clicked: %1$s
+ Update time clicked: %1$s
+ Clicked: %1$s
+ Entered: %1$s
+ Selected: %1$s
+ Phone: %1$s
+ Email: %1$s
+ Account: %1$s
+ User type: %1$s
+ Registration time: %1$s
+ Permission granted: %1$s
+ Permission denied: %1$s
+ Device ID: %1$s
+ API response error: %1$d
+ Not bound
+ N/A
+ This project overview demonstrates common layout constraints. See the XML comments for details.
+ Smart Irrigation Upgrade
+ In Progress
+ Owner: Android Learner
+ Progress
+ Project Details
+ Hide Details
+ Start Time
+ Responsible Department
+ Mobile Development Team
+ This Week
+ 12
Tasks
+ 9
Completed
+ 3
Pending
+ 16:9 Area
+ Width constrained by both sides
Height calculated automatically at 16:9
+ Key difference: LinearLayout focuses on arrangement, RelativeLayout on relative rules, and ConstraintLayout uses bidirectional constraints and helper tools to define relationships. Complex screens usually require fewer nested layouts.
+ Email
+ Enter verification code
+ ConstraintLayout Demo
+ Bind
+ Enter device code
+ Device Name
+ Enter device name
+ Up to 20 characters
+ The IMEI is next to the QR code label on the device.
+ OK
+ Hood Switch
+ Device A
+ Online
+ Updated
+ Signal Strength
+ Strong
+ Hood Connection
+ Connected
+ Hood Battery
+ Leg Band Battery
+ Current Location
+ Above Tianmushan Road National Forest Park, Xihu District, Hangzhou, Zhejiang, altitude 236 m.
+ View Map
+ Cancel
+ Confirm Changes
+ Loading...
+ Enter phone number
+ Enter an 8-20 character password
+ Re-enter the new password
+ Password Requirements
+ 8-20 characters
+ Includes uppercase, lowercase, and numbers
+ Must differ from the current password
+ (Time remaining)
+ Title
+ Username
+ Rebind
+ Clear Cache
+ Version Info
+ Privacy Agreement
+ User Agreement
+ Language
+ Delete Account
+ Sign Out
+ Enter current password
+ Notice
+ Content
+ Add Manually
+ Scan QR Code
+ Currently Bound Account
+ Permission Required
+ Allow the app to use the required permissions
+ Permission Denied
+ Enable the required permissions in Settings
+ Go to Settings
+ Enter content
+ Device Details
+ Invalid device information
+ Device is offline
+ Rename Device
+ Up to 20 characters; letters, numbers, and symbols supported.
+ Unbind this device?
+ No location available
+ Camera Permission
+ Scanning requires camera permission
+ No content recognized
+ Scan failed
+ Phone
+ Enter email
+ Phone number is required
+ Email is required
+ Verification code is required
+ Password is required
+ Re-enter password
+ Passwords do not match
+ Password must be 8-20 characters and include uppercase, lowercase, and numbers
+ Password changed successfully
+ Please read and agree to the User Agreement and Privacy Policy first
+ Account is required
+ Switch to email verification login
+ Switch to phone password login
+ Switch to email password login
+ Signed in successfully
+ Signed up successfully
+ Camera and Microphone Permissions
+ Taking photos and recording audio require camera and microphone permissions
+ Location Permission
+ Location features require access to your location
+ Confirm Action
+ Are you sure you want to perform this action?
+ Canceled
+ Confirmed
+ Enter test content
+ Input canceled
+ Nickname not set
+ Clear the app cache?
+ Delete your account?
+ After deletion, your account will be deleted and all devices
+ unbound and personal data cleared. This cannot be undone. Proceed with caution.
+ Confirm Deletion
+ Sign out of the current account?
+ Username not set
+ Phone number not bound
+ Change Bound Phone
+ Change Bound Email
+ Cache cleared
+ Current password is required
+ New password is required
+ New password must differ from the current password
+ New passwords do not match
+ Change Bound Phone
+ Change Bound Email
+ New phone number is required
+ New email is required
+ Enter new phone number
+ Enter new email
+ Privacy Policy
+ Invalid page parameters
+ Unable to open this link
+ Page failed to load. Try again later.
+ Image file not found
+ Image compression failed
+ You have the latest version
+ Notification permission is required to show download progress
+ Download the update now?
+ Download
+ Download URL is empty
+ App Update
+ Empty download response
+ Downloading update
+ Download complete
+ Failed to send verification code
+ Network request failed
+ Phone verification failed
+ Email verification failed
+ Failed to change phone number
+ Failed to change email
+ Failed to sign out
+ Failed to delete account
+ Failed to get profile
+ Failed to check for updates
+ Failed to change password
+ Verification code sent
+ Phone verified
+ Email verified
+ Phone number changed
+ Email changed
+ Signed out successfully
+ Account deleted
+ Service error
+ Sign-in failed
+ Sign-up failed
+ Unnamed Device
+ Offline
+ Weak
+ Fair
+ Disconnected
+ Failed to get device list
+ Failed to get device details
+ Failed to control hood
+ Failed to bind device
+ Failed to rename device
+ Failed to unbind device
+ Hood status updated
+ Device bound successfully
+ Device ID and name are required
+ Device renamed successfully
+ Device ID is required
+ Device unbound successfully
+ Show Details
+ Device Inspection Records
+ Completed
+ Automatic Watering Plan
+ Running
+ Sensor Data Sync
+ Pending
+ System Configuration Backup
+ Firmware Upgrade Task
+ Failed
+ User Activity Log
+ Year
+ Month
+ Day
+ Hour
+ Minute
+ Second
+ Start time cannot be later than end time
+ A selection listener must be set
+ 中文
+ English
+ العربية
+ Arabic is not available yet
+ Token will be saved automatically after sign-in
+ Go to Sign In
+ Plan page header image
+ Back
+ Profile
+ Reload
+ Check for Updates
+ Test camera and microphone permissions
+ Test location permission
+ Test Standard Dialog
+ Test Input Dialog
+ Device List
+ Plans
+ History
+ Profile
+ Home
+ Rename
+ Unbind Device
+ Cancel Scan
+ Plans
+ History
+ Profile
+ Open Component Center
+ Change Password
+ Select Country or Region
+ Date & Time Picker
+ Select Date
+ Select Time
+ Select Date and Time
+ Not selected
+ RecyclerView List Demo
+ List Component Demo
+ ID: %1$d
+ Status: %1$s
+ Updated: %1$s
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index f61d6f2..3a437fc 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -16,19 +16,252 @@
切换手机号验证码登录
切换密码登录
我已阅读并同意用户协议和隐私政策以及定制条款
+ 我已阅读并同意《用户协议》《隐私政策》
+ 《用户协议》
+ 《隐私政策》
没有账号?立即注册
获取验证码
%1$d秒后重新获取
已经到底了
我的设备
+
+ 发现新版本 %1$s
+ 下载失败:%1$s
+ 点击了标题:%1$s
+ 点击了编号:%1$d
+ 点击了状态:%1$s
+ 点击了更新时间:%1$s
+ 点击了:%1$s
+ 输入内容:%1$s
+ 已选择:%1$s
+ 手机号:%1$s
+ 邮箱:%1$s
+ 账号:%1$s
+ 用户类型:%1$s
+ 注册时间:%1$s
+ 权限获取成功:%1$s
+ 权限被拒绝:%1$s
+ 设备编号:%1$s
+ 接口响应异常:%1$d
+ 未绑定
+ 暂无
+ 下面用一个项目概览页演示常用约束。可以结合 XML 注释观察效果。
+ 智能灌溉升级项目
+ 进行中
+ 负责人:Android 学习者
+ 完成进度
+ 项目详情
+ 隐藏详情
+ 开始时间
+ 责任部门很长
+ 移动研发组
+ 本周数据
+ 12\n任务
+ 9\n完成
+ 3\n待处理
+ 16:9 比例区域
+ 宽度由两侧约束\n高度按 16:9 自动计算
+ 核心区别:LinearLayout 强调排列,RelativeLayout 强调相对规则;ConstraintLayout 用双向约束与辅助工具描述关系,复杂页面通常能减少嵌套层级。
+ 邮箱
+ 请输入验证码
+ ConstraintLayout 综合示例
+ 绑定
+ 请输入设备编码
+ 设备名称
+ 请输入设备名称
+ 最多支持20个字符
+ IMEI码可在设备机身上的二维码标签旁边找到
+ 确定
+ 眼罩开关
+ 设备A
+ 在线
+ 更新时间
+ 设备信号强度
+ 强
+ 头套连接状态
+ 已连接
+ 头套设备电量
+ 脚环设备电量
+ 当前位置
+ 浙江省杭州市西湖区天目山路国家森林公园上空,海拔236米。
+ 查看地图
+ 取消
+ 确认修改
+ 加载中...
+ 请输入手机号
+ 请输入8-20位密码
+ 请再次输入新密码
+ 密码规则
+ 长度在8-20个字符之间
+ 包含大小写字母和数字
+ 不能与当前密码相同
+ (剩余时间)
+ 标题名称
+ 用户名
+ 换绑
+ 清除缓存
+ 版本信息
+ 隐私协议
+ 用户协议
+ 切换语言
+ 注销账号
+ 退出登录
+ 请输入当前密码
+ 提示
+ 内容内容
+ 手动添加
+ 扫一扫
+ 当前绑定账号
+ 需要权限
+ 请允许应用使用相关权限
+ 权限被拒绝
+ 请在应用设置中开启所需权限
+ 去设置
+ 请输入内容
+ 设备详情
+ 设备信息无效
+ 设备已经离线
+ 重命名设备
+ 最多支持20个字符,支持中英文、数字和字符。
+ 确定要解绑该设备?
+ 暂无定位
+ 相机权限
+ 扫一扫功能需要使用相机权限
+ 未识别到内容
+ 扫码失败
+ 手机
+ 请输入邮箱
+ 手机号不能为空
+ 邮箱不能为空
+ 验证码不能为空
+ 密码不能为空
+ 请再次输入密码
+ 两次输入的密码不一致
+ 密码需为8-20位,且包含大小写字母和数字
+ 密码修改成功
+ 请先阅读并同意用户协议和隐私政策
+ 账号不能为空
+ 切换邮箱验证码登录
+ 切换手机号密码登录
+ 切换邮箱密码登录
+ 登录成功
+ 注册成功
+ 相机和麦克风权限
+ 拍照和录音功能需要使用相机及麦克风权限
+ 定位权限
+ 定位功能需要获取您的位置信息
+ 操作确认
+ 确定要执行这项操作吗?
+ 已取消
+ 已点击确定
+ 请输入测试内容
+ 已取消输入
+ 未设置昵称
+ 清除当前应用缓存?
+ 确认注销账号?
+ 注销账号后,账号将被删除,所有设备
+ 解绑、个人信息被清除,无法恢复,请谨慎操作?
+ 确认注销
+ 确定要退出当前账号吗?
+ 未设置用户名
+ 未绑定手机号
+ 换绑手机号
+ 换绑邮箱
+ 缓存清理成功
+ 当前密码不能为空
+ 新密码不能为空
+ 新密码不能与当前密码相同
+ 两次输入的新密码不一致
+ 更换绑定手机号
+ 更换绑定邮箱
+ 新手机号不能为空
+ 新邮箱不能为空
+ 请输入新手机号
+ 请输入新邮箱
+ 隐私政策
+ 页面参数无效
+ 无法打开此链接
+ 页面加载失败,请稍后重试
+ 图片文件不存在
+ 图片压缩失败
+ 当前已是最新版本
+ 需要通知权限才能显示下载进度
+ 是否立即下载更新?
+ 下载
+ 下载地址为空
+ 应用更新
+ 下载响应为空
+ 正在下载更新
+ 下载完成
+ 验证码发送失败
+ 网络请求失败
+ 当前手机号验证失败
+ 当前邮箱验证失败
+ 手机号更换失败
+ 邮箱更换失败
+ 退出登录失败
+ 注销账号失败
+ 获取个人信息失败
+ 版本检测失败
+ 密码修改失败
+ 验证码已发送
+ 当前手机号验证成功
+ 当前邮箱验证成功
+ 手机号更换成功
+ 邮箱更换成功
+ 退出登录成功
+ 账号注销成功
+ 服务异常
+ 登录失败
+ 注册失败
+ 未命名设备
+ 离线
+ 弱
+ 一般
+ 断开
+ 获取设备列表失败
+ 获取设备详情失败
+ 控制眼罩失败
+ 绑定设备失败
+ 设备重命名失败
+ 设备解绑失败
+ 眼罩状态已更新
+ 设备绑定成功
+ 设备编号和名称不能为空
+ 设备重命名成功
+ 设备编号不能为空
+ 设备解绑成功
+ 显示详情
+ 设备巡检记录
+ 已完成
+ 自动浇水计划
+ 执行中
+ 传感器数据同步
+ 待处理
+ 系统配置备份
+ 固件升级任务
+ 失败
+ 用户操作日志
+ 年
+ 月
+ 日
+ 时
+ 分
+ 秒
+ 开始时间不能晚于结束时间
+ 必须设置选择结果监听器
+ 中文
+ English
+ العربية
+ 阿拉伯语暂未开放
-
+
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index febfdb5..7f862a7 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -11,4 +11,18 @@
- true
- @android:style/Animation.Dialog
+
+
+
+