调整探路目录关系

Signed-off-by: 董宏宇 <martindhy@gmail.com>
This commit is contained in:
董宏宇
2021-09-16 20:52:59 +08:00
parent 5d35caa1d4
commit 62bfbedaa3
57 changed files with 4 additions and 12 deletions

View File

@@ -0,0 +1,66 @@
package com.zhidao.roadcondition;
import android.content.Context;
import android.util.ArrayMap;
import com.alibaba.android.arouter.facade.annotation.Route;
import com.mogo.commons.analytics.AnalyticsUtils;
import com.mogo.map.location.MogoLocation;
import com.mogo.module.common.MogoApisHandler;
import com.mogo.service.share.IMogoTanluProvider;
import com.mogo.service.share.TanluUploadParams;
import com.mogo.utils.logger.Logger;
import com.zhidao.roadcondition.service.MainService;
import com.zhidao.roadcondition.service.UploadParams;
import java.util.Map;
import static com.zhidao.roadcondition.constant.ConstKt.API_MODULE_NAME;
import static com.zhidao.roadcondition.constant.ConstKt.API_MODULE_PATH;
/**
* 探路api
*
* @author tongchenfei
*/
@Route(path = API_MODULE_PATH)
public class MogoTanluApiProvider implements IMogoTanluProvider {
private Context context;
/**
* 上传情报
*
* @param params 情报类型
*/
@Override
public void uploadRoadCondition(TanluUploadParams params) {
if(params!=null) {
Logger.d(API_MODULE_NAME, "uploadRoadCondition: " + params);
Map<String, Object> properties = new ArrayMap<>();
properties.put("type", params.getEventType());
properties.put("from", params.getFromType());
AnalyticsUtils.track("v2x_share_type", properties);
MogoLocation location = MogoApisHandler.getInstance().getApis().getMapServiceApi().getSingletonLocationClient(context).getLastKnowLocation();
MainService.Companion.launchService(context, new UploadParams(params.getEventType(),
params.getFromType(), params.getDuration(), params.getParentId(),
params.getLocation().lat, params.getLocation().lon, location.getAddress(), location.getBearing(), location.getAdCode(), location.getCityCode()));
}else{
Logger.e(API_MODULE_NAME,"params为空无法上报情报");
}
}
/**
* 开启探路业务服务
*/
@Override
public void startTanluService() {
Logger.d(API_MODULE_NAME, "startTanluService");
MainService.Companion.launchService(context, null);
}
@Override
public void init(Context context) {
this.context = context;
Logger.d(API_MODULE_NAME,"新TanluApi模块 init====");
}
}

View File

@@ -0,0 +1,39 @@
package com.zhidao.roadcondition.base
import android.app.IntentService
import android.content.Intent
import android.os.IBinder
open class BaseIntentService : IntentService("BaseIntentService") {
protected var tag: String = this.javaClass.simpleName
override fun onHandleIntent(p0: Intent?) {
tag = this.javaClass.simpleName
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
return super.onStartCommand(intent, flags, startId)
}
override fun onCreate() {
super.onCreate()
}
override fun onStart(intent: Intent?, startId: Int) {
super.onStart(intent, startId)
}
override fun onBind(intent: Intent?): IBinder? {
return super.onBind(intent)
}
override fun setIntentRedelivery(enabled: Boolean) {
super.setIntentRedelivery(enabled)
}
override fun onDestroy() {
super.onDestroy()
}
}

View File

@@ -0,0 +1,10 @@
package com.zhidao.roadcondition.base
import com.zhidao.roadcondition.model.BaseResponse
open class BaseRepository {
suspend fun <T : Any> apiCall(call: suspend () -> BaseResponse<T>): BaseResponse<T> {
return call.invoke()
}
}

View File

@@ -0,0 +1,87 @@
package com.zhidao.roadcondition.constant
public val STRATEGY_TYPE_PHOTO: String = "0FOoOuB7" //拍照的策略ID
public val STRATEGY_TYPE_VIDEO: String = "gVCl9VdW" //拍视频的策略ID
public val SOCKET_MSG_TYPE: Int = 0x040001 //长连接消息类型
public val SOCKET_PRODUCT_LINE: Int = 0x04 //长连接消息产品线id目前SDK是写死的
const val INFORMATION_DATA_SIZE_LIMIT = 50
const val INFORMATION_DEFAULT_MAP_RADIUS = 10
const val REQUESTCODE_MAINACTIVITY = 0
const val REQUESTCODE_MEDIAACTIVITY = 1
const val CUSTOM_SEND_CMD = "custom_send_cmd" //主动上报语音命令
const val VOICE_PREVIOUS_INFO_CMD = "voice_previous_info_cmd" //上一条
const val VOICE_NEXT_INFO_CMD = "voice_next_info_cmd" //下一条
const val SCHEME_SPLASH_TYPE = "channelType"
const val SCHEME_MAIN_TYPE = "type"
const val PARAM_MAIN_PAGE_ACTION = "action" // 判断主页该干什么
const val VAL_ACTION_TQZ = "tqz" //通勤族
const val VAL_ACTION_PLAY_DIRECTLY = "play" //直接播放情报
const val VAL_ACTION_DHLX = "dhlx" //导航路线
const val CONFIG_NEED_AUTH = "0001" //公共配置 是否查询授权
const val CONFIG_ACTIVE_POSITION = "0002" //公共配置 查询运营位配置
//广播action、key
const val AUTONAVI_STANDARD_BROADCAST_SEND = "AUTONAVI_STANDARD_BROADCAST_SEND"
const val AUTONAVI_STANDARD_BROADCAST_ROAD_INFO = "EXTRA_ROAD_INFO"
const val AUTONAVI_STANDARD_BROADCAST_EXTRA_STATE = "EXTRA_STATE"
//EXTRA_STATE
const val AUTO_NAVI_START = 8 //开始导航
const val AUTO_NAVI_END = 9 //结束导航
const val VALUE_DICT_DATA_TYPE = "deva_infomation_type" // dict 数据请求类型:情报类型数据
//事件类型
const val TANLU_TRAFFIC_CHECK = "10002" //交通检查
const val TANLU_ROAD_CLOSURE = "10003" //封路
const val TANLU_ROAD_WORK = "10006" //施工
const val TANLU_ROAD_CONGESTION = "10007" //拥堵
const val TANLU_ROAD_PONDING = "10008" //积水
const val TANLU_ROAD_HEAVY_FOG = "10010" //大雾
const val TANLU_ROAD_ICING = "10011" //积冰
const val TANLU_ROAD_ACCIDENT = "10013" //事故
const val TANLU_ROAD_CURRENT = "10015" //实时路况
// 上报类型
/**
* 用户手点上报
*/
const val UPLOAD_FROM_USER = "1"
/**
* 用户语音上报
*/
const val UPLOAD_FROM_VOICE = "2"
/**
* 数据策略:拥堵自动上报
*/
const val UPLOAD_FROM_STRATEGY_BLOCK_AUTO = "3"
/**
* 数据策略:已有事件云端校验
*/
const val UPLOAD_FROM_STRATEGY_CLOUD_CHECK = "4"
/**
* 数据策略:交通事故上报
*/
const val UPLOAD_FROM_STRATEGY_ACCIDENT_AUTO = "5"
/**
* 策略上报集合
*/
val STRATEGY_UPLOAD_TYPE_ARRAY = arrayOf(UPLOAD_FROM_STRATEGY_BLOCK_AUTO,
UPLOAD_FROM_STRATEGY_CLOUD_CHECK, UPLOAD_FROM_STRATEGY_ACCIDENT_AUTO)
/**
* 默认视频抓取时长,单位是秒
*/
const val DEFAULT_VIDEO_DURATION = 10
const val DEF_NEWS_LABEL = "拥堵"
const val DEF_NEWS_VALUE = "traffic_jam"
const val DEF_NEWS_TYPE = "0"
const val API_MODULE_NAME = "MogoTanluApi"
const val API_MODULE_PATH = "/tanlulib/api"

View File

@@ -0,0 +1,24 @@
package com.zhidao.roadcondition.constant
import com.mogo.commons.debug.DebugConfig
class HttpConstants {
companion object {
const val DEV_BASE_URL_OWNER = "http://dzt-test.zhidaozhixing.com/"
const val RELEASE_BASE_URL_OWNER = "http://dzt.zhidaozhixing.com/"
const val SHOW_BASE_URL_OWNER = "http://dzt-show.zhidaozhixing.com/"
fun getBaseUrl(): String {
return when (DebugConfig.getNetMode()) {
DebugConfig.NET_MODE_QA -> DEV_BASE_URL_OWNER
DebugConfig.NET_MODE_RELEASE -> RELEASE_BASE_URL_OWNER
DebugConfig.NET_MODE_DEMO -> SHOW_BASE_URL_OWNER
else -> DEV_BASE_URL_OWNER
}
}
}
}

View File

@@ -0,0 +1,26 @@
package com.zhidao.roadcondition.event
/**
* @description
*
* @author lixiaopeng
* @since 2019-10-13
*/
class GetImageSuccessEvent {
private var imageUrl: String? = null
private var type: String? = null
constructor(imageUrl: String?, type: String?) {
this.imageUrl = imageUrl
this.type = type
}
fun getImageUrl(): String? {
return imageUrl
}
fun getType(): String? {
return type
}
}

View File

@@ -0,0 +1,18 @@
package com.zhidao.roadcondition.event
import org.greenrobot.eventbus.EventBus
class LatLngStickyEventBus {
companion object {
private var mEventBus = EventBus.builder()
.logNoSubscriberMessages(false)
.sendNoSubscriberEvent(false)
.build()
fun getInstance(): EventBus {
return mEventBus
}
}
}

View File

@@ -0,0 +1,19 @@
package com.zhidao.roadcondition.exception
class ApiException : Exception {
companion object{
val NETWORK_API_EXCEPTION = ApiException(1, "network is error")
val NULL_REQUEST_DATA_API_EXCEPTION = ApiException(2, "request data is null")
}
private var code: Int = 0
private var msg: String = ""
constructor(code: Int, msg: String) : super(msg) {
this.code = code
this.msg = msg
}
}

View File

@@ -0,0 +1,5 @@
package com.zhidao.roadcondition.model
class BaseRequest<T>(var sn: String, var data: T?) {
}

View File

@@ -0,0 +1,12 @@
package com.zhidao.roadcondition.model
import com.zhidao.roadcondition.constant.HttpConstants
class BaseResponse<out T>(val code: Int, val msg: String, val result: T) {
fun isSuccess(baseUrl: String): Boolean {
return when (baseUrl) {
HttpConstants.getBaseUrl() -> true
else -> false
}
}
}

View File

@@ -0,0 +1,91 @@
package com.zhidao.roadcondition.model
class CityStrategy(video: Video, pic: Pic) {
var video: Video? = video
var pic: Pic? = pic
}
fun Video.getMaxSpeed(): Int {
return if (maxSpeed == 0) {
-1
} else {
maxSpeed
}
}
class Video {
var strategyId: String //策略ID
var reportType: Int //一次性、周期性
var reportTimeInterval: Int //上传时间间隔 单位:分钟
var infoTimeout: Int //情报失效时间 单位:分钟
var minSpeed: Int = -1//最小速度
var maxSpeed: Int = -1//最大速度
var strategyType: Int //策略类型:城市、热门区域、非上报区域等
constructor(
strategyId: String,
reportType: Int,
reportTimeInterval: Int,
infoTimeout: Int,
minSpeed: Int,
maxSpeed: Int,
strategyType: Int
) {
this.strategyId = strategyId
this.reportType = reportType
this.reportTimeInterval = reportTimeInterval
this.infoTimeout = infoTimeout
this.minSpeed = minSpeed
this.maxSpeed = maxSpeed
this.strategyType = strategyType
}
}
fun Pic.getMaxSpeed(): Int {
return if (maxSpeed == 0) {
-1
} else {
maxSpeed
}
}
class Pic {
var strategyId: String //策略ID
var reportType: Int //一次性、周期性
var reportTimeInterval: Int //上传时间间隔 单位:分钟
var infoTimeout: Int //情报失效时间 单位:分钟
var minSpeed: Int = -1 //最小速度
var maxSpeed: Int = -1 //最大速度
var strategyType: Int //策略类型:城市、热门区域、非上报区域等
constructor(
strategyId: String,
reportType: Int,
reportTimeInterval: Int,
infoTimeout: Int,
minSpeed: Int,
maxSpeed: Int,
strategyType: Int
) {
this.strategyId = strategyId
this.reportType = reportType
this.reportTimeInterval = reportTimeInterval
this.infoTimeout = infoTimeout
this.minSpeed = minSpeed
this.maxSpeed = maxSpeed
this.strategyType = strategyType
}
}
class StrategyRequest {
var lon: Double
var lat: Double
var cityCode: String
constructor(lon: Double, lat: Double, cityCode: String) {
this.lon = lon
this.lat = lat
this.cityCode = cityCode
}
}

View File

@@ -0,0 +1,47 @@
package com.zhidao.roadcondition.model
class CommonConfig {
var active:Active //活动配置
// var promotion:Promotion //Splash页面图片以及语音推广配置
var auth:Auth //授权配置
constructor(active: Active, auth: Auth) {
this.active = active
// this.promotion = promotion
this.auth = auth
}
}
class Active{
var imageUrl:String
var webUrl:String
var status:Int
constructor(imageUrl: String, webUrl: String, status: Int) {
this.imageUrl = imageUrl
this.webUrl = webUrl
this.status = status
}
}
class Promotion{
var imageUrl:String
var voiceContent:String
var status:Int
constructor(imageUrl: String, voiceContent: String, status: Int) {
this.imageUrl = imageUrl
this.voiceContent = voiceContent
this.status = status
}
}
class Auth{
var isNeedAuth:Int
constructor(isNeedAuth: Int) {
this.isNeedAuth = isNeedAuth
}
}

View File

