新增周期3天,5天的订阅,后台已同步以天为单位的订阅;

添加图片后,再输入邮箱,输入框被挡住;
订阅加载套餐详情时,保险有个默认值64.8元,导致买3个月套餐却支付了3年的保险;
wifi 设置跳过密码输入页面。
This commit is contained in:
yezhiqiu
2026-01-06 09:41:22 +08:00
parent 9a6df5bbc9
commit 5381a21c63
15 changed files with 106 additions and 43 deletions

View File

@@ -30,7 +30,7 @@ android {
targetSdkVersion 35 targetSdkVersion 35
versionCode 2101 versionCode 2101
// versionName "2.1.1" // versionName "2.1.1"
versionName "2.1.1-Beta13" versionName "2.1.1-Beta15"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

View File

@@ -8,6 +8,7 @@ import com.abbidot.baselibrary.util.Utils
import com.abbidot.tracker.R import com.abbidot.tracker.R
import com.abbidot.tracker.bean.PackageBean import com.abbidot.tracker.bean.PackageBean
import com.abbidot.tracker.constant.ConstantInt import com.abbidot.tracker.constant.ConstantInt
import com.abbidot.tracker.constant.ConstantString
/** /**
*Created by .yzq on 2024/8/6/006. *Created by .yzq on 2024/8/6/006.
@@ -42,6 +43,10 @@ class ChoosePlanAdapter(
mContext.getString(R.string.txt_money_unit), "${item.planPrice}" mContext.getString(R.string.txt_money_unit), "${item.planPrice}"
) )
getTextView(R.id.tv_choose_plan_item_unit).text =
if (item.mealUnit == ConstantString.PackageUnitDay) mContext.getString(R.string.txt_per_day)
else mContext.getString(R.string.txt_per_month)
getTextView(R.id.tv_choose_plan_item_billed_money).text = item.billedMode getTextView(R.id.tv_choose_plan_item_billed_money).text = item.billedMode

View File

@@ -50,14 +50,17 @@ class MySubscriptionAdapter(
mContext.getString(R.string.txt_auto_subscription_year), mContext.getString(R.string.txt_auto_subscription_year),
item.autoRenewPrice.toString() item.autoRenewPrice.toString()
) )
else if (item.mealUnit == ConstantString.PackageUnitDay) String.format(
mContext.getString(R.string.txt_auto_subscription_day),
"${item.autoRenewPrice}",
"${item.mealPeriod}"
)
else String.format( else String.format(
mContext.getString(R.string.txt_auto_subscription_month), mContext.getString(R.string.txt_auto_subscription_month),
item.autoRenewPrice.toString() item.autoRenewPrice.toString()
) )
View.VISIBLE View.VISIBLE
} else { } else View.GONE
View.GONE
}
} }
holder.setText( holder.setText(
R.id.tv_my_subscription_active_time, R.id.tv_my_subscription_active_time,

View File

@@ -62,16 +62,19 @@ class SubscriptionHistoryAdapter(
R.id.tv_subscription_annual_care_active_time, R.id.tv_subscription_annual_care_active_time,
Utils.stringToDate(item.createTime, resultFormat = Utils.DATE_FORMAT_PATTERN_EN7) Utils.stringToDate(item.createTime, resultFormat = Utils.DATE_FORMAT_PATTERN_EN7)
) )
//保险时长不足一年按12个月算
val insuranceTime =
if (item.planTimeMonthsCount > 12) item.mealPeriod else 1
holder.setText( holder.setText(
R.id.tv_subscription_annual_care_expiry_time, Utils.formatTime( R.id.tv_subscription_annual_care_expiry_time, Utils.formatTime(
Utils.getAfterHowTimestamp( Utils.getAfterHowTimestamp(
Utils.stringToTimestamp(item.createTime), item.mealPeriod * 365L Utils.stringToTimestamp(item.createTime), insuranceTime * 365L
), Utils.DATE_FORMAT_PATTERN_EN7 ), Utils.DATE_FORMAT_PATTERN_EN7
) )
) )
holder.setText( holder.setText(
R.id.tv_subscription_annual_care_package, R.id.tv_subscription_annual_care_package,
String.format(context.getString(R.string.txt_year_care), "${item.mealPeriod}") String.format(context.getString(R.string.txt_year_care), "$insuranceTime")
) )
holder.setText( holder.setText(
R.id.tv_subscription_annual_care_price, R.id.tv_subscription_annual_care_price,

View File

@@ -54,6 +54,7 @@ import androidx.annotation.StringDef
ConstantString.PetWeight, ConstantString.PetWeight,
ConstantString.Upgrade, ConstantString.Upgrade,
ConstantString.Password, ConstantString.Password,
ConstantString.PackageUnitDay,
ConstantString.PackageUnitMonth, ConstantString.PackageUnitMonth,
ConstantString.PackageUnitYear, ConstantString.PackageUnitYear,
ConstantString.SetMealIndex ConstantString.SetMealIndex
@@ -111,6 +112,7 @@ annotation class ConstantString {
//套餐单位 //套餐单位
const val PackageUnitMonth = "MONTH" const val PackageUnitMonth = "MONTH"
const val PackageUnitYear = "YEAR" const val PackageUnitYear = "YEAR"
const val PackageUnitDay = "DAY"
//只是用来区分类型 //只是用来区分类型
const val Type = "type" const val Type = "type"

View File

@@ -214,7 +214,7 @@ class AddNewTracker3Activity :
private fun scanFinished() { private fun scanFinished() {
mAnimatorSet?.cancel() mAnimatorSet?.cancel()
mViewBinding.ivAddNewTracker3RefreshBtn.isEnabled = true mViewBinding.ivAddNewTracker3RefreshBtn.isEnabled = true
if (mScanDeviceAdapter.getData().size == 0) { if (isFrontRunning && mScanDeviceAdapter.getData().size == 0) {
ViewUtil.instance.showDialog( ViewUtil.instance.showDialog(
mContext, mContext,
R.string.txt_no_search_device, R.string.txt_no_search_device,

View File

@@ -107,7 +107,7 @@ class AddWifiPowerZone3Activity :
showLoading(true, R.string.txt_please_wait) showLoading(true, R.string.txt_please_wait)
mRightImageButton?.let { mRightImageButton?.let {
it.isEnabled = false it.isEnabled = false
mAnimatorSet = ViewUtil.instance.viewAlphaAndRotationObjectAnimator(it, true) mAnimatorSet = ViewUtil.instance.viewRotationAnimator(it, true)
} }
mWiFiListAdapter.setData(null) mWiFiListAdapter.setData(null)
SRBleUtil.instance.writeData( SRBleUtil.instance.writeData(
@@ -148,7 +148,8 @@ class AddWifiPowerZone3Activity :
private fun goSetWifiPassword(pos: Int) { private fun goSetWifiPassword(pos: Int) {
if (mWiFiListAdapter.getData().isNotEmpty()) { if (mWiFiListAdapter.getData().isNotEmpty()) {
val wiFiBean = mWiFiListAdapter.getData()[pos] val wiFiBean = mWiFiListAdapter.getData()[pos]
val intent = Intent(mContext, AddWifiPasswordActivity::class.java) val intent = Intent(mContext, EditWifiPowerZoneActivity::class.java)
// val intent = Intent(mContext, AddWifiPasswordActivity::class.java)
intent.putExtra(ConstantString.WiFi, wiFiBean) intent.putExtra(ConstantString.WiFi, wiFiBean)
mDecWiFiAddressData?.let { mDecWiFiAddressData?.let {
intent.putExtra(ConstantString.HistoryDataBean, it) intent.putExtra(ConstantString.HistoryDataBean, it)

View File

@@ -16,6 +16,7 @@ import com.abbidot.tracker.R
import com.abbidot.tracker.adapter.FeedbackImageAdapter import com.abbidot.tracker.adapter.FeedbackImageAdapter
import com.abbidot.tracker.base.BaseActivity import com.abbidot.tracker.base.BaseActivity
import com.abbidot.tracker.bean.MenuTxtBean import com.abbidot.tracker.bean.MenuTxtBean
import com.abbidot.tracker.constant.ConstantInt
import com.abbidot.tracker.constant.GetResultCallback import com.abbidot.tracker.constant.GetResultCallback
import com.abbidot.tracker.databinding.ActivityFeedbackBinding import com.abbidot.tracker.databinding.ActivityFeedbackBinding
import com.abbidot.tracker.util.ViewUtil import com.abbidot.tracker.util.ViewUtil
@@ -89,7 +90,19 @@ class FeedbackActivity : BaseActivity<ActivityFeedbackBinding>(ActivityFeedbackB
setViewClickHideInputMethod(llSettingFeedbackLayout) setViewClickHideInputMethod(llSettingFeedbackLayout)
} }
setListenKeyboardChange() setListenKeyboardChange(300)
}
//添加图片后,再输入邮箱,输入框被挡住
override fun showSoftKeyboardCallback() {
mViewBinding.apply {
if (ilFeedbackPhoneLayout.etInputContent.hasFocus()) {
if (mUploadImageUrlList.size > 0) svSettingFeedbackScroll.postDelayed({
svSettingFeedbackScroll.scrollY = 1000
}, 400)
else svSettingFeedbackScroll.scrollY = 600
}
}
} }
override fun liveDataObserve() { override fun liveDataObserve() {
@@ -119,6 +132,11 @@ class FeedbackActivity : BaseActivity<ActivityFeedbackBinding>(ActivityFeedbackB
mUploadImageIndex++ mUploadImageIndex++
uploadImageList() uploadImageList()
} }
override fun onRequestError(exceptionCode: String?) {
super.onRequestError(exceptionCode)
setButtonEnabled(mViewBinding.btnSubmitSettingFeedback, ConstantInt.Type1)
}
}) })
} }
@@ -127,6 +145,11 @@ class FeedbackActivity : BaseActivity<ActivityFeedbackBinding>(ActivityFeedbackB
override fun onResult(any: Any) { override fun onResult(any: Any) {
showToast(R.string.txt_feedback_success, isFinish = true) showToast(R.string.txt_feedback_success, isFinish = true)
} }
override fun onRequestError(exceptionCode: String?) {
super.onRequestError(exceptionCode)
setButtonEnabled(mViewBinding.btnSubmitSettingFeedback, ConstantInt.Type1)
}
}) })
} }
} }
@@ -198,6 +221,7 @@ class FeedbackActivity : BaseActivity<ActivityFeedbackBinding>(ActivityFeedbackB
mUploadImageIndex = 0 mUploadImageIndex = 0
mUploadImageUrlList.clear() mUploadImageUrlList.clear()
setButtonEnabled(mViewBinding.btnSubmitSettingFeedback, ConstantInt.Type0)
uploadImageList() uploadImageList()
} }
@@ -239,6 +263,7 @@ class FeedbackActivity : BaseActivity<ActivityFeedbackBinding>(ActivityFeedbackB
//相机或相册回调 //相机或相册回调
if (requestCode == ResultCode.ResultCode_1) { if (requestCode == ResultCode.ResultCode_1) {
if (null != data) { if (null != data) {
hideInputMethod(mViewBinding.root)
//返回对象集合:如果你需要了解图片的宽、高、大小、用户是否选中原图选项等信息,可以用这个 //返回对象集合:如果你需要了解图片的宽、高、大小、用户是否选中原图选项等信息,可以用这个
val resultPhotos: ArrayList<Photo> = val resultPhotos: ArrayList<Photo> =
data.getParcelableArrayListExtra(EasyPhotos.RESULT_PHOTOS)!! data.getParcelableArrayListExtra(EasyPhotos.RESULT_PHOTOS)!!
@@ -249,11 +274,11 @@ class FeedbackActivity : BaseActivity<ActivityFeedbackBinding>(ActivityFeedbackB
} }
override fun onClick(v: View?) { override fun onClick(v: View?) {
if (isLimitClick()) return
mViewBinding.apply { mViewBinding.apply {
when (v!!) { when (v!!) {
mViewBinding.btnSubmitSettingFeedback -> startSubmitFeedback() btnSubmitSettingFeedback -> startSubmitFeedback()
llSelectImageBtnLayout -> {
mViewBinding.llSelectImageBtnLayout -> {
if (mFeedbackImageAdapter.getData().size == mLimitPhotoCount) { if (mFeedbackImageAdapter.getData().size == mLimitPhotoCount) {
showToast( showToast(
String.format( String.format(

View File

@@ -275,6 +275,8 @@ class SureSubscriptionPlanActivity :
private fun sureSubscriptionDetail() { private fun sureSubscriptionDetail() {
mViewBinding.apply { mViewBinding.apply {
showNoCancelableLoading(false) showNoCancelableLoading(false)
ViewUtil.instance.viewShow(svSubscribePlanLayout)
ViewUtil.instance.viewShow(btnSureSubscribePlan1Continue)
setButtonEnabled(btnSureSubscribePlan1Continue, ConstantInt.Type1) setButtonEnabled(btnSureSubscribePlan1Continue, ConstantInt.Type1)
mOrderBean?.let { mOrderBean?.let {
if (isUpgrade) { if (isUpgrade) {
@@ -295,17 +297,19 @@ class SureSubscriptionPlanActivity :
val nowTimestamp = System.currentTimeMillis() val nowTimestamp = System.currentTimeMillis()
mPackageBean?.let { p -> mPackageBean?.let { p ->
ilSubscribePlanDetail.tvSureSubscribePlanPer.text = String.format( ilSubscribePlanDetail.tvSureSubscribePlanPer.text =
if (p.mealUnit == ConstantString.PackageUnitDay) String.format(
getString(R.string.txt_day_unit), "${p.planTimeMonthsCount}"
)
else String.format(
getString(R.string.txt_month_unit), "${p.planTimeMonthsCount}" getString(R.string.txt_month_unit), "${p.planTimeMonthsCount}"
) )
val renewalString = if (p.mealUnit == ConstantString.PackageUnitYear) { val renewalString = if (p.mealUnit == ConstantString.PackageUnitYear) {
// ilSubscribePlanDetail.tvSureSubscribePlanPer.text =
// String.format(getString(R.string.txt_year_unit), p.period)
val renewalTimestamp = val renewalTimestamp =
Utils.getAfterHowTimestamp(nowTimestamp, p.period * 365L) Utils.getAfterHowTimestamp(nowTimestamp, p.period * 365L)
val renewalDate = val renewalDate =
Utils.formatTime(renewalTimestamp, Utils.DATE_FORMAT_PATTERN_EN7) Utils.formatTime(renewalTimestamp, Utils.DATE_FORMAT_PATTERN_EN7)
if (p.period > 1) { if (p.planTimeMonthsCount > 12) {
String.format( String.format(
getString(R.string.txt_renewal_years), getString(R.string.txt_renewal_years),
"${p.autoRenewPrice}", "${p.autoRenewPrice}",
@@ -319,6 +323,17 @@ class SureSubscriptionPlanActivity :
renewalDate renewalDate
) )
} }
} else if (p.mealUnit == ConstantString.PackageUnitDay) {
val renewalTimestamp =
Utils.getAfterHowTimestamp(nowTimestamp, p.period.toLong())
val renewalDate =
Utils.formatTime(renewalTimestamp, Utils.DATE_FORMAT_PATTERN_EN7)
String.format(
getString(R.string.txt_renewal_day),
"${p.autoRenewPrice}",
"${p.period}",
renewalDate
)
} else { } else {
val renewalTimestamp = val renewalTimestamp =
Utils.getAfterHowTimestamp(nowTimestamp, 30L * p.planTimeMonthsCount) Utils.getAfterHowTimestamp(nowTimestamp, 30L * p.planTimeMonthsCount)
@@ -361,8 +376,11 @@ class SureSubscriptionPlanActivity :
colorRedId = R.color.data_black_color colorRedId = R.color.data_black_color
) )
mTotalMoney += price mTotalMoney += price
//保险时长不足一年按12个月算
val insuranceTime =
if (p.planTimeMonthsCount > 12) p.planTimeMonthsCount else 12
mSumInsuranceMoney = mSumInsuranceMoney =
Utils.formatDecimal(p.everyMonthSafeFee * p.period * 12, 1).toDouble() Utils.formatDecimal(p.everyMonthSafeFee * insuranceTime, 1).toDouble()
ViewUtil.instance.addMenuBean( ViewUtil.instance.addMenuBean(
mSummaryAdapter.getData(), mSummaryAdapter.getData(),
getString(R.string.txt_annual_care_text), getString(R.string.txt_annual_care_text),
@@ -372,7 +390,7 @@ class SureSubscriptionPlanActivity :
mTotalMoney += mSumInsuranceMoney mTotalMoney += mSumInsuranceMoney
val insuranceMoneyString = val insuranceMoneyString =
String.format(getString(R.string.txt_month_unit), "${p.period * 12}") String.format(getString(R.string.txt_month_unit), "$insuranceTime")
ilSubscribePlanInsurance.tvSureSubscribePlanInsurancePer.text = ilSubscribePlanInsurance.tvSureSubscribePlanInsurancePer.text =
insuranceMoneyString insuranceMoneyString
ilSubscribePlanInsurance.tvSureSubscribePlanInsurancePerMoney.text = ilSubscribePlanInsurance.tvSureSubscribePlanInsurancePerMoney.text =
@@ -398,8 +416,8 @@ class SureSubscriptionPlanActivity :
) )
} }
//判断套餐是否过期 //判断套餐是否过期 或者套餐没退款
if (!TextUtils.isEmpty(it.orderNum) && it.surplusDays == 0L && it.subscriptionStatus == ConstantInt.Close) { if (!TextUtils.isEmpty(it.orderNum) && it.orderStatus != 6 && it.surplusDays == 0L && it.subscriptionStatus == ConstantInt.Close) {
// mOrderBean?.reactivation = 1 // mOrderBean?.reactivation = 1
mSubscriptionViewModel.getReactivationFee(it.orderNum) mSubscriptionViewModel.getReactivationFee(it.orderNum)
} else { } else {
@@ -455,14 +473,16 @@ class SureSubscriptionPlanActivity :
mResidualMoney = Utils.formatDecimal( mResidualMoney = Utils.formatDecimal(
mResidualMoney + surplusSafeValidFee, 1 mResidualMoney + surplusSafeValidFee, 1
).toDouble() ).toDouble()
val insuranceTime =
if (p.planTimeMonthsCount > 12) p.planTimeMonthsCount else 12
val insuranceMoneyString = String.format( val insuranceMoneyString = String.format(
getString(R.string.txt_month_unit), "${12 * p.period}" getString(R.string.txt_month_unit), "$insuranceTime"
) )
mViewBinding.ilSubscribePlanInsurance.tvSureSubscribePlanInsurancePer.text = mViewBinding.ilSubscribePlanInsurance.tvSureSubscribePlanInsurancePer.text =
insuranceMoneyString insuranceMoneyString
mSumInsuranceMoney = Utils.formatDecimal( mSumInsuranceMoney =
12 * p.everyMonthSafeFee * p.period, 1 Utils.formatDecimal(p.everyMonthSafeFee * insuranceTime, 1)
).toDouble() .toDouble()
mTotalMoney += mSumInsuranceMoney mTotalMoney += mSumInsuranceMoney
mViewBinding.ilSubscribePlanInsurance.tvSureSubscribePlanInsuranceMoney.text = mViewBinding.ilSubscribePlanInsurance.tvSureSubscribePlanInsuranceMoney.text =
String.format( String.format(

View File

@@ -100,22 +100,14 @@ class FencesAddEditMapCommon : BaseMapCommon() {
mFencesGoogleMapFragment?.apply { mFencesGoogleMapFragment?.apply {
view.postDelayed({ view.postDelayed({
if (activity.isFinishing) return@postDelayed if (activity.isFinishing) return@postDelayed
resetFencesViewCentre( resetFencesViewCentre(LatLng(it.latitudeCenter, it.longitudeCenter))
LatLng(
it.latitudeCenter, it.longitudeCenter
)
)
}, 100) }, 100)
} }
mFencesBaiduMapFragment?.apply { mFencesBaiduMapFragment?.apply {
view.postDelayed({ view.postDelayed({
if (activity.isFinishing) return@postDelayed if (activity.isFinishing) return@postDelayed
resetFencesViewCentre( resetFencesViewCentre(getBaiduMapLatLng(it.latitudeCenter, it.longitudeCenter))
getBaiduMapLatLng(
it.latitudeCenter, it.longitudeCenter
)
)
}, 600) }, 600)
} }
} }

View File

@@ -965,13 +965,14 @@ class ViewUtil private constructor() {
fun showAndHidePasswordClick( fun showAndHidePasswordClick(
editText: EditText, hideShowImBtn: QMUIAlphaImageButton, isShowPassword: Boolean editText: EditText, hideShowImBtn: QMUIAlphaImageButton, isShowPassword: Boolean
) { ) {
if (isShowPassword) { val imageResId = if (isShowPassword) {
hideShowImBtn.setImageResource(R.drawable.icon_show_password_svg)
editText.transformationMethod = HideReturnsTransformationMethod.getInstance() editText.transformationMethod = HideReturnsTransformationMethod.getInstance()
R.drawable.icon_show_password_svg
} else { } else {
hideShowImBtn.setImageResource(R.drawable.icon_hide_password_svg)
editText.transformationMethod = PasswordTransformationMethod.getInstance() editText.transformationMethod = PasswordTransformationMethod.getInstance()
R.drawable.icon_hide_password_svg
} }
hideShowImBtn.setImageResource(imageResId)
editText.setSelection(editText.text.toString().length) editText.setSelection(editText.text.toString().length)
} }

View File

@@ -12,13 +12,16 @@
layout="@layout/layout_top_bar" /> layout="@layout/layout_top_bar" />
<androidx.core.widget.NestedScrollView style="@style/my_ScrollView_style"> <androidx.core.widget.NestedScrollView
android:id="@+id/sv_setting_feedback_scroll"
style="@style/my_ScrollView_style">
<androidx.appcompat.widget.LinearLayoutCompat <androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/ll_setting_feedback_layout"
style="@style/root_layout_style" style="@style/root_layout_style"
android:layout_marginHorizontal="@dimen/dp_16" android:layout_marginHorizontal="@dimen/dp_16"
android:layout_marginVertical="@dimen/dp_18" android:layout_marginVertical="@dimen/dp_18"
android:id="@+id/ll_setting_feedback_layout" android:fitsSystemWindows="true"
android:orientation="vertical"> android:orientation="vertical">
<com.abbidot.tracker.widget.TypefaceTextView <com.abbidot.tracker.widget.TypefaceTextView

View File

@@ -14,7 +14,8 @@
<androidx.core.widget.NestedScrollView <androidx.core.widget.NestedScrollView
android:id="@+id/sv_subscribe_plan_layout" android:id="@+id/sv_subscribe_plan_layout"
style="@style/my_ScrollView_style" style="@style/my_ScrollView_style"
android:layout_weight="1"> android:layout_weight="1"
android:visibility="gone">
<androidx.appcompat.widget.LinearLayoutCompat <androidx.appcompat.widget.LinearLayoutCompat
style="@style/root_layout_style" style="@style/root_layout_style"
@@ -79,5 +80,6 @@
android:layout_marginHorizontal="@dimen/dp_16" android:layout_marginHorizontal="@dimen/dp_16"
android:text="@string/txt_continue" android:text="@string/txt_continue"
app:qmui_radius="@dimen/dp_64" app:qmui_radius="@dimen/dp_64"
android:visibility="gone"
app:typeface="@string/roboto_bold_font" /> app:typeface="@string/roboto_bold_font" />
</androidx.appcompat.widget.LinearLayoutCompat> </androidx.appcompat.widget.LinearLayoutCompat>

View File

@@ -73,6 +73,7 @@
android:textStyle="bold" /> android:textStyle="bold" />
<com.abbidot.tracker.widget.TypefaceTextView <com.abbidot.tracker.widget.TypefaceTextView
android:id="@+id/tv_choose_plan_item_unit"
style="@style/my_TextView_style_v2" style="@style/my_TextView_style_v2"
android:layout_alignBaseline="@id/tv_choose_plan_item_money" android:layout_alignBaseline="@id/tv_choose_plan_item_money"
android:layout_alignTop="@id/tv_choose_plan_item_money" android:layout_alignTop="@id/tv_choose_plan_item_money"

View File

@@ -1057,4 +1057,9 @@
<string name="txt_edit_profile">Edit Profile</string> <string name="txt_edit_profile">Edit Profile</string>
<string name="txt_no_internet">No internet connection</string> <string name="txt_no_internet">No internet connection</string>
<string name="txt_per_day">/day</string>
<string name="txt_renewal_day">Renewal: $%s/%s day on %s</string>
<string name="txt_day_unit">/day x%s</string>
<string name="txt_auto_subscription_day">(Renew at $%s per %s day thereafter)</string>
</resources> </resources>