优化语言

This commit is contained in:
fengjignqi
2026-09-24 17:23:47 +08:00
parent 495e43f017
commit 3d926d3d60
71 changed files with 1896 additions and 542 deletions

View File

@@ -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">
<service
android:name="androidx.appcompat.app.AppLocalesMetadataHolderService"
android:enabled="false"
android:exported="false">
<meta-data
android:name="autoStoreLocales"
android:value="true" />
</service>
<meta-data
android:name="design_width_in_dp"
android:value="393"/>

View File

@@ -29,7 +29,9 @@ class MyApplication : Application(), Application.ActivityLifecycleCallbacks {
instance = this
if (!isMainProcess()) return
if (!isMainProcess()) {
return
}
registerActivityLifecycleCallbacks(this)
initialize()

View File

@@ -105,8 +105,10 @@ abstract class BaseActivity<VB : ViewBinding, P : IPresenter<*>> :
}
/** 显示加载弹窗;按返回键取消时关闭当前 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<VB : ViewBinding, P : IPresenter<*>> :
/** 使用默认说明文案申请一个或多个权限。 */
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<VB : ViewBinding, P : IPresenter<*>> :
protected open fun onPermissionsDenied(permissions: Array<String>, 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<VB : ViewBinding, P : IPresenter<*>> :
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<VB : ViewBinding, P : IPresenter<*>> :
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<VB : ViewBinding, P : IPresenter<*>> :
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<VB : ViewBinding, P : IPresenter<*>> :
fun showPermissionDialog(icon: Drawable?, showIcon: Boolean, title: String, content: String) {
showPermissionDialog(title, content)
permissionDialog?.findViewById<ImageView>(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<VB : ViewBinding, P : IPresenter<*>> :
view.findViewById<TextView>(R.id.permissionTitle).text = title
view.findViewById<TextView>(R.id.permissionContent).text = content
val dialog = AlertDialog.Builder(this).setView(view).create()
view.findViewById<TextView>(R.id.permissionCancel).setOnClickListener { dialog.dismiss(); onCancel() }
view.findViewById<TextView>(R.id.permissionAllow).setOnClickListener { dialog.dismiss(); onAllow() }
view.findViewById<TextView>(R.id.permissionCancel).setOnClickListener {
dialog.dismiss()
onCancel()
}
view.findViewById<TextView>(R.id.permissionAllow).setOnClickListener {
dialog.dismiss()
onAllow()
}
dialog.setOnShowListener {
dialog.window?.apply {
// 动态权限确认弹窗固定左右各 50dp,避免使用 AlertDialog 默认宽度。
@@ -426,3 +445,4 @@ abstract class BaseActivity<VB : ViewBinding, P : IPresenter<*>> :
super.onDestroy()
}
}

View File

@@ -99,9 +99,11 @@ abstract class BaseFragment<VB : ViewBinding, P : IPresenter<*>> : 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<VB : ViewBinding, P : IPresenter<*>> : 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<VB : ViewBinding, P : IPresenter<*>> : Fragment(), I
protected open fun onPermissionsDenied(permissions: Array<String>, 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<VB : ViewBinding, P : IPresenter<*>> : 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<VB : ViewBinding, P : IPresenter<*>> : 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<VB : ViewBinding, P : IPresenter<*>> : 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<VB : ViewBinding, P : IPresenter<*>> : Fragment(), I
fun showPermissionDialog(icon: Drawable?, showIcon: Boolean, title: String, content: String) {
showPermissionDialog(title, content)
permissionDialog?.findViewById<ImageView>(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<VB : ViewBinding, P : IPresenter<*>> : Fragment(), I
view.findViewById<TextView>(R.id.permissionTitle).text = title
view.findViewById<TextView>(R.id.permissionContent).text = content
val dialog = AlertDialog.Builder(requireContext()).setView(view).create()
view.findViewById<TextView>(R.id.permissionCancel).setOnClickListener { dialog.dismiss(); onCancel() }
view.findViewById<TextView>(R.id.permissionAllow).setOnClickListener { dialog.dismiss(); onAllow() }
view.findViewById<TextView>(R.id.permissionCancel).setOnClickListener {
dialog.dismiss()
onCancel()
}
view.findViewById<TextView>(R.id.permissionAllow).setOnClickListener {
dialog.dismiss()
onAllow()
}
dialog.setOnShowListener {
dialog.window?.apply {
// 动态权限确认弹窗固定左右各 50dp,避免使用 AlertDialog 默认宽度。
@@ -421,3 +440,4 @@ abstract class BaseFragment<VB : ViewBinding, P : IPresenter<*>> : Fragment(), I
super.onDestroyView()
}
}

View File

@@ -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(
}
}
}

View File

@@ -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);

View File

@@ -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<ActivityConstraintL
binding.backButton.setOnClickListener { finish() }
binding.toggleDetailsButton.setOnClickListener {
detailsVisible = !detailsVisible
binding.detailGroup.visibility = if (detailsVisible) View.VISIBLE else View.GONE
binding.toggleDetailsButton.text = if (detailsVisible) "隐藏详情" else "显示详情"
binding.detailGroup.visibility = if (detailsVisible) {
View.VISIBLE
} else {
View.GONE
}
binding.toggleDetailsButton.text = if (detailsVisible) {
mContext.getString(R.string.i18n_007)
} else {
mContext.getString(R.string.i18n_187)
} //隐藏详情 / 显示详情
}
}
}

View File

@@ -17,7 +17,7 @@ class RecyclerViewDemoActivity : BaseActivityNoPresenter<ActivityRecyclerViewDem
}
override fun initView(savedInstanceState: Bundle?) {
title = getString(R.string.recycler_demo_title)
title = getString(R.string.recycler_demo_title) //列表组件示例
val adapter = RecyclerDemoAdapter(this, R.layout.item_recycler_demo, createDemoData())
binding.recyclerView.layoutManager = LinearLayoutManager(this)
@@ -31,10 +31,10 @@ class RecyclerViewDemoActivity : BaseActivityNoPresenter<ActivityRecyclerViewDem
adapter.setOnItemChildClickListener { _, view, position ->
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<ActivityRecyclerViewDem
}
private fun showSelectedItem(item: RecyclerDemoItem) {
android.widget.Toast.makeText(this, "点击了:${item.title}", android.widget.Toast.LENGTH_SHORT).show()
android.widget.Toast.makeText(this, getString(R.string.demo_clicked_item, item.title), android.widget.Toast.LENGTH_SHORT).show() //点击了:%1$s
}
/** 创建固定演示数据,接入接口后可直接替换为服务端返回的列表。 */
private fun createDemoData(): List<RecyclerDemoItem> {
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") //用户操作日志 / 已完成
)
}
}

View File

@@ -33,11 +33,11 @@ public class RecyclerDemoAdapter extends BaseQuickAdapter<RecyclerDemoItem, Base
@Override
protected void convert(@NonNull BaseViewHolder holder, RecyclerDemoItem item) {
holder.setText(R.id.titleTextView, item.getTitle());
holder.setText(R.id.idTextView, mContext.getString(R.string.recycler_item_id, item.getId()));
holder.setText(R.id.statusTextView, mContext.getString(R.string.recycler_item_status, item.getStatus()));
holder.setText(R.id.idTextView, mContext.getString(R.string.recycler_item_id, item.getId())); //编号:%1$d
holder.setText(R.id.statusTextView, mContext.getString(R.string.recycler_item_status, item.getStatus())); //状态:%1$s
holder.setText(
R.id.timeTextView,
mContext.getString(R.string.recycler_item_update_time, item.getUpdateTime())
mContext.getString(R.string.recycler_item_update_time, item.getUpdateTime()) //更新时间:%1$s
);
}
}

View File

@@ -22,7 +22,7 @@ class BindDeviceActivity: BaseActivity<BindDeviceLayoutBinding, DevicePresenter>
}
override fun createPresenter(): DevicePresenter? {
return DevicePresenter(DeviceImpl())
return DevicePresenter(DeviceImpl(mContext), mContext)
}
override fun initWindows() {
@@ -35,7 +35,7 @@ class BindDeviceActivity: BaseActivity<BindDeviceLayoutBinding, DevicePresenter>
}
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<BindDeviceLayoutBinding, DevicePresenter>
?.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<BindDeviceLayoutBinding, DevicePresenter>
}

View File

@@ -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 :
}

View File

@@ -35,7 +35,7 @@ class DeviceFragment : BaseFragment<FragmentHomeBinding, DevicePresenter>(),
}
override fun createPresenter(): DevicePresenter {
return DevicePresenter(DeviceImpl())
return DevicePresenter(DeviceImpl(mContext), mContext)
}
override fun initView(savedInstanceState: Bundle?) {
@@ -59,11 +59,11 @@ class DeviceFragment : BaseFragment<FragmentHomeBinding, DevicePresenter>(),
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<FragmentHomeBinding, DevicePresenter>(),
},
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<FragmentHomeBinding, DevicePresenter>(),
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<FragmentHomeBinding, DevicePresenter>(),
// 扫一扫:先申请相机权限,授权后再打开扫码页
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<FragmentHomeBinding, DevicePresenter>(),
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<FragmentHomeBinding, DevicePresenter>(),
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<FragmentHomeBinding, DevicePresenter>(),
?.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<FragmentHomeBinding, DevicePresenter>(),
)
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<FragmentHomeBinding, DevicePresenter>(),
}
}

View File

@@ -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<DeviceEntity, BaseViewHolder>(R.layout.device_item) {
private val progressDrawables = mutableMapOf<Int, Drawable.ConstantState?>()
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<DeviceEntity, BaseViewHolder>(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<DeviceEntity, BaseViewHolder>(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<DeviceEntity, BaseViewHolder>(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<DeviceEntity, BaseViewHolder>(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
}
}

View File

@@ -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<BaseBean<List<DeviceEntity>>>? = null
private var deviceDetailCall: Call<BaseBean<DeviceEntity>>? = 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<BaseBean<Any>>, 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<BaseBean<Any>>, 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<BaseBean<Any>>, 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<BaseBean<Any>>, 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)
}
}

View File

@@ -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<DeviceContract.DeviceView>() {
/** 获取当前账号已经绑定的设备列表。 */
@@ -65,7 +68,7 @@ class DevicePresenter(
override fun success(data: BaseBean<Any>) {
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<Any>) {
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<Any>) {
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<Any>) {
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()
}
}

View File

@@ -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(
}
}
}

View File

@@ -33,7 +33,7 @@ class ForgetPsdActivity : BaseActivity<ForgetPsdLayoutBinding, LoginPresenter>()
}
override fun createPresenter(): LoginPresenter? {
return LoginPresenter(LoginImpl())
return LoginPresenter(LoginImpl(mContext), mContext)
}
override fun initWindows() {
@@ -46,7 +46,7 @@ class ForgetPsdActivity : BaseActivity<ForgetPsdLayoutBinding, LoginPresenter>()
}
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<ForgetPsdLayoutBinding, LoginPresenter>()
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<ForgetPsdLayoutBinding, LoginPresenter>()
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<ForgetPsdLayoutBinding, LoginPresenter>()
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<ForgetPsdLayoutBinding, LoginPresenter>()
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<ForgetPsdLayoutBinding, LoginPresenter>()
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<ForgetPsdLayoutBinding, LoginPresenter>()
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<ForgetPsdLayoutBinding, LoginPresenter>()
const val PASSWORD_MAX_LENGTH = 20
}
}

View File

@@ -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<ActivityLoginBinding, LoginPresenter>(),
}
override fun createPresenter(): LoginPresenter {
return LoginPresenter(LoginImpl())
return LoginPresenter(LoginImpl(mContext), mContext)
}
override fun initWindows() {
@@ -61,6 +68,7 @@ class LoginActivity : BaseActivity<ActivityLoginBinding, LoginPresenter>(),
).forEach { button ->
button.setOnClickListener(this)
}
setupAgreementText()
updateLoginModeText()
}
@@ -84,13 +92,25 @@ class LoginActivity : BaseActivity<ActivityLoginBinding, LoginPresenter>(),
// 切换手机号和邮箱登录,并同步输入框图标、提示和输入类型。
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<ActivityLoginBinding, LoginPresenter>(),
// 根据当前账号类型选择手机或邮箱验证码接口。
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<ActivityLoginBinding, LoginPresenter>(),
}
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<ActivityLoginBinding, LoginPresenter>(),
} 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<ActivityLoginBinding, LoginPresenter>(),
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 {
if (loginByPhone) "切换手机号密码登录" else "切换邮箱密码登录"
mContext.getString(R.string.i18n_097)
} //切换手机号验证码登录 / 切换邮箱验证码登录
} else {
if (loginByPhone) {
mContext.getString(R.string.i18n_098)
} else {
mContext.getString(R.string.i18n_099)
} //切换手机号密码登录 / 切换邮箱密码登录
}
}
@@ -191,13 +274,13 @@ class LoginActivity : BaseActivity<ActivityLoginBinding, LoginPresenter>(),
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<ActivityLoginBinding, LoginPresenter>(),
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<ActivityLoginBinding, LoginPresenter>(),
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<ActivityLoginBinding, LoginPresenter>(),
const val KEY_USER_LOGIN = "user_login"
}
}

View File

@@ -38,7 +38,7 @@ class RegisterActivity : BaseActivity<RegisterLayoutBinding, LoginPresenter>(),
}
override fun createPresenter(): LoginPresenter? {
return LoginPresenter(LoginImpl())
return LoginPresenter(LoginImpl(mContext), mContext)
}
override fun initWindows() {
@@ -51,7 +51,7 @@ class RegisterActivity : BaseActivity<RegisterLayoutBinding, LoginPresenter>(),
}
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<RegisterLayoutBinding, LoginPresenter>(),
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<RegisterLayoutBinding, LoginPresenter>(),
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<RegisterLayoutBinding, LoginPresenter>(),
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<RegisterLayoutBinding, LoginPresenter>(),
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<RegisterLayoutBinding, LoginPresenter>(),
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<RegisterLayoutBinding, LoginPresenter>(),
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<RegisterLayoutBinding, LoginPresenter>(),
const val PASSWORD_MAX_LENGTH = 20
}
}

View File

@@ -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<BaseBean<UserEntity>>? = 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<BaseBean<UserEntity>>, 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)
}
}