@@ -0,0 +1,27 @@
package com.zhidao.roadcondition.model
data class InformationBody(
val data: String,
val addr: String,
val areaCode: String,
val areaName: String,
val cityCode: String,
val cityName: String,
val generateTime: Long,
val lat: Double,
val lon: Double,
val provinceName: String,
val sn: String,
val street: String,
val type: Int,
val uid: Int,
val infoType: Int,
val infoTimeout:Int,
val trafficInfoType:String, // 上报情报类型
val isShare: Boolean, // 是否分享给附近车机
val direction: Float,
val poiType: String, //类型分类
val mainInfoId: Long, //事件id
val speed: Float, //车速
val fromType: String //上报触发来源
)

View File

@@ -0,0 +1,41 @@
package com.zhidao.roadcondition.model
/**
* @author congtaowang
* @since 2019-11-22
*
* 一次情报的数据
*/
class InformationResource {
var id: Long
var isCosResourceReady: Boolean = false
var isInformationSelected: Boolean = false
var sourceType: Int = 0 // 情报载体类型:图片、视频
var cosParameter: Map<String, String>? = null // 情报参数
var informationType: InformationType? = null //情报类型
var isCustomSend: Boolean = false // 自动上传
var callback: ((customSend: Boolean) -> Unit)? = null
lateinit var newsType: String // 埋点:情报类型
lateinit var operType: String // 埋点:操作类型
constructor(id: Long) {
this.id = id
}
fun isReady() = isCosResourceReady and isInformationSelected
fun release() {
cosParameter = null
informationType = null
callback = null
}
override fun toString(): String {
return "upload information: sourceType=${sourceType}, informationType={${informationType?.dictLabel}, ${informationType?.dictValue}}"
}
}

View File

@@ -0,0 +1,29 @@
package com.zhidao.roadcondition.model
import com.google.gson.Gson
import com.zhidao.roadcondition.constant.DEF_NEWS_LABEL
import com.zhidao.roadcondition.constant.DEF_NEWS_TYPE
import com.zhidao.roadcondition.constant.DEF_NEWS_VALUE
/**
* @author congtaowang
* @since 2019-11-18
*
* 情报类型
*/
data class InformationType(var dictLabel: String, var dictValue: String, var remark: String) {
companion object {
val def = InformationType(DEF_NEWS_LABEL, DEF_NEWS_VALUE, DEF_NEWS_TYPE)
}
override fun equals(other: Any?): Boolean {
when (other) {
is InformationType -> return other.dictValue.compareTo(dictValue) == 0
}
return super.equals(other)
}
override fun toString(): String {
return Gson().toJson(this)
}
}

View File

@@ -0,0 +1,9 @@
package com.zhidao.roadcondition.model
/**
* @author congtaowang
* @since 2019-11-18
*
* 描述
*/
data class InformationTypeResult(var types: List<InformationType>)

View File

@@ -0,0 +1,63 @@
package com.zhidao.roadcondition.model
import android.os.Parcel
import android.os.Parcelable
class Informations(
var type: Int,
var lon: Double,
var lat: Double,
var addr: String?,
var generateTime: Long,
var cityName: String?,
// var items: ArrayList<Items>,
var distance: Int,
var nickName: String?,
var headImgUrl: String?
) :
Parcelable {
var position = 0
constructor(parcel: Parcel) : this(
parcel.readInt(),
parcel.readDouble(),
parcel.readDouble(),
parcel.readString(),
parcel.readLong(),
parcel.readString(),
// parcel.readArrayList(Items::class.java.classLoader) as ArrayList<Items>,
parcel.readInt(),
parcel.readString(),
parcel.readString()
)
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeInt(type)
parcel.writeDouble(lon)
parcel.writeDouble(lat)
parcel.writeString(addr)
parcel.writeLong(generateTime)
parcel.writeString(cityName)
// parcel.writeList(items)
parcel.writeInt(distance)
parcel.writeString(nickName)
parcel.writeString(headImgUrl)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<Informations> {
override fun createFromParcel(parcel: Parcel): Informations {
return Informations(parcel)
}
override fun newArray(size: Int): Array<Informations?> {
return arrayOfNulls(size)
}
}
}

View File

@@ -0,0 +1,33 @@
package com.zhidao.roadcondition.model
import android.os.Parcel
import android.os.Parcelable
class Items(var url: String?, var thumbnail: String? = null) : Parcelable {
constructor(parcel: Parcel) : this(
parcel.readString(),
parcel.readString()
) {
}
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeString(url)
parcel.writeString(thumbnail)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<Items> {
override fun createFromParcel(parcel: Parcel): Items {
return Items(parcel)
}
override fun newArray(size: Int): Array<Items?> {
return arrayOfNulls(size)
}
}
}

View File

@@ -0,0 +1,56 @@
package com.zhidao.roadcondition.model
import com.mogo.map.MogoLatLng
fun LocationInfo.toLatLngPoint():MogoLatLng{
return MogoLatLng(latitude,longitude)
}
class LocationInfo {
var provinceName: String = "" //省的名称
var cityName: String = "" //城市的名称
var cityCode :String = "" //城市编码
var areaName :String = "" //区县名称
var areaCode :String = "" //区县编码
var street :String = "" //街道名称
var longitude: Double = 0.0
var latitude: Double = 0.0
var address: String = ""
var time: Long = 0L
var direction: Float = 0.0f
constructor()
constructor(longitude: Double, latitude: Double, address: String, time: Long) {
this.longitude = longitude
this.latitude = latitude
this.address = address
this.time = time
}
constructor(
provinceName: String,
cityName: String,
cityCode: String,
areaName: String,
areaCode: String,
street: String,
longitude: Double,
latitude: Double,
address: String,
time: Long,
direction: Float = 0.0f
) {
this.provinceName = provinceName
this.cityName = cityName
this.cityCode = cityCode
this.areaName = areaName
this.areaCode = areaCode
this.street = street
this.longitude = longitude
this.latitude = latitude
this.address = address
this.time = time
this.direction = direction
}
}

View File

@@ -0,0 +1,6 @@
package com.zhidao.roadcondition.model
/**
* 城市策略实体
*/
class Results(var cityStrategy: CityStrategy)

View File

@@ -0,0 +1,37 @@
package com.zhidao.roadcondition.model
import android.util.Log
import com.google.gson.Gson
import com.mogo.cloud.passport.MoGoAiCloudClientConfig
import com.mogo.module.common.constants.HostConst
import com.zhidao.roadcondition.base.BaseRepository
import com.zhidao.roadcondition.net.HttpClient
import com.zhidao.roadcondition.util.LocationUtil
class StrategyServiceModel : BaseRepository() {
suspend fun getCityStrategy(): BaseResponse<Results> {
return apiCall {
val map = hashMapOf<String, String>()
map["sn"] = MoGoAiCloudClientConfig.getInstance().getSn()
val locInfo = LocationUtil.getInstance().getLocationInfo()
map["data"] = Gson().toJson(
StrategyRequest(
locInfo.longitude,
locInfo.latitude,
locInfo.cityCode
)
)
HttpClient.getInstance(HostConst.GEOFENCE_HOST).getHttpApi().getCityStrategy(map)
}
}
suspend fun uploadInformation(informationBody: InformationBody): BaseResponse<UploadResult> {
return apiCall {
val informationBodyStr = Gson().toJson(informationBody)
Log.d("MainServiceController", "uploadInformation informationBody = $informationBodyStr")
HttpClient.getInstance(HostConst.DEVA_HOST).getHttpApi()
.uploadInformation(mapOf("sn" to MoGoAiCloudClientConfig.getInstance().getSn(), "data" to informationBodyStr))
}
}
}

View File

@@ -0,0 +1,8 @@
package com.zhidao.roadcondition.model
/**
* 上报成功返回
*/
class UploadResult(var id: Long) {
}

View File

@@ -0,0 +1,19 @@
package com.zhidao.roadcondition.model.proxy
import androidx.annotation.IntDef
const val INFO_TYPE_GONE = 0
const val INFO_TYPE_SHOW = 1
@IntDef(INFO_TYPE_GONE, INFO_TYPE_SHOW)
@Retention(AnnotationRetention.SOURCE)
annotation class ActiveInfoType
fun isActiveShow(@ActiveInfoType type: Int): Boolean {
return when (type) {
INFO_TYPE_GONE -> false
INFO_TYPE_SHOW -> true
else -> false
}
}

View File

@@ -0,0 +1,14 @@
package com.zhidao.roadcondition.model.proxy
import androidx.annotation.IntDef
const val INFO_TYPE_IMG = 0 //图片
const val INFO_TYPE_VIDEO = 1
const val INFO_TYPE_VOICE = 2
const val INFO_TYPE_WORD = 3
@IntDef(INFO_TYPE_IMG, INFO_TYPE_VIDEO, INFO_TYPE_VOICE, INFO_TYPE_WORD)
@Retention(AnnotationRetention.SOURCE)
annotation class InformationsType

View File

@@ -0,0 +1,71 @@
package com.zhidao.roadcondition.net
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.OnLifecycleEvent
import com.zhidao.roadcondition.exception.ApiException
import kotlinx.coroutines.*
import kotlinx.coroutines.Dispatchers.Main
import java.net.SocketTimeoutException
import java.net.UnknownHostException
import java.util.concurrent.TimeoutException
/**
* 后续考虑加入Lifecycle管理暂时不做扩展
*/
class CoroutineChain {
internal class CoroutineLifecycleListener(private val deferred: Deferred<*>, private val lifecycle: Lifecycle) :
LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
fun cancelCoroutine() {
if (deferred.isActive) {
deferred.cancel()
}
lifecycle.removeObserver(this)
}
}
infix fun LifecycleOwner.start(start: (() -> Unit)): LifecycleOwner {
start()
return this
}
infix fun <T> LifecycleOwner.request(loader: suspend () -> T): Deferred<T> {
return request(loader, true)
}
private fun <T> LifecycleOwner.request(loader: suspend () -> T, needAutoCancel: Boolean = true): Deferred<T> {
val deferred = GlobalScope.async(Dispatchers.IO, start = CoroutineStart.LAZY) {
loader()
}
if (needAutoCancel) {
lifecycle.addObserver(CoroutineLifecycleListener(deferred, lifecycle))
}
return deferred
}
fun <T> Deferred<T>.then(
onSuccess: suspend (T) -> Unit,
onError: suspend (java.lang.Exception) -> Unit,
onComplete: (() -> Unit)? = null
): Job {
return GlobalScope.launch(context = Main) {
try {
val result = this@then.await()
onSuccess(result)
} catch (e: Exception) {
e.printStackTrace()
when (e) {
is UnknownHostException -> onError.invoke(ApiException.NETWORK_API_EXCEPTION)
is TimeoutException -> onError.invoke(ApiException.NETWORK_API_EXCEPTION)
is SocketTimeoutException -> onError.invoke(ApiException.NETWORK_API_EXCEPTION)
else -> onError(e)
}
} finally {
onComplete?.invoke()
}
}
}
}

View File

@@ -0,0 +1,104 @@
package com.zhidao.roadcondition.net
import androidx.lifecycle.LifecycleOwner
import com.zhidao.roadcondition.exception.ApiException
import com.zhidao.roadcondition.exception.ApiException.Companion.NETWORK_API_EXCEPTION
import com.zhidao.roadcondition.exception.ApiException.Companion.NULL_REQUEST_DATA_API_EXCEPTION
import com.zhidao.roadcondition.model.BaseResponse
import kotlinx.coroutines.*
import java.net.SocketTimeoutException
import java.net.UnknownHostException
import java.util.concurrent.TimeoutException
class Request<T> {
private lateinit var loader: suspend () -> T
private var start: (() -> Unit)? = null
private var onSuccess: ((T) -> Unit)? = null
private var onError: ((java.lang.Exception) -> Unit)? = null
private var onComplete: (() -> Unit)? = null
private var addLifecycle: LifecycleOwner? = null
infix fun loader(loader: suspend () -> T) {
this.loader = loader
}
infix fun start(start: (() -> Unit)?) {
this.start = start
}
infix fun onSuccess(onSuccess: ((T) -> Unit)?) {
this.onSuccess = onSuccess
}
infix fun onError(onError: ((java.lang.Exception) -> Unit)?) {
this.onError = onError
}
infix fun onComplete(onComplete: (() -> Unit)?) {
this.onComplete = onComplete
}
infix fun addLifecycle(addLifecycle: LifecycleOwner?) {
this.addLifecycle = addLifecycle
}
fun request() {
request(addLifecycle)
}
fun request(addLifecycle: LifecycleOwner?) {
GlobalScope.launch(context = Dispatchers.Main) {
start?.invoke()
try {
val deferred = GlobalScope.async(Dispatchers.IO, start = CoroutineStart.LAZY) {
loader()
}
addLifecycle?.apply {
lifecycle.addObserver(
CoroutineChain.CoroutineLifecycleListener(
deferred,
lifecycle
)
)
}
val result = deferred.await()
if (result != null && result is BaseResponse<*>) {
if (result.code == 0) {
onSuccess?.invoke(result)
} else {
throw ApiException(result.code, result.msg)
}
} else {
throw NULL_REQUEST_DATA_API_EXCEPTION
}
} catch (e: Exception) {
e.printStackTrace()
//数据打点
when (e) {
is UnknownHostException -> onError?.invoke(NETWORK_API_EXCEPTION)
is TimeoutException -> onError?.invoke(NETWORK_API_EXCEPTION)
is SocketTimeoutException -> onError?.invoke(NETWORK_API_EXCEPTION)
else -> onError?.invoke(java.lang.Exception(e.message))
}
} finally {
onComplete?.invoke()
}
}
}
}
inline fun <T> request(buildRequest: Request<T>.() -> Unit) {
Request<T>().apply(buildRequest).request()
}
inline fun <T> LifecycleOwner.request(buildRequest: Request<T>.() -> Unit) {
Request<T>().apply(buildRequest).request(this)
}

View File

@@ -0,0 +1,22 @@
package com.zhidao.roadcondition.net
import com.zhidao.roadcondition.model.BaseResponse
import com.zhidao.roadcondition.model.Results
import com.zhidao.roadcondition.model.UploadResult
import retrofit2.http.*
/**
* 接口声明
*/
interface HttpApi {
//获取城市策略
@FormUrlEncoded
@POST("yycp-geo-fence-carService/car/carStrategy/no/getCityStrategy/v1")
suspend fun getCityStrategy(@FieldMap cityStrategy: Map<String, String>): BaseResponse<Results>
//上报情报数据
@FormUrlEncoded
@POST("/deva/car/path/no/addInfomation/v2")
suspend fun uploadInformation(@FieldMap information: Map<String, String>): BaseResponse<UploadResult>
}

