diff --git a/modules/mogo-module-event-panel/src/main/java/com/zhidao/mogo/module/event/panel/EventPanelModuleProvider.kt b/modules/mogo-module-event-panel/src/main/java/com/zhidao/mogo/module/event/panel/EventPanelModuleProvider.kt index e682243ef6..461581fc28 100644 --- a/modules/mogo-module-event-panel/src/main/java/com/zhidao/mogo/module/event/panel/EventPanelModuleProvider.kt +++ b/modules/mogo-module-event-panel/src/main/java/com/zhidao/mogo/module/event/panel/EventPanelModuleProvider.kt @@ -17,6 +17,7 @@ import com.zhidao.mogo.module.event.panel.EventPanelConstants.MODULE_NAME import com.zhidao.mogo.module.event.panel.EventPanelConstants.PATH_NAME import com.zhidao.mogo.module.event.panel.fragment.EventPanelFragment import com.zhidao.mogo.module.event.panel.util.MogoApiManager +import com.zhidao.mogo.module.event.panel.util.TripRecordDataManager /** * 事件面板provider @@ -33,6 +34,7 @@ class EventPanelModuleProvider : IEventPanelProvider { override fun init(context: Context) { Logger.d(MODULE_NAME, "模块初始化====") MogoApiManager.init(context) + TripRecordDataManager.init(context) } override fun createFragment(context: Context, data: Bundle?): Fragment? { diff --git a/modules/mogo-module-event-panel/src/main/java/com/zhidao/mogo/module/event/panel/dao/TripRecordDao.kt b/modules/mogo-module-event-panel/src/main/java/com/zhidao/mogo/module/event/panel/dao/TripRecordDao.kt index bd865ef6d7..4f95c8261f 100644 --- a/modules/mogo-module-event-panel/src/main/java/com/zhidao/mogo/module/event/panel/dao/TripRecordDao.kt +++ b/modules/mogo-module-event-panel/src/main/java/com/zhidao/mogo/module/event/panel/dao/TripRecordDao.kt @@ -20,7 +20,7 @@ interface TripRecordDao { @Query(value = "SELECT * FROM TripRecord WHERE eventId = :eventId") fun queryTripRecordByEventId(eventId:String):Single - @Insert + @Insert(onConflict = OnConflictStrategy.REPLACE) fun insert(vararg tripRecord: TripRecord) @Update diff --git a/modules/mogo-module-event-panel/src/main/java/com/zhidao/mogo/module/event/panel/fragment/TripRecordFragment.kt b/modules/mogo-module-event-panel/src/main/java/com/zhidao/mogo/module/event/panel/fragment/TripRecordFragment.kt index f439eb51e1..f2e0708d24 100644 --- a/modules/mogo-module-event-panel/src/main/java/com/zhidao/mogo/module/event/panel/fragment/TripRecordFragment.kt +++ b/modules/mogo-module-event-panel/src/main/java/com/zhidao/mogo/module/event/panel/fragment/TripRecordFragment.kt @@ -14,44 +14,13 @@ import kotlin.random.Random /** * 出行动态fragment */ -const val TAG = "TripRecordFragment" - class TripRecordFragment : MvpFragment() { private val tripRecordList = ArrayList() override fun getLayoutId(): Int = R.layout.module_event_panel_fragment_trip_record - private val typeArray = arrayOf(V2XMessageEntity.V2XTypeEnum.ALERT_ROAD_WARNING, V2XMessageEntity.V2XTypeEnum.ALERT_ILLEGAL_PARK_WARNING, V2XMessageEntity.V2XTypeEnum.ALERT_SEEK_WARNING) override fun initViews() { - btnInsert.setOnClickListener { - val randomId = Random.nextInt(10000) - val randomType = Random.nextInt(3) - val tripRecord = TripRecord(id = randomId, eventType = typeArray[randomType]) - val random = Random.nextInt(10) - tripRecord.entity = "entity: $random" - tripRecord.recordTime = System.currentTimeMillis() - tripRecordList.add(tripRecord) - mPresenter.insertTripRecord(tripRecord) - Log.d(TAG, "insert over: $tripRecord") - } - - btnDelete.setOnClickListener { - val random = Random.nextInt(tripRecordList.size) - val tripRecord = tripRecordList.removeAt(random) - mPresenter.deleteTripRecords(tripRecord) - Log.d(TAG, "delete over: $tripRecord") - } - - btnUpdate.setOnClickListener { - val random = Random.nextInt(tripRecordList.size) - val tripRecord = tripRecordList[random] - Log.d(TAG, "before update: $tripRecord") - tripRecord.recordTime = System.currentTimeMillis() - mPresenter.updateTripRecords(tripRecord) - Log.d(TAG, "update over: $tripRecord") - } - btnQuery.setOnClickListener { - Log.d(TAG, "local list: $tripRecordList") + Log.d(MODULE_NAME, "local list: $tripRecordList") mPresenter.queryAllTripRecord() } } @@ -69,6 +38,5 @@ class TripRecordFragment : MvpFragment( */ fun refreshTripRecord(tripRecord: TripRecord) { Logger.d(MODULE_NAME, "刷新单个出行动态: $tripRecord") - } } \ No newline at end of file diff --git a/modules/mogo-module-event-panel/src/main/java/com/zhidao/mogo/module/event/panel/listener/ITripRecordCallback.kt b/modules/mogo-module-event-panel/src/main/java/com/zhidao/mogo/module/event/panel/listener/ITripRecordCallback.kt new file mode 100644 index 0000000000..6bf2e6208e --- /dev/null +++ b/modules/mogo-module-event-panel/src/main/java/com/zhidao/mogo/module/event/panel/listener/ITripRecordCallback.kt @@ -0,0 +1,18 @@ +package com.zhidao.mogo.module.event.panel.listener + +import com.zhidao.mogo.module.event.panel.bean.TripRecord + +/** + * 数据库异步操作数据回调 + */ +interface ITripRecordCallback { + /** + * 查询全部出行动态成功 + */ + fun queryTripRecordListSuccess(tripRecordList:List) + + /** + * 有出行动态新增或更新后,回调此接口 + */ + fun insertOrUpdateTripRecordSuccess(tripRecord:TripRecord) +} \ No newline at end of file diff --git a/modules/mogo-module-event-panel/src/main/java/com/zhidao/mogo/module/event/panel/presenter/TripRecordPresenter.kt b/modules/mogo-module-event-panel/src/main/java/com/zhidao/mogo/module/event/panel/presenter/TripRecordPresenter.kt index 4b08a60c56..e1dd809210 100644 --- a/modules/mogo-module-event-panel/src/main/java/com/zhidao/mogo/module/event/panel/presenter/TripRecordPresenter.kt +++ b/modules/mogo-module-event-panel/src/main/java/com/zhidao/mogo/module/event/panel/presenter/TripRecordPresenter.kt @@ -1,162 +1,33 @@ package com.zhidao.mogo.module.event.panel.presenter -import android.content.BroadcastReceiver -import android.content.Context -import android.content.Intent -import android.content.IntentFilter -import android.util.Log import androidx.lifecycle.LifecycleOwner -import androidx.localbroadcastmanager.content.LocalBroadcastManager import com.mogo.commons.mvp.Presenter -import com.mogo.module.common.entity.V2XMessageEntity -import com.mogo.module.common.entity.V2XMessageEntity.V2XTypeEnum.* -import com.mogo.module.common.entity.V2XRoadEventEntity -import com.mogo.utils.ThreadPoolService -import com.mogo.utils.logger.Logger -import com.mogo.utils.network.utils.GsonUtil -import com.zhidao.mogo.module.event.panel.EventPanelConstants.MODULE_NAME import com.zhidao.mogo.module.event.panel.bean.TripRecord -import com.zhidao.mogo.module.event.panel.dao.TripRecordDatabase import com.zhidao.mogo.module.event.panel.fragment.TripRecordFragment -import io.reactivex.Single -import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.schedulers.Schedulers -import java.util.* +import com.zhidao.mogo.module.event.panel.listener.ITripRecordCallback +import com.zhidao.mogo.module.event.panel.util.TripRecordDataManager -private const val BROADCAST_SCENE_EVENT_ACTION = "com.v2x.scene_local_broadcast" -private const val BROADCAST_SCENE_MODIFY_ACTION = "com.zhidao.tanlu.dataerror" - -private const val BROADCAST_SCENE_EXTRA_KEY = "V2XMessageEntity" -private const val BORADCAST_SCENE_MODIFY_EVENT_ID_KEY = "id" -private const val BROADCAST_SCENE_MODIFY_EVENT_UPDATE_TYPE_KEY = "updateType" - -class TripRecordPresenter(view: TripRecordFragment) : Presenter(view) { - private val sceneEventReceiver = SceneEventReceiver() - private val tripRecordDao = TripRecordDatabase.getInstance(context).getTripRecordDao() +class TripRecordPresenter(view: TripRecordFragment) : Presenter(view),ITripRecordCallback { init { - LocalBroadcastManager.getInstance(context).registerReceiver(sceneEventReceiver, IntentFilter(BROADCAST_SCENE_EVENT_ACTION)) - context.registerReceiver(sceneEventReceiver, IntentFilter(BROADCAST_SCENE_MODIFY_ACTION)) - } - - private val compositeDisposable = CompositeDisposable() - - fun queryAllTripRecord() { - val queryDisposable = tripRecordDao.queryAllTripRecord(countLimitTime()).subscribeOn(Schedulers.io()).observeOn(Schedulers.io()).map { - Logger.d(MODULE_NAME, "delete over time record when query thread is ${Thread.currentThread().name}") - tripRecordDao.deleteOvertimeTripRecord(countLimitTime()) - it - }.observeOn(AndroidSchedulers.mainThread()).subscribe { it -> - Log.d(MODULE_NAME, "db query: $it, thread is ${Thread.currentThread().name}") - mView.refreshTripRecordList(it) - } - compositeDisposable.add(queryDisposable) - } - - fun insertTripRecord(record: TripRecord) { - val disposable = Single.create { - Logger.d(MODULE_NAME, "准备将数据插入数据库: $record") - tripRecordDao.insert(record) - it.onSuccess(record) - }.subscribeOn(Schedulers.io()).observeOn(Schedulers.io()).map { - // 为了防止只进不出,所以在插入新数据时,进行过期数据删除操作 - Logger.d(MODULE_NAME, "delete over time record when insert: $it") - tripRecordDao.deleteOvertimeTripRecord(countLimitTime()) - record - }.observeOn(AndroidSchedulers.mainThread()).subscribe{it-> - Logger.d(MODULE_NAME, "插入+删除操作完成,做界面展示===$it") - mView.refreshTripRecord(it) - } - compositeDisposable.add(disposable) - } - - fun updateTripRecords(vararg records: TripRecord) { - ThreadPoolService.execute { - tripRecordDao.update(*records) - } - } - - fun deleteTripRecords(vararg records: TripRecord) { - ThreadPoolService.execute { - tripRecordDao.delete(*records) - } - } - - /** - * 计算约束时间,即当日0时 - */ - private fun countLimitTime(): Long { - val calendar = Calendar.getInstance() - calendar.time = Date() - calendar.set(Calendar.HOUR_OF_DAY, 0) - calendar.set(Calendar.MINUTE, 0) - calendar.set(Calendar.SECOND, 0) - return calendar.timeInMillis + TripRecordDataManager.addTripRecordCallback(this) } override fun onDestroy(owner: LifecycleOwner) { super.onDestroy(owner) - if(!compositeDisposable.isDisposed) { - compositeDisposable.dispose() - } + TripRecordDataManager.removeTripRecordCallback(this) } - /** - * 处理道路事件推送,保存到本地数据库 - * 目前只处理道路事件,违章提醒,他车求助,其他事件暂不处理 - */ - private fun dealSceneMessage(type: Int, content: Any) { - if (type in arrayOf(ALERT_ROAD_WARNING, ALERT_ILLEGAL_PARK_WARNING, ALERT_SEEK_WARNING)) { - val eventId = if(type == ALERT_ROAD_WARNING){ - val event = content as V2XRoadEventEntity - event.noveltyInfo.infoId - }else{ - "" - } - Logger.d(MODULE_NAME, "处理场景事件,准备插入数据库===eventId: $eventId") - insertTripRecord(TripRecord(id = content.hashCode(), eventId = eventId, eventType = type, entity = GsonUtil.jsonFromObject(content))) - } + fun queryAllTripRecord(){ + TripRecordDataManager.queryAllTripRecord() } - /** - * 本地数据库同步v2x传过来的纠错信息 - */ - private fun syncRoadEventModifyState(eventId: String, modifyType: String) { - Logger.d(MODULE_NAME, "准备同步纠错信息: $eventId, $modifyType") - val disposable = tripRecordDao.queryTripRecordByEventId(eventId).subscribeOn(Schedulers.io()).observeOn(Schedulers.io()).map { - Logger.d(MODULE_NAME, "查询将纠错数据: $it") - it.usefulStatus = modifyType - it - }.map { - Logger.d(MODULE_NAME, "准备更新纠错数据: $it") - tripRecordDao.update(it) - it - }.observeOn(AndroidSchedulers.mainThread()).subscribe { it-> - Logger.d(MODULE_NAME, "数据纠错更新完成,准备刷新界面==$it") - mView.refreshTripRecord(it) - } - compositeDisposable.add(disposable) + override fun queryTripRecordListSuccess(tripRecordList: List) { + mView.refreshTripRecordList(tripRecordList) } - inner class SceneEventReceiver : BroadcastReceiver() { - override fun onReceive(context: Context, intent: Intent) { - Logger.d(MODULE_NAME, "收到V2X事件推送===") - when (intent.action) { - BROADCAST_SCENE_EVENT_ACTION -> { - // 道路事件推送 - val message = intent.getSerializableExtra(BROADCAST_SCENE_EXTRA_KEY) as V2XMessageEntity<*> - Logger.d(MODULE_NAME, "道路事件推送 type: ${message.type}, content: ${message.content}") - dealSceneMessage(message.type, message.content) - } - BROADCAST_SCENE_MODIFY_ACTION -> { - // 纠错推送 - syncRoadEventModifyState(intent.getStringExtra(BORADCAST_SCENE_MODIFY_EVENT_ID_KEY), intent.getStringExtra(BROADCAST_SCENE_MODIFY_EVENT_UPDATE_TYPE_KEY)) - } - } - - } + override fun insertOrUpdateTripRecordSuccess(tripRecord: TripRecord) { + mView.refreshTripRecord(tripRecord) } - } diff --git a/modules/mogo-module-event-panel/src/main/java/com/zhidao/mogo/module/event/panel/util/TripRecordDataManager.kt b/modules/mogo-module-event-panel/src/main/java/com/zhidao/mogo/module/event/panel/util/TripRecordDataManager.kt new file mode 100644 index 0000000000..52e82af6c7 --- /dev/null +++ b/modules/mogo-module-event-panel/src/main/java/com/zhidao/mogo/module/event/panel/util/TripRecordDataManager.kt @@ -0,0 +1,183 @@ +package com.zhidao.mogo.module.event.panel.util + +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.util.Log +import androidx.lifecycle.LifecycleOwner +import androidx.localbroadcastmanager.content.LocalBroadcastManager +import com.mogo.module.common.entity.V2XMessageEntity +import com.mogo.module.common.entity.V2XMessageEntity.V2XTypeEnum.* +import com.mogo.module.common.entity.V2XRoadEventEntity +import com.mogo.utils.ThreadPoolService +import com.mogo.utils.logger.Logger +import com.mogo.utils.network.utils.GsonUtil +import com.zhidao.mogo.module.event.panel.EventPanelConstants.MODULE_NAME +import com.zhidao.mogo.module.event.panel.bean.TripRecord +import com.zhidao.mogo.module.event.panel.dao.TripRecordDao +import com.zhidao.mogo.module.event.panel.dao.TripRecordDatabase +import com.zhidao.mogo.module.event.panel.listener.ITripRecordCallback +import io.reactivex.Single +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.CompositeDisposable +import io.reactivex.schedulers.Schedulers +import java.util.* +import kotlin.collections.ArrayList + + +private const val BROADCAST_SCENE_EVENT_ACTION = "com.v2x.scene_local_broadcast" +private const val BROADCAST_SCENE_MODIFY_ACTION = "com.zhidao.tanlu.dataerror" + +private const val BROADCAST_SCENE_EXTRA_KEY = "V2XMessageEntity" +private const val BORADCAST_SCENE_MODIFY_EVENT_ID_KEY = "id" +private const val BROADCAST_SCENE_MODIFY_EVENT_UPDATE_TYPE_KEY = "updateType" + +/** + * 出行动态的数据管理类,由于TripRecordFragment初始化时机较晚,所以封装一个单例类,提早初始化 + */ +object TripRecordDataManager { + lateinit var context: Context + private val sceneEventReceiver = SceneEventReceiver() + private lateinit var tripRecordDao:TripRecordDao + + private val tripRecordCallbackList = ArrayList() + + fun init(context: Context) { + this.context = context + LocalBroadcastManager.getInstance(context).registerReceiver(sceneEventReceiver, IntentFilter(BROADCAST_SCENE_EVENT_ACTION)) + context.registerReceiver(sceneEventReceiver, IntentFilter(BROADCAST_SCENE_MODIFY_ACTION)) + tripRecordDao = TripRecordDatabase.getInstance(TripRecordDataManager.context).getTripRecordDao() + } + + fun addTripRecordCallback(callback: ITripRecordCallback) { + tripRecordCallbackList.add(callback) + } + + fun removeTripRecordCallback(callback: ITripRecordCallback) { + tripRecordCallbackList.remove(callback) + } + + private val compositeDisposable = CompositeDisposable() + + fun queryAllTripRecord() { + val queryDisposable = tripRecordDao.queryAllTripRecord(countLimitTime()).subscribeOn(Schedulers.io()).observeOn(Schedulers.io()).map { + Logger.d(MODULE_NAME, "delete over time record when query thread is ${Thread.currentThread().name}") + tripRecordDao.deleteOvertimeTripRecord(countLimitTime()) + it + }.observeOn(AndroidSchedulers.mainThread()).subscribe { it -> + Log.d(MODULE_NAME, "db query: $it, thread is ${Thread.currentThread().name}") + tripRecordCallbackList.forEach {callback-> + callback.queryTripRecordListSuccess(it) + } + } + compositeDisposable.add(queryDisposable) + } + + private fun insertTripRecord(record: TripRecord) { + val disposable = Single.create { + Logger.d(MODULE_NAME, "准备将数据插入数据库: $record") + tripRecordDao.insert(record) + it.onSuccess(record) + }.subscribeOn(Schedulers.io()).observeOn(Schedulers.io()).map { + // 为了防止只进不出,所以在插入新数据时,进行过期数据删除操作 + Logger.d(MODULE_NAME, "delete over time record when insert: $it") + tripRecordDao.deleteOvertimeTripRecord(countLimitTime()) + record + }.observeOn(AndroidSchedulers.mainThread()).subscribe{ it-> + Logger.d(MODULE_NAME, "插入+删除操作完成,做界面展示===$it") + tripRecordCallbackList.forEach { callback-> + callback.insertOrUpdateTripRecordSuccess(it) + } + } + compositeDisposable.add(disposable) + } + + fun updateTripRecords(vararg records: TripRecord) { + ThreadPoolService.execute { + tripRecordDao.update(*records) + } + } + + fun deleteTripRecords(vararg records: TripRecord) { + ThreadPoolService.execute { + tripRecordDao.delete(*records) + } + } + + /** + * 计算约束时间,即当日0时 + */ + private fun countLimitTime(): Long { + val calendar = Calendar.getInstance() + calendar.time = Date() + calendar.set(Calendar.HOUR_OF_DAY, 0) + calendar.set(Calendar.MINUTE, 0) + calendar.set(Calendar.SECOND, 0) + return calendar.timeInMillis + } + + fun release(owner: LifecycleOwner) { + if(!compositeDisposable.isDisposed) { + compositeDisposable.dispose() + } + } + + /** + * 处理道路事件推送,保存到本地数据库 + * 目前只处理道路事件,违章提醒,他车求助,其他事件暂不处理 + */ + private fun dealSceneMessage(type: Int, content: Any) { + if (type in arrayOf(ALERT_ROAD_WARNING, ALERT_ILLEGAL_PARK_WARNING, ALERT_SEEK_WARNING)) { + val eventId = if(type == V2XMessageEntity.V2XTypeEnum.ALERT_ROAD_WARNING){ + val event = content as V2XRoadEventEntity + event.noveltyInfo.infoId + }else{ + "" + } + Logger.d(MODULE_NAME, "处理场景事件,准备插入数据库===eventId: $eventId") + insertTripRecord(TripRecord(id = content.hashCode(), eventId = eventId, eventType = type, entity = GsonUtil.jsonFromObject(content))) + } + } + + /** + * 本地数据库同步v2x传过来的纠错信息 + */ + private fun syncRoadEventModifyState(eventId: String, modifyType: String) { + Logger.d(MODULE_NAME, "准备同步纠错信息: $eventId, $modifyType") + val disposable = tripRecordDao.queryTripRecordByEventId(eventId).subscribeOn(Schedulers.io()).observeOn(Schedulers.io()).map { + Logger.d(MODULE_NAME, "查询将纠错数据: $it") + it.usefulStatus = modifyType + it + }.map { + Logger.d(MODULE_NAME, "准备更新纠错数据: $it") + tripRecordDao.update(it) + it + }.observeOn(AndroidSchedulers.mainThread()).subscribe { it-> + Logger.d(MODULE_NAME, "数据纠错更新完成,准备刷新界面==$it") + tripRecordCallbackList.forEach { callback-> + callback.insertOrUpdateTripRecordSuccess(it) + } + } + compositeDisposable.add(disposable) + } + + class SceneEventReceiver : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + Logger.d(MODULE_NAME, "收到V2X事件推送===") + when (intent.action) { + BROADCAST_SCENE_EVENT_ACTION -> { + // 道路事件推送 + val message = intent.getSerializableExtra(BROADCAST_SCENE_EXTRA_KEY) as V2XMessageEntity<*> + Logger.d(MODULE_NAME, "道路事件推送 type: ${message.type}, content: ${message.content}") + dealSceneMessage(message.type, message.content) + } + BROADCAST_SCENE_MODIFY_ACTION -> { + // 纠错推送 + syncRoadEventModifyState(intent.getStringExtra(BORADCAST_SCENE_MODIFY_EVENT_ID_KEY), intent.getStringExtra(BROADCAST_SCENE_MODIFY_EVENT_UPDATE_TYPE_KEY)) + } + } + + } + } +} \ No newline at end of file diff --git a/modules/mogo-module-left-panel/src/main/java/com/zhidao/mogo/module/left/panel/fragment/SimpleSpeedFragment.kt b/modules/mogo-module-left-panel/src/main/java/com/zhidao/mogo/module/left/panel/fragment/SimpleSpeedFragment.kt index ca71d33501..ab33d7cdb3 100644 --- a/modules/mogo-module-left-panel/src/main/java/com/zhidao/mogo/module/left/panel/fragment/SimpleSpeedFragment.kt +++ b/modules/mogo-module-left-panel/src/main/java/com/zhidao/mogo/module/left/panel/fragment/SimpleSpeedFragment.kt @@ -10,7 +10,7 @@ import kotlinx.android.synthetic.main.module_left_panel_simple_speed.* * * @author tongchenfei */ -private const val SPEED_THRESHOLD = 90 +private const val SPEED_THRESHOLD = 40 class SimpleSpeedFragment: MvpFragment() { override fun getLayoutId(): Int { return R.layout.module_left_panel_simple_speed diff --git a/modules/mogo-module-left-panel/src/main/res/layout/module_left_panel_simple_speed.xml b/modules/mogo-module-left-panel/src/main/res/layout/module_left_panel_simple_speed.xml index ccd9e3ac69..7d8df35030 100644 --- a/modules/mogo-module-left-panel/src/main/res/layout/module_left_panel_simple_speed.xml +++ b/modules/mogo-module-left-panel/src/main/res/layout/module_left_panel_simple_speed.xml @@ -29,4 +29,16 @@ android:textColor="#fff" android:background="@drawable/module_left_panel_normal_speed_bg" /> + + \ No newline at end of file diff --git a/modules/mogo-module-left-panel/src/main/res/values/dimens.xml b/modules/mogo-module-left-panel/src/main/res/values/dimens.xml index 7c45135ef1..497d822e85 100644 --- a/modules/mogo-module-left-panel/src/main/res/values/dimens.xml +++ b/modules/mogo-module-left-panel/src/main/res/values/dimens.xml @@ -2,4 +2,7 @@ 80px 69.6px + 20px + 28px + \ No newline at end of file