View File

@@ -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<LoginContract.LoginView>() {
class LoginPresenter(
private val loginImpl: LoginImpl,
private val mContext: Context
) : BasePresenter<LoginContract.LoginView>() {
fun getPhoneSmsCode(phoneNumber: String, sendCodeSceneRegister: String) {
@@ -22,10 +27,10 @@ class LoginPresenter(private val loginImpl: LoginImpl) : BasePresenter<LoginCont
// 发码接口通常只返回 code/msg,data 可能为 null,不能依赖 data 非空
if (data.code == 0 || data.code == 200) {
attachedView.sendCodeSuccess(
data.message.ifBlank { "验证码已发送" }
data.message.ifBlank { mContext.getString(R.string.i18n_160) } //验证码已发送
)
} else {
attachedView.showError(data.message.ifBlank { "服务异常" })
attachedView.showError(data.message.ifBlank { mContext.getString(R.string.i18n_167) }) //服务异常
}
}
@@ -81,7 +86,7 @@ class LoginPresenter(private val loginImpl: LoginImpl) : BasePresenter<LoginCont
if ((data.code == 0 || data.code == 200) && user != null) {
attachedView.onLoginSuccess(user)
} else {
attachedView.showError(data.message.ifBlank { "登录失败" })
attachedView.showError(data.message.ifBlank { mContext.getString(R.string.i18n_168) }) //登录失败
}
}
@@ -117,10 +122,10 @@ class LoginPresenter(private val loginImpl: LoginImpl) : BasePresenter<LoginCont
currentView.hideLoading()
if (data.code == 0 || data.code == 200) {
currentView.onRegisterSuccess(
data.message.ifBlank { "注册成功" }
data.message.ifBlank { mContext.getString(R.string.i18n_101) } //注册成功
)
} else {
currentView.showError(data.message.ifBlank { "注册失败" })
currentView.showError(data.message.ifBlank { mContext.getString(R.string.i18n_169) }) //注册失败
}
}
@@ -154,10 +159,10 @@ class LoginPresenter(private val loginImpl: LoginImpl) : BasePresenter<LoginCont
// 重置密码接口 data 可能为空,以 code 判断成功
if (data.code == 0 || data.code == 200) {
currentView.onForgetPsdSuccess(
data.message.ifBlank { "密码修改成功" }
data.message.ifBlank { mContext.getString(R.string.i18n_094) } //密码修改成功
)
} else {
currentView.showError(data.message.ifBlank { "密码修改失败" })
currentView.showError(data.message.ifBlank { mContext.getString(R.string.i18n_159) }) //密码修改失败
}
}
@@ -187,10 +192,10 @@ class LoginPresenter(private val loginImpl: LoginImpl) : BasePresenter<LoginCont
attachedView.hideLoading()
if (data.code == 0 || data.code == 200) {
attachedView.sendCodeSuccess(
data.message.ifBlank { "验证码已发送" }
data.message.ifBlank { mContext.getString(R.string.i18n_160) } //验证码已发送
)
} else {
attachedView.showError(data.message.ifBlank { "服务异常" })
attachedView.showError(data.message.ifBlank { mContext.getString(R.string.i18n_167) }) //服务异常
}
}
@@ -224,10 +229,10 @@ class LoginPresenter(private val loginImpl: LoginImpl) : BasePresenter<LoginCont
currentView.hideLoading()
if (data.code == 0 || data.code == 200) {
currentView.onRegisterSuccess(
data.message.ifBlank { "注册成功" }
data.message.ifBlank { mContext.getString(R.string.i18n_101) } //注册成功
)
} else {
currentView.showError(data.message.ifBlank { "注册失败" })
currentView.showError(data.message.ifBlank { mContext.getString(R.string.i18n_169) }) //注册失败
}
}
@@ -261,7 +266,7 @@ class LoginPresenter(private val loginImpl: LoginImpl) : BasePresenter<LoginCont
if ((data.code == 0 || data.code == 200) && user != null) {
currentView.onLoginSuccess(user)
} else {
currentView.showError(data.message.ifBlank { "登录失败" })
currentView.showError(data.message.ifBlank { mContext.getString(R.string.i18n_168) }) //登录失败
}
}
@@ -295,7 +300,7 @@ class LoginPresenter(private val loginImpl: LoginImpl) : BasePresenter<LoginCont
if ((data.code == 0 || data.code == 200) && user != null) {
currentView.onLoginSuccess(user)
} else {
currentView.showError(data.message.ifBlank { "登录失败" })
currentView.showError(data.message.ifBlank { mContext.getString(R.string.i18n_168) }) //登录失败
}
}
@@ -328,10 +333,10 @@ class LoginPresenter(private val loginImpl: LoginImpl) : BasePresenter<LoginCont
currentView.hideLoading()
if (data.code == 0 || data.code == 200) {
currentView.onForgetPsdSuccess(
data.message.ifBlank { "密码修改成功" }
data.message.ifBlank { mContext.getString(R.string.i18n_094) } //密码修改成功
)
} else {
currentView.showError(data.message.ifBlank { "密码修改失败" })
currentView.showError(data.message.ifBlank { mContext.getString(R.string.i18n_159) }) //密码修改失败
}
}
@@ -346,3 +351,4 @@ class LoginPresenter(private val loginImpl: LoginImpl) : BasePresenter<LoginCont
}
}

View File

@@ -39,7 +39,7 @@ class MineCenterActivity : BaseActivity<ActivityMineCenterBinding, MinePresenter
}
override fun createPresenter(): MinePresenter {
return MinePresenter(MineImpl())
return MinePresenter(MineImpl(mContext), mContext)
}
override fun initView(savedInstanceState: Bundle?) {
@@ -88,8 +88,8 @@ class MineCenterActivity : BaseActivity<ActivityMineCenterBinding, MinePresenter
Manifest.permission.CAMERA,
Manifest.permission.RECORD_AUDIO
),
"相机和麦克风权限",
"拍照和录音功能需要使用相机及麦克风权限",
mContext.getString(R.string.i18n_102), //相机和麦克风权限
mContext.getString(R.string.i18n_103), //拍照和录音功能需要使用相机及麦克风权限
REQUEST_CAMERA_AND_AUDIO
)
}
@@ -97,25 +97,25 @@ class MineCenterActivity : BaseActivity<ActivityMineCenterBinding, MinePresenter
R.id.locationPermissionButton -> {
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<ActivityMineCenterBinding, MinePresenter
R.id.inputDialogButton -> {
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<ActivityMineCenterBinding, MinePresenter
.setOnSelectedListener { result ->
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<ActivityMineCenterBinding, MinePresenter
}
override fun getUserInfoSuccess(userInfo: MineUserInfo) {
binding.nicknameTextView.text = userInfo.nickName.ifBlank { "未设置昵称" }
binding.phoneTextView.text = "手机号:${userInfo.phoneNumber?.ifBlank { null } ?: "未绑定"}"
binding.emailTextView.text = "邮箱:${userInfo.email?.ifBlank { null } ?: "未绑定"}"
binding.userIdTextView.text = "账号:${userInfo.userName.ifBlank { "暂无" }}"
binding.userTypeTextView.text = "用户类型:${userInfo.userType.ifBlank { "暂无" }}"
binding.loginDateTextView.text = "注册时间:${userInfo.createTime.ifBlank { "暂无" }}"
binding.nicknameTextView.text = userInfo.nickName.ifBlank { mContext.getString(R.string.i18n_112) } //未设置昵称
binding.phoneTextView.text = getString(
R.string.phone_value, //手机号:%1$s
userInfo.phoneNumber?.ifBlank { null } ?: getString(R.string.not_bound) //未绑定
)
binding.emailTextView.text = getString(
R.string.email_value, //邮箱:%1$s
userInfo.email?.ifBlank { null } ?: getString(R.string.not_bound) //未绑定
)
binding.userIdTextView.text = getString(
R.string.account_value, //账号:%1$s
userInfo.userName.ifBlank { getString(R.string.not_available) } //暂无
)
binding.userTypeTextView.text = getString(
R.string.user_type_value, //用户类型:%1$s
userInfo.userType.ifBlank { getString(R.string.not_available) } //暂无
)
binding.loginDateTextView.text = getString(
R.string.registration_time_value, //注册时间:%1$s
userInfo.createTime.ifBlank { getString(R.string.not_available) } //暂无
)
}
override fun showError(message: String) {
@@ -213,11 +228,17 @@ class MineCenterActivity : BaseActivity<ActivityMineCenterBinding, MinePresenter
override fun onPermissionsGranted(permissions: Array<String>) {
showMessage("权限获取成功:${permissions.joinToString { it.substringAfterLast('.') }}")
showMessage(getString(
R.string.permission_granted_value, //权限获取成功:%1$s
permissions.joinToString { it.substringAfterLast('.') }
))
}
override fun onPermissionsDenied(permissions: Array<String>, permanentlyDenied: Boolean) {
showMessage("权限被拒绝:${permissions.joinToString { it.substringAfterLast('.') }}")
showMessage(getString(
R.string.permission_denied_value, //权限被拒绝:%1$s
permissions.joinToString { it.substringAfterLast('.') }
))
super.onPermissionsDenied(permissions, permanentlyDenied)
}
@@ -244,3 +265,5 @@ class MineCenterActivity : BaseActivity<ActivityMineCenterBinding, MinePresenter
// }
// }
//}

View File

@@ -4,6 +4,10 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.os.LocaleListCompat
import java.io.File
import java.util.Locale
import com.juhabi.birds.base.BaseFragment
@@ -30,7 +34,7 @@ class MineFragment : BaseFragment<MineCenterLayoutBinding, MinePresenter>(),
}
override fun createPresenter(): MinePresenter {
return MinePresenter(MineImpl())
return MinePresenter(MineImpl(mContext), mContext)
}
override fun onResume() {
@@ -78,10 +82,10 @@ class MineFragment : BaseFragment<MineCenterLayoutBinding, MinePresenter>(),
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<MineCenterLayoutBinding, MinePresenter>(),
)
}
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<MineCenterLayoutBinding, MinePresenter>(),
//退出登录
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<MineCenterLayoutBinding, MinePresenter>(),
}
}
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<View>(R.id.languageChinese).setOnClickListener {
dialog.dismiss()
setAppLanguage("zh-CN")
}
view.findViewById<View>(R.id.languageEnglish).setOnClickListener {
dialog.dismiss()
setAppLanguage("en")
}
view.findViewById<View>(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<MineCenterLayoutBinding, MinePresenter>(),
/** 将个人信息请求结果更新到个人中心页面。 */
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<MineCenterLayoutBinding, MinePresenter>(),
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<MineCenterLayoutBinding, MinePresenter>(),
}
}
}

View File

@@ -25,7 +25,7 @@ class ModifyPsdActivity : BaseActivity<ModifyPsdLayoutBinding, MinePresenter>(),
}
override fun createPresenter(): MinePresenter? {
return MinePresenter(MineImpl())
return MinePresenter(MineImpl(mContext), mContext)
}
override fun initWindows() {
@@ -39,7 +39,7 @@ class ModifyPsdActivity : BaseActivity<ModifyPsdLayoutBinding, MinePresenter>(),
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<ModifyPsdLayoutBinding, MinePresenter>(),
// 按顺序校验输入项,避免无效数据提交到服务端。
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<ModifyPsdLayoutBinding, MinePresenter>(),
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<ModifyPsdLayoutBinding, MinePresenter>(),
}

View File