View File

@@ -0,0 +1,100 @@
package com.zhidao.roadcondition.net
import com.mogo.commons.AbsMogoApplication
import com.mogo.utils.network.NetConfig
import com.zhidao.roadcondition.constant.HttpConstants
import okhttp3.Cache
import okhttp3.Dns
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import java.io.File
import java.util.*
import java.util.concurrent.TimeUnit
class HttpClient private constructor(baseUrl: String) {
companion object {
const val DEFAULT_CONNECT_TIME = 30L
const val DEFAULT_WRITE_TIMEOUT = 30L
const val DEFAULT_READ_TIMEOUT = 30L
private var baseUrlClientMap = HashMap<String, HttpClient>()
fun getInstance(): HttpClient {
val baseUrl = HttpConstants.getBaseUrl()
var httpClient = baseUrlClientMap[baseUrl]
if (httpClient == null) {
synchronized(HttpClient::class.java) {
if (httpClient == null) {
httpClient = HttpClient(baseUrl)
baseUrlClientMap[baseUrl] = httpClient!!
}
}
}
return httpClient!!
}
fun getInstance(baseUrl: String): HttpClient {
var httpClient = baseUrlClientMap[baseUrl]
if (httpClient == null) {
synchronized(HttpClient::class.java) {
if (httpClient == null) {
httpClient = HttpClient(baseUrl)
baseUrlClientMap[baseUrl] = httpClient!!
}
}
}
return httpClient!!
}
}
private var retrofit: Retrofit
private var httpApi: HttpApi
init {
retrofit = Retrofit.Builder()
.client(getOkHttpClient())
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(baseUrl)
.build()
httpApi = retrofit.create(HttpApi::class.java)
}
fun getHttpApi(): HttpApi {
return httpApi
}
private fun getOkHttpClient(): OkHttpClient {
val builder = OkHttpClient.Builder()
val httpLoggingInterceptor = HttpLoggingInterceptor()
httpLoggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
val cacheFile = File(AbsMogoApplication.getApp().applicationContext.cacheDir, "cache")
val cache = Cache(cacheFile, 1024 * 1024 * 50)
val httpDns = NetConfig.instance().httpDns
if (httpDns != null) {
builder.dns(Dns { hostname: String? ->
val addresses = httpDns.lookup(hostname)
if (addresses != null && !addresses.isEmpty()) {
return@Dns addresses
}
Dns.SYSTEM.lookup(hostname)
})
}
return builder
.addInterceptor(httpLoggingInterceptor)
.cache(cache)
.connectTimeout(DEFAULT_CONNECT_TIME, TimeUnit.SECONDS)
.readTimeout(DEFAULT_READ_TIMEOUT, TimeUnit.SECONDS)
.writeTimeout(DEFAULT_WRITE_TIMEOUT, TimeUnit.SECONDS)
.retryOnConnectionFailure(true)
.build()
}
}

View File

@@ -0,0 +1,18 @@
package com.zhidao.roadcondition.receiver
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.util.Log
class ShareRoadReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Log.d("MainService", "ShareRoadReceiver ------> intent.action = " + intent.action)
if (intent.action == "com.zhidao.share.roadcondition.action") {
val type = intent.getStringExtra("type")
Log.e("MainService", "ShareRoadReceiver type ----> $type ----> 此方法已经废弃了,无法调起服务")
// MainService.launchService(context, type)
}
}
}

View File

@@ -0,0 +1,553 @@
package com.zhidao.roadcondition.service
import android.text.TextUtils
import android.util.Log
import com.hw.videoprocessor.VideoProcessor
import com.mogo.cloud.network.BaseData
import com.mogo.cloud.passport.MoGoAiCloudClientConfig
import com.mogo.cloud.tanlu.api.ITanluUploadCallback
import com.mogo.cloud.tanlu.api.MogoUploadManager
import com.mogo.cloud.tanlu.bean.UploadInfo
import com.mogo.cloud.tanlu.bean.UploadResult
import com.mogo.commons.AbsMogoApplication
import com.mogo.commons.debug.DebugConfig
import com.mogo.utils.NetworkUtils
import com.zhidao.auto.carcorder.callback.TakePhotoCallback
import com.zhidao.auto.carcorder.callback.TakeVideoCallback
import com.zhidao.auto.carcorder.controller.ZdCarCoderController
import com.zhidao.roadcondition.constant.STRATEGY_UPLOAD_TYPE_ARRAY
import com.zhidao.roadcondition.event.GetImageSuccessEvent
import com.zhidao.roadcondition.event.LatLngStickyEventBus
import com.zhidao.roadcondition.util.*
import com.zhidao.roadcondition.util.StrategyPreferenceUtil.Companion.getStrategyMaxSpeed
import com.zhidao.roadcondition.util.StrategyPreferenceUtil.Companion.getStrategyMinSpeed
import com.zhidao.roadcondition.util.StrategyPreferenceUtil.Companion.getStrategyType
import java.io.File
import kotlin.math.abs
/**
* @description 记录仪相关操作
*
* @author lixiaopeng
* @since 2019-10-30
*/
object CarCorderController : TakePhotoCallback, TakeVideoCallback {
const val TAG: String = "CarCorderController"
private lateinit var zdCarCoderController: ZdCarCoderController
private var outputVideoPath: String = ""
private var mType: String = "" //1 上报拥堵, 2 封路和查车
private var mainInfoId: Long = 0
private var mFromType: String = ""
private var mLongitude: Double = 0.0
private var mLatitude: Double = 0.0
private var mSpeed: Float = 0f
private var mAddress: String = ""
private var mDirection: Float = 0f
private var mAreaCode: String = ""
private var mCityCode: String = ""
private var getVideoFailed: (() -> Unit)? = null
private var interceptors: ArrayList<TakePhotoInterceptor> = ArrayList(1)
fun getVideoFailed(getVideoFailed: (() -> Unit)) {
this.getVideoFailed = getVideoFailed
}
fun registerTakePhotoInterceptor(interceptor: TakePhotoInterceptor) {
interceptor.apply {
interceptors.add(this)
}
}
fun unregisterTakePhotoInterceptor(interceptor: TakePhotoInterceptor) {
interceptor.apply {
interceptors.remove(interceptor)
}
}
fun initCarCorderController() {
try {
zdCarCoderController =
ZdCarCoderController.getInstance(AbsMogoApplication.getApp().applicationContext)
zdCarCoderController.addCallback(this)
zdCarCoderController.addVideoCallback(this)
zdCarCoderController.init()
} catch (e: Exception) {
e.printStackTrace()
}
}
fun takePhoto(
photoType: Int,
cameraId: Int,
haveVoice: Boolean,
isCustom: Boolean = false,
type: String,
mainInfoId: Long,
fromType: String,
longitude: Double,
latitude: Double,
speed: Float,
address: String,
direction: Float,
areaCode: String,
cityCode: String
) {
Log.d(TAG, "takePhoto ---------- type = $type --- mType = $mType ---fromType = $fromType")
this.mType = type
this.mainInfoId = mainInfoId
this.mFromType = fromType
this.mLongitude = longitude
this.mLatitude = latitude
this.mSpeed = speed
this.mAddress = address
this.mDirection = direction
this.mAreaCode = areaCode
this.mCityCode = cityCode
CustomStatusHandler.offerPhotoStatus(isCustom)
zdCarCoderController.takePhoto(photoType, cameraId, haveVoice)
trackGetPhoto(1)
}
/**
* @param isCustom: 是否手动上传
* @param id: 标志是哪一个上传
*/
fun takeVideo(
cameraId: Int,
duration: Int,
isCustom: Boolean = false,
id: Long = 0L,
type: String,
mainInfoId: Long,
fromType: String,
longitude: Double,
latitude: Double,
speed: Float,
address: String,
direction: Float,
areaCode: String,
cityCode: String
) {
Log.d(TAG, "takeVideo -------- isCustom = $isCustom ---mFromType = $mFromType ---type = $type ---speed = $speed")
this.mType = type
this.mainInfoId = mainInfoId
this.mFromType = fromType
this.mLongitude = longitude
this.mLatitude = latitude
this.mSpeed = speed
this.mAddress = address
this.mDirection = direction
this.mAreaCode = areaCode
this.mCityCode = cityCode
if (DebugConfig.getCarMachineType() == 0) { //自研车机
outputVideoPath = getCompressVideoPath()
CustomStatusHandler.offerVideoStatus(TakeEntity(isCustom, id, fromType))
zdCarCoderController.takeVideo(cameraId, duration)
trackGetVideo(1)
} else { //比亚迪
Log.d(TAG, "takeVideo ------ isnet = " + NetworkUtils.isConnected(AbsMogoApplication.getApp().applicationContext))
// if (!NetworkUtils.isConnected(AbsMogoApplication.getApp().applicationContext)) {
// TipToast.shortTip("分享失败,请检查网络")
// } else {
//失败了,传空地址,发起请求
// videoAndThumbMap["video"] = ""
// videoAndThumbMap["thumb"] = ""
// CosStatusController().sendInformationDirectly(
// INFO_TYPE_VIDEO,
// videoAndThumbMap,
// mType,
// entity,
// mainInfoId,
// mLongitude,
// mLatitude,
// mSpeed,
// mFromType
// )
getInfo("", mAddress, mLongitude, mLatitude, mType, mDirection, mAreaCode, mCityCode, 1, mFromType, CarCorderController.mainInfoId)?.let { uploadRoadInfo(it) }
//地图上打点
taskAsync(3_000) {
try {
// LatLngStickyEventBus.getInstance()
// .postSticky(GetImageSuccessEvent("", mType))
} catch (e: java.lang.Exception) {
e.printStackTrace()
}
}
// }
}
}
fun release() {
zdCarCoderController.release()
interceptors.clear()
}
//拍照失败回调
override fun onTakePhotoFail(photoType: Int, camera: Int) {
trackGetPhoto(3)
interceptors.forEach {
it.onTakePhotoFail(photoType, camera)
}
val isCustom = CustomStatusHandler.pollPhotoStatus()
Log.e(TAG, "onTakePhotoFail -----mType = $mType --- isCustom = $isCustom")
//获取图片失败也上报,图片不打点
if (isCustom) {
// CosStatusController().sendInformationDirectly(
// INFO_TYPE_IMG,
// mutableMapOf("pic" to ""),
// mType,
// entity,
// mainInfoId,
// mLongitude,
// mLatitude,
// mSpeed,
// mFromType
// )
getInfo("", mAddress, mLongitude, mLatitude, mType, mDirection, mAreaCode, mCityCode, 0, mFromType, mainInfoId)?.let { uploadRoadInfo(it) }
}
}
//拍照成功回调返回图片本地路径
override fun onTakePhotoSuccess(photoType: Int, camera: Int, photoPath: String?) {
trackGetPhoto(2)
Log.d(TAG, "onTakePhotoSuccess -----mType = $mType --- mainInfoId = $mainInfoId")
val isCustom = CustomStatusHandler.pollPhotoStatus()
if (!isCustom) {
trackNormalEvent(CarNet_auto_upload, null)
}
var interceptor = false
interceptors.forEach {
interceptor = it.intercept()
it.onTakePhotoSuccess(photoType, camera, photoPath)
}
if (interceptor) {
return
}
val minSpeedPic =
getStrategyMinSpeed(
AbsMogoApplication.getApp().applicationContext,
"pic",
getStrategyType("pic")
)
val maxSpeedPic =
getStrategyMaxSpeed(
AbsMogoApplication.getApp().applicationContext,
"pic",
getStrategyType("pic")
)
val speed = LocationUtil.getInstance().getSpeed()
if (!TextUtils.isEmpty(mType)) {
Log.d(TAG, "onTakePhotoSuccess mType != null")
// CosStatusController().uploadFile(mutableListOf(photoPath as String), entity, mType, mainInfoId, mFromType, mLongitude,
// mLatitude, mSpeed)
if (photoPath != null) {
getInfo(photoPath, mAddress, mLongitude, mLatitude, mType, mDirection, mAreaCode, mCityCode, 0, mFromType, mainInfoId)?.let { uploadRoadInfo(it) }
}
} else {
Log.d(TAG, "onTakePhotoSuccess mType == null")
Log.d(
TAG,
"onTakePhotoSuccess maxSpeedPic = $maxSpeedPic ---> speed = $speed ---->minSpeedPic= $minSpeedPic"
)
if (maxSpeedPic == -1) {
if (speed >= (abs(minSpeedPic) / 3.6f)) {
Log.d(TAG, "onTakePhotoSuccess abs =" + (abs(minSpeedPic) / 3.6f))
// CosStatusController().uploadFile(
// mutableListOf(photoPath as String),
// entity,
// mType,
// mainInfoId,
// mFromType,
// mLongitude,
// mLatitude,
// mSpeed
// )
if (photoPath != null) {
getInfo(photoPath, mAddress, mLongitude, mLatitude, mType, mDirection, mAreaCode, mCityCode, 0, mFromType, mainInfoId)?.let { uploadRoadInfo(it) }
}
}
}
if (minSpeedPic > 0 && maxSpeedPic > 0) {
Log.d(TAG, "onTakePhotoSuccess minSpeedPic > 0 -- speed = $speed")
if ((speed >= (minSpeedPic / 3.6f)) && speed <= (maxSpeedPic / 3.6f)) {
// CosStatusController().uploadFile(
// mutableListOf(photoPath as String),
// entity,
// mType,
// mainInfoId,
// mFromType,
// mLongitude,
// mLatitude,
// mSpeed
// )
if (photoPath != null) {
getInfo(photoPath, mAddress, mLongitude, mLatitude, mType, mDirection, mAreaCode, mCityCode, 0, mFromType, mainInfoId)?.let { uploadRoadInfo(it) }
}
}
}
}
}
//获取视频成功
override fun onTakeVideoSuccess(camera: Int, videoPath: String?) {
val thumbnailPath =
AbsMogoApplication.getApp().applicationContext.filesDir.parent + File.separator + "Thumbnail${System.currentTimeMillis()}.jpg"
val isSuccess = getVideoThumbnail(videoPath!!, thumbnailPath)
Log.d(
TAG,
"getVideo onTakeVideoSuccess===$videoPath -----> isSuccess= $isSuccess ----> mType = $mType --- mainInfoId = $mainInfoId"
)
val entity = CustomStatusHandler.pollVideoStatus()
entity?.let {
if (!entity.isCustom) {
trackNormalEvent(CarNet_auto_upload_video, null)
}
}
if (isSuccess) {
val minSpeedVideo = getStrategyMinSpeed(
AbsMogoApplication.getApp().applicationContext, "video",
getStrategyType("video")
)
val maxSpeedVideo = getStrategyMaxSpeed(
AbsMogoApplication.getApp().applicationContext, "video",
getStrategyType("video")
)
val speed = LocationUtil.getInstance().getSpeed()
entity?.let {
if (it.isCustom) {
compressVideo(videoPath, thumbnailPath, it)
} else if (entity.fromType in STRATEGY_UPLOAD_TYPE_ARRAY) {
// 属于策略上报
compressVideo(videoPath, thumbnailPath, it)
} else {
Log.d(
TAG,
"onTakeVideoSuccess maxSpeedVideo = $maxSpeedVideo --->speed= $speed + minSpeedVideo = $minSpeedVideo"
)
if (maxSpeedVideo == -1) {
Log.d(TAG, "onTakeVideoSuccess 111 abs =" + (abs(minSpeedVideo) / 3.6f))
if (speed >= (abs(minSpeedVideo) / 3.6f)) {
//获取视频以及缩略图成功,开始上报
compressVideo(videoPath, thumbnailPath, it)
}
}
if (minSpeedVideo > 0 && maxSpeedVideo > 0) {
Log.d(TAG, "onTakeVideoSuccess minSpeedVideo > 0 -- speed = $speed")
if ((speed >= (minSpeedVideo / 3.6f)) && speed <= (maxSpeedVideo / 3.6f)) {
compressVideo(videoPath, thumbnailPath, it)
} else {
}
} else {
}
}
}
trackGetVideo(2)
}
}
private var videoAndThumbMap: MutableMap<String, String> = mutableMapOf()
//获取视频失败
override fun onTakeVideoFail(camera: Int) {
// trackGetVideo(3)
// Log.e(TAG, "getVideo onTakeVideoFail")
// getVideoFailed?.invoke()
// val entity = CustomStatusHandler.pollVideoStatus()
//
// entity?.let {
// InformationUploadController.release(it.id)
// Log.e(TAG, "getVideo onTakeVideoFail entity?.isCustom =" + it.isCustom)
//// if (it.isCustom) {
//// sendGetInfoFailedReceiver(mType)
//// }
// }
//失败了,传空地址,发起请求
// videoAndThumbMap["video"] = ""
// videoAndThumbMap["thumb"] = ""
// CosStatusController().sendInformationDirectly(
// INFO_TYPE_VIDEO,
// videoAndThumbMap,
// mType,
// entity,
// mainInfoId,
// mLongitude,
// mLatitude,
// mSpeed,
// mFromType
// )
getInfo("", mAddress, mLongitude, mLatitude, mType, mDirection, mAreaCode, mCityCode, 1, mFromType, mainInfoId)?.let { uploadRoadInfo(it) }
//地图上打点
taskAsync(3_000) {
try {
LatLngStickyEventBus.getInstance().postSticky(GetImageSuccessEvent("", mType))
} catch (e: java.lang.Exception) {
e.printStackTrace()
}
}
}
/**
* 压缩视频并且上传
*/
private fun compressVideo(videoPath: String, thumbnailPath: String, entity: TakeEntity) {
Log.d(
TAG,
"outputVideoPath = " + outputVideoPath + ">> videoPath=" + videoPath + " isCustom = ${entity.isCustom}"
)
val startTime = System.currentTimeMillis()
Thread(Runnable {
try {
VideoProcessor.processor(AbsMogoApplication.getApp().applicationContext)
.input(videoPath)
.output(outputVideoPath)
.removeAudio(true)
.outWidth(1920)
.outHeight(1080)
.bitrate(2000 * 1024)
.frameRate(25)
.process()
Log.d(TAG, "compress cost time =" + (System.currentTimeMillis() - startTime))
// CosStatusController().uploadFile(
// mutableListOf(outputVideoPath, thumbnailPath),
// entity,
// mType,
// mainInfoId,
// mFromType,
// mLongitude,
// mLatitude,
// mSpeed
// )
getInfo(outputVideoPath, mAddress, mLongitude, mLatitude, mType, mDirection, mAreaCode, mCityCode, 1, mFromType, mainInfoId)?.let { uploadRoadInfo(it) }
//删除压缩前的视频
deletePicFile(videoPath)
} catch (e: Exception) {
Log.e(TAG, "compressVideo e = $e")
//删除压缩前的视频
deletePicFile(videoPath)
e.printStackTrace()
}
}).start()
}
/**
* 上报路况的视频和图片
*/
private fun uploadRoadInfo(info: UploadInfo) {
MogoUploadManager.getInstance(AbsMogoApplication.getApp().applicationContext).uploadInfo(info, object : ITanluUploadCallback {
override fun onSuccess(result: BaseData<UploadResult>) {
if (result.result != null) {
Log.d(TAG, "result =" + result.result);
}
}
override fun onFailure(code: Int) {
}
override fun onError(e: Throwable) {
}
})
}
private fun getInfo(filePath: String, addr: String, lon: Double, lat: Double, poiType: String,
direction: Float, areaCode: String, cityCode: String, type: Int, fromType: String
, mainInfoId: Long): UploadInfo? {
val info = UploadInfo()
info.filePath = filePath
info.addr = addr
info.longitude = lon
info.latitude = lat
info.poiType = poiType
info.direction = direction
info.areaCode = areaCode
info.cityCode = cityCode
info.sn = MoGoAiCloudClientConfig.getInstance().getSn()
info.type = type //0为图片 1为视频
info.fromType = fromType
info.mainInfoId = mainInfoId
info.infoType = 1
return info
}
override fun onTakePhotoCancel(photoType: Int, camera: Int) {
Log.d(TAG, "onTakePhotoCancel -----photoType = $photoType")
}
override fun onTakeVideoCancel(camera: Int) {
Log.d(TAG, "onTakeVideoCancel -----camera = $camera")
//失败了,传空地址,发起请求
videoAndThumbMap["video"] = ""
videoAndThumbMap["thumb"] = ""
// CosStatusController().sendInformationDirectly(
// INFO_TYPE_VIDEO,
// videoAndThumbMap,
// mType,
// entity,
// mainInfoId,
// mLongitude,
// mLatitude,
// mSpeed,
// mFromType
// )
getInfo("", mAddress, mLongitude, mLatitude, mType, mDirection, mAreaCode, mCityCode, 1, mFromType, mainInfoId)?.let { uploadRoadInfo(it) }
}
//获取图片
private fun trackGetPhoto(type: Int) {
trackNormalEvent(
CarNet_Get_Picture, mutableMapOf("type" to type),
AbsMogoApplication.getApp().applicationContext
)
}
//获取视频
private fun trackGetVideo(type: Int) {
trackNormalEvent(
CarNet_Get_Video, mutableMapOf("type" to type),
AbsMogoApplication.getApp().applicationContext
)
}
}

