| | |
| | | package com.driver.igotrip.ui.main |
| | | |
| | | import android.Manifest |
| | | import android.annotation.SuppressLint |
| | | import android.content.ActivityNotFoundException |
| | | import android.content.Intent |
| | | import android.content.pm.PackageManager |
| | | import android.os.Build |
| | | import android.os.Bundle |
| | | import android.os.Handler |
| | | import android.os.Looper |
| | | import android.os.Message |
| | | import android.provider.Settings |
| | | import android.util.Log |
| | | import android.view.View |
| | | import android.widget.ImageView |
| | | import android.widget.LinearLayout |
| | | import android.widget.TextView |
| | | import android.widget.Toast |
| | | import androidx.core.app.ActivityCompat |
| | | import androidx.core.net.toUri |
| | | import cn.sinata.rxnetty.LogHelper |
| | | import cn.sinata.rxnetty.NettyClient |
| | | import cn.sinata.xldutils.utils.SPUtils |
| | | import cn.sinata.xldutils.utils.Utils |
| | | import cn.sinata.xldutils.utils.clickDelay |
| | | import cn.sinata.xldutils.utils.doubleOne |
| | | import cn.sinata.xldutils.utils.getVersionName |
| | | import cn.sinata.xldutils.utils.gone |
| | | import cn.sinata.xldutils.utils.setDrawableLeft |
| | | import cn.sinata.xldutils.utils.textColor |
| | | import com.driver.igotrip.BuildConfig |
| | | import com.driver.igotrip.R |
| | | import com.driver.igotrip.base.BaseEvent |
| | | import com.driver.igotrip.base.Const |
| | | import com.driver.igotrip.base.MyApplication |
| | | import com.driver.igotrip.base.MyBaseActivity |
| | | import com.driver.igotrip.base.OrderTimeOutManager |
| | | import com.driver.igotrip.base.gaode.AMapKit |
| | | import com.driver.igotrip.bean.BooleanBean |
| | | import com.driver.igotrip.bean.HomeDataBean |
| | |
| | | import com.driver.igotrip.netUtls.Api |
| | | import com.driver.igotrip.netUtls.callNet |
| | | import com.driver.igotrip.netUtls.getMapByAny |
| | | import com.driver.igotrip.service.LocationForegroundService |
| | | import com.driver.igotrip.ui.DialogUtil |
| | | import com.driver.igotrip.ui.DialogUtil.getDelAndSureDialog |
| | | import com.driver.igotrip.ui.main.event.MineEventActivity |
| | |
| | | import com.driver.mask_talk.utils.toTimeEnglish |
| | | import com.driver.mask_talk.utils.toTimeFrench |
| | | import com.google.android.gms.maps.GoogleMap |
| | | import com.google.android.gms.maps.SupportMapFragment |
| | | import com.google.android.gms.maps.model.LatLng |
| | | import com.google.gson.Gson |
| | | import com.tbruyelle.rxpermissions2.RxPermissions |
| | | import com.wang.avi.AVLoadingIndicatorView |
| | |
| | | } |
| | | |
| | | var homeDataBean: HomeDataBean? = null |
| | | /* var navigator: Navigator? = null |
| | | var vehicleReporter: RidesharingVehicleReporter? = null*/ |
| | | /* var navigator: Navigator? = null |
| | | var vehicleReporter: RidesharingVehicleReporter? = null*/ |
| | | |
| | | private val MSG_REFRESH_LOCATIAON = 1 |
| | | |
| | |
| | | private val tv_today_time by lazy { find<TextView>(R.id.tv_today_time) } |
| | | private val tv_week_time by lazy { find<TextView>(R.id.tv_week_time) } |
| | | private val tv_month_time by lazy { find<TextView>(R.id.tv_month_time) } |
| | | private val map_view by lazy { supportFragmentManager.findFragmentById(R.id.map_view) } |
| | | private val avv_anim by lazy { find<AVLoadingIndicatorView>(R.id.avv_anim) } |
| | | private val tv_up_avv by lazy { find<TextView>(R.id.tv_up_avv) } |
| | | private val tv_order_num by lazy { find<TextView>(R.id.tv_order_num) } |
| | |
| | | |
| | | override fun setContentView() { |
| | | setContentView(R.layout.activity_main) |
| | | /* lifecycleScope.launch { |
| | | while (true){ |
| | | delay(5000) |
| | | var map = getMapByAny() |
| | | callNet(false,getDriverOrderMessage, map) { |
| | | Log.e("长连接接口消息: ",it.toString() ) |
| | | } |
| | | } |
| | | }*/ |
| | | /* lifecycleScope.launch { |
| | | while (true){ |
| | | delay(5000) |
| | | var map = getMapByAny() |
| | | callNet(false,getDriverOrderMessage, map) { |
| | | Log.e("长连接接口消息: ",it.toString() ) |
| | | } |
| | | } |
| | | }*/ |
| | | } |
| | | |
| | | override fun initView() { |
| | | // setTitleText(getString(R.string.IGO司机)) |
| | | // setTitleText(getString(R.string.IGO司机)) |
| | | tv_Left.setDrawableLeft(R.mipmap.icon_nav_user) |
| | | // iv_right_title.setImageResource(R.mipmap.icon_nav_news) |
| | | // iv_right_title.setImageResource(R.mipmap.icon_nav_news) |
| | | |
| | | findViewById<View>(R.id.img_leftuser).setOnClickListener { |
| | | startActivity<MineActivity>() |
| | |
| | | // showOrder("","12634","1",10,1) |
| | | } |
| | | |
| | | // initDriver() |
| | | // initDriver() |
| | | MyApplication.isLogin = true |
| | | beOnDuty(false) |
| | | initCall() |
| | |
| | | /** |
| | | * 初始化司机sdk |
| | | */ |
| | | /* private fun initDriver() { |
| | | if (CacheKey.getKeyStr("vehicleId").isNullOrEmpty()) |
| | | return |
| | | NavigationApi.getNavigator( |
| | | this, // Activity |
| | | object : NavigationApi.NavigatorListener { |
| | | override fun onError(p0: Int) { |
| | | Log.e(Const.GoogleDriver.TAG, "初始化司机sdk失败$p0") |
| | | } |
| | | /* private fun initDriver() { |
| | | if (CacheKey.getKeyStr("vehicleId").isNullOrEmpty()) |
| | | return |
| | | NavigationApi.getNavigator( |
| | | this, // Activity |
| | | object : NavigationApi.NavigatorListener { |
| | | override fun onError(p0: Int) { |
| | | Log.e(Const.GoogleDriver.TAG, "初始化司机sdk失败$p0") |
| | | } |
| | | |
| | | override fun onNavigatorReady(navigator: Navigator) { |
| | | // Keep a reference to the Navigator (used to configure and start nav) |
| | | this@MainActivity.navigator = navigator |
| | | Log.e(Const.GoogleDriver.TAG, "初始化司机sdk成功") |
| | | val driverContext = |
| | | DriverContext.builder(application) |
| | | //.setProviderId("i-go-odrd-testing") |
| | | .setProviderId("gopost-475301") |
| | | .setVehicleId(CacheKey.getKeyStr("vehicleId")) |
| | | .setAuthTokenFactory(JsonAuthTokenFactory()) |
| | | .setNavigator(navigator) |
| | | .setRoadSnappedLocationProvider( |
| | | NavigationApi.getRoadSnappedLocationProvider( |
| | | application |
| | | ) |
| | | ) |
| | | .setDriverStatusListener(this@MainActivity) |
| | | .build() |
| | | val ridesharingDriverApi = RidesharingDriverApi.createInstance(driverContext) |
| | | vehicleReporter = ridesharingDriverApi.ridesharingVehicleReporter |
| | | vehicleReporter!!.enableLocationTracking() |
| | | } |
| | | } |
| | | ) |
| | | }*/ |
| | | override fun onNavigatorReady(navigator: Navigator) { |
| | | // Keep a reference to the Navigator (used to configure and start nav) |
| | | this@MainActivity.navigator = navigator |
| | | Log.e(Const.GoogleDriver.TAG, "初始化司机sdk成功") |
| | | val driverContext = |
| | | DriverContext.builder(application) |
| | | //.setProviderId("i-go-odrd-testing") |
| | | .setProviderId("gopost-475301") |
| | | .setVehicleId(CacheKey.getKeyStr("vehicleId")) |
| | | .setAuthTokenFactory(JsonAuthTokenFactory()) |
| | | .setNavigator(navigator) |
| | | .setRoadSnappedLocationProvider( |
| | | NavigationApi.getRoadSnappedLocationProvider( |
| | | application |
| | | ) |
| | | ) |
| | | .setDriverStatusListener(this@MainActivity) |
| | | .build() |
| | | val ridesharingDriverApi = RidesharingDriverApi.createInstance(driverContext) |
| | | vehicleReporter = ridesharingDriverApi.ridesharingVehicleReporter |
| | | vehicleReporter!!.enableLocationTracking() |
| | | } |
| | | } |
| | | ) |
| | | }*/ |
| | | |
| | | override fun onRestart() { |
| | | super.onRestart() |
| | |
| | | } |
| | | |
| | | private fun callTime(type: String) { |
| | | OrderTimeOutManager.instance.init() |
| | | var map = getMapByAny() |
| | | callNet(Api.getDriverOnlineTime, map) { |
| | | var bean = gson.fromJson<TimeBean>(it, TimeBean::class.java) |
| | |
| | | } |
| | | } |
| | | |
| | | var qeeOrderView = object : MyApplication.OrderStatueView { |
| | | private var qeeOrderView = object : MyApplication.OrderStatueView { |
| | | override fun orderInfo(data: OrderSimpleData?) { |
| | | data?.let { |
| | | when (it.orderType) { |
| | |
| | | |
| | | private fun checkGps() { |
| | | if (!AMapKit.isLocServiceEnable(this)) { |
| | | DialogUtil.getDelAndSureDialog(this, |
| | | DialogUtil.getDelAndSureDialog( |
| | | this, |
| | | getString(R.string.查看定位), |
| | | getString(R.string.退出), |
| | | getString( |
| | | R.string.您暂未开启手机定位 |
| | | ), |
| | | { |
| | | onBackPressed() |
| | | onBackPressedHandle() |
| | | }) { |
| | | val intent = Intent() |
| | | intent.action = Settings.ACTION_LOCATION_SOURCE_SETTINGS; |
| | |
| | | // if (!MyUtils.canBackgroundStart(this)) { |
| | | // DialogUtil.getDelAndSureDialog(this, getString(R.string.前往开启), getString(R.string.退出), getString( |
| | | // R.string.可能导致无法接单), { |
| | | // onBackPressed() |
| | | // onBackPressedHandle() |
| | | // }) { |
| | | // MyUtils.toSettingPro(this) |
| | | // } |
| | |
| | | } |
| | | |
| | | private fun callVersion() { |
| | | var map = getMapByAny() |
| | | val map = getMapByAny() |
| | | // map["id"] = CacheKey.getUserId() |
| | | callNet(Api.queryNewData, map) { |
| | | var data = gson.fromJson<VersionBean>(it, VersionBean::class.java) |
| | | callNet(isUseAllTrustService = true, showDialog = true, api = Api.queryNewData, map = map, clickAction = { |
| | | val data = gson.fromJson<VersionBean>(it, VersionBean::class.java) |
| | | if (data.data.version != "" && data.data.version != getVersionName()) { |
| | | if (data.data.mandatory == 2) { |
| | | DialogUtil.getDelAndSureDialog( |
| | | this, |
| | | getString(R.string.新版本更新), |
| | | data.data.content, |
| | | {}, |
| | | { |
| | | browse(data.data.url) |
| | | }) |
| | | } else if (data.data.mandatory == 1) { |
| | | DialogUtil.getDelAndSureDialog( |
| | | this, |
| | | getString(R.string.新版本更新), |
| | | data.data.content, |
| | | { |
| | | EventBus.getDefault().post(BaseEvent(BaseEvent.OUT_APP)) |
| | | }, |
| | | { |
| | | browse(data.data.url) |
| | | }) |
| | | } else { |
| | | // toast("已经是最新版本") |
| | | when (data.data.mandatory) { |
| | | 2 -> { |
| | | DialogUtil.getDelAndSureDialog( |
| | | this, |
| | | getString(R.string.新版本更新), |
| | | data.data.content, |
| | | {}, |
| | | { |
| | | browse(data.data.url) |
| | | }) |
| | | } |
| | | |
| | | 1 -> { |
| | | DialogUtil.getDelAndSureDialog( |
| | | this, |
| | | getString(R.string.新版本更新), |
| | | data.data.content, |
| | | { |
| | | EventBus.getDefault().post(BaseEvent(BaseEvent.OUT_APP)) |
| | | }, |
| | | { |
| | | browse(data.data.url) |
| | | }) |
| | | } |
| | | |
| | | else -> { |
| | | // toast("已经是最新版本") |
| | | } |
| | | } |
| | | } else { |
| | | // toast("已经是最新版本") |
| | | } |
| | | }) { _, _ -> |
| | | |
| | | } |
| | | } |
| | | |
| | | lateinit var aMap: GoogleMap |
| | | override fun onCreate(savedInstanceState: Bundle?) { |
| | | super.onCreate(savedInstanceState) |
| | | if (CacheKey.getKeyStr(Const.REFUSE_LOCATION) |
| | | .isNotEmpty() && !RxPermissions(this).isGranted( |
| | | Manifest.permission.ACCESS_FINE_LOCATION |
| | | ) |
| | | if (CacheKey.getKeyStr(Const.REFUSE_LOCATION).isNotEmpty() && |
| | | !RxPermissions(this).isGranted(Manifest.permission.ACCESS_FINE_LOCATION) |
| | | ) { |
| | | toast("Without positioning permission, you will not be able to receive new order push notifications") |
| | | toast(getString(R.string.没有定位权限提示)) |
| | | } else { |
| | | if (!RxPermissions(this).isGranted(Manifest.permission.ACCESS_FINE_LOCATION)) { |
| | | DialogUtil.getDelAndSureDialog( |
| | |
| | | getString(R.string.拒绝权限), |
| | | getString(R.string.定位权限说明), |
| | | { |
| | | LogHelper.logRecord("用户拒绝定位权限1") |
| | | }) { |
| | | val subscribe = |
| | | RxPermissions(this).request(Manifest.permission.ACCESS_FINE_LOCATION) |
| | | .subscribe { |
| | | .subscribe { it -> |
| | | if (!it) { |
| | | toast("Without positioning permission, you will not be able to receive new order push notifications") |
| | | toast(getString(R.string.没有定位权限提示)) |
| | | CacheKey.putKeyStr(Const.REFUSE_LOCATION, "1") |
| | | } else |
| | | initLocation() |
| | | LogHelper.logRecord("用户拒绝定位权限2") |
| | | } else { |
| | | val subscribe = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { |
| | | RxPermissions(this).request(Manifest.permission.ACCESS_BACKGROUND_LOCATION) |
| | | .subscribe { |
| | | if (!it) { |
| | | toast(getString(R.string.没有定位权限提示)) |
| | | CacheKey.putKeyStr(Const.REFUSE_LOCATION, "1") |
| | | LogHelper.logRecord("用户拒绝定位权限3") |
| | | } else { |
| | | initLocation() |
| | | initFloatingWindow() |
| | | } |
| | | } |
| | | } else { |
| | | initLocation() |
| | | initFloatingWindow() |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } else |
| | | } else { |
| | | initLocation() |
| | | } |
| | | } |
| | | |
| | | @SuppressLint("MissingPermission", "CheckResult") |
| | | private fun initLocation() { |
| | | val fragment = map_view as SupportMapFragment |
| | | AMapKit.initMap(fragment) { |
| | | // zmc-remark 测试代码 给个初始位置 |
| | | // MyApplication.aMapLocation = LatLng(30.659221715540937,104.06676612263988) |
| | | |
| | | aMap = it |
| | | RxPermissions(this).request(Manifest.permission.ACCESS_FINE_LOCATION).subscribe { |
| | | if (it) { |
| | | aMap.isMyLocationEnabled = true |
| | | aMap.setOnMyLocationChangeListener { |
| | | it.let { latlon -> |
| | | Log.e("定位==============》", "${it.latitude},${it.longitude}") |
| | | MyApplication.aMapLocation = LatLng(it.latitude, it.longitude) |
| | | } |
| | | } |
| | | |
| | | |
| | | // val fusedLocationClient = LocationServices.getFusedLocationProviderClient(this) |
| | | // val locationRequest = LocationRequest.create().apply { |
| | | // interval = 10000 // 10秒间隔 |
| | | // priority = LocationRequest.PRIORITY_HIGH_ACCURACY |
| | | // } |
| | | // |
| | | // fusedLocationClient.requestLocationUpdates(locationRequest, |
| | | // object : LocationCallback() { |
| | | // override fun onLocationResult(it: LocationResult) { |
| | | // // 处理位置更新 |
| | | // Log.e("定位 by fusedLocationClient ==============》","${it.lastLocation?.latitude},${it.lastLocation?.longitude}") |
| | | // } |
| | | // }, Looper.getMainLooper() |
| | | // ) |
| | | |
| | | } else { |
| | | |
| | | } |
| | | initFloatingWindow() |
| | | } |
| | | } |
| | | } |
| | | |
| | | //初始化定位 |
| | | private fun initLocation() { |
| | | if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { |
| | | return |
| | | } |
| | | LocationForegroundService.start() |
| | | } |
| | | |
| | | //初始化悬浮窗 |
| | | private fun initFloatingWindow() { |
| | | if (!Settings.canDrawOverlays(this) && CacheKey.getKeyStr(Const.REFUSE_FLOATING_WINDOW) != "1") { |
| | | DialogUtil.getDelAndSureDialog( |
| | | this, |
| | | getString(R.string.确认), |
| | | getString(R.string.拒绝权限), |
| | | getString(R.string.悬浮窗说明), |
| | | { |
| | | CacheKey.putKeyStr(Const.REFUSE_FLOATING_WINDOW, "1") |
| | | }) { |
| | | val intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, "package:${packageName}".toUri()).apply { |
| | | putExtra("package", packageName) |
| | | } |
| | | startActivity(intent) |
| | | } |
| | | } |
| | | } |
| | | |
| | | override fun onResume() { |
| | | super.onResume() |
| | | MyApplication.couldShowFloatingWindow = Settings.canDrawOverlays(this) && CacheKey.getKeyStr(Const.REFUSE_FLOATING_WINDOW) != "1" |
| | | mainFragment?.refresh() |
| | | callNet(Api.needToSettle, getMapByAny()) { |
| | | var bean = gson.fromJson(it, BooleanBean::class.java).data |
| | |
| | | |
| | | fun beOnDuty(b: Boolean) { |
| | | if (b) { |
| | | // avv_anim.visible() |
| | | // tv_up_avv.visible() |
| | | // avv_anim.visible() |
| | | // tv_up_avv.visible() |
| | | } else { |
| | | avv_anim.gone() |
| | | tv_up_avv.gone() |
| | |
| | | tv_lien_name.text = data.data.brand.toString() |
| | | tv_car.text = |
| | | if (data.data.work == 1) getString(R.string.下班) else getString(R.string.出车) |
| | | MyApplication.isWorking = data.data.work == 1 |
| | | if (data.data.work == 1) { |
| | | /*try { |
| | | vehicleReporter?.setVehicleState(RidesharingVehicleReporter.VehicleState.ONLINE) |
| | |
| | | clickCallOrder() |
| | | } |
| | | |
| | | private fun clickCallOrder(){ |
| | | private fun clickCallOrder() { |
| | | tv_car.clickDelay { |
| | | if (homeDataBean?.data?.work == 1) { |
| | | callNet(Api.work, getMapByAny()) { |
| | | homeDataBean?.data?.work = 2 |
| | | tv_car.text = getString(R.string.出车) |
| | | MyApplication.isWorking = false |
| | | beOnDuty(false) |
| | | val bean = Gson().fromJson<UpCarInfo>(it, UpCarInfo::class.java) |
| | | MyApplication.getTTsManager().setVideoText(bean.data?.audioUrl) |
| | |
| | | it.find<TextView>(R.id.tv_hint_base).text = warn |
| | | } |
| | | } |
| | | |
| | | private fun showPayDialog() { |
| | | val l = SPUtils.getString(Const.language) |
| | | val pay:String |
| | | val off:String |
| | | val msg:String |
| | | val warn:String |
| | | when(l){ |
| | | Const.Language.FRENCH->{ |
| | | val pay: String |
| | | val off: String |
| | | val msg: String |
| | | val warn: String |
| | | when (l) { |
| | | Const.Language.FRENCH -> { |
| | | pay = "Payer" |
| | | off = "Fermer" |
| | | warn = "Rappel" |
| | | msg = "Vous avez des frais de règlement en attente de paiement, vous ne pouvez recevoir des commandes qu’après paiement, veuillez payer dès que possible" |
| | | msg = |
| | | "Vous avez des frais de règlement en attente de paiement, vous ne pouvez recevoir des commandes qu’après paiement, veuillez payer dès que possible" |
| | | } |
| | | Const.Language.CHINESE,"zh"->{ |
| | | |
| | | Const.Language.CHINESE, "zh" -> { |
| | | pay = "支付" |
| | | off = "关闭" |
| | | warn = "提示" |
| | | msg = "您有一笔结算费待支付,须支付后才能接单,请尽快支付" |
| | | } |
| | | else->{ |
| | | |
| | | else -> { |
| | | pay = "Pay" |
| | | off = "Close" |
| | | warn = "Reminder" |
| | | msg = "You have a settlement fee pending payment, you can only receive orders after payment, please pay as soon as possible" |
| | | msg = |
| | | "You have a settlement fee pending payment, you can only receive orders after payment, please pay as soon as possible" |
| | | } |
| | | } |
| | | val delAndSureDialog = getDelAndSureDialog(this, |
| | | val delAndSureDialog = getDelAndSureDialog( |
| | | this, |
| | | pay, |
| | | off, |
| | | msg, |
| | |
| | | private |
| | | var mBackAppTime: Long = 0 |
| | | |
| | | override fun onBackPressed() { |
| | | override fun onBackPressedHandle() { |
| | | if (System.currentTimeMillis() - mBackAppTime > 2000) { |
| | | Toast.makeText(this, getString(R.string.再按一次退出程序), Toast.LENGTH_SHORT).show() |
| | | mBackAppTime = System.currentTimeMillis() |
| | |
| | | when (event!!.code) { |
| | | BaseEvent.START_CAR -> { |
| | | beOnDuty(true) |
| | | /* if (vehicleReporter == null) { |
| | | initDriver() |
| | | }*/ |
| | | /* if (vehicleReporter == null) { |
| | | initDriver() |
| | | }*/ |
| | | initCall() |
| | | } |
| | | |
| | |
| | | mainFragment?.refresh() |
| | | } |
| | | |
| | | BaseEvent.OFF_LINE -> { |
| | | Thread() { |
| | | try { |
| | | /* vehicleReporter?.disableLocationTracking() |
| | | RidesharingDriverApi.clearInstance()*/ |
| | | } catch (e: Exception) { |
| | | } |
| | | }.start() |
| | | } |
| | | |
| | | BaseEvent.FORCED_OFFLINE -> { |
| | | initCall() |
| | | } |
| | |
| | | } |
| | | |
| | | |
| | | /* override fun updateStatus( |
| | | p0: DriverContext.DriverStatusListener.StatusLevel, |
| | | p1: DriverContext.DriverStatusListener.StatusCode, |
| | | p2: String, |
| | | p3: Throwable?, |
| | | ) { |
| | | Log.e( |
| | | Const.GoogleDriver.TAG, |
| | | "StatusLevel:${p0.name},StatusCode:${p1},p2:${p2},错误信息:${p3?.message}" |
| | | ) |
| | | } |
| | | /* override fun updateStatus( |
| | | p0: DriverContext.DriverStatusListener.StatusLevel, |
| | | p1: DriverContext.DriverStatusListener.StatusCode, |
| | | p2: String, |
| | | p3: Throwable?, |
| | | ) { |
| | | Log.e( |
| | | Const.GoogleDriver.TAG, |
| | | "StatusLevel:${p0.name},StatusCode:${p1},p2:${p2},错误信息:${p3?.message}" |
| | | ) |
| | | } |
| | | |
| | | */ |
| | | override fun onDestroy() { |
| | | MyApplication.couldShowFloatingWindow = false |
| | | LocationForegroundService.stop() |
| | | super.onDestroy() |
| | | RidesharingDriverApi.clearInstance() |
| | | }*/ |
| | | MyApplication.removeOrderView(qeeOrderView) |
| | | } |
| | | } |