@@ -31,7 +31,7 @@ class UpdateUserAccount : BaseActivity<UpdateUserPhoneLayoutBinding, MinePresen
}
override fun createPresenter(): MinePresenter? {
return MinePresenter(MineImpl())
return MinePresenter(MineImpl(mContext), mContext)
}
override fun initWindows() {
ImmersionBar.with(this)
@@ -47,11 +47,15 @@ class UpdateUserAccount : BaseActivity<UpdateUserPhoneLayoutBinding, MinePresen
val userinfo: MineUserInfo? = UserInfoManager.getUserInfo(mContext)
userLoginByPhone = userinfo?.userLoginByPhone ?: true
binding.includedTitle.tvTitle.text = if (userinfo?.phoneBound == true) {
"更换绑定手机号"
mContext.getString(R.string.i18n_128) //更换绑定手机号
} else {
"更换绑定邮箱"
mContext.getString(R.string.i18n_129) //更换绑定邮箱
}
if(userLoginByPhone){
binding.tvUserAccount.text = userinfo?.phoneNumber
}else{
binding.tvUserAccount.text = userinfo?.email
}
binding.tvUserAccount.text = userinfo?.nickName
updateNewAccountInputMode()
listOf(
binding.tvConfirmGetcode,
@@ -71,7 +75,9 @@ class UpdateUserAccount : BaseActivity<UpdateUserPhoneLayoutBinding, MinePresen
}
R.id.tv_confirm_getcode ->{
if (userLoginByPhone) presenter?.getCurrentPhoneCode()
if (userLoginByPhone) {
presenter?.getCurrentPhoneCode()
}
else presenter?.getCurrentEmailCode()
}
@@ -79,12 +85,14 @@ class UpdateUserAccount : BaseActivity<UpdateUserPhoneLayoutBinding, MinePresen
// 只有填写旧手机号收到的验证码后,才能进入新手机号填写页面。
val confirmCode = binding.EtConfirmCode.text.toString().trim()
if (confirmCode.isBlank()) {
ToastUtil.showShort(mContext, "验证码不能为空")
ToastUtil.showShort(mContext, mContext.getString(R.string.i18n_089)) //验证码不能为空
return
}
// 服务端校验通过后,再进入新手机号填写页面。
if (userLoginByPhone) presenter?.verifyOldPhone(confirmCode)
if (userLoginByPhone) {
presenter?.verifyOldPhone(confirmCode)
}
else presenter?.verifyOldEmail(confirmCode)
}
@@ -92,10 +100,17 @@ class UpdateUserAccount : BaseActivity<UpdateUserPhoneLayoutBinding, MinePresen
// 新账号不能为空,按手机号或邮箱模式发送 rebind 验证码。
val newAccount = binding.etNewPhone.text.toString().trim()
if (newAccount.isBlank()) {
ToastUtil.showShort(mContext, if (userLoginByPhone) "新手机号不能为空" else "新邮箱不能为空")
val accountError = if (userLoginByPhone) {
mContext.getString(R.string.i18n_130)
} else {
mContext.getString(R.string.i18n_131)
} //新手机号不能为空 / 新邮箱不能为空
ToastUtil.showShort(mContext, accountError)
return
}
if (userLoginByPhone) presenter?.sendNewPhoneCode(newAccount)
if (userLoginByPhone) {
presenter?.sendNewPhoneCode(newAccount)
}
else presenter?.sendNewEmailCode(newAccount)
}
@@ -104,15 +119,22 @@ class UpdateUserAccount : BaseActivity<UpdateUserPhoneLayoutBinding, MinePresen
val newAccount = binding.etNewPhone.text.toString().trim()
val code = binding.etNewPhoneCode.text.toString().trim()
if (newAccount.isBlank()) {
ToastUtil.showShort(mContext, if (userLoginByPhone) "新手机号不能为空" else "新邮箱不能为空")
val accountError = if (userLoginByPhone) {
mContext.getString(R.string.i18n_130)
} else {
mContext.getString(R.string.i18n_131)
} //新手机号不能为空 / 新邮箱不能为空
ToastUtil.showShort(mContext, accountError)
return
}
if (code.isBlank()) {
ToastUtil.showShort(mContext, "验证码不能为空")
ToastUtil.showShort(mContext, mContext.getString(R.string.i18n_089)) //验证码不能为空
return
}
if (userLoginByPhone) presenter?.updateBindNewPhone(newAccount, code)
if (userLoginByPhone) {
presenter?.updateBindNewPhone(newAccount, code)
}
else presenter?.updateBindNewEmail(newAccount, code)
}
@@ -132,13 +154,13 @@ class UpdateUserAccount : BaseActivity<UpdateUserPhoneLayoutBinding, MinePresen
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.tvConfirmGetcode.text = getString(
R.string.register_code_countdown,
R.string.register_code_countdown, //%1$d秒后重新获取
secondsRemaining
)
}
@@ -149,7 +171,7 @@ class UpdateUserAccount : BaseActivity<UpdateUserPhoneLayoutBinding, MinePresen
isEnabled = true
isClickable = true
setBackgroundResource(R.drawable.shape_register_code_bg)
setText(R.string.register_get_code)
setText(R.string.register_get_code) //获取验证码
}
}
}.start()
@@ -183,13 +205,13 @@ class UpdateUserAccount : BaseActivity<UpdateUserPhoneLayoutBinding, MinePresen
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秒后重新获取
}
newPhoneCodeCountDownTimer = object : CountDownTimer(CODE_COUNTDOWN_MILLIS, 1_000L) {
override fun onTick(millisUntilFinished: Long) {
val secondsRemaining = (millisUntilFinished + 999L) / 1_000L
binding.tvNewPhoneGetcode.text = getString(
R.string.register_code_countdown,
R.string.register_code_countdown, //%1$d秒后重新获取
secondsRemaining
)
}
@@ -200,7 +222,7 @@ class UpdateUserAccount : BaseActivity<UpdateUserPhoneLayoutBinding, MinePresen
isEnabled = true
isClickable = true
setBackgroundResource(R.drawable.shape_register_code_bg)
setText(R.string.register_get_code)
setText(R.string.register_get_code) //获取验证码
}
}
}.start()
@@ -228,10 +250,18 @@ class UpdateUserAccount : BaseActivity<UpdateUserPhoneLayoutBinding, MinePresen
private fun updateNewAccountInputMode() {
binding.imgUserLoginType.setImageResource(
if (userLoginByPhone) R.mipmap.icon_login_phone else R.mipmap.icon_login_email
if (userLoginByPhone) {
R.mipmap.icon_login_phone
} else {
R.mipmap.icon_login_email
}
)
binding.etNewPhone.apply {
hint = if (userLoginByPhone) "请输入新手机号" else "请输入新邮箱"
hint = if (userLoginByPhone) {
mContext.getString(R.string.i18n_132)
} else {
mContext.getString(R.string.i18n_133)
} //请输入新手机号 / 请输入新邮箱
inputType = if (userLoginByPhone) {
InputType.TYPE_CLASS_PHONE
} else {
@@ -261,3 +291,5 @@ class UpdateUserAccount : BaseActivity<UpdateUserPhoneLayoutBinding, MinePresen
}

View File

@@ -12,6 +12,7 @@ import android.webkit.WebResourceRequest
import android.webkit.WebView
import android.webkit.WebViewClient
import com.gyf.immersionbar.ImmersionBar
import com.juhabi.birds.R
import com.juhabi.birds.base.BaseActivity
import com.juhabi.birds.databinding.ActivitySerivceWebLayoutBinding
import com.juhabi.birds.mine.mvp.MinePresenter
@@ -47,12 +48,12 @@ class WebviewActivity : BaseActivity<ActivitySerivceWebLayoutBinding, MinePresen
intent.getIntExtra(LEGACY_EXTRA_PAGE, PAGE_AGREEMENT)
)
val page = when (pageCode) {
PAGE_PRIVACY -> "隐私政策" 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<ActivitySerivceWebLayoutBinding, MinePresen
}
private fun handleUrl(view: WebView, uri: Uri): Boolean {
if (uri.scheme == "file" && uri.host == "android_asset") return false
if (uri.scheme == "file" && uri.host == "android_asset") {
return false
}
if (uri.scheme == "http" || uri.scheme == "https" || uri.scheme == "mailto") {
return try {
startActivity(Intent(Intent.ACTION_VIEW, uri))
true
} catch (_: Exception) {
showMessage("无法打开此链接")
showMessage(mContext.getString(R.string.i18n_136)) //无法打开此链接
true
}
}
@@ -127,7 +130,9 @@ class WebviewActivity : BaseActivity<ActivitySerivceWebLayoutBinding, MinePresen
error: WebResourceError
) {
super.onReceivedError(view, request, error)
if (request.isForMainFrame) showMessage("页面加载失败,请稍后重试")
if (request.isForMainFrame) {
showMessage(mContext.getString(R.string.i18n_137))
} //页面加载失败,请稍后重试
}
}
@@ -146,3 +151,4 @@ class WebviewActivity : BaseActivity<ActivitySerivceWebLayoutBinding, MinePresen
const val PAGE_PRIVACY = 10002
}
}

View File

@@ -1,7 +1,9 @@
package com.juhabi.birds.mine.mvp
import android.content.Context
import android.os.Handler
import android.os.Looper
import com.juhabi.birds.R
import com.juhabi.birds.mine.model.MineUserInfo
import com.juhabi.birds.main.model.AppVersionInfo
import com.juhabi.birds.network.ApiClient
@@ -13,7 +15,7 @@ import retrofit2.Response
import org.json.JSONObject
import com.juhabi.birds.login.model.SendCodeEntity
class MineImpl : MineContract.DataModel {
class MineImpl(private val mContext: Context) : MineContract.DataModel {
private val mainHandler = Handler(Looper.getMainLooper())
private var userInfoCall: Call<BaseBean<MineUserInfo>>? = null
private var versionCall: Call<BaseBean<AppVersionInfo>>? = 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<BaseBean<Any>>) {
ApiClient.apiService.getCurrentEmailCode(NetworkManager.getToken()).enqueue(object : Callback<BaseBean<Any>> {
override fun onResponse(call: Call<BaseBean<Any>>, response: Response<BaseBean<Any>>) {
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<BaseBean<Any>>, 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<BaseBean<SendCodeEntity>>) {
ApiClient.apiService.sendEmailCode(NetworkManager.getToken(), NetworkManager.toRequestBody(request)).enqueue(object : Callback<BaseBean<SendCodeEntity>> {
ApiClient.apiService.sendEmailCode(
NetworkManager.getToken(),
NetworkManager.toRequestBody(request)
).enqueue(object : Callback<BaseBean<SendCodeEntity>> {
override fun onResponse(call: Call<BaseBean<SendCodeEntity>>, response: Response<BaseBean<SendCodeEntity>>) {
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<BaseBean<SendCodeEntity>>, 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<BaseBean<Any>>, 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<BaseBean<Any>> {
override fun onResponse(call: Call<BaseBean<Any>>, response: Response<BaseBean<Any>>) {
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<BaseBean<Any>>, 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<BaseBean<Any>>, 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<BaseBean<Any>>) {
val body = JSONObject().apply { put("newEmail", newEmail.trim()); put("code", code.trim()) }
ApiClient.apiService.updateBindNewEmail(NetworkManager.getToken(), NetworkManager.toRequestBody(body)).enqueue(object : Callback<BaseBean<Any>> {
val body = JSONObject().apply {
put("newEmail", newEmail.trim())
put("code", code.trim())
}
ApiClient.apiService.updateBindNewEmail(
NetworkManager.getToken(),
NetworkManager.toRequestBody(body)
).enqueue(object : Callback<BaseBean<Any>> {
override fun onResponse(call: Call<BaseBean<Any>>, response: Response<BaseBean<Any>>) {
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<BaseBean<Any>>, 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<BaseBean<Any>>, 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<BaseBean<Any>>, 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<BaseBean<MineUserInfo>>, 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<BaseBean<Any>>, 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)
}
}

View File

@@ -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<MineContract.MineView>() {
class MinePresenter(
private val mineImpl: MineImpl,
private val mContext: Context
) : BasePresenter<MineContract.MineView>() {
/** 通过登录态向当前绑定的手机号发送验证码,无需额外请求参数。 */
fun getCurrentPhoneCode() {
@@ -18,9 +23,9 @@ class MinePresenter(private val mineImpl: MineImpl) : BasePresenter<MineContract
view?.apply {
hideLoading()
if (data.code == 0 || data.code == 200) {
sendCodeSuccess(data.message.ifBlank { "验证码已发送" })
sendCodeSuccess(data.message.ifBlank { mContext.getString(R.string.i18n_160) }) //验证码已发送
} else {
showError(data.message.ifBlank { "验证码发送失败" })
showError(data.message.ifBlank { mContext.getString(R.string.i18n_149) }) //验证码发送失败
}
}
}
@@ -43,9 +48,9 @@ class MinePresenter(private val mineImpl: MineImpl) : BasePresenter<MineContract
view?.apply {
hideLoading()
if (data.code == 0 || data.code == 200) {
sendCodeSuccess(data.message.ifBlank { "验证码已发送" })
sendCodeSuccess(data.message.ifBlank { mContext.getString(R.string.i18n_160) }) //验证码已发送
} else {
showError(data.message.ifBlank { "验证码发送失败" })
showError(data.message.ifBlank { mContext.getString(R.string.i18n_149) }) //验证码发送失败
}
}
}
@@ -72,9 +77,9 @@ class MinePresenter(private val mineImpl: MineImpl) : BasePresenter<MineContract
view?.apply {
hideLoading()
if (data.code == 0 || data.code == 200) {
newPhoneCodeSuccess(data.message.ifBlank { "验证码已发送" })
newPhoneCodeSuccess(data.message.ifBlank { mContext.getString(R.string.i18n_160) }) //验证码已发送
} else {
showError(data.message.ifBlank { "验证码发送失败" })
showError(data.message.ifBlank { mContext.getString(R.string.i18n_149) }) //验证码发送失败
}
}
}
@@ -101,9 +106,9 @@ class MinePresenter(private val mineImpl: MineImpl) : BasePresenter<MineContract
view?.apply {
hideLoading()
if (data.code == 0 || data.code == 200) {
newEmailCodeSuccess(data.message.ifBlank { "验证码已发送" })
newEmailCodeSuccess(data.message.ifBlank { mContext.getString(R.string.i18n_160) }) //验证码已发送
} else {
showError(data.message.ifBlank { "验证码发送失败" })
showError(data.message.ifBlank { mContext.getString(R.string.i18n_149) }) //验证码发送失败
}
}
}
@@ -127,10 +132,10 @@ class MinePresenter(private val mineImpl: MineImpl) : BasePresenter<MineContract
hideLoading()
if (data.code == 0 || data.code == 200) {
onVerifyOldPhoneSuccess(
data.message.ifBlank { "当前手机号验证成功" }
data.message.ifBlank { mContext.getString(R.string.i18n_161) } //当前手机号验证成功
)
} else {
showError(data.message.ifBlank { "当前手机号验证失败" })
showError(data.message.ifBlank { mContext.getString(R.string.i18n_151) }) //当前手机号验证失败
}
}
}
@@ -154,10 +159,10 @@ class MinePresenter(private val mineImpl: MineImpl) : BasePresenter<MineContract
hideLoading()
if (data.code == 0 || data.code == 200) {
onVerifyOldEmailSuccess(
data.message.ifBlank { "当前邮箱验证成功" }
data.message.ifBlank { mContext.getString(R.string.i18n_162) } //当前邮箱验证成功
)
} else {
showError(data.message.ifBlank { "当前邮箱验证失败" })
showError(data.message.ifBlank { mContext.getString(R.string.i18n_152) }) //当前邮箱验证失败
}
}
}
@@ -184,10 +189,10 @@ class MinePresenter(private val mineImpl: MineImpl) : BasePresenter<MineContract
hideLoading()
if (data.code == 0 || data.code == 200) {
onUpdateBindNewPhoneSuccess(
data.message.ifBlank { "手机号更换成功" }
data.message.ifBlank { mContext.getString(R.string.i18n_163) } //手机号更换成功
)
} else {
showError(data.message.ifBlank { "手机号更换失败" })
showError(data.message.ifBlank { mContext.getString(R.string.i18n_153) }) //手机号更换失败
}
}
}
@@ -212,10 +217,10 @@ class MinePresenter(private val mineImpl: MineImpl) : BasePresenter<MineContract
hideLoading()
if (data.code == 0 || data.code == 200) {
onUpdateBindNewEmailSuccess(
data.message.ifBlank { "邮箱更换成功" }
data.message.ifBlank { mContext.getString(R.string.i18n_164) } //邮箱更换成功
)
} else {
showError(data.message.ifBlank { "邮箱更换失败" })
showError(data.message.ifBlank { mContext.getString(R.string.i18n_154) }) //邮箱更换失败
}
}
}
@@ -238,9 +243,9 @@ class MinePresenter(private val mineImpl: MineImpl) : BasePresenter<MineContract
view?.apply {
hideLoading()
if (data.code == 0 || data.code == 200) {
onLogoutSuccess(data.message.ifBlank { "退出登录成功" })
onLogoutSuccess(data.message.ifBlank { mContext.getString(R.string.i18n_165) }) //退出登录成功
} else {
showError(data.message.ifBlank { "退出登录失败" })
showError(data.message.ifBlank { mContext.getString(R.string.i18n_155) }) //退出登录失败
}
}
}
@@ -263,9 +268,9 @@ class MinePresenter(private val mineImpl: MineImpl) : BasePresenter<MineContract
view?.apply {
hideLoading()
if (data.code == 0 || data.code == 200) {
onLogoffSuccess(data.message.ifBlank { "账号注销成功" })
onLogoffSuccess(data.message.ifBlank { mContext.getString(R.string.i18n_166) }) //账号注销成功
} else {
showError(data.message.ifBlank { "注销账号失败" })
showError(data.message.ifBlank { mContext.getString(R.string.i18n_156) }) //注销账号失败
}
}
}
@@ -344,3 +349,4 @@ class MinePresenter(private val mineImpl: MineImpl) : BasePresenter<MineContract
super.onDestroy()
}
}

View File

@@ -35,7 +35,7 @@ object LubanCompressUtil {
callback: CompressCallback?
) {
if (!oldFile.exists() || !oldFile.isFile) {
callback?.fail("图片文件不存在")
callback?.fail(context.getString(com.juhabi.birds.R.string.i18n_138)) //图片文件不存在
return
}
@@ -57,9 +57,10 @@ object LubanCompressUtil {
/** 将压缩异常转换为业务错误消息。 */
override fun onError(error: Throwable) {
callback?.fail(error.message ?: "图片压缩失败")
callback?.fail(error.message ?: context.getString(com.juhabi.birds.R.string.i18n_139)) //图片压缩失败
}
})
.launch()
}
}