View File

@@ -0,0 +1,79 @@
package com.zhidao.roadcondition.service
import com.zhidao.cosupload.callback.CosStatusCallback
import com.zhidao.cosupload.callback.CosStatusCallbackManager
/**
* @author congtaowang
* @since 2019-11-25
*
*/
object CosCallbackMapController : CosStatusCallback {
private val map: MutableMap<String, CosStatusCallback> = mutableMapOf()
var uploadFailed: (() -> Unit)? = null
var mainService: MainService? = null
fun init(mainService: MainService) {
this.mainService = mainService
CosStatusCallbackManager.getInstance().register(this)
}
fun uploadFailed(uploadFailed: (() -> Unit)) {
this.uploadFailed = uploadFailed
}
fun registerCallback(paths: List<String?>?, callback: CosStatusCallback) {
paths?.let { list ->
list.forEach { path ->
path?.let {
map[it] = callback
}
}
}
}
fun unregisterCallback(path: String?) {
path?.let {
map.remove(path)
}
}
fun unregisterCallbacks(paths: Map<String, String>?) {
paths?.let {
it.keys.let { keys ->
keys.forEach { path ->
unregisterCallback(path)
}
}
}
}
override fun onStartUpload(eventId: String?, localPath: String?) {
map[localPath]?.onStartUpload(eventId, localPath)
}
override fun uploadCosFailed(cosPath: String?, eventId: String?, localPath: String?) {
map[localPath]?.uploadCosFailed(cosPath, eventId, localPath)
}
override fun uploadCosCompleted(
cosPath: String?,
eventId: String?,
downloadUrl: String?,
localPath: String?
) {
map[localPath]?.uploadCosCompleted(cosPath, eventId, downloadUrl, localPath)
}
override fun onProgress(localPath: String?, progress: Float) {
map[localPath]?.onProgress(localPath, progress)
}
fun release() {
CosStatusCallbackManager.getInstance().unregister(this)
map.clear()
uploadFailed = null
}
}

View File

@@ -0,0 +1,240 @@
package com.zhidao.roadcondition.service
import android.content.Intent
import android.os.Environment
import android.util.Log
import com.google.gson.Gson
import com.mogo.commons.AbsMogoApplication
import com.zhidao.cosupload.DbPriorityConfig
import com.zhidao.cosupload.callback.CosStatusCallback
import com.zhidao.cosupload.manager.CosUploadManagerImpl
import com.zhidao.roadcondition.event.GetImageSuccessEvent
import com.zhidao.roadcondition.event.LatLngStickyEventBus
import com.zhidao.roadcondition.model.proxy.INFO_TYPE_IMG
import com.zhidao.roadcondition.model.proxy.INFO_TYPE_VIDEO
import com.zhidao.roadcondition.util.*
import java.io.File
/**
* @description cos上传操作
*
* @author lixiaopeng
* @since 2019-10-30
*/
class CosStatusController : CosStatusCallback {
companion object{
const val TAG: String = "CosStatusController"
}
//存储单次请求的视频和缩略图url
private var videoAndThumbMap: MutableMap<String, String> = mutableMapOf()
//图片上传的eventId
private lateinit var mPicEventId: String
private var isRetry = false //是否重试上传过图片
private var mainServiceHttpModel = MainServiceController()
private lateinit var entity: TakeEntity
private var mType: String = ""
private var mainInfoId: Long = 0
private var mFromType: String = ""
private var mLongitude: Double = 0.0
private var mLatitude: Double = 0.0
private var mSpeed: Float = 0f
val moviesDir = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_MOVIES + File.separator
)
//上传文件
fun uploadFile(picPath: MutableList<String?>?, entity: TakeEntity, type: String, mainInfoId: Long,
fromType: String, longitude: Double, latitude: Double, speed: Float) {
CosCallbackMapController.registerCallback(picPath, this)
// CosLogger.setLogStatus(true)
this.entity = entity
this.mType = type
this.mainInfoId = mainInfoId
this.mFromType = fromType
this.mLongitude = longitude
this.mLatitude = latitude
this.mSpeed = speed
Log.d(TAG, "uploadFile type===$type ---- mainInfoId =$mainInfoId ----mFromType = $mFromType ---- picPath = $picPath --speed = $speed")
trackUploadCos(3)
if(picPath == null){
return
}
if (picPath.contains("backPic")) return
//参数说明: paths本地文件路径上传的本地路径不要重复config文件上传的优先级
mPicEventId =
CosUploadManagerImpl.getInstance(AbsMogoApplication.getApp().applicationContext).eventId
CosUploadManagerImpl.getInstance(AbsMogoApplication.getApp().applicationContext)
.upload(picPath, mPicEventId, DbPriorityConfig.PRIORITY_HIGH)
}
override fun onStartUpload(eventId: String?, localPath: String?) {
}
private fun sendGetInfoFailedReceiver(type: String) {
Log.d(CarCorderController.TAG, "sendGetInfoFailedReceiver ------>")
val intent = Intent()
intent.action = "com.zhidao.roadcondition.getinfo.failed"
intent.putExtra("type", type)
AbsMogoApplication.getApp().applicationContext.sendBroadcast(intent)
}
override fun uploadCosFailed(cosPath: String?, eventId: String?, localPath: String?) {
Log.d(TAG, "uploadCosFailed = $localPath")
trackUploadCos(2)
//语音播报 1上报路况2交通检查3封路 默认 mType 应该为null
if (!isRetry) {
isRetry = true
// taskAsync(30_000) { //去掉重试
// try {
// uploadFile(mutableListOf(localPath as String), entity, mType)
// } catch (e: Exception) {
// e.printStackTrace()
// }
// }
} else {
if (localPath!!.endsWith("mp4") || localPath.contains("Thumbnail")) {
CosCallbackMapController.uploadFailed?.invoke()
}
CosCallbackMapController.unregisterCallback(localPath)
InformationUploadController.release(entity.id)
// deletePicFile(localPath)
deleteCompressVideoFile(moviesDir.toString())
}
}
override fun uploadCosCompleted(
cosPath: String?,
eventId: String?,
downloadUrl: String?,
localPath: String?
) {
Log.d(TAG, "uploadCosCompleted localPath = $localPath")
Log.d(TAG, "uploadCosCompleted downloadUrl = $downloadUrl")
Log.d(TAG, "uploadCosCompleted cosPath = $cosPath")
trackUploadCos(1)
if (localPath!!.endsWith("mp4") || localPath.contains("Thumbnail")) {
//如果是视频文件或者缩略图文件
if (localPath.endsWith("mp4")) {
videoAndThumbMap["video"] = downloadUrl!!
Log.i(TAG, "videoAndThumbMap add mp4")
} else {
videoAndThumbMap["thumb"] = downloadUrl!!
Log.i(TAG, "videoAndThumbMap add thumb")
}
Log.d(TAG, "videoAndThumbMap $videoAndThumbMap")
if (videoAndThumbMap.size == 2) {
trackUploadCos(4)
Log.d(TAG, "videoAndThumbMap.size == 2 ")
val locationInfo = LocationUtil.getInstance().getLocationInfo()
val locationStr: String = Gson().toJson(locationInfo)
Log.d(TAG, "locationStr = $locationStr")
//上传录像以及缩略图成功
sendInformation(INFO_TYPE_VIDEO, videoAndThumbMap)
} else {
trackUploadCos(5)
}
} else {
//上传图片成功, 如果是上报路况,直接上传
Log.d(TAG, "uploadCosCompleted 分享成功 ---- mType = $mType")
sendInformationDirectly(
INFO_TYPE_IMG,
mutableMapOf("pic" to downloadUrl as String),
mType,
entity,
mainInfoId,
mLongitude,
mLatitude,
mSpeed,
mFromType
)
}
Log.d(TAG, "delete file: $localPath")
CosCallbackMapController.unregisterCallback(localPath)
// deletePicFile(localPath)
deleteCompressVideoFile(moviesDir.toString())
}
private fun sendInformation(type: Int, map: Map<String, String>) {
Log.d(TAG, "isCustomSend = ${entity.isCustom}")
// if (entity?.isCustom) {
// InformationUploadController.cosResourceReady(type, map, entity?.isCustom, entity?.id) {
// CosCallbackMapController.mainService?.sendCustomResult(it)
// }
// } else {
sendInformationDirectly(type, map, mType, entity, mainInfoId, mLongitude, mLatitude, mSpeed,mFromType)
// }
}
/**
* 被动上报时直接上报,不用等待、选择情报类型
*/
fun sendInformationDirectly(
type: Int,
map: Map<String, String>,
poiType: String,
entity: TakeEntity?,
mainInfoId: Long,
longitude: Double,
latitude: Double,
speed: Float,
fromType: String
) {
Log.d(TAG, "sendInformationDirectly poiType= $poiType --fromType =$fromType ---- mainInfoId= $mainInfoId --- isCustom = ${entity?.isCustom}")
//开始上传
entity?.isCustom?.let {
mainServiceHttpModel.sendInformationMessage(
fromType = fromType,
type = type,
url = map,
isCustom = it,
poiType = poiType,
mainInfoId = mainInfoId,
speed = speed,
longitude = longitude,
latitude = latitude
) { success ->
CosCallbackMapController.unregisterCallbacks(map)
// CosCallbackMapController.mainService?.let {
// CosCallbackMapController.mainService?.sendCustomResult(success)
// }
Log.d(TAG, "type = $type ----success = $success ----fromType = $mFromType ----poiType = $poiType")
if (success) {
//分享成功并打点如果是上报拥堵需要takeVideo
if (type == INFO_TYPE_VIDEO) {
try {
LatLngStickyEventBus.getInstance()
.postSticky(GetImageSuccessEvent("", poiType))
} catch (e: java.lang.Exception) {
e.printStackTrace()
}
}
} else {
}
}
}
}
override fun onProgress(localPath: String?, progress: Float) {
}
//上传COS
private fun trackUploadCos(type: Int) {
trackNormalEvent(
CarNet_Cos_Upload, mutableMapOf("type" to type),
AbsMogoApplication.getApp().applicationContext
)
}
}

View File

@@ -0,0 +1,31 @@
package com.zhidao.roadcondition.service
import java.util.*
/**
* @author congtaowang
* @since 2019-11-25
*
* 拍照、拍视频手动、被动状态队列
*/
object CustomStatusHandler {
private val takePhotoStatusQueue: Queue<Boolean> = ArrayDeque(5)
private val takeVideoStatusQueue: Queue<TakeEntity> = ArrayDeque(5)
fun offerPhotoStatus(status: Boolean) {
takePhotoStatusQueue.offer(status)
}
fun pollPhotoStatus() = takePhotoStatusQueue.poll() ?: false
fun offerVideoStatus(entity: TakeEntity) {
takeVideoStatusQueue.offer(entity)
}
fun pollVideoStatus(): TakeEntity? = takeVideoStatusQueue.poll()
}
class TakeEntity(
var isCustom: Boolean, var id: Long, var fromType: String = "0"
)

View File

@@ -0,0 +1,67 @@
package com.zhidao.roadcondition.service
import android.app.IntentService
import android.content.Context
import android.content.Intent
import android.os.IBinder
import android.util.Log
import com.zhidao.roadcondition.util.*
import java.io.File
import java.lang.Exception
class DelayService : IntentService("DelayService") {
companion object {
const val TAG = "DelayService"
fun launchService(context: Context) {
context.startService(Intent(context, DelayService::class.java))
}
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
return super.onStartCommand(intent, flags, startId)
}
override fun onCreate() {
super.onCreate()
}
override fun onStart(intent: Intent?, startId: Int) {
super.onStart(intent, startId)
}
override fun onBind(intent: Intent?): IBinder? {
return super.onBind(intent)
}
override fun setIntentRedelivery(enabled: Boolean) {
super.setIntentRedelivery(enabled)
}
override fun onDestroy() {
super.onDestroy()
}
override fun onHandleIntent(intent: Intent?) {
Log.d(TAG, "start delay ----> ")
taskAsync(20_000) {
try {
Log.d(TAG, "delay finish, start Service")
// MainService.launchService(this@DelayService, "0")
//删除一个原始视频(可能没删除的)文件夹下的所有文件(包括子目录内的文件)
val file = File("/mnt/sdcard/DCIM/Camera/video/small/") //输入要删除文件目录的绝对路径
deleteAllFile(file)
val compressFile = File("/mnt/sdcard/Movies/") //压缩过的视频
deleteAllFile(compressFile)
} catch (e: Exception) {
e.printStackTrace()
}
}
}
}

View File

@@ -0,0 +1,124 @@
package com.zhidao.roadcondition.service
import android.util.Log
import com.zhidao.roadcondition.model.InformationResource
import com.zhidao.roadcondition.model.InformationType
import com.zhidao.roadcondition.util.*
/**
* @author congtaowang
* @since 2019-11-20
*
* 情报上报控制
*/
object InformationUploadController {
private val TAG : String = this.javaClass.simpleName
private var mainServiceHttpModel = MainServiceController()
private val locker: Any = Any()
/**
* 情报
* Long -> 一个情报的id
* InformationResource -> 一个情报的资源
*/
private val informationCache = mutableMapOf<Long, InformationResource>()
/**
* 情报已上传到cos
*/
fun cosResourceReady(
sourceType: Int,
cosParameter: Map<String, String>,
isCustomSend: Boolean,
id: Long,
callback: ((customSend: Boolean) -> Unit)
) {
Log.d(TAG, "cos resource ready. id=${id}")
val ir = getInformationResourceById(id)
ir?.let {
it.isCosResourceReady = true
it.sourceType = sourceType
it.cosParameter = cosParameter
it.isCustomSend = isCustomSend
it.callback = callback
// tryUploadInformation(it)
}
}
private fun getInformationResourceById(id: Long): InformationResource? {
if (!informationCache.containsKey(id)) {
informationCache[id] = InformationResource(id)
}
return informationCache[id]
}
/**
* 已选择情报类型
*/
fun informationSelected(
informationType: InformationType,
newsType: String,
operType: String,
id: Long
) {
Log.d(TAG, "information type selected id=${id}, type=${informationType}")
val ir = getInformationResourceById(id)
ir?.let {
it.isInformationSelected = true
it.informationType = informationType
it.newsType = newsType
it.operType = operType
// tryUploadInformation(it)
}
}
// private fun tryUploadInformation(ir: InformationResource?) {
// synchronized(locker) {
// ir?.let {
// if (!it.isReady()) {
// log(TAG, "source isn't ready.")
// return@let
// }
// mainServiceHttpModel.sendInformationMessage(
// it.sourceType,
// it.cosParameter!!,
// it.isCustomSend,
// it.informationType?.dictValue!!,
// isShare = true
// ) { success ->
// log(TAG, "上传成功!" + ir?.toString())
// trackNormalEvent(
// CarNet_user_upload,
// mutableMapOf(
// TRACK_KEY_TYPE to it.operType,
// TRACK_KEY_NEWS_TYPE to it.newsType
// )
// )
// if (it.isCustomSend) {
// it.callback?.invoke(success)
// }
// informationCache.remove(it.id)
// it.release()
// recordUploadTime()
// }
// }
// }
// }
fun release(id: Long) {
val target = informationCache.remove(id)
target?.release()
}
private fun recordUploadTime() {
putLong("lastUploadTime", System.currentTimeMillis())
}
fun isUpload2Frequently(): Boolean {
val lastUploadTime = getLong("lastUploadTime", 0L)
return System.currentTimeMillis() - lastUploadTime < 10_000L
}
}

View File