View File

@@ -92,7 +92,9 @@ object SharedPreferencesUtil {
preferencesName: String = DEFAULT_PREFERENCES_NAME
): T? {
val json = getString(context, key, preferencesName = preferencesName)
if (json.isBlank()) return null
if (json.isBlank()) {
return null
}
return try {
gson.fromJson(json, clazz)

View File

@@ -43,15 +43,25 @@ object IDCardValidate {
else -> 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")

View File

@@ -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) {

View File

@@ -40,7 +40,9 @@ object ActivityManager {
@JvmStatic
fun removeActivityByTag(tag: String) {
if (tag.isBlank()) return
if (tag.isBlank()) {
return
}
finishAndRemove { key, _ -> key.contains(tag) }
}

View File

@@ -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)

View File

@@ -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"
}
}

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@mipmap/icon_login_user_uncheck" android:state_checked="false" />
<item android:drawable="@mipmap/icon_login_select" android:state_checked="true" />
</selector>

View File

@@ -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"

View File

@@ -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" />
</LinearLayout>
@@ -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" />
<TextView
android:id="@+id/agreementText"
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:gravity="center"
android:text="@string/register_agreement"
android:textColor="@color/color_DDE6F5"
android:textSize="12sp" />
android:layout_marginTop="30dp"
android:layout_marginStart="20dp">
<RelativeLayout
android:id="@+id/realtive_read_finish"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<RadioButton
android:id="@+id/checkbox_read"
android:layout_width="14dp"
android:layout_height="14dp"
android:background="@drawable/select_radio"
android:layout_centerVertical="true"
android:button="@null"
android:clickable="false"
android:visibility="visible" />
<TextView
android:id="@+id/tv_agreement_full"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginEnd="12dp"
android:layout_toEndOf="@+id/checkbox_read"
android:autoLink="none"
android:lineSpacingExtra="4dp"
android:textColor="@color/color_gray_tab"
android:textSize="11sp" />
</RelativeLayout>
</RelativeLayout>
<TextView
android:id="@+id/registerButton"

View File

@@ -189,7 +189,7 @@
android:layout_height="52dp"
android:layout_marginTop="12dp"
android:layout_marginBottom="24dp"
android:text="ConstraintLayout 综合示例" />
android:text="@string/i18n_020" />
</LinearLayout>
</ScrollView>

View File

@@ -30,7 +30,7 @@
android:layout_height="wrap_content"
android:textSize="15sp"
android:textColor="@color/color_333333"
android:text="绑定"/>
android:text="@string/i18n_021"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -44,7 +44,7 @@
android:layout_weight="8"
android:layout_marginStart="15dp"
android:background="@null"
android:hint="请输入设备编码"/>
android:hint="@string/i18n_022"/>
<ImageView
android:layout_width="0dp"
android:layout_height="30dp"
@@ -59,7 +59,7 @@
android:layout_marginTop="10dp"
android:textSize="15sp"
android:textColor="@color/color_333333"
android:text="设备名称"/>
android:text="@string/i18n_023"/>
<LinearLayout
android:layout_width="match_parent"
@@ -75,7 +75,7 @@
android:layout_marginStart="15dp"
android:background="@null"
android:maxLength="20"
android:hint="请输入设备名称"/>
android:hint="@string/i18n_024"/>
<ImageView
android:layout_width="0dp"
android:layout_height="30dp"
@@ -92,7 +92,7 @@
android:layout_marginTop="10dp"
android:textSize="12sp"
android:textColor="@color/color_999999"
android:text="最多支持20个字符"/>
android:text="@string/i18n_025"/>
</LinearLayout>
@@ -112,7 +112,7 @@
android:layout_weight="7"
android:textSize="15sp"
android:textColor="@color/color_333333"
android:text="IMEI码可在设备机身上的二维码标签旁边找到"/>
android:text="@string/i18n_026"/>
<ImageView
android:layout_width="85dp"
android:layout_height="50dp"
@@ -130,7 +130,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" />

View File

@@ -103,7 +103,7 @@
android:layout_marginStart="10dp"
android:textSize="13sp"
android:textColor="@color/color_333333"
android:text="眼罩开关"/>
android:text="@string/i18n_028"/>
<ImageView
android:id="@+id/img_switch_ctrl"
android:layout_width="36dp"
@@ -116,7 +116,7 @@
android:layout_height="wrap_content"
android:padding="10dp"
android:textSize="15sp"
android:text="绑定"
android:text="@string/i18n_021"
android:background="@color/color_zhu"/>
<Switch

View File

@@ -23,7 +23,7 @@
android:textSize="16sp"
android:textColor="@color/color_333333"
android:textStyle="bold"
android:text="设备A"/>
android:text="@string/i18n_029"/>
<ImageView
android:id="@+id/img_dev_eye_status"
android:layout_width="24dp"
@@ -39,7 +39,7 @@
android:paddingHorizontal="8dp"
android:layout_marginStart="10dp"
android:textSize="12sp"
android:text="在线"
android:text="@string/i18n_030"
android:textColor="@color/white"
android:background="@drawable/shape_device_online_bg"/>
<View
@@ -75,7 +75,7 @@
android:layout_height="wrap_content"
android:textSize="13sp"
android:textColor="@color/color_black1"
android:text="更新时间"/>
android:text="@string/i18n_031"/>
</LinearLayout>
<LinearLayout
@@ -103,7 +103,7 @@
android:layout_height="wrap_content"
android:textSize="13sp"
android:textColor="@color/color_black1"
android:text="设备信号强度"/>
android:text="@string/i18n_032"/>
<View
android:layout_width="0dp"
android:layout_height="1dp"
@@ -123,7 +123,7 @@
android:textSize="18sp"
android:textColor="@color/color_zhu"
android:textStyle="bold"
android:text="强"/>
android:text="@string/i18n_033"/>
</LinearLayout>
@@ -147,7 +147,7 @@
android:layout_height="wrap_content"
android:textSize="13sp"
android:textColor="@color/color_black1"
android:text="头套连接状态"/>
android:text="@string/i18n_034"/>
<View
android:layout_width="0dp"
android:layout_height="1dp"
@@ -162,7 +162,7 @@
android:textSize="18sp"
android:textColor="@color/color_zhu"
android:textStyle="bold"
android:text="已连接"/>
android:text="@string/i18n_035"/>
</LinearLayout>
</LinearLayout>
@@ -185,7 +185,7 @@
android:layout_height="wrap_content"
android:textSize="13sp"
android:textColor="@color/color_black1"
android:text="头套设备电量"/>
android:text="@string/i18n_036"/>
<View
android:layout_width="0dp"
android:layout_height="1dp"
@@ -234,7 +234,7 @@
android:layout_height="wrap_content"
android:textSize="13sp"
android:textColor="@color/color_black1"
android:text="脚环设备电量"/>
android:text="@string/i18n_037"/>
<View
android:layout_width="0dp"
android:layout_height="1dp"
@@ -272,7 +272,7 @@
android:textSize="16sp"
android:textColor="@color/color_333333"
android:textStyle="bold"
android:text="当前位置"/>
android:text="@string/i18n_038"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -288,11 +288,12 @@
android:id="@+id/tv_location_detail"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="start"
android:layout_weight="4"
android:layout_marginLeft="5dp"
android:layout_marginStart="5dp"
android:textSize="13sp"
android:textColor="@color/color_black1"
android:text="浙江省杭州市西湖区天目山路国家森林公园上空,海拔236米。"/>
android:text="@string/i18n_039"/>
<TextView
android:id="@+id/tv_show_map"
android:layout_width="0dp"
@@ -303,7 +304,7 @@
android:textSize="13sp"
android:textStyle="bold"
android:textColor="@color/color_zhu"
android:text="查看地图"/>
android:text="@string/i18n_040"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>

View File

@@ -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" />

View File

@@ -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" />
</LinearLayout>

View File

@@ -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" />
</LinearLayout>
@@ -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" />
</LinearLayout>
<LinearLayout
@@ -180,7 +180,7 @@
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@null"
android:hint="请再次输入新密码"
android:hint="@string/i18n_046"
android:inputType="textPassword"
android:textColorHint="@color/color_999999"
android:textSize="16sp" />
@@ -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" />
</LinearLayout>
<LinearLayout
@@ -207,7 +207,7 @@
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="密码规则"
android:text="@string/i18n_047"
android:textColor="@color/color_white"
android:textSize="18sp" />
<LinearLayout
@@ -224,7 +224,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
android:text="长度在8-20个字符之间"
android:text="@string/i18n_048"
android:textColor="@color/white"
android:textSize="14sp" />
</LinearLayout>
@@ -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" />
</LinearLayout>
@@ -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" />
</LinearLayout>
@@ -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" />

View File

@@ -52,7 +52,8 @@
<com.scwang.smart.refresh.layout.SmartRefreshLayout
android:id="@+id/refresh"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="wrap_content"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -65,6 +66,7 @@
tools:listitem="@layout/device_item"
android:clipToPadding="false"
android:paddingHorizontal="15dp"
android:paddingBottom="20dp"
tools:itemCount="3"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />

View File

@@ -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" />
<TextView
android:layout_width="wrap_content"

View File

@@ -12,7 +12,7 @@
android:layout_width="22dp"
android:layout_height="match_parent"
android:layout_centerVertical="true"
android:src="@mipmap/icon_back_white"
android:src="@drawable/icon_back_white_auto_mirrored"
android:layout_marginStart="10dp"/>
<TextView
android:id="@+id/tv_title"
@@ -31,10 +31,10 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toRightOf="@+id/tv_title"
android:layout_toEndOf="@+id/tv_title"
android:textSize="14sp"
android:textColor="@color/color_2E2E2E"
android:text="(剩余时间)"
android:text="@string/i18n_051"
android:layout_marginStart="5dp"
android:visibility="gone"/>
<TextView
@@ -42,8 +42,8 @@
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:layout_alignParentRight="true"
android:layout_marginRight="10dp"
android:layout_alignParentEnd="true"
android:layout_marginEnd="10dp"
android:textSize="14sp"
android:textColor="@color/color_333333"
android:text=""
@@ -54,7 +54,7 @@
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_centerVertical="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:src="@mipmap/ic_launcher"
android:layout_marginEnd="20dp"
android:visibility="gone"/>

View File

@@ -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"/>
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
@@ -22,8 +22,8 @@
android:textSize="18sp"
android:textColor="#2E2E2E"
android:textStyle="bold"
android:text="标题名称"
android:layout_marginLeft="10dp"
android:text="@string/i18n_052"
android:layout_marginStart="10dp"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true"/>
<TextView
@@ -31,32 +31,32 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toRightOf="@+id/tv_title"
android:layout_toEndOf="@+id/tv_title"
android:textSize="14sp"
android:textColor="#2E2E2E"
android:text="(剩余时间)"
android:layout_marginLeft="5dp"
android:text="@string/i18n_051"
android:layout_marginStart="5dp"
android:visibility="gone"/>
<TextView
android:id="@+id/tv_title_right"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:layout_alignParentRight="true"
android:layout_marginRight="10dp"
android:layout_alignParentEnd="true"
android:layout_marginEnd="10dp"
android:textSize="14sp"
android:textColor="#333333"
android:text=""
android:layout_marginLeft="5dp"
android:layout_marginStart="5dp"
android:visibility="visible"/>
<ImageView
android:id="@+id/img_setting"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_centerVertical="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:src="@android:drawable/ic_menu_preferences"
android:layout_marginRight="20dp"
android:layout_marginEnd="20dp"
android:visibility="gone"/>
</RelativeLayout>
</RelativeLayout>

View File

@@ -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"/>
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
@@ -22,8 +22,8 @@
android:textSize="18sp"
android:textColor="#2E2E2E"
android:textStyle="bold"
android:text="标题名称"
android:layout_marginLeft="10dp"
android:text="@string/i18n_052"
android:layout_marginStart="10dp"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true"/>
<TextView
@@ -31,32 +31,32 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toRightOf="@+id/tv_title"
android:layout_toEndOf="@+id/tv_title"
android:textSize="14sp"
android:textColor="#2E2E2E"
android:text="(剩余时间)"
android:layout_marginLeft="5dp"
android:text="@string/i18n_051"
android:layout_marginStart="5dp"
android:visibility="gone"/>
<TextView
android:id="@+id/tv_title_right"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:layout_alignParentRight="true"
android:layout_marginRight="10dp"
android:layout_alignParentEnd="true"
android:layout_marginEnd="10dp"
android:textSize="14sp"
android:textColor="#333333"
android:text=""
android:layout_marginLeft="5dp"
android:layout_marginStart="5dp"
android:visibility="visible"/>
<ImageView
android:id="@+id/img_setting"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_centerVertical="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:src="@android:drawable/ic_menu_preferences"
android:layout_marginRight="20dp"
android:layout_marginEnd="20dp"
android:visibility="gone"/>
</RelativeLayout>
</RelativeLayout>

View File

@@ -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" />
<TextView
android:id="@+id/tv_title"

View File

@@ -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" />
<TextView
android:id="@+id/tv_title"

View File

@@ -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" />
<TextView
android:id="@+id/tv_title"

View File

@@ -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" />
<TextView
android:id="@+id/tv_title"

View File

@@ -46,7 +46,7 @@
android:textStyle="bold"
android:textSize="16sp"
android:textColor="@color/white"
android:text="用户名"/>
android:text="@string/i18n_053"/>
</LinearLayout>
@@ -83,13 +83,13 @@
android:textStyle="bold"
android:textSize="16sp"
android:textColor="@color/white"
android:text="修改密码"/>
android:text="@string/modify_password"/>
<ImageView
android:layout_width="0dp"
android:layout_height="24dp"
android:layout_weight="1"
android:src="@mipmap/icon_mine_point"/>
android:src="@drawable/icon_mine_point_auto_mirrored"/>
</LinearLayout>
<LinearLayout
@@ -115,13 +115,13 @@
android:textStyle="bold"
android:textSize="16sp"
android:textColor="@color/white"
android:text="换绑"/>
android:text="@string/i18n_054"/>
<ImageView
android:layout_width="0dp"
android:layout_height="24dp"
android:layout_weight="1"
android:src="@mipmap/icon_mine_point"/>
android:src="@drawable/icon_mine_point_auto_mirrored"/>
</LinearLayout>
<LinearLayout
@@ -146,7 +146,7 @@
android:textStyle="bold"
android:textSize="16sp"
android:textColor="@color/white"
android:text="清除缓存"/>
android:text="@string/i18n_055"/>
<TextView
android:id="@+id/tv_total_cache"
@@ -164,7 +164,7 @@
android:layout_width="0dp"
android:layout_height="24dp"
android:layout_weight="1"
android:src="@mipmap/icon_mine_point"/>
android:src="@drawable/icon_mine_point_auto_mirrored"/>
</LinearLayout>
<LinearLayout
@@ -189,13 +189,13 @@
android:textStyle="bold"
android:textSize="16sp"
android:textColor="@color/white"
android:text="版本信息"/>
android:text="@string/i18n_056"/>
<ImageView
android:layout_width="0dp"
android:layout_height="24dp"
android:layout_weight="1"
android:src="@mipmap/icon_mine_point"/>
android:src="@drawable/icon_mine_point_auto_mirrored"/>
</LinearLayout>
<LinearLayout
@@ -220,13 +220,13 @@
android:textStyle="bold"
android:textSize="16sp"
android:textColor="@color/white"
android:text="隐私协议"/>
android:text="@string/i18n_057"/>
<ImageView
android:layout_width="0dp"
android:layout_height="24dp"
android:layout_weight="1"
android:src="@mipmap/icon_mine_point"/>
android:src="@drawable/icon_mine_point_auto_mirrored"/>
</LinearLayout>
<LinearLayout
@@ -251,13 +251,13 @@
android:textStyle="bold"
android:textSize="16sp"
android:textColor="@color/white"
android:text="用户协议"/>
android:text="@string/i18n_058"/>
<ImageView
android:layout_width="0dp"
android:layout_height="24dp"
android:layout_weight="1"
android:src="@mipmap/icon_mine_point"/>
android:src="@drawable/icon_mine_point_auto_mirrored"/>
</LinearLayout>
<LinearLayout
@@ -282,13 +282,13 @@
android:textStyle="bold"
android:textSize="16sp"
android:textColor="@color/white"
android:text="切换语言"/>
android:text="@string/i18n_059"/>
<ImageView
android:layout_width="0dp"
android:layout_height="24dp"
android:layout_weight="1"
android:src="@mipmap/icon_mine_point"/>
android:src="@drawable/icon_mine_point_auto_mirrored"/>
</LinearLayout>
<LinearLayout
@@ -313,13 +313,13 @@
android:textStyle="bold"
android:textSize="16sp"
android:textColor="@color/white"
android:text="注销账号"/>
android:text="@string/i18n_060"/>
<ImageView
android:layout_width="0dp"
android:layout_height="24dp"
android:layout_weight="1"
android:src="@mipmap/icon_mine_point"/>
android:src="@drawable/icon_mine_point_auto_mirrored"/>
</LinearLayout>
<TextView
@@ -333,7 +333,7 @@
android:background="@drawable/shape_logout_field_bg"
android:backgroundTint="@null"
android:stateListAnimator="@null"
android:text="退出登录"
android:text="@string/i18n_061"
android:textAllCaps="false"
android:textColor="@color/white"
android:textSize="17sp" />

View File

@@ -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">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:contentDescription="@null"
android:src="@mipmap/icon_login_code" />
<EditText
android:id="@+id/oldPasswordEditText"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@null"
android:hint="请输入当前密码"
android:hint="@string/i18n_062"
android:inputType="textPassword"
android:textColorHint="@color/color_999999"
android:textSize="16sp" />
@@ -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">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:contentDescription="@null"
android:src="@mipmap/icon_login_code" />
<EditText
android:id="@+id/passwordEditText"
android:layout_width="0dp"
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" />
@@ -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" />
</LinearLayout>
<LinearLayout
@@ -94,16 +102,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">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:contentDescription="@null"
android:src="@mipmap/icon_login_code" />
<EditText
android:id="@+id/confirmPasswordEditText"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@null"
android:hint="请再次输入新密码"
android:hint="@string/i18n_046"
android:inputType="textPassword"
android:textColorHint="@color/color_999999"
android:textSize="16sp" />
@@ -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" />
</LinearLayout>
<LinearLayout
@@ -130,7 +142,7 @@
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="密码规则"
android:text="@string/i18n_047"
android:textColor="@color/color_white"
android:textSize="18sp" />
<LinearLayout
@@ -147,7 +159,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
android:text="长度在8-20个字符之间"
android:text="@string/i18n_048"
android:textColor="@color/white"
android:textSize="14sp" />
</LinearLayout>
@@ -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" />
</LinearLayout>
@@ -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" />
</LinearLayout>
@@ -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" />

View File

@@ -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" />

View File

@@ -19,7 +19,7 @@
<ImageView
android:layout_width="22dp"
android:layout_height="22dp"
android:contentDescription="手动添加"
android:contentDescription="@string/i18n_065"
android:duplicateParentState="true"
android:src="@drawable/icon_input_index" />
@@ -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" />
</LinearLayout>
@@ -47,7 +47,7 @@
<ImageView
android:layout_width="22dp"
android:layout_height="22dp"
android:contentDescription="扫一扫"
android:contentDescription="@string/i18n_066"
android:duplicateParentState="true"
android:src="@drawable/icon_scan_index" />
@@ -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" />
</LinearLayout>

View File

@@ -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" />
</LinearLayout>
@@ -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" />
</LinearLayout>

View File

@@ -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" />
</LinearLayout>
@@ -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" />
</LinearLayout>
<LinearLayout
@@ -181,7 +181,7 @@
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@null"
android:hint="请再次输入新密码"
android:hint="@string/i18n_046"
android:inputType="textPassword"
android:textColorHint="@color/color_999999"
android:textSize="16sp" />
@@ -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" />
</LinearLayout>
<LinearLayout
@@ -208,7 +208,7 @@
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="密码规则"
android:text="@string/i18n_047"
android:textColor="@color/color_white"
android:textSize="18sp" />
<LinearLayout
@@ -225,7 +225,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
android:text="长度在8-20个字符之间"
android:text="@string/i18n_048"
android:textColor="@color/white"
android:textSize="14sp" />
</LinearLayout>
@@ -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" />
</LinearLayout>
@@ -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" />

View File

@@ -40,7 +40,7 @@
android:layout_height="wrap_content"
android:textSize="13sp"
android:textColor="@color/color_black1"
android:text="当前绑定账号"/>
android:text="@string/i18n_067"/>
<TextView
android:id="@+id/tv_user_account"
@@ -76,7 +76,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"
@@ -111,7 +111,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" />
@@ -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" />

Binary file not shown.

After

Width:  |  Height:  |  Size: 671 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 951 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 524 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 976 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,292 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_chinese_name">JUMHABI Saqr GPS Smart Falcon Hood</string>
<string name="app_name">JUMHABI Saqr GPS Smart Falcon Hood</string>
<string name="login_title">تسجيل الدخول</string>
<string name="login_subtitle">أدخل الحساب وكلمة المرور</string>
<string name="login_username">رقم الهاتف</string>
<string name="login_password">كلمة المرور</string>
<string name="login_button">تسجيل الدخول</string>
<string name="login_register">التسجيل برقم الهاتف</string>
<string name="bind_device">ربط الجهاز</string>
<string name="register_account_hint">أدخل الحساب</string>
<string name="register_password_hint">أدخل كلمة المرور</string>
<string name="register_show_password">إظهار كلمة المرور</string>
<string name="register_forgot_password">نسيت كلمة المرور؟</string>
<string name="forget_password_title">نسيت كلمة المرور</string>
<string name="register_change_code_login">التبديل إلى الدخول برمز الهاتف</string>
<string name="register_change_psd_login">التبديل إلى الدخول بكلمة المرور</string>
<string name="register_agreement">لقد قرأت ووافقت على اتفاقية المستخدم وسياسة الخصوصية وشروط التخصيص</string>
<string name="agree_user_policy">لقد قرأت ووافقت على اتفاقية المستخدم وسياسة الخصوصية</string>
<string name="user_agreement">اتفاقية المستخدم</string>
<string name="privacy_policy">سياسة الخصوصية</string>
<string name="register_prompt">ليس لديك حساب؟ سجل الآن</string>
<string name="register_get_code">إرسال الرمز</string>
<string name="register_code_countdown">إعادة الإرسال بعد %1$d ثانية</string>
<string name="no_more_data">لا مزيد من البيانات</string>
<string name="my_device">أجهزتي</string>
<string name="update_new_version_title">يتوفر إصدار جديد: %1$s</string>
<string name="update_download_failed">فشل التنزيل: %1$s</string>
<string name="demo_clicked_title">تم النقر على العنوان: %1$s</string>
<string name="demo_clicked_id">تم النقر على المعرف: %1$d</string>
<string name="demo_clicked_status">تم النقر على الحالة: %1$s</string>
<string name="demo_clicked_update_time">تم النقر على وقت التحديث: %1$s</string>
<string name="demo_clicked_item">تم النقر على: %1$s</string>
<string name="input_content_result">النص المدخل: %1$s</string>
<string name="selected_result">المحدد: %1$s</string>
<string name="phone_value">الهاتف: %1$s</string>
<string name="email_value">البريد الإلكتروني: %1$s</string>
<string name="account_value">الحساب: %1$s</string>
<string name="user_type_value">نوع المستخدم: %1$s</string>
<string name="registration_time_value">وقت التسجيل: %1$s</string>
<string name="permission_granted_value">تم منح الإذن: %1$s</string>
<string name="permission_denied_value">تم رفض الإذن: %1$s</string>
<string name="device_number_value">معرف الجهاز: %1$s</string>
<string name="api_response_error">خطأ في استجابة API: %1$d</string>
<string name="not_bound">غير مرتبط</string>
<string name="not_available">غير متوفر</string>
<string name="i18n_001">تعرض هذه الصفحة نظرة عامة على المشروع لتوضيح قيود التخطيط الشائعة. راجع تعليقات XML للتفاصيل.</string>
<string name="i18n_002">ترقية الري الذكي</string>
<string name="i18n_003">قيد التنفيذ</string>
<string name="i18n_004">المسؤول: متعلم Android</string>
<string name="i18n_005">التقدم</string>
<string name="i18n_006">تفاصيل المشروع</string>
<string name="i18n_007">إخفاء التفاصيل</string>
<string name="i18n_008">وقت البدء</string>
<string name="i18n_009">القسم المسؤول</string>
<string name="i18n_010">فريق تطوير الجوال</string>
<string name="i18n_011">هذا الأسبوع</string>
<string name="i18n_012">12&#10;مهمة</string>
<string name="i18n_013">9&#10;مكتملة</string>
<string name="i18n_014">3&#10;معلقة</string>
<string name="i18n_015">منطقة بنسبة 16:9</string>
<string name="i18n_016">العرض مقيد من الجانبين&#10;يحسب الارتفاع تلقائيا بنسبة 16:9</string>
<string name="i18n_017">الفرق الأساسي: يركز LinearLayout على الترتيب، ويركز RelativeLayout على القواعد النسبية، بينما يستخدم ConstraintLayout قيودا ثنائية الاتجاه وأدوات مساعدة لوصف العلاقات، مما يقلل عادة تداخل التخطيطات في الشاشات المعقدة.</string>
<string name="i18n_018">البريد الإلكتروني</string>
<string name="i18n_019">أدخل رمز التحقق</string>
<string name="i18n_020">مثال ConstraintLayout</string>
<string name="i18n_021">ربط</string>
<string name="i18n_022">أدخل رمز الجهاز</string>
<string name="i18n_023">اسم الجهاز</string>
<string name="i18n_024">أدخل اسم الجهاز</string>
<string name="i18n_025">حتى 20 حرفا</string>
<string name="i18n_026">يوجد رمز IMEI بجوار ملصق رمز QR على الجهاز.</string>
<string name="i18n_027">موافق</string>
<string name="i18n_028">مفتاح غطاء الصقر</string>
<string name="i18n_029">الجهاز A</string>
<string name="i18n_030">متصل</string>
<string name="i18n_031">آخر تحديث</string>
<string name="i18n_032">قوة الإشارة</string>
<string name="i18n_033">قوية</string>
<string name="i18n_034">اتصال الغطاء</string>
<string name="i18n_035">متصل</string>
<string name="i18n_036">بطارية الغطاء</string>
<string name="i18n_037">بطارية حلقة الساق</string>
<string name="i18n_038">الموقع الحالي</string>
<string name="i18n_039">فوق متنزه طريق تيانموشان الوطني للغابات، حي شيهو، هانغتشو، تشجيانغ، على ارتفاع 236 مترا.</string>
<string name="i18n_040">عرض الخريطة</string>
<string name="i18n_041">إلغاء</string>
<string name="i18n_042">تأكيد التغييرات</string>
<string name="i18n_043">جار التحميل...</string>
<string name="i18n_044">أدخل رقم الهاتف</string>
<string name="i18n_045">أدخل كلمة مرور من 8 إلى 20 حرفا</string>
<string name="i18n_046">أعد إدخال كلمة المرور الجديدة</string>
<string name="i18n_047">متطلبات كلمة المرور</string>
<string name="i18n_048">من 8 إلى 20 حرفا</string>
<string name="i18n_049">تتضمن أحرفا كبيرة وصغيرة وأرقاما</string>
<string name="i18n_050">يجب أن تختلف عن كلمة المرور الحالية</string>
<string name="i18n_051">(الوقت المتبقي)</string>
<string name="i18n_052">العنوان</string>
<string name="i18n_053">اسم المستخدم</string>
<string name="i18n_054">إعادة الربط</string>
<string name="i18n_055">مسح ذاكرة التخزين المؤقت</string>
<string name="i18n_056">معلومات الإصدار</string>
<string name="i18n_057">اتفاقية الخصوصية</string>
<string name="i18n_058">اتفاقية المستخدم</string>
<string name="i18n_059">اللغة</string>
<string name="i18n_060">حذف الحساب</string>
<string name="i18n_061">تسجيل الخروج</string>
<string name="i18n_062">أدخل كلمة المرور الحالية</string>
<string name="i18n_063">تنبيه</string>
<string name="i18n_064">المحتوى</string>
<string name="i18n_065">إضافة يدوية</string>
<string name="i18n_066">مسح رمز QR</string>
<string name="i18n_067">الحساب المرتبط حاليا</string>
<string name="i18n_068">الإذن مطلوب</string>
<string name="i18n_069">اسمح للتطبيق باستخدام الأذونات المطلوبة</string>
<string name="i18n_070">تم رفض الإذن</string>
<string name="i18n_071">فعل الأذونات المطلوبة من الإعدادات</string>
<string name="i18n_072">الانتقال إلى الإعدادات</string>
<string name="i18n_073">أدخل المحتوى</string>
<string name="i18n_074">تفاصيل الجهاز</string>
<string name="i18n_075">معلومات الجهاز غير صالحة</string>
<string name="i18n_076">الجهاز غير متصل</string>
<string name="i18n_077">إعادة تسمية الجهاز</string>
<string name="i18n_078">حتى 20 حرفا؛ يدعم الحروف والأرقام والرموز.</string>
<string name="i18n_079">هل تريد إلغاء ربط هذا الجهاز؟</string>
<string name="i18n_080">لا يتوفر موقع</string>
<string name="i18n_081">إذن الكاميرا</string>
<string name="i18n_082">يتطلب المسح إذن الكاميرا</string>
<string name="i18n_083">لم يتم التعرف على محتوى</string>
<string name="i18n_084">فشل المسح</string>
<string name="i18n_085">الهاتف</string>
<string name="i18n_086">أدخل البريد الإلكتروني</string>
<string name="i18n_087">رقم الهاتف مطلوب</string>
<string name="i18n_088">البريد الإلكتروني مطلوب</string>
<string name="i18n_089">رمز التحقق مطلوب</string>
<string name="i18n_090">كلمة المرور مطلوبة</string>
<string name="i18n_091">أعد إدخال كلمة المرور</string>
<string name="i18n_092">كلمتا المرور غير متطابقتين</string>
<string name="i18n_093">يجب أن تتكون كلمة المرور من 8 إلى 20 حرفا وأن تتضمن أحرفا كبيرة وصغيرة وأرقاما</string>
<string name="i18n_094">تم تغيير كلمة المرور</string>
<string name="i18n_095">اقرأ ووافق على اتفاقية المستخدم وسياسة الخصوصية أولا</string>
<string name="i18n_096">الحساب مطلوب</string>
<string name="i18n_097">التبديل إلى الدخول برمز البريد الإلكتروني</string>
<string name="i18n_098">التبديل إلى الدخول بالهاتف وكلمة المرور</string>
<string name="i18n_099">التبديل إلى الدخول بالبريد وكلمة المرور</string>
<string name="i18n_100">تم تسجيل الدخول</string>
<string name="i18n_101">تم إنشاء الحساب</string>
<string name="i18n_102">إذنا الكاميرا والميكروفون</string>
<string name="i18n_103">يتطلب التقاط الصور وتسجيل الصوت إذني الكاميرا والميكروفون</string>
<string name="i18n_104">إذن الموقع</string>
<string name="i18n_105">تتطلب ميزات الموقع الوصول إلى موقعك</string>
<string name="i18n_106">تأكيد الإجراء</string>
<string name="i18n_107">هل تريد بالتأكيد تنفيذ هذا الإجراء؟</string>
<string name="i18n_108">تم الإلغاء</string>
<string name="i18n_109">تم التأكيد</string>
<string name="i18n_110">أدخل نص الاختبار</string>
<string name="i18n_111">تم إلغاء الإدخال</string>
<string name="i18n_112">لم يتم تعيين اسم مستعار</string>
<string name="i18n_113">هل تريد مسح ذاكرة التخزين المؤقت للتطبيق؟</string>
<string name="i18n_114">هل تريد حذف حسابك؟</string>
<string name="i18n_115">بعد الحذف، سيحذف حسابك وتلغى روابط جميع الأجهزة</string>
<string name="i18n_116">وتمسح بياناتك الشخصية. لا يمكن التراجع عن ذلك، لذا تابع بحذر.</string>
<string name="i18n_117">تأكيد الحذف</string>
<string name="i18n_118">هل تريد تسجيل الخروج من الحساب الحالي؟</string>
<string name="i18n_119">لم يتم تعيين اسم المستخدم</string>
<string name="i18n_120">رقم الهاتف غير مرتبط</string>
<string name="i18n_121">تغيير الهاتف المرتبط</string>
<string name="i18n_122">تغيير البريد المرتبط</string>
<string name="i18n_123">تم مسح ذاكرة التخزين المؤقت</string>
<string name="i18n_124">كلمة المرور الحالية مطلوبة</string>
<string name="i18n_125">كلمة المرور الجديدة مطلوبة</string>
<string name="i18n_126">يجب أن تختلف كلمة المرور الجديدة عن الحالية</string>
<string name="i18n_127">كلمتا المرور الجديدتان غير متطابقتين</string>
<string name="i18n_128">تغيير الهاتف المرتبط</string>
<string name="i18n_129">تغيير البريد المرتبط</string>
<string name="i18n_130">رقم الهاتف الجديد مطلوب</string>
<string name="i18n_131">البريد الإلكتروني الجديد مطلوب</string>
<string name="i18n_132">أدخل رقم الهاتف الجديد</string>
<string name="i18n_133">أدخل البريد الإلكتروني الجديد</string>
<string name="i18n_134">سياسة الخصوصية</string>
<string name="i18n_135">معلمات الصفحة غير صالحة</string>
<string name="i18n_136">تعذر فتح هذا الرابط</string>
<string name="i18n_137">فشل تحميل الصفحة. حاول لاحقا.</string>
<string name="i18n_138">ملف الصورة غير موجود</string>
<string name="i18n_139">فشل ضغط الصورة</string>
<string name="i18n_140">لديك أحدث إصدار</string>
<string name="i18n_141">يلزم إذن الإشعارات لإظهار تقدم التنزيل</string>
<string name="i18n_142">هل تريد تنزيل التحديث الآن؟</string>
<string name="i18n_143">تنزيل</string>
<string name="i18n_144">رابط التنزيل فارغ</string>
<string name="i18n_145">تحديث التطبيق</string>
<string name="i18n_146">استجابة التنزيل فارغة</string>
<string name="i18n_147">جار تنزيل التحديث</string>
<string name="i18n_148">اكتمل التنزيل</string>
<string name="i18n_149">فشل إرسال رمز التحقق</string>
<string name="i18n_150">فشل طلب الشبكة</string>
<string name="i18n_151">فشل التحقق من الهاتف</string>
<string name="i18n_152">فشل التحقق من البريد الإلكتروني</string>
<string name="i18n_153">فشل تغيير رقم الهاتف</string>
<string name="i18n_154">فشل تغيير البريد الإلكتروني</string>
<string name="i18n_155">فشل تسجيل الخروج</string>
<string name="i18n_156">فشل حذف الحساب</string>
<string name="i18n_157">فشل تحميل الملف الشخصي</string>
<string name="i18n_158">فشل التحقق من التحديثات</string>
<string name="i18n_159">فشل تغيير كلمة المرور</string>
<string name="i18n_160">تم إرسال رمز التحقق</string>
<string name="i18n_161">تم التحقق من الهاتف</string>
<string name="i18n_162">تم التحقق من البريد الإلكتروني</string>
<string name="i18n_163">تم تغيير رقم الهاتف</string>
<string name="i18n_164">تم تغيير البريد الإلكتروني</string>
<string name="i18n_165">تم تسجيل الخروج</string>
<string name="i18n_166">تم حذف الحساب</string>
<string name="i18n_167">خطأ في الخدمة</string>
<string name="i18n_168">فشل تسجيل الدخول</string>
<string name="i18n_169">فشل إنشاء الحساب</string>
<string name="i18n_170">جهاز بلا اسم</string>
<string name="i18n_171">غير متصل</string>
<string name="i18n_172">ضعيفة</string>
<string name="i18n_173">متوسطة</string>
<string name="i18n_174">مفصول</string>
<string name="i18n_175">فشل تحميل قائمة الأجهزة</string>
<string name="i18n_176">فشل تحميل تفاصيل الجهاز</string>
<string name="i18n_177">فشل التحكم في الغطاء</string>
<string name="i18n_178">فشل ربط الجهاز</string>
<string name="i18n_179">فشل تغيير اسم الجهاز</string>
<string name="i18n_180">فشل إلغاء ربط الجهاز</string>
<string name="i18n_181">تم تحديث حالة الغطاء</string>
<string name="i18n_182">تم ربط الجهاز</string>
<string name="i18n_183">معرف الجهاز واسمه مطلوبان</string>
<string name="i18n_184">تم تغيير اسم الجهاز</string>
<string name="i18n_185">معرف الجهاز مطلوب</string>
<string name="i18n_186">تم إلغاء ربط الجهاز</string>
<string name="i18n_187">إظهار التفاصيل</string>
<string name="i18n_188">سجلات فحص الجهاز</string>
<string name="i18n_189">مكتمل</string>
<string name="i18n_190">خطة الري التلقائي</string>
<string name="i18n_191">قيد التشغيل</string>
<string name="i18n_192">مزامنة بيانات المستشعر</string>
<string name="i18n_193">معلق</string>
<string name="i18n_194">نسخ إعدادات النظام احتياطيا</string>
<string name="i18n_195">مهمة ترقية البرنامج الثابت</string>
<string name="i18n_196">فشل</string>
<string name="i18n_197">سجل نشاط المستخدم</string>
<string name="i18n_198">السنة</string>
<string name="i18n_199">الشهر</string>
<string name="i18n_200">اليوم</string>
<string name="i18n_201">الساعة</string>
<string name="i18n_202">الدقيقة</string>
<string name="i18n_203">الثانية</string>
<string name="i18n_204">لا يمكن أن يكون وقت البدء بعد وقت الانتهاء</string>
<string name="i18n_205">يجب تعيين مستمع لنتيجة الاختيار</string>
<string name="language_chinese">中文</string>
<string name="language_english">English</string>
<string name="language_arabic">العربية</string>
<string name="language_not_available">العربية غير متاحة حاليا</string>
<string name="login_demo_hint">سيحفظ الرمز المميز تلقائيا بعد تسجيل الدخول</string>
<string name="mine_open_login">الانتقال إلى تسجيل الدخول</string>
<string name="plan_top_image_description">صورة رأس صفحة الخطط</string>
<string name="back_button_description">رجوع</string>
<string name="mine_title">الملف الشخصي</string>
<string name="mine_reload">إعادة التحميل</string>
<string name="mine_check_version">التحقق من التحديثات</string>
<string name="permission_test_multiple">اختبار إذني الكاميرا والميكروفون</string>
<string name="permission_test_location">اختبار إذن الموقع</string>
<string name="dialog_test_normal">اختبار مربع الحوار</string>
<string name="dialog_test_input">اختبار مربع الإدخال</string>
<string name="device_list">قائمة الأجهزة</string>
<string name="tab_plan">الخطط</string>
<string name="tab_history">السجل</string>
<string name="tab_mine">حسابي</string>
<string name="home_page_title">الرئيسية</string>
<string name="device_action_rename">إعادة التسمية</string>
<string name="device_action_unbind">إلغاء ربط الجهاز</string>
<string name="cancel_scan">إلغاء المسح</string>
<string name="plan_page_title">الخطط</string>
<string name="history_page_title">السجل</string>
<string name="mine_page_title">حسابي</string>
<string name="open_component_center">فتح مركز المكونات</string>
<string name="modify_password">تغيير كلمة المرور</string>
<string name="select_country_code">اختر الدولة أو المنطقة</string>
<string name="picker_component_title">اختيار التاريخ والوقت</string>
<string name="picker_select_date">اختر التاريخ</string>
<string name="picker_select_time">اختر الوقت</string>
<string name="picker_select_date_time">اختر التاريخ والوقت</string>
<string name="picker_result_empty">لم يتم الاختيار</string>
<string name="recycler_demo_entry">مثال قائمة RecyclerView</string>
<string name="recycler_demo_title">مثال مكون القائمة</string>
<string name="recycler_item_id">المعرف: %1$d</string>
<string name="recycler_item_status">الحالة: %1$s</string>
<string name="recycler_item_update_time">آخر تحديث: %1$s</string>
</resources>

View File

@@ -0,0 +1,292 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_chinese_name">JUMHABI Saqr GPS Smart Falcon Hood</string>
<string name="app_name">JUMHABI Saqr GPS Smart Falcon Hood</string>
<string name="login_title">Sign In</string>
<string name="login_subtitle">Enter your account and password</string>
<string name="login_username">Phone Number</string>
<string name="login_password">Password</string>
<string name="login_button">Sign In</string>
<string name="login_register">Sign Up with Phone</string>
<string name="bind_device">Bind Device</string>
<string name="register_account_hint">Enter account</string>
<string name="register_password_hint">Enter password</string>
<string name="register_show_password">Show password</string>
<string name="register_forgot_password">Forgot password?</string>
<string name="forget_password_title">Forgot Password</string>
<string name="register_change_code_login">Switch to phone verification login</string>
<string name="register_change_psd_login">Switch to password login</string>
<string name="register_agreement">I have read and agree to the User Agreement, Privacy Policy, and Customization Terms</string>
<string name="agree_user_policy">I have read and agree to the User Agreement and Privacy Policy</string>
<string name="user_agreement">User Agreement</string>
<string name="privacy_policy">Privacy Policy</string>
<string name="register_prompt">No account? Sign up now</string>
<string name="register_get_code">Get Code</string>
<string name="register_code_countdown">Get again in %1$d seconds</string>
<string name="no_more_data">No more data</string>
<string name="my_device">My Devices</string>
<string name="update_new_version_title">New version found: %1$s</string>
<string name="update_download_failed">Download failed: %1$s</string>
<string name="demo_clicked_title">Title clicked: %1$s</string>
<string name="demo_clicked_id">ID clicked: %1$d</string>
<string name="demo_clicked_status">Status clicked: %1$s</string>
<string name="demo_clicked_update_time">Update time clicked: %1$s</string>
<string name="demo_clicked_item">Clicked: %1$s</string>
<string name="input_content_result">Entered: %1$s</string>
<string name="selected_result">Selected: %1$s</string>
<string name="phone_value">Phone: %1$s</string>
<string name="email_value">Email: %1$s</string>
<string name="account_value">Account: %1$s</string>
<string name="user_type_value">User type: %1$s</string>
<string name="registration_time_value">Registration time: %1$s</string>
<string name="permission_granted_value">Permission granted: %1$s</string>
<string name="permission_denied_value">Permission denied: %1$s</string>
<string name="device_number_value">Device ID: %1$s</string>
<string name="api_response_error">API response error: %1$d</string>
<string name="not_bound">Not bound</string>
<string name="not_available">N/A</string>
<string name="i18n_001">This project overview demonstrates common layout constraints. See the XML comments for details.</string>
<string name="i18n_002">Smart Irrigation Upgrade</string>
<string name="i18n_003">In Progress</string>
<string name="i18n_004">Owner: Android Learner</string>
<string name="i18n_005">Progress</string>
<string name="i18n_006">Project Details</string>
<string name="i18n_007">Hide Details</string>
<string name="i18n_008">Start Time</string>
<string name="i18n_009">Responsible Department</string>
<string name="i18n_010">Mobile Development Team</string>
<string name="i18n_011">This Week</string>
<string name="i18n_012">12&#10;Tasks</string>
<string name="i18n_013">9&#10;Completed</string>
<string name="i18n_014">3&#10;Pending</string>
<string name="i18n_015">16:9 Area</string>
<string name="i18n_016">Width constrained by both sides&#10;Height calculated automatically at 16:9</string>
<string name="i18n_017">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.</string>
<string name="i18n_018">Email</string>
<string name="i18n_019">Enter verification code</string>
<string name="i18n_020">ConstraintLayout Demo</string>
<string name="i18n_021">Bind</string>
<string name="i18n_022">Enter device code</string>
<string name="i18n_023">Device Name</string>
<string name="i18n_024">Enter device name</string>
<string name="i18n_025">Up to 20 characters</string>
<string name="i18n_026">The IMEI is next to the QR code label on the device.</string>
<string name="i18n_027">OK</string>
<string name="i18n_028">Hood Switch</string>
<string name="i18n_029">Device A</string>
<string name="i18n_030">Online</string>
<string name="i18n_031">Updated</string>
<string name="i18n_032">Signal Strength</string>
<string name="i18n_033">Strong</string>
<string name="i18n_034">Hood Connection</string>
<string name="i18n_035">Connected</string>
<string name="i18n_036">Hood Battery</string>
<string name="i18n_037">Leg Band Battery</string>
<string name="i18n_038">Current Location</string>
<string name="i18n_039">Above Tianmushan Road National Forest Park, Xihu District, Hangzhou, Zhejiang, altitude 236 m.</string>
<string name="i18n_040">View Map</string>
<string name="i18n_041">Cancel</string>
<string name="i18n_042">Confirm Changes</string>
<string name="i18n_043">Loading...</string>
<string name="i18n_044">Enter phone number</string>
<string name="i18n_045">Enter an 8-20 character password</string>
<string name="i18n_046">Re-enter the new password</string>
<string name="i18n_047">Password Requirements</string>
<string name="i18n_048">8-20 characters</string>
<string name="i18n_049">Includes uppercase, lowercase, and numbers</string>
<string name="i18n_050">Must differ from the current password</string>
<string name="i18n_051">(Time remaining)</string>
<string name="i18n_052">Title</string>
<string name="i18n_053">Username</string>
<string name="i18n_054">Rebind</string>
<string name="i18n_055">Clear Cache</string>
<string name="i18n_056">Version Info</string>
<string name="i18n_057">Privacy Agreement</string>
<string name="i18n_058">User Agreement</string>
<string name="i18n_059">Language</string>
<string name="i18n_060">Delete Account</string>
<string name="i18n_061">Sign Out</string>
<string name="i18n_062">Enter current password</string>
<string name="i18n_063">Notice</string>
<string name="i18n_064">Content</string>
<string name="i18n_065">Add Manually</string>
<string name="i18n_066">Scan QR Code</string>
<string name="i18n_067">Currently Bound Account</string>
<string name="i18n_068">Permission Required</string>
<string name="i18n_069">Allow the app to use the required permissions</string>
<string name="i18n_070">Permission Denied</string>
<string name="i18n_071">Enable the required permissions in Settings</string>
<string name="i18n_072">Go to Settings</string>
<string name="i18n_073">Enter content</string>
<string name="i18n_074">Device Details</string>
<string name="i18n_075">Invalid device information</string>
<string name="i18n_076">Device is offline</string>
<string name="i18n_077">Rename Device</string>
<string name="i18n_078">Up to 20 characters; letters, numbers, and symbols supported.</string>
<string name="i18n_079">Unbind this device?</string>
<string name="i18n_080">No location available</string>
<string name="i18n_081">Camera Permission</string>
<string name="i18n_082">Scanning requires camera permission</string>
<string name="i18n_083">No content recognized</string>
<string name="i18n_084">Scan failed</string>
<string name="i18n_085">Phone</string>
<string name="i18n_086">Enter email</string>
<string name="i18n_087">Phone number is required</string>
<string name="i18n_088">Email is required</string>
<string name="i18n_089">Verification code is required</string>
<string name="i18n_090">Password is required</string>
<string name="i18n_091">Re-enter password</string>
<string name="i18n_092">Passwords do not match</string>
<string name="i18n_093">Password must be 8-20 characters and include uppercase, lowercase, and numbers</string>
<string name="i18n_094">Password changed successfully</string>
<string name="i18n_095">Please read and agree to the User Agreement and Privacy Policy first</string>
<string name="i18n_096">Account is required</string>
<string name="i18n_097">Switch to email verification login</string>
<string name="i18n_098">Switch to phone password login</string>
<string name="i18n_099">Switch to email password login</string>
<string name="i18n_100">Signed in successfully</string>
<string name="i18n_101">Signed up successfully</string>
<string name="i18n_102">Camera and Microphone Permissions</string>
<string name="i18n_103">Taking photos and recording audio require camera and microphone permissions</string>
<string name="i18n_104">Location Permission</string>
<string name="i18n_105">Location features require access to your location</string>
<string name="i18n_106">Confirm Action</string>
<string name="i18n_107">Are you sure you want to perform this action?</string>
<string name="i18n_108">Canceled</string>
<string name="i18n_109">Confirmed</string>
<string name="i18n_110">Enter test content</string>
<string name="i18n_111">Input canceled</string>
<string name="i18n_112">Nickname not set</string>
<string name="i18n_113">Clear the app cache?</string>
<string name="i18n_114">Delete your account?</string>
<string name="i18n_115">After deletion, your account will be deleted and all devices</string>
<string name="i18n_116">unbound and personal data cleared. This cannot be undone. Proceed with caution.</string>
<string name="i18n_117">Confirm Deletion</string>
<string name="i18n_118">Sign out of the current account?</string>
<string name="i18n_119">Username not set</string>
<string name="i18n_120">Phone number not bound</string>
<string name="i18n_121">Change Bound Phone</string>
<string name="i18n_122">Change Bound Email</string>
<string name="i18n_123">Cache cleared</string>
<string name="i18n_124">Current password is required</string>
<string name="i18n_125">New password is required</string>
<string name="i18n_126">New password must differ from the current password</string>
<string name="i18n_127">New passwords do not match</string>
<string name="i18n_128">Change Bound Phone</string>
<string name="i18n_129">Change Bound Email</string>
<string name="i18n_130">New phone number is required</string>
<string name="i18n_131">New email is required</string>
<string name="i18n_132">Enter new phone number</string>
<string name="i18n_133">Enter new email</string>
<string name="i18n_134">Privacy Policy</string>
<string name="i18n_135">Invalid page parameters</string>
<string name="i18n_136">Unable to open this link</string>
<string name="i18n_137">Page failed to load. Try again later.</string>
<string name="i18n_138">Image file not found</string>
<string name="i18n_139">Image compression failed</string>
<string name="i18n_140">You have the latest version</string>
<string name="i18n_141">Notification permission is required to show download progress</string>
<string name="i18n_142">Download the update now?</string>
<string name="i18n_143">Download</string>
<string name="i18n_144">Download URL is empty</string>
<string name="i18n_145">App Update</string>
<string name="i18n_146">Empty download response</string>
<string name="i18n_147">Downloading update</string>
<string name="i18n_148">Download complete</string>
<string name="i18n_149">Failed to send verification code</string>
<string name="i18n_150">Network request failed</string>
<string name="i18n_151">Phone verification failed</string>
<string name="i18n_152">Email verification failed</string>
<string name="i18n_153">Failed to change phone number</string>
<string name="i18n_154">Failed to change email</string>
<string name="i18n_155">Failed to sign out</string>
<string name="i18n_156">Failed to delete account</string>
<string name="i18n_157">Failed to get profile</string>
<string name="i18n_158">Failed to check for updates</string>
<string name="i18n_159">Failed to change password</string>
<string name="i18n_160">Verification code sent</string>
<string name="i18n_161">Phone verified</string>
<string name="i18n_162">Email verified</string>
<string name="i18n_163">Phone number changed</string>
<string name="i18n_164">Email changed</string>
<string name="i18n_165">Signed out successfully</string>
<string name="i18n_166">Account deleted</string>
<string name="i18n_167">Service error</string>
<string name="i18n_168">Sign-in failed</string>
<string name="i18n_169">Sign-up failed</string>
<string name="i18n_170">Unnamed Device</string>
<string name="i18n_171">Offline</string>
<string name="i18n_172">Weak</string>
<string name="i18n_173">Fair</string>
<string name="i18n_174">Disconnected</string>
<string name="i18n_175">Failed to get device list</string>
<string name="i18n_176">Failed to get device details</string>
<string name="i18n_177">Failed to control hood</string>
<string name="i18n_178">Failed to bind device</string>
<string name="i18n_179">Failed to rename device</string>
<string name="i18n_180">Failed to unbind device</string>
<string name="i18n_181">Hood status updated</string>
<string name="i18n_182">Device bound successfully</string>
<string name="i18n_183">Device ID and name are required</string>
<string name="i18n_184">Device renamed successfully</string>
<string name="i18n_185">Device ID is required</string>
<string name="i18n_186">Device unbound successfully</string>
<string name="i18n_187">Show Details</string>
<string name="i18n_188">Device Inspection Records</string>
<string name="i18n_189">Completed</string>
<string name="i18n_190">Automatic Watering Plan</string>
<string name="i18n_191">Running</string>
<string name="i18n_192">Sensor Data Sync</string>
<string name="i18n_193">Pending</string>
<string name="i18n_194">System Configuration Backup</string>
<string name="i18n_195">Firmware Upgrade Task</string>
<string name="i18n_196">Failed</string>
<string name="i18n_197">User Activity Log</string>
<string name="i18n_198">Year</string>
<string name="i18n_199">Month</string>
<string name="i18n_200">Day</string>
<string name="i18n_201">Hour</string>
<string name="i18n_202">Minute</string>
<string name="i18n_203">Second</string>
<string name="i18n_204">Start time cannot be later than end time</string>
<string name="i18n_205">A selection listener must be set</string>
<string name="language_chinese">中文</string>
<string name="language_english">English</string>
<string name="language_arabic">العربية</string>
<string name="language_not_available">Arabic is not available yet</string>
<string name="login_demo_hint">Token will be saved automatically after sign-in</string>
<string name="mine_open_login">Go to Sign In</string>
<string name="plan_top_image_description">Plan page header image</string>
<string name="back_button_description">Back</string>
<string name="mine_title">Profile</string>
<string name="mine_reload">Reload</string>
<string name="mine_check_version">Check for Updates</string>
<string name="permission_test_multiple">Test camera and microphone permissions</string>
<string name="permission_test_location">Test location permission</string>
<string name="dialog_test_normal">Test Standard Dialog</string>
<string name="dialog_test_input">Test Input Dialog</string>
<string name="device_list">Device List</string>
<string name="tab_plan">Plans</string>
<string name="tab_history">History</string>
<string name="tab_mine">Profile</string>
<string name="home_page_title">Home</string>
<string name="device_action_rename">Rename</string>
<string name="device_action_unbind">Unbind Device</string>
<string name="cancel_scan">Cancel Scan</string>
<string name="plan_page_title">Plans</string>
<string name="history_page_title">History</string>
<string name="mine_page_title">Profile</string>
<string name="open_component_center">Open Component Center</string>
<string name="modify_password">Change Password</string>
<string name="select_country_code">Select Country or Region</string>
<string name="picker_component_title">Date &amp; Time Picker</string>
<string name="picker_select_date">Select Date</string>
<string name="picker_select_time">Select Time</string>
<string name="picker_select_date_time">Select Date and Time</string>
<string name="picker_result_empty">Not selected</string>
<string name="recycler_demo_entry">RecyclerView List Demo</string>
<string name="recycler_demo_title">List Component Demo</string>
<string name="recycler_item_id">ID: %1$d</string>
<string name="recycler_item_status">Status: %1$s</string>
<string name="recycler_item_update_time">Updated: %1$s</string>
</resources>

View File

@@ -16,19 +16,252 @@
<string name="register_change_code_login">切换手机号验证码登录</string>
<string name="register_change_psd_login">切换密码登录</string>
<string name="register_agreement">我已阅读并同意用户协议和隐私政策以及定制条款</string>
<string name="agree_user_policy">我已阅读并同意《用户协议》《隐私政策》</string>
<string name="user_agreement">《用户协议》</string>
<string name="privacy_policy">《隐私政策》</string>
<string name="register_prompt">没有账号?立即注册</string>
<string name="register_get_code">获取验证码</string>
<string name="register_code_countdown">%1$d秒后重新获取</string>
<string name="no_more_data">已经到底了</string>
<string name="my_device">我的设备</string>
<!--开始-->
<string name="update_new_version_title">发现新版本 %1$s</string>
<string name="update_download_failed">下载失败:%1$s</string>
<string name="demo_clicked_title">点击了标题:%1$s</string>
<string name="demo_clicked_id">点击了编号:%1$d</string>
<string name="demo_clicked_status">点击了状态:%1$s</string>
<string name="demo_clicked_update_time">点击了更新时间:%1$s</string>
<string name="demo_clicked_item">点击了:%1$s</string>
<string name="input_content_result">输入内容:%1$s</string>
<string name="selected_result">已选择:%1$s</string>
<string name="phone_value">手机号:%1$s</string>
<string name="email_value">邮箱:%1$s</string>
<string name="account_value">账号:%1$s</string>
<string name="user_type_value">用户类型:%1$s</string>
<string name="registration_time_value">注册时间:%1$s</string>
<string name="permission_granted_value">权限获取成功:%1$s</string>
<string name="permission_denied_value">权限被拒绝:%1$s</string>
<string name="device_number_value">设备编号:%1$s</string>
<string name="api_response_error">接口响应异常:%1$d</string>
<string name="not_bound">未绑定</string>
<string name="not_available">暂无</string>
<string name="i18n_001">下面用一个项目概览页演示常用约束。可以结合 XML 注释观察效果。</string>
<string name="i18n_002">智能灌溉升级项目</string>
<string name="i18n_003">进行中</string>
<string name="i18n_004">负责人:Android 学习者</string>
<string name="i18n_005">完成进度</string>
<string name="i18n_006">项目详情</string>
<string name="i18n_007">隐藏详情</string>
<string name="i18n_008">开始时间</string>
<string name="i18n_009">责任部门很长</string>
<string name="i18n_010">移动研发组</string>
<string name="i18n_011">本周数据</string>
<string name="i18n_012">12\n任务</string>
<string name="i18n_013">9\n完成</string>
<string name="i18n_014">3\n待处理</string>
<string name="i18n_015">16:9 比例区域</string>
<string name="i18n_016">宽度由两侧约束\n高度按 16:9 自动计算</string>
<string name="i18n_017">核心区别:LinearLayout 强调排列,RelativeLayout 强调相对规则;ConstraintLayout 用双向约束与辅助工具描述关系,复杂页面通常能减少嵌套层级。</string>
<string name="i18n_018">邮箱</string>
<string name="i18n_019">请输入验证码</string>
<string name="i18n_020">ConstraintLayout 综合示例</string>
<string name="i18n_021">绑定</string>
<string name="i18n_022">请输入设备编码</string>
<string name="i18n_023">设备名称</string>
<string name="i18n_024">请输入设备名称</string>
<string name="i18n_025">最多支持20个字符</string>
<string name="i18n_026">IMEI码可在设备机身上的二维码标签旁边找到</string>
<string name="i18n_027">确定</string>
<string name="i18n_028">眼罩开关</string>
<string name="i18n_029">设备A</string>
<string name="i18n_030">在线</string>
<string name="i18n_031">更新时间</string>
<string name="i18n_032">设备信号强度</string>
<string name="i18n_033">强</string>
<string name="i18n_034">头套连接状态</string>
<string name="i18n_035">已连接</string>
<string name="i18n_036">头套设备电量</string>
<string name="i18n_037">脚环设备电量</string>
<string name="i18n_038">当前位置</string>
<string name="i18n_039">浙江省杭州市西湖区天目山路国家森林公园上空,海拔236米。</string>
<string name="i18n_040">查看地图</string>
<string name="i18n_041">取消</string>
<string name="i18n_042">确认修改</string>
<string name="i18n_043">加载中...</string>
<string name="i18n_044">请输入手机号</string>
<string name="i18n_045">请输入8-20位密码</string>
<string name="i18n_046">请再次输入新密码</string>
<string name="i18n_047">密码规则</string>
<string name="i18n_048">长度在8-20个字符之间</string>
<string name="i18n_049">包含大小写字母和数字</string>
<string name="i18n_050">不能与当前密码相同</string>
<string name="i18n_051">(剩余时间)</string>
<string name="i18n_052">标题名称</string>
<string name="i18n_053">用户名</string>
<string name="i18n_054">换绑</string>
<string name="i18n_055">清除缓存</string>
<string name="i18n_056">版本信息</string>
<string name="i18n_057">隐私协议</string>
<string name="i18n_058">用户协议</string>
<string name="i18n_059">切换语言</string>
<string name="i18n_060">注销账号</string>
<string name="i18n_061">退出登录</string>
<string name="i18n_062">请输入当前密码</string>
<string name="i18n_063">提示</string>
<string name="i18n_064">内容内容</string>
<string name="i18n_065">手动添加</string>
<string name="i18n_066">扫一扫</string>
<string name="i18n_067">当前绑定账号</string>
<string name="i18n_068">需要权限</string>
<string name="i18n_069">请允许应用使用相关权限</string>
<string name="i18n_070">权限被拒绝</string>
<string name="i18n_071">请在应用设置中开启所需权限</string>
<string name="i18n_072">去设置</string>
<string name="i18n_073">请输入内容</string>
<string name="i18n_074">设备详情</string>
<string name="i18n_075">设备信息无效</string>
<string name="i18n_076">设备已经离线</string>
<string name="i18n_077">重命名设备</string>
<string name="i18n_078">最多支持20个字符,支持中英文、数字和字符。</string>
<string name="i18n_079">确定要解绑该设备?</string>
<string name="i18n_080">暂无定位</string>
<string name="i18n_081">相机权限</string>
<string name="i18n_082">扫一扫功能需要使用相机权限</string>
<string name="i18n_083">未识别到内容</string>
<string name="i18n_084">扫码失败</string>
<string name="i18n_085">手机</string>
<string name="i18n_086">请输入邮箱</string>
<string name="i18n_087">手机号不能为空</string>
<string name="i18n_088">邮箱不能为空</string>
<string name="i18n_089">验证码不能为空</string>
<string name="i18n_090">密码不能为空</string>
<string name="i18n_091">请再次输入密码</string>
<string name="i18n_092">两次输入的密码不一致</string>
<string name="i18n_093">密码需为8-20位,且包含大小写字母和数字</string>
<string name="i18n_094">密码修改成功</string>
<string name="i18n_095">请先阅读并同意用户协议和隐私政策</string>
<string name="i18n_096">账号不能为空</string>
<string name="i18n_097">切换邮箱验证码登录</string>
<string name="i18n_098">切换手机号密码登录</string>
<string name="i18n_099">切换邮箱密码登录</string>
<string name="i18n_100">登录成功</string>
<string name="i18n_101">注册成功</string>
<string name="i18n_102">相机和麦克风权限</string>
<string name="i18n_103">拍照和录音功能需要使用相机及麦克风权限</string>
<string name="i18n_104">定位权限</string>
<string name="i18n_105">定位功能需要获取您的位置信息</string>
<string name="i18n_106">操作确认</string>
<string name="i18n_107">确定要执行这项操作吗?</string>
<string name="i18n_108">已取消</string>
<string name="i18n_109">已点击确定</string>
<string name="i18n_110">请输入测试内容</string>
<string name="i18n_111">已取消输入</string>
<string name="i18n_112">未设置昵称</string>
<string name="i18n_113">清除当前应用缓存?</string>
<string name="i18n_114">确认注销账号?</string>
<string name="i18n_115">注销账号后,账号将被删除,所有设备</string>
<string name="i18n_116">解绑、个人信息被清除,无法恢复,请谨慎操作?</string>
<string name="i18n_117">确认注销</string>
<string name="i18n_118">确定要退出当前账号吗?</string>
<string name="i18n_119">未设置用户名</string>
<string name="i18n_120">未绑定手机号</string>
<string name="i18n_121">换绑手机号</string>
<string name="i18n_122">换绑邮箱</string>
<string name="i18n_123">缓存清理成功</string>
<string name="i18n_124">当前密码不能为空</string>
<string name="i18n_125">新密码不能为空</string>
<string name="i18n_126">新密码不能与当前密码相同</string>
<string name="i18n_127">两次输入的新密码不一致</string>
<string name="i18n_128">更换绑定手机号</string>
<string name="i18n_129">更换绑定邮箱</string>
<string name="i18n_130">新手机号不能为空</string>
<string name="i18n_131">新邮箱不能为空</string>
<string name="i18n_132">请输入新手机号</string>
<string name="i18n_133">请输入新邮箱</string>
<string name="i18n_134">隐私政策</string>
<string name="i18n_135">页面参数无效</string>
<string name="i18n_136">无法打开此链接</string>
<string name="i18n_137">页面加载失败,请稍后重试</string>
<string name="i18n_138">图片文件不存在</string>
<string name="i18n_139">图片压缩失败</string>
<string name="i18n_140">当前已是最新版本</string>
<string name="i18n_141">需要通知权限才能显示下载进度</string>
<string name="i18n_142">是否立即下载更新?</string>
<string name="i18n_143">下载</string>
<string name="i18n_144">下载地址为空</string>
<string name="i18n_145">应用更新</string>
<string name="i18n_146">下载响应为空</string>
<string name="i18n_147">正在下载更新</string>
<string name="i18n_148">下载完成</string>
<string name="i18n_149">验证码发送失败</string>
<string name="i18n_150">网络请求失败</string>
<string name="i18n_151">当前手机号验证失败</string>
<string name="i18n_152">当前邮箱验证失败</string>
<string name="i18n_153">手机号更换失败</string>
<string name="i18n_154">邮箱更换失败</string>
<string name="i18n_155">退出登录失败</string>
<string name="i18n_156">注销账号失败</string>
<string name="i18n_157">获取个人信息失败</string>
<string name="i18n_158">版本检测失败</string>
<string name="i18n_159">密码修改失败</string>
<string name="i18n_160">验证码已发送</string>
<string name="i18n_161">当前手机号验证成功</string>
<string name="i18n_162">当前邮箱验证成功</string>
<string name="i18n_163">手机号更换成功</string>
<string name="i18n_164">邮箱更换成功</string>
<string name="i18n_165">退出登录成功</string>
<string name="i18n_166">账号注销成功</string>
<string name="i18n_167">服务异常</string>
<string name="i18n_168">登录失败</string>
<string name="i18n_169">注册失败</string>
<string name="i18n_170">未命名设备</string>
<string name="i18n_171">离线</string>
<string name="i18n_172">弱</string>
<string name="i18n_173">一般</string>
<string name="i18n_174">断开</string>
<string name="i18n_175">获取设备列表失败</string>
<string name="i18n_176">获取设备详情失败</string>
<string name="i18n_177">控制眼罩失败</string>
<string name="i18n_178">绑定设备失败</string>
<string name="i18n_179">设备重命名失败</string>
<string name="i18n_180">设备解绑失败</string>
<string name="i18n_181">眼罩状态已更新</string>
<string name="i18n_182">设备绑定成功</string>
<string name="i18n_183">设备编号和名称不能为空</string>
<string name="i18n_184">设备重命名成功</string>
<string name="i18n_185">设备编号不能为空</string>
<string name="i18n_186">设备解绑成功</string>
<string name="i18n_187">显示详情</string>
<string name="i18n_188">设备巡检记录</string>
<string name="i18n_189">已完成</string>
<string name="i18n_190">自动浇水计划</string>
<string name="i18n_191">执行中</string>
<string name="i18n_192">传感器数据同步</string>
<string name="i18n_193">待处理</string>
<string name="i18n_194">系统配置备份</string>
<string name="i18n_195">固件升级任务</string>
<string name="i18n_196">失败</string>
<string name="i18n_197">用户操作日志</string>
<string name="i18n_198">年</string>
<string name="i18n_199">月</string>
<string name="i18n_200">日</string>
<string name="i18n_201">时</string>
<string name="i18n_202">分</string>
<string name="i18n_203">秒</string>
<string name="i18n_204">开始时间不能晚于结束时间</string>
<string name="i18n_205">必须设置选择结果监听器</string>
<string name="language_chinese">中文</string>
<string name="language_english">English</string>
<string name="language_arabic">العربية</string>
<string name="language_not_available">阿拉伯语暂未开放</string>
<!--结束-->

View File

@@ -11,4 +11,18 @@
<item name="android:backgroundDimEnabled">true</item>
<item name="android:windowAnimationStyle">@android:style/Animation.Dialog</item>
</style>
<style name="LanguageDialogTheme" parent="Theme.MaterialComponents.DayNight.Dialog">
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowIsFloating">true</item>
<item name="android:windowMinWidthMajor">100%</item>
<item name="android:windowMinWidthMinor">100%</item>
<item name="android:windowAnimationStyle">@style/LanguageDialogAnimation</item>
</style>
<style name="LanguageDialogAnimation" parent="@android:style/Animation.Dialog">
<item name="android:windowEnterAnimation">@anim/language_dialog_enter</item>
<item name="android:windowExitAnimation">@anim/language_dialog_exit</item>
</style>
</resources>