@@ -0,0 +1,389 @@
package com.zhidao.roadcondition.service
import android.app.AlarmManager
import android.app.PendingIntent
import android.app.Service
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.IBinder
import android.util.Log
import com.alibaba.android.arouter.launcher.ARouter
import com.elegant.analytics.Analytics
import com.mogo.commons.AbsMogoApplication
import com.mogo.commons.debug.DebugConfig
import com.mogo.module.common.MogoApisHandler
import com.mogo.service.IMogoServiceApis
import com.mogo.service.MogoServicePaths
import com.mogo.utils.logger.Logger
import com.mogo.utils.storage.SharedPrefsMgr
import com.zhidao.cosupload.manager.CosUploadManagerImpl
import com.zhidao.roadcondition.BuildConfig
import com.zhidao.roadcondition.constant.DEFAULT_VIDEO_DURATION
import com.zhidao.roadcondition.constant.TANLU_ROAD_CURRENT
import com.zhidao.roadcondition.constant.UPLOAD_FROM_STRATEGY_ACCIDENT_AUTO
import com.zhidao.roadcondition.constant.UPLOAD_FROM_STRATEGY_BLOCK_AUTO
import com.zhidao.roadcondition.event.GetImageSuccessEvent
import com.zhidao.roadcondition.event.LatLngStickyEventBus
import com.zhidao.roadcondition.util.*
import com.zhidao.roadcondition.util.StrategyPreferenceUtil.Companion.clearStrategyType
import com.zhidao.roadcondition.util.StrategyPreferenceUtil.Companion.getStrategyFrequency
import com.zhidao.roadcondition.util.StrategyPreferenceUtil.Companion.getStrategyInterval
import com.zhidao.roadcondition.util.StrategyPreferenceUtil.Companion.getStrategyType
import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
import java.util.concurrent.TimeUnit
/**
* 上报流程服务
*/
class MainService : Service() {
companion object {
fun launchService(context: Context, params: UploadParams?) {
val intent = Intent(context, MainService::class.java).apply {
Log.e("MainService", "launchService type = $params")
putExtra("params", params)
}
context.startService(intent)
}
}
private lateinit var mainServiceHttpModel: MainServiceController
private var mAlarmManager: AlarmManager =
AbsMogoApplication.getApp().applicationContext.getSystemService(Context.ALARM_SERVICE) as AlarmManager
//是否已经获取过策略
var isGetStrategies: Boolean = false
private val TAG: String = this.javaClass.simpleName
//1是一次性2是周期性
private var picType: Int = 0
private var videoType: Int = 0
private var shareType: String = "type"
private var fromType: String = ""
private var isCustom: Boolean = false
private var speed: Float = 0f
private var params: UploadParams? = null
private var mainInfoId: Long = 0
private var mLongitude: Double = 0.0
private var mLatitude: Double = 0.0
private lateinit var serviceApis: IMogoServiceApis
private var mAddress: String = ""
private var mDirection: Float = 0f
private var mAreaCode: String = ""
private var mCityCode: String = ""
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
//清理多媒体资源和sp策略数据
clearStrategyType(this)
//初始化埋点
Analytics.getInstance().start(this)
//参数说明appKey: app唯一标识比如包名
CosUploadManagerImpl.getInstance(AbsMogoApplication.getApp().applicationContext)
.init(BuildConfig.APPLICATION_ID, 0)
serviceApis = ARouter.getInstance().build(MogoServicePaths.PATH_SERVICE_APIS).navigation(this) as IMogoServiceApis
if (serviceApis.mapServiceApi.getSingletonLocationClient(this) != null &&
serviceApis.mapServiceApi.getSingletonLocationClient(this).lastKnowLocation != null) {
speed = serviceApis.mapServiceApi.getSingletonLocationClient(this).lastKnowLocation.speed
Log.d(TAG, "onStartCommand speed = $speed")
}
if (intent != null) {
params = intent.getParcelableExtra("params")
params?.let {
shareType = it.eventType
fromType = it.fromType
mainInfoId = it.parentId
mLongitude = it.lon
mLatitude = it.lat
mAddress = it.addr
mDirection = it.direction
mAreaCode = it.areaCode
mCityCode = it.cityCode
Log.d(TAG, "onStartCommand shareType = $shareType --fromType = $fromType --mainInfoId = $mainInfoId -- mLongitude = $mLongitude --mLatitude = $mLatitude --it.duration = ${it.duration} ")
if (fromType == UPLOAD_FROM_STRATEGY_ACCIDENT_AUTO || fromType == UPLOAD_FROM_STRATEGY_BLOCK_AUTO) {
// 如果是策略上报isCustom = false
takeVideo(it.duration)
} else {
takeVideo(it.duration, isCustom = true, id = id)
}
}
} else {
Log.e(TAG, "intent == null ")
}
return super.onStartCommand(intent, flags, startId)
}
//定时任务回调广播
private var mAlarmBroadCast: BroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(p0: Context?, p1: Intent) {
Log.d(TAG, "receive alarm!!!!!!!!!!AlarmType===${p1.getIntExtra("AlarmType", 1)}")
if (p1.action == alarmBroadAction) {
if (p1.getIntExtra("AlarmType", 1) == AlarmTypePic) {
//拍照
takePhoto(true)
} else {
//录像
val duration = p1.getIntExtra("duration", 3)
takeVideo(duration, true)
}
}
}
}
//主动上报图片情报广播
var mCustomSendBroadCast: BroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(p0: Context?, p1: Intent) {
val id = p1.getLongExtra("id", 0L)
if (p1.action == sendInformationAction) {
takeVideo(DEFAULT_VIDEO_DURATION, isCustom = true, id = id)
}
}
}
override fun onBind(intent: Intent?): IBinder? {
return null
}
override fun onCreate() {
super.onCreate()
init()
//初始化定位
LocationUtil.getInstance().initLocation()
initLocationListener()
loadConfigurations()
LatLngStickyEventBus.getInstance().register(this)
trackNormalEvent(CarNet_MainService_Start, null)
}
private fun init() {
//初始化拍照sdk
CarCorderController.initCarCorderController()
mainServiceHttpModel = MainServiceController()
registReceiver()
// CosStatusController().registerCos(this)
CosCallbackMapController.init(this)
}
/**
* 加载各种配置
*/
private fun loadConfigurations() {
// mainServiceHttpModel.getAuthorization()
// mainServiceHttpModel.getNeedAuth { authStatus ->
// // isNeedAuth = authStatus
// }
// mainServiceHttpModel.getSplashConfig()
}
private var id: Long = System.currentTimeMillis()
/**
* 获取图片成功
*/
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true, priority = 98)
fun getImageEvent(getImageSuccessEvent: GetImageSuccessEvent) {
val info = LocationUtil.getInstance().getLocationInfo()
Log.d("MainService", "getImageEvent long = ${info.longitude} ----> lat= ${info.latitude}")
Log.d(
"MainService",
"getImageEvent url = " + getImageSuccessEvent.getImageUrl() + ">>>>type =" + getImageSuccessEvent.getType()
)
if (!DebugConfig.isMapBased()) {
MogoApisHandler.getInstance().apis.statusManagerApi.setUploadingStatus("CARD_TYPE_ROAD_CONDITION", false)
}
// sendMarkerInfoReceiver(
// info.latitude,
// info.longitude,
// getImageSuccessEvent.getImageUrl(),
// getImageSuccessEvent.getType()
// )
}
/**
* 初始化定位监听
*/
private fun initLocationListener() {
val isOpen = SharedPrefsMgr.getInstance(AbsMogoApplication.getApp().applicationContext).getBoolean("KEY_SERVER_REPORTSTRATEGY_SWITCH", false)
Logger.d("EntrancePresenter", " initLocationListener ---- isOpen = $isOpen")
//开始开始监听速度,只要超过一次5公里每小时则即开始获取策略进行本地上报 只有release才加此判断qa环境方便测试
LocationUtil.getInstance().setonSpeedlistenner(object : LocationUtil.SpeedListener {
override fun onSpeedGet(speed: Float) {
if (isOpen) {
if (speed > (5 / 3.6f) && !isGetStrategies) {
mainServiceHttpModel.initStrategies {
handleReportStrategy()
}
Log.d("MainService", "initLocationListener more than 5 start upload speed = $speed")
isGetStrategies = true
}
}
}
})
}
/**
* 上报策略判断
*/
private fun handleReportStrategy() {
//1是一次性2是周期性
picType = getStrategyFrequency(
AbsMogoApplication.getApp().applicationContext,
"pic",
getStrategyType("pic")
)
videoType = getStrategyFrequency(
AbsMogoApplication.getApp().applicationContext,
"video",
getStrategyType("video")
)
Log.d("MainService", "handleReportStrategy picType = $picType ---videoType = $videoType")
shareType = TANLU_ROAD_CURRENT
if (picType == 1) {
takePhoto()
} else if (picType == 2) {
postPhotoAlarmTask(true)
}
if (videoType == 1) {
takeVideo(DEFAULT_VIDEO_DURATION)
} else if (videoType == 2) {
postVideoAlarmTask(true)
}
}
private fun registReceiver() {
//注册定时任务回调
val intentFilter = IntentFilter(alarmBroadAction)
intentFilter.addAction("com.zhidao.sendmessage.test")
intentFilter.addAction("com.zhidao.takevideo.test")
intentFilter.addAction("com.zhidao.takepic.test")
registerReceiver(mAlarmBroadCast, intentFilter)
//注册主动上报广播
// var customSendIntentFilter = IntentFilter(sendInformationAction)
// registerReceiver(mCustomSendBroadCast, customSendIntentFilter)
}
//获取图片
private fun takePhoto(isInterval: Boolean = false, isCustom: Boolean = false) {
Log.d("MainService", "takePhoto -----1----->")
//判断是否授权
// if (isAuthorization(BaseApplication.getAppContext())) {
//目前不支持连拍只能定时2秒拍一张 第一期每次只拍一张
Observable.intervalRange(0, 1, 0, 2_000, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
CarCorderController.takePhoto(1, 1, false, isCustom, TANLU_ROAD_CURRENT, mainInfoId, fromType, mLongitude, mLatitude, speed, mAddress, mDirection, mAreaCode, mCityCode)
}
// }
postPhotoAlarmTask(isInterval)
this@MainService.isCustom = isCustom
}
//获取录像
private fun takeVideo(
duration: Int,
isInterval: Boolean = false,
isCustom: Boolean = false,
id: Long = 0
) {
Log.d("MainService", "takeVideo --------1----> fromType = $fromType --isCustom = $isCustom")
// if (isAuthorization(BaseApplication.getAppContext())) {
CarCorderController.takeVideo(1, duration, isCustom, id, shareType, mainInfoId, fromType, mLongitude, mLatitude, speed, mAddress, mDirection, mAreaCode, mCityCode)
// }
postVideoAlarmTask(isInterval)
this@MainService.isCustom = isCustom
}
private fun postPhotoAlarmTask(isInterval: Boolean = false) {
if (isInterval) {
trackNormalEvent(CarNet_Create_Task, null)
val intent = Intent()
intent.action = alarmBroadAction
intent.putExtra("number", getLong(PIC_NUMBER, PIC_NUMBER_DEFAULT))
intent.putExtra("AlarmType", AlarmTypePic)
val pendingIntent = PendingIntent.getBroadcast(this, AlarmTypePic, intent, 0)
Log.d(
"MainService",
"postPhotoAlarmTask time =" + getStrategyInterval(
AbsMogoApplication.getApp().applicationContext,
"pic",
getStrategyType("pic")
)
)
mAlarmManager.setExact(
AlarmManager.RTC, System.currentTimeMillis() +
getStrategyInterval(
AbsMogoApplication.getApp().applicationContext,
"pic",
getStrategyType("pic")
)
, pendingIntent
)
}
}
//开始倒计时任务
private fun postVideoAlarmTask(isInterval: Boolean = false) {
if (isInterval) {
trackNormalEvent(CarNet_Create_Task, null)
val videoIntent = Intent()
videoIntent.action = alarmBroadAction
videoIntent.putExtra(
"duration", (VIDEO_DURATION_DEFAULT / 1000).toInt()
)
videoIntent.putExtra("AlarmType", AlarmTypeVideo)
val videoPendingIntent =
PendingIntent.getBroadcast(this, AlarmTypeVideo, videoIntent, 0)
Log.d(
"MainService",
"postVideoAlarmTask time =" + getStrategyInterval(
AbsMogoApplication.getApp().applicationContext,
"video",
getStrategyType("video")
)
)
mAlarmManager.setExact(
AlarmManager.RTC, System.currentTimeMillis() +
getStrategyInterval(
AbsMogoApplication.getApp().applicationContext,
"video",
getStrategyType("video")
)
, videoPendingIntent
)
}
}
override fun onDestroy() {
super.onDestroy()
unregisterReceiver(mAlarmBroadCast)
// unregisterReceiver(mCustomSendBroadCast)
CarCorderController.release()
CosCallbackMapController.release()
LatLngStickyEventBus.getInstance().unregister(this)
LatLngStickyEventBus.getInstance().removeAllStickyEvents()
trackNormalEvent(CarNet_MainService_Destory, null)
}
}

View File

@@ -0,0 +1,255 @@
package com.zhidao.roadcondition.service
import android.content.Intent
import android.util.Log
import com.mogo.commons.AbsMogoApplication
import com.zhidao.roadcondition.model.*
import com.zhidao.roadcondition.net.request
import com.zhidao.roadcondition.util.*
import com.zhidao.roadcondition.util.StrategyPreferenceUtil.Companion.setStrategyFrequency
import com.zhidao.roadcondition.util.StrategyPreferenceUtil.Companion.setStrategyInterval
import com.zhidao.roadcondition.util.StrategyPreferenceUtil.Companion.setStrategyMaxSpeed
import com.zhidao.roadcondition.util.StrategyPreferenceUtil.Companion.setStrategyMinSpeed
import com.zhidao.roadcondition.util.StrategyPreferenceUtil.Companion.setStrategyType
import com.zhidao.roadcondition.util.StrategyPreferenceUtil.Companion.setStrategyValidity
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
class MainServiceController {
companion object {
const val TAG = "MainServiceController"
}
//逆地理编码是否重试
private var geoRetry = false
private var mFromType: String = ""
private var mPoiType: String = ""
private val strategyModel by lazy { StrategyServiceModel() }
fun initStrategies(initFinish: (() -> Unit)? = null) {
request<BaseResponse<Results>> {
loader {
strategyModel.getCityStrategy()
}
onSuccess {
it.result.let { strategy ->
trackNormalEvent(CarNet_Get_Strategy, null)
Log.d(TAG, "pic =" + strategy.cityStrategy.pic)
Log.d(TAG, "video =" + strategy.cityStrategy.video)
syncStrategiesData(strategy.cityStrategy)
initFinish?.invoke()
}
}
onError {
reInitStrategies()
Log.e(TAG, "initStrategies onError ${it.message}")
}
}
}
private fun reInitStrategies() = runBlocking {
launch {
// delay(30_000)
initStrategies()
}
}
//将数据同步到sharePreference中
private fun syncStrategiesData(strategy: CityStrategy) {
if (strategy.pic != null && strategy.video!=null) {
setStrategyType(AbsMogoApplication.getApp().applicationContext, strategy.pic!!.strategyType, "pic")
setStrategyType(AbsMogoApplication.getApp().applicationContext, strategy.video!!.strategyType, "video")
setStrategyFrequency(
AbsMogoApplication.getApp().applicationContext,
strategy.pic!!.strategyType,
"pic",
strategy.pic!!.reportType
)
setStrategyFrequency(
AbsMogoApplication.getApp().applicationContext,
strategy.video!!.strategyType,
"video",
strategy.video!!.reportType
)
setStrategyInterval(
AbsMogoApplication.getApp().applicationContext,
strategy.pic!!.strategyType,
"pic",
strategy.pic!!.reportTimeInterval * 60 * 1000L
)
setStrategyInterval(
AbsMogoApplication.getApp().applicationContext,
strategy.video!!.strategyType,
"video",
strategy.video!!.reportTimeInterval * 60 * 1000L
)
setStrategyValidity(
AbsMogoApplication.getApp().applicationContext,
strategy.pic!!.strategyType,
"pic",
strategy.pic!!.infoTimeout
)
setStrategyValidity(
AbsMogoApplication.getApp().applicationContext,
strategy.video!!.strategyType,
"video",
strategy.video!!.infoTimeout
)
setStrategyMaxSpeed(
AbsMogoApplication.getApp().applicationContext,
strategy.pic!!.strategyType,
"pic",
strategy.pic!!.getMaxSpeed()
)
setStrategyMaxSpeed(
AbsMogoApplication.getApp().applicationContext,
strategy.video!!.strategyType,
"video",
strategy.video!!.getMaxSpeed()
)
setStrategyMinSpeed(
AbsMogoApplication.getApp().applicationContext,
strategy.pic!!.strategyType,
"pic",
strategy.pic!!.minSpeed
)
setStrategyMinSpeed(
AbsMogoApplication.getApp().applicationContext,
strategy.video!!.strategyType,
"video",
strategy.video!!.minSpeed
)
} else {
Log.d(TAG, "strategy Data is null")
}
}
//上传情报数据
fun sendInformationMessage(
fromType: String,
type: Int,
url: Map<String, String>,
isCustom: Boolean = false,
trafficInfoType:String = "",
isShare:Boolean = false,
poiType:String,
mainInfoId:Long,
speed: Float,
longitude: Double,
latitude: Double,
customSend: ((Boolean) -> Unit)? = null
) {
Log.d(TAG, " sendInformationMessage poiType = $poiType -- fromType = $fromType")
mFromType = fromType
mPoiType = poiType
val locationInfo = LocationUtil.getInstance().getLocationInfo()
if (locationInfo.address.isEmpty()) {
Log.d(TAG, " sendInformationMessage locationInfo.address = $locationInfo.address")
trackUploadGeo(1)
geoLocation(type, url, locationInfo, isCustom,trafficInfoType, isShare, customSend,poiType,mainInfoId,speed, longitude,latitude)
} else {
postInformationMessage(
getInformationBody(type, url, locationInfo, isCustom, trafficInfoType, isShare,poiType,mainInfoId,longitude,latitude, speed, mFromType),
customSend
)
}
}
//如果address为空则逆地理编码
private fun geoLocation(
type: Int,
url: Map<String, String>,
locationInfo: LocationInfo,
isCustom: Boolean,
trafficInfoType:String,
isShare: Boolean,
customSend: ((Boolean) -> Unit)? = null,
poiType: String,
mainInfoId: Long,
speed: Float,
longitude: Double,
latitude: Double
) {
Log.d(TAG, " geoLocation -- poiType = $poiType")
LocationUtil.getInstance()
.geoCodeLocation(locationInfo.toLatLngPoint(), {
Log.d(TAG, "geoLocation -------start -->")
postInformationMessage(
getInformationBody(type, url, locationInfo, isCustom, trafficInfoType, isShare, poiType,mainInfoId,longitude,latitude, speed, mFromType),
customSend
)
}, {
//转换失败的情况下再重试一次
Log.d(TAG, "geoLocation -------true-->")
geoRetry = if (!geoRetry) {
geoLocation(type, url, locationInfo, isCustom, trafficInfoType, isShare, customSend, poiType,mainInfoId,speed, longitude,latitude)
true
} else {
//如果两次都失败,直接上报服务端
Log.d(TAG, "geoLocation ---- postInformationMessage ---false-->")
postInformationMessage(
getInformationBody(type, url, locationInfo, isCustom, trafficInfoType, isShare, poiType,mainInfoId,longitude,latitude,speed, mFromType),
customSend
)
false
}
})
}
private fun postInformationMessage(
informationBody: InformationBody,
customSend: ((Boolean) -> Unit)? = null
) {
trackUploadServer(3)
request<BaseResponse<UploadResult>> {
loader {
strategyModel.uploadInformation(informationBody)
}
onSuccess {
Log.i(TAG, "upload success id = " + it.result.id)
if (mFromType == "6") {
sendUgcStatusReceiver(it.result.id, mPoiType, mFromType)
}
trackUploadServer(1)
customSend?.invoke(true)
}
onError {
Log.i(TAG, "$it upload message ${it.message}")
trackUploadServer(2)
if (mFromType == "6") {
sendUgcStatusReceiver(0, mPoiType, mFromType)
}
customSend?.invoke(false)
}
}
}
private fun sendUgcStatusReceiver(id: Long, type: String?, fromType: String?) {
Log.d(TAG, "sendUgcStatusReceiver id = $id ---type = $type --fromType = $fromType ")
val intent = Intent()
intent.action = "com.v2x.ugc.upload.status"
intent.putExtra("id", id)
intent.putExtra("type", type)
intent.putExtra("fromType", fromType)
AbsMogoApplication.getApp().applicationContext.sendBroadcast(intent)
}
//上传服务器
private fun trackUploadServer(type: Int) {
trackNormalEvent(
CarNet_Servers_Upload, mutableMapOf("type" to type)
)
}
//上传
private fun trackUploadGeo(type: Int) {
trackNormalEvent(
CarNet_Geo, mutableMapOf("type" to type)
)
}
}

View File

@@ -0,0 +1,8 @@
package com.zhidao.roadcondition.service
val AlarmTypePic: Int = 1 //定时任务类型 1照片
val AlarmTypeVideo: Int = 2 //定时任务类型 2视频
val alarmBroadAction: String = "com.zhidao.roadCondition.AlarmBroadCast" //定时广播
val sendInformationAction: String = "com.zhidao.roadCondition.SendInformation" //主动上报广播
val customResultAction: String = "com.zhidao.roadCondition.CustomResult" //主动上报结果广播

View File

@@ -0,0 +1,14 @@
package com.zhidao.roadcondition.service
import com.zhidao.auto.carcorder.callback.TakePhotoCallback
/**
* @author congtaowang
* @since 2019-11-19
*
* 拍照拦截器
*/
interface TakePhotoInterceptor : TakePhotoCallback {
fun intercept(): Boolean
}

View File

@@ -0,0 +1,52 @@
package com.zhidao.roadcondition.service
import android.os.Parcel
import android.os.Parcelable
class UploadParams(val eventType: String, val fromType: String, val duration: Int, val parentId: Long, val lat: Double, val lon: Double,val addr:String,val direction:Float,val areaCode:String,val cityCode:String) : Parcelable {
constructor(parcel: Parcel) : this(
parcel.readString()!!,
parcel.readString()!!,
parcel.readInt(),
parcel.readLong(),
parcel.readDouble(),
parcel.readDouble(),
parcel.readString()!!,
parcel.readFloat(),
parcel.readString()!!,
parcel.readString()!!
)
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeString(eventType)
parcel.writeString(fromType)
parcel.writeInt(duration)
parcel.writeLong(parentId)
parcel.writeDouble(lat)
parcel.writeDouble(lon)
parcel.writeString(addr)
parcel.writeFloat(direction)
parcel.writeString(areaCode)
parcel.writeString(cityCode)
}
override fun describeContents(): Int {
return 0
}
override fun toString(): String {
return "UploadParams(eventType='$eventType', fromType='$fromType', duration=$duration, parentId=$parentId, lat=$lat, lon=$lon, addr='$addr', direction=$direction, areaCode='$areaCode', cityCode='$cityCode')"
}
companion object CREATOR : Parcelable.Creator<UploadParams> {
override fun createFromParcel(parcel: Parcel): UploadParams {
return UploadParams(parcel)
}
override fun newArray(size: Int): Array<UploadParams?> {
return arrayOfNulls(size)
}
}
}

View File

@@ -0,0 +1,53 @@
package com.zhidao.roadcondition.util
import android.content.Context
import com.elegant.analytics.Analytics
import com.mogo.cloud.passport.MoGoAiCloudClientConfig
import com.mogo.commons.AbsMogoApplication
import com.mogo.commons.network.Utils.getFotaVersion
import java.util.HashMap
const val CarNet_Alive:String = "CarNet_Alive"//探路日活DAU埋点
const val CarNet_Scheme_Channel:String = "CarNet_Scheme_Channel"//探路Scheme渠道区分
const val APPENTER_FRONT:String = "appenterfront"//探路Scheme渠道区分
const val CarNet_auto_upload:String = "CarNet_auto_upload"//情报自动上传时间(自动上报图片)
const val CarNet_click:String ="CarNet_click"//用户点击查看大图
const val CarNet_right_handle:String ="CarNet_right_handle"//用户切换情报-地图切换
const val CarNet_event_number:String ="CarNet_event_number" //用户获取的信息数量
const val CarNet_user_upload:String ="CarNet_user_upload" //用户主动上报视频type=1 手动点击 type=2 语音
const val CarNet_auto_upload_video:String ="CarNet_auto_upload_video"//自动上报视频
const val CarNet_click_search:String = "CarNet_click_search" //点击地图搜索
const val CarNet_user_get:String = "CarNet_user_get"//用户查看情报(首页视频/图片开始播放即算做用户查看type=1 视频 type=2 图片
const val CarNet_road_search:String = "CarNet_road_search"//有导航路线时的沿途查询
const val CarNet_MainService:String = "CarNet_MainService" //开机
const val CarNet_MainService_Start:String ="CarNet_MainService_Start" //服务开启
const val CarNet_MainService_Destory:String ="CarNet_MainService_Destory" //服务销毁
const val CarNet_Get_Strategy:String = "CarNet_Get_Strategy" //策略拉取成功
const val CarNet_Create_Task:String = "CarNet_Create_Task" //创建定时任务
const val CarNet_Get_Picture:String = "CarNet_Get_Picture" //获取图片,type=1 调用, type=2 成功, type=3 失败
const val CarNet_Get_Video:String = "CarNet_Get_Video" //获取视频,type=1 调用,type=2 成功,type=3 失败
const val CarNet_Cos_Upload:String = "CarNet_Cos_Upload" //上传COS, type=1 成功, type=2 失败, type=3 开始, type=4 成功返回视频和缩略图type=5 成功但没有返回全视频和缩略图
const val CarNet_Servers_Upload:String = "CarNet_Servers_Upload" //上传服务端, type=1 成功,type=2 失败, type=3 开始
const val CarNet_Voice_Search:String = "CarNet_Voice_Search" //语音搜索路况,type=1 成功,type=2 失败
const val CarNet_USER_SHOW:String = "CarNet_user_show" //情报展示时长,showtime 加载列表时长
const val CarNet_USER_LOAD:String = "CarNet_user_load" //情报加载时长,type=1 视频2图片
const val CarNet_Geo:String = "CarNet_Geo_Location" //上传服务端, type=1开始 ,type=2 成功, type=3 失败
const val CarNet_live_broadcast = "CarNet_live_broadcast" // 地图页面点击直播(在线可直播车机)
//自定义埋点
fun trackNormalEvent(event: String, _data: MutableMap<String, Any>?, context: Context = AbsMogoApplication.getApp().applicationContext) {
var data = _data
if (data == null) {
data = HashMap()
}
val localParams = HashMap<String, Any>()
//公共参数 time sn
localParams["time"] = System.currentTimeMillis()
localParams["systemversion"] = getFotaVersion()
localParams["sn"] = MoGoAiCloudClientConfig.getInstance().getSn()
Analytics.getInstance().track(event, data)
}

View File

@@ -0,0 +1,143 @@
package com.zhidao.roadcondition.util
import android.graphics.Bitmap
import android.media.MediaMetadataRetriever
import android.os.Environment
import okhttp3.MediaType
import okhttp3.MultipartBody
import okhttp3.RequestBody
import java.io.*
import java.text.SimpleDateFormat
import java.util.*
//创建文件上传请求体
fun fileToMultiPart(fileUrl: String): MultipartBody.Part? {
val file = File(fileUrl)
if (file.exists()) {
val requestBody = RequestBody.create(MediaType.parse("image/jpg"), file)
return MultipartBody.Part.createFormData("file", file.name, requestBody)
}
return null
}
fun deletePicFile(filePath: String?): Boolean {
val file = File(filePath)
if (file.exists()) {
//如果图片地址包含此路径则是C上面的拍照需要再删除后摄图片
if (filePath!!.contains("usbotg-1-1.1")) {
//将地址替换成后摄图片地址
val backFile =
File(filePath.replace("frontPic", "backPic").replace("PhotoFront", "PhotoBack"))
if (backFile.exists()) {
return backFile.delete()
}
return file.delete()
} else
return file.delete()
}
return false
}
//删除某个目录下所有文件
fun deleteAllFile(file: File?) { //判断文件不为null或文件目录存在
if (file == null || !file.exists()) {
return
}
//取得这个目录下的所有子文件对象
val files: Array<File> = file.listFiles()
//遍历该目录下的文件对象
for (f in files) {
//判断子目录是否存在子目录,如果是文件则删除
if (f.isDirectory) {
deleteAllFile(f)
} else {
f.delete()
}
}
//删除空文件夹 for循环已经把上一层节点的目录清空。
// file.delete()
}
//根据本地视频文件生成缩略图文件
fun getVideoThumbnail(filePath: String, picPath: String): Boolean {
var b: Bitmap? = null
val retriever = MediaMetadataRetriever()
try {
retriever.setDataSource(filePath)
b = retriever.getFrameAtTime(0)
} catch (e: IllegalArgumentException) {
e.printStackTrace()
} catch (e: RuntimeException) {
e.printStackTrace()
} finally {
try {
retriever.release()
} catch (e: RuntimeException) {
e.printStackTrace()
}
}
return bitmapToFile(b, picPath)
}
//bitmap转为file
fun bitmapToFile(bitmap: Bitmap?, filePath: String): Boolean {
val baos = ByteArrayOutputStream()
bitmap?.compress(Bitmap.CompressFormat.JPEG, 50, baos)
val file = File(filePath)
try {
if (file.exists())
file.delete()
file.createNewFile()
val fos = FileOutputStream(file)
val ins = ByteArrayInputStream(baos.toByteArray())
var x = 0
val b = ByteArray(1024 * 100)
while ({ x = ins.read(b);x }() != -1) {
fos.write(b, 0, x)
}
fos.close()
bitmap?.recycle()
} catch (e: Exception) {
e.printStackTrace()
return false
}
return true
}
fun deleteCompressVideoFile(path: String?) {
val file = File(path)
val files = file.listFiles()
for (f in files) {
// /storage/emulated/0/Movies/compress_video_20210202121826.mp4
if (f.toString().contains("compress_video")) {
f.delete()
}
}
}
//获取压缩后的视频路径
fun getCompressVideoPath(): String {
val moviesDir = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_MOVIES
)
moviesDir.mkdirs()
val builder = StringBuilder()
builder.append("compress_video_")
.append(SimpleDateFormat("yyyyMMddHHmmss").format(Date()))
val filePrefix = builder.toString()
val fileExtn = ".mp4"
var destPath = File(moviesDir, filePrefix + fileExtn)
var fileNo = 0
while (destPath.exists()) {
fileNo++
destPath = File(moviesDir, filePrefix + fileNo + fileExtn)
}
return destPath.absolutePath
}

View File

@@ -0,0 +1,162 @@
package com.zhidao.roadcondition.util
import android.util.Log
import com.mogo.commons.AbsMogoApplication
import com.mogo.map.MogoLatLng
import com.mogo.map.location.MogoLocation
import com.mogo.map.search.geo.IMogoGeoSearchListener
import com.mogo.map.search.geo.MogoRegeocodeAddress
import com.mogo.map.search.geo.MogoRegeocodeResult
import com.mogo.map.search.geo.query.MogoRegeocodeQuery
import com.mogo.module.common.MogoApisHandler
import com.zhidao.roadcondition.model.LocationInfo
private fun toLocInfo(
address: MogoRegeocodeAddress,
latlngPoint: MogoLatLng
): LocationInfo {
return LocationInfo(
address.province,
address.city,
address.cityCode,
address.district,
address.adCode,
address.roads[0].name,
latlngPoint.lon,
latlngPoint.lat,
address.formatAddress,
System.currentTimeMillis()
)
}
private fun toLocInfo(location: MogoLocation): LocationInfo {
return LocationInfo(
location.province,
location.cityName,
location.cityCode,
location.district,
location.adCode,
location.street,
location.longitude,
location.latitude,
location.address,
location.time,
location.bearing
)
}
class LocationUtil private constructor() {
companion object {
private const val TAG = "LocationUtil"
private var instance: LocationUtil? = null
@Synchronized
fun getInstance(): LocationUtil {
if (instance == null) {
instance = LocationUtil()
}
return instance!!
}
}
interface OnMapLocationChangedListener {
fun onChanged(locationInfo: LocationInfo)
}
private var listener: OnMapLocationChangedListener? = null
private var init: Boolean = false
private var mContext = AbsMogoApplication.getApp().applicationContext
private var speedListener: SpeedListener? = null
private var locationInfo: LocationInfo? = null
private var speed: Float = 0.0f
fun initLocation() {
//初始化client
init = true
MogoApisHandler.getInstance().apis.mapServiceApi.getSingletonLocationClient(mContext).addLocationListener {location->
if (null != location) {
locationInfo = LocationInfo()
locationInfo!!.longitude = location.longitude
locationInfo!!.latitude = location.latitude
locationInfo!!.address = location.address
locationInfo!!.time = location.time
locationInfo!!.provinceName = location.province
locationInfo!!.cityName = location.cityName
locationInfo!!.cityCode = location.cityCode
locationInfo!!.areaName = location.district
locationInfo!!.areaCode = location.adCode
locationInfo!!.street = location.street
locationInfo!!.direction = location.bearing
listener?.onChanged(locationInfo!!)
speed = location.speed
speedListener?.onSpeedGet(location.speed)
} else {
Log.d(TAG, "定位失败 -> location is null")
}
}
}
fun getLocationInfo(): LocationInfo {
return if (null != locationInfo) {
locationInfo!!
} else {
val location = MogoApisHandler.getInstance().apis.mapServiceApi.getSingletonLocationClient(mContext).lastKnowLocation
toLocInfo(location)
}
}
//如果获取到的location address为空可以通过高德逆地理编码获得
fun geoCodeLocation(
latlngPoint: MogoLatLng,
locGeoCode: (((locInfo: LocationInfo) -> Unit)),
onError: ((msg: String) -> Unit)
) {
val geocoderSearch = MogoApisHandler.getInstance().apis.mapServiceApi.getGeoSearch(AbsMogoApplication.getApp().applicationContext)
val regeocodeQuery = MogoRegeocodeQuery()
regeocodeQuery.latlngType = ""
regeocodeQuery.point = latlngPoint
regeocodeQuery.radius = 200
geocoderSearch.getFromLocationAsyn(regeocodeQuery)
geocoderSearch.setGeoSearchListener(object : IMogoGeoSearchListener {
override fun onRegeocodeSearched(regeocodeResult: MogoRegeocodeResult?) {
super.onRegeocodeSearched(regeocodeResult)
if( regeocodeResult == null ){
trackUploadGeo(3)
onError.invoke("geoCode")
} else {
trackUploadGeo(2)
val regeocodeAddress = regeocodeResult.regeocodeAddress
regeocodeAddress?.let {
val locInfo = toLocInfo(regeocodeAddress, latlngPoint)
locGeoCode.invoke(locInfo)
}
}
}
})
}
//上传
private fun trackUploadGeo(type: Int) {
trackNormalEvent(
CarNet_Geo, mutableMapOf("type" to type)
)
}
fun getSpeed():Float{
return speed
}
fun setonSpeedlistenner(speedListener: SpeedListener) {
this.speedListener = speedListener
}
interface SpeedListener {
fun onSpeedGet(speed: Float)
}
}

View File

@@ -0,0 +1,78 @@
package com.zhidao.roadcondition.util
import android.util.Log
import com.google.gson.JsonArray
import com.google.gson.JsonObject
import com.mogo.cloud.passport.MoGoAiCloudClientConfig
import com.mogo.commons.AbsMogoApplication
import com.zhidao.roadcondition.model.InformationBody
import com.zhidao.roadcondition.model.LocationInfo
import com.zhidao.roadcondition.model.proxy.INFO_TYPE_IMG
import com.zhidao.roadcondition.model.proxy.INFO_TYPE_VIDEO
import com.zhidao.roadcondition.util.StrategyPreferenceUtil.Companion.getStrategyType
import com.zhidao.roadcondition.util.StrategyPreferenceUtil.Companion.getStrategyValidity
fun getInformationBody(
types: Int,
urls: Map<String, String>,
locationInfo: LocationInfo,
isCustom: Boolean,
trafficInfoType:String = "",
isShare:Boolean,
poiType: String,
mainInfoId: Long,
longitude: Double,
latitude: Double,
speed: Float,
fromType: String
): InformationBody {
val jsonArray = JsonArray()
val type: Int
type = if (types == INFO_TYPE_VIDEO) {
val videoObject = JsonObject()
videoObject.addProperty("thumbnail", urls["thumb"])
videoObject.addProperty("url", urls["video"])
jsonArray.add(videoObject)
INFO_TYPE_VIDEO
} else {
val urlObject = JsonObject()
urlObject.addProperty("url", urls["pic"])
jsonArray.add(urlObject)
INFO_TYPE_IMG
}
val infoType = if (isCustom) 1 else 0
Log.d("MainServiceController", "isCustom = $isCustom ---- infoType = $infoType")
return InformationBody(
jsonArray.toString(),
locationInfo.address,
locationInfo.areaCode,
locationInfo.areaName,
locationInfo.cityCode,
locationInfo.cityName,
System.currentTimeMillis(),
if (latitude == 0.0) locationInfo.latitude else latitude,
if (longitude == 0.0) locationInfo.longitude else longitude,
locationInfo.provinceName,
MoGoAiCloudClientConfig.getInstance().getSn(),
locationInfo.street,
type,
0,
infoType,
getStrategyValidity(AbsMogoApplication.getApp().applicationContext, convertUploadTypeOfSP(type), getStrategyType(convertUploadTypeOfSP(type))),
trafficInfoType,
isShare,
locationInfo.direction,
poiType,
mainInfoId,
speed,
fromType
)
}
private fun convertUploadTypeOfSP(type: Int): String {
return when (type) {
INFO_TYPE_IMG -> "pic"
INFO_TYPE_VIDEO -> "video"
else -> "pic"
}
}

View File

@@ -0,0 +1,50 @@
package com.zhidao.roadcondition.util
import android.content.Context
import androidx.core.content.edit
import com.mogo.commons.AbsMogoApplication
const val FILE_NAME = "settings_data"
const val PIC_NUMBER = "PIC_NUMBER" //图片拍摄张数
const val VIDEO_DURATION_DEFAULT = 10_000L //视频拍摄时长
const val PIC_NUMBER_DEFAULT = 1L //图片拍摄张数
fun putLong(key: String, value: Long) {
val sharedPreferences =
AbsMogoApplication.getApp().applicationContext.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE)
sharedPreferences.edit {
putLong(key, value).apply()
}
}
fun getLong(key: String, defaultValue: Long): Long {
return AbsMogoApplication.getApp().applicationContext.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE)
.getLong(key, defaultValue)
}
fun putInt(key: String, value: Int) {
val sharedPreferences =
AbsMogoApplication.getApp().applicationContext.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE)
sharedPreferences.edit {
putInt(key, value).apply()
}
}
fun getInt(key: String, defaultValue: Int): Int {
return AbsMogoApplication.getApp().applicationContext.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE)
.getInt(key, defaultValue)
}
fun putCommonString(key: String, value: String) {
val sharedPreferences =
AbsMogoApplication.getApp().applicationContext.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE)
sharedPreferences.edit {
putString(key, value).apply()
}
}
fun getCommonBoolean(key: String, defaultValue: Boolean): Boolean {
return AbsMogoApplication.getApp().applicationContext.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE)
.getBoolean(key, defaultValue)
}

View File

@@ -0,0 +1,199 @@
package com.zhidao.roadcondition.util
import android.content.Context
import android.util.Log
import androidx.core.content.edit
import com.mogo.commons.AbsMogoApplication
const val STRATEGY_FILE_NAME = "strategy_data"
const val STRATEGY_TYPE_COPY = "strategy_data_copy"
/**
* 策略SP存储模式STRATEGY_TYPE + 策略类型 + 情报类型 + 对应项
*/
const val STRATEGY_TYPE = "STRATEGY_"
const val FREQUENCY = "FREQUENCY"
const val DEFAULT_FREQUENCY_PIC = 2 //默认上报类型:周期性上报
const val DEFAULT_FREQUENCY_VIDEO = 1 //默认上报类型:单次上报
const val INTERVAL = "INTERVAL"
const val DEFAULT_INTERVAL_PIC = 600_000L //默认图片定时任务默认间隔
const val DEFAULT_INTERVAL_VIDEO = 900_000L //默认视频定时任务默认间隔
const val VALIDITY = "VALIDITY"
const val DEFAULT_VALIDITY = 4 * 60 //默认情报有效期4小时
const val MAX_SPEED = "MAX_SPEED"
const val MIN_SPEED = "MIN_SPEED"
const val DEFAULT_MAX_SPEED = -1
const val DEFAULT_MIN_SPEED = -1
class StrategyPreferenceUtil {
companion object {
//保存策略类型在AccOff的时候清除数据更新时需要在没有情报上传时操作
fun setStrategyType(context: Context, strategyType: Int, type: String) {
val sharedPreferences =
context.getSharedPreferences(STRATEGY_TYPE_COPY, Context.MODE_PRIVATE)
sharedPreferences.edit {
putInt(STRATEGY_TYPE + type, strategyType).commit()
}
}
fun getStrategyType(
type: String,
context: Context = AbsMogoApplication.getApp().applicationContext
): Int {
return context.getSharedPreferences(STRATEGY_TYPE_COPY, Context.MODE_PRIVATE)
.getInt(STRATEGY_TYPE + type, 1)
}
fun clearStrategyType(context: Context) {
val sharedPreferences =
context.getSharedPreferences(STRATEGY_TYPE_COPY, Context.MODE_PRIVATE)
sharedPreferences.edit {
clear()
val clearStrategyType = commit()
Log.d(STRATEGY_TYPE_COPY, "clearStrategyType = $clearStrategyType")
}
}
fun setStrategyFrequency(
context: Context,
strategyType: Int,
infoType: String,
frequency: Int
) {
val sharedPreferences =
context.getSharedPreferences(STRATEGY_FILE_NAME, Context.MODE_PRIVATE)
sharedPreferences.edit {
putInt(
STRATEGY_TYPE + strategyType + "_" + infoType + FREQUENCY,
frequency
).commit()
}
}
fun getStrategyFrequency(context: Context, infoType: String, strategyType: Int = 1): Int {
return context.getSharedPreferences(STRATEGY_FILE_NAME, Context.MODE_PRIVATE)
.getInt(
STRATEGY_TYPE + strategyType + "_" + infoType + FREQUENCY,
convertFrequencyOfInfoType(infoType)
)
}
private fun convertFrequencyOfInfoType(infoType: String): Int {
return when (infoType) {
"pic" -> DEFAULT_FREQUENCY_PIC
"video" -> DEFAULT_FREQUENCY_VIDEO
else -> DEFAULT_FREQUENCY_PIC
}
}
fun setStrategyInterval(
context: Context,
strategyType: Int,
infoType: String,
interval: Long
) {
val sharedPreferences =
context.getSharedPreferences(STRATEGY_FILE_NAME, Context.MODE_PRIVATE)
sharedPreferences.edit {
putLong(
STRATEGY_TYPE + strategyType + "_" + infoType + INTERVAL,
interval
).commit()
}
}
fun getStrategyInterval(context: Context, infoType: String, strategyType: Int = 1): Long {
return context.getSharedPreferences(STRATEGY_FILE_NAME, Context.MODE_PRIVATE)
.getLong(
STRATEGY_TYPE + strategyType + "_" + infoType + INTERVAL,
convertIntervalOfInfoType(infoType)
)
}
private fun convertIntervalOfInfoType(infoType: String): Long {
return when (infoType) {
"pic" -> DEFAULT_INTERVAL_PIC
"video" -> DEFAULT_INTERVAL_VIDEO
else -> DEFAULT_INTERVAL_PIC
}
}
fun setStrategyValidity(
context: Context,
strategyType: Int,
infoType: String,
validity: Int
) {
val sharedPreferences =
context.getSharedPreferences(STRATEGY_FILE_NAME, Context.MODE_PRIVATE)
sharedPreferences.edit {
putInt(
STRATEGY_TYPE + strategyType + "_" + infoType + VALIDITY,
validity
).commit()
}
}
fun getStrategyValidity(context: Context, infoType: String, strategyType: Int = 1): Int {
return context.getSharedPreferences(STRATEGY_FILE_NAME, Context.MODE_PRIVATE)
.getInt(
STRATEGY_TYPE + strategyType + "_" + infoType + VALIDITY,
DEFAULT_VALIDITY
)
}
fun setStrategyMaxSpeed(
context: Context,
strategyType: Int,
infoType: String,
maxSpeed: Int
) {
val sharedPreferences =
context.getSharedPreferences(STRATEGY_FILE_NAME, Context.MODE_PRIVATE)
sharedPreferences.edit {
putInt(
STRATEGY_TYPE + strategyType + "_" + infoType + MAX_SPEED,
maxSpeed
).commit()
}
}
fun getStrategyMaxSpeed(context: Context, infoType: String, strategyType: Int = 1): Int {
return context.getSharedPreferences(STRATEGY_FILE_NAME, Context.MODE_PRIVATE)
.getInt(
STRATEGY_TYPE + strategyType + "_" + infoType + MAX_SPEED,
DEFAULT_MAX_SPEED
)
}
fun setStrategyMinSpeed(
context: Context,
strategyType: Int,
infoType: String,
minSpeed: Int
) {
val sharedPreferences =
context.getSharedPreferences(STRATEGY_FILE_NAME, Context.MODE_PRIVATE)
sharedPreferences.edit {
putInt(
STRATEGY_TYPE + strategyType + "_" + infoType + MIN_SPEED,
minSpeed
).commit()
}
}
fun getStrategyMinSpeed(context: Context, infoType: String, strategyType: Int = 1): Int {
return context.getSharedPreferences(STRATEGY_FILE_NAME, Context.MODE_PRIVATE)
.getInt(
STRATEGY_TYPE + strategyType + "_" + infoType + MIN_SPEED,
DEFAULT_MIN_SPEED
)
}
}
}

View File

@@ -0,0 +1,28 @@
package com.zhidao.roadcondition.util
import kotlinx.coroutines.*
@ObsoleteCoroutinesApi
@PublishedApi
internal var ThreadPool =
newFixedThreadPoolContext(Runtime.getRuntime().availableProcessors() * 2, "ThreadPool")
/**
* 在主线程中顺序执行,协程函数,一般用于最外层
* 该函数会阻塞代码继续执行
*/
//inline fun taskBlockOnMainThread(delayTime: Long = 0, noinline job: suspend () -> Unit) = runBlocking {
// delay(delayTime)
// job()
//}
/**
* 并发执行,常用于最外层
* 特点带返回值
*/
@ObsoleteCoroutinesApi
fun <T> taskAsync(delayTime: Long = 0, job: suspend () -> T) = GlobalScope.async(ThreadPool) {
delay(delayTime)
job()
}