Merge branch 'dev' into dev_custom_map

# Conflicts:
#	libraries/mogo-map/src/main/java/com/mogo/map/MogoNavi.java
#	modules/mogo-module-v2x/src/main/java/com/mogo/module/v2x/V2XModuleProvider.java
This commit is contained in:
wangcongtao
2020-08-14 16:24:47 +08:00
928 changed files with 25369 additions and 4487 deletions

3
.idea/dictionaries/admin.xml generated Normal file
View File

@@ -0,0 +1,3 @@
<component name="ProjectDictionaryState">
<dictionary name="admin" />
</component>

7
.idea/gradle.xml generated
View File

@@ -22,6 +22,8 @@
<option value="$PROJECT_DIR$/libraries/map-custom" />
<option value="$PROJECT_DIR$/libraries/mogo-map" />
<option value="$PROJECT_DIR$/libraries/mogo-map-api" />
<option value="$PROJECT_DIR$/libraries/mogo-tanlu-api" />
<option value="$PROJECT_DIR$/libraries/tanlulib" />
<option value="$PROJECT_DIR$/main-extensions" />
<option value="$PROJECT_DIR$/main-extensions/mogo-module-main-independent" />
<option value="$PROJECT_DIR$/main-extensions/mogo-module-main-launcher" />
@@ -30,8 +32,6 @@
<option value="$PROJECT_DIR$/modules/mogo-module-authorize" />
<option value="$PROJECT_DIR$/modules/mogo-module-back" />
<option value="$PROJECT_DIR$/modules/mogo-module-common" />
<option value="$PROJECT_DIR$/modules/mogo-module-event-panel" />
<option value="$PROJECT_DIR$/modules/mogo-module-event-panel-noop" />
<option value="$PROJECT_DIR$/modules/mogo-module-extensions" />
<option value="$PROJECT_DIR$/modules/mogo-module-gps-simulator" />
<option value="$PROJECT_DIR$/modules/mogo-module-gps-simulator-debug" />
@@ -41,6 +41,7 @@
<option value="$PROJECT_DIR$/modules/mogo-module-main" />
<option value="$PROJECT_DIR$/modules/mogo-module-map" />
<option value="$PROJECT_DIR$/modules/mogo-module-media" />
<option value="$PROJECT_DIR$/modules/mogo-module-monitor" />
<option value="$PROJECT_DIR$/modules/mogo-module-obu" />
<option value="$PROJECT_DIR$/modules/mogo-module-push" />
<option value="$PROJECT_DIR$/modules/mogo-module-push-base" />
@@ -50,7 +51,9 @@
<option value="$PROJECT_DIR$/modules/mogo-module-share" />
<option value="$PROJECT_DIR$/modules/mogo-module-splash" />
<option value="$PROJECT_DIR$/modules/mogo-module-splash-noop" />
<option value="$PROJECT_DIR$/modules/mogo-module-tanlu" />
<option value="$PROJECT_DIR$/modules/mogo-module-v2x" />
<option value="$PROJECT_DIR$/modules/mogo-module-widgets" />
<option value="$PROJECT_DIR$/services" />
<option value="$PROJECT_DIR$/services/mogo-service" />
<option value="$PROJECT_DIR$/services/mogo-service-api" />

View File

@@ -2,11 +2,9 @@ apply plugin: 'com.android.application'
apply plugin: 'com.alibaba.arouter'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
import java.text.SimpleDateFormat
android {
compileSdkVersion rootProject.ext.android.compileSdkVersion
// buildToolsVersion rootProject.ext.android.buildToolsVersion
defaultConfig {
minSdkVersion rootProject.ext.android.minSdkVersion
targetSdkVersion rootProject.ext.android.targetSdkVersion
@@ -72,73 +70,83 @@ android {
independent {
manifest.srcFile 'src/independent/AndroidManifest.xml'
}
e8xx{
e8xx {
manifest.srcFile 'src/e8xx/AndroidManifest.xml'
}
em4 {
manifest.srcFile 'src/em4/AndroidManifest.xml'
}
f8xxLauncherOnlineRelease {
manifest.srcFile 'src/f8xxLauncherOnlineRelease/AndroidManifest.xml'
}
}
flavorDimensions "product", "basic", "env"
productFlavors {
//独立app
independent{
independent {
dimension "basic"
applicationId rootProject.ext.android.independentApplicationId
// 是否启动位置服务
buildConfigField 'boolean', 'LAUNCH_LOCATION_SERVICE', 'false'
// 是否使用高德sdk自定义导航
buildConfigField 'boolean', 'USE_CUSTOM_NAVI', 'true'
// 是否作为 launcher 运行
buildConfigField 'boolean', 'IS_LAUNCHER', 'false'
buildConfigField 'String', 'SOCKET_APP_ID', '\"com.mogo.launcher\"'
}
// launcher app
launcher{
launcher {
dimension "basic"
applicationId rootProject.ext.android.launcherApplicationId
// 是否启动位置服务
buildConfigField 'boolean', 'LAUNCH_LOCATION_SERVICE', 'true'
// 是否使用高德sdk自定义导航
buildConfigField 'boolean', 'USE_CUSTOM_NAVI', 'true'
// 是否作为 launcher 运行
buildConfigField 'boolean', 'IS_LAUNCHER', 'true'
buildConfigField 'String', 'SOCKET_APP_ID', '\"com.mogo.launcher\"'
}
// f系列-分体机全系列,未细分
f8xx{
f8xx {
applicationId rootProject.ext.android.fLauncherApplicationId
dimension "product"
// 使用思必驰语音
buildConfigField 'int', 'AIType','2'
buildConfigField 'int', 'AIType', '2'
buildConfigField 'boolean', 'AI_ASSIST_ACTIVE_STAUTS', 'false'
// 车机类型主要用于区分自研车机还是别人家的车机自研车机类型为0
buildConfigField 'int', 'CAR_MACHINE_TYPE', '0'
buildConfigField 'boolean', 'ROAD_EVENT_ANIMATED', 'true'
manifestPlaceholders = [AMAP_API_VALUE: rootProject.ext.android.fLauncherAmapApiValue]
// 是否使用高德sdk自定义导航
buildConfigField 'boolean', 'USE_CUSTOM_NAVI', 'false'
}
// e系列采用Launcher方案
e8xx {
applicationId rootProject.ext.android.launcherApplicationId
dimension "product"
// 使用思必驰语音
buildConfigField 'int', 'AIType','2'
buildConfigField 'int', 'AIType', '2'
buildConfigField 'boolean', 'AI_ASSIST_ACTIVE_STAUTS', 'true'
// 车机类型主要用于区分自研车机还是别人家的车机自研车机类型为0
buildConfigField 'int', 'CAR_MACHINE_TYPE', '0'
buildConfigField 'boolean', 'ROAD_EVENT_ANIMATED', 'true'
manifestPlaceholders = [AMAP_API_VALUE: rootProject.ext.android.commonLauncherAmapApiValue]
// 是否使用高德sdk自定义导航
buildConfigField 'boolean', 'USE_CUSTOM_NAVI', 'false'
}
// 同上
em4 {
applicationId rootProject.ext.android.launcherApplicationId
dimension "product"
// 使用思必驰语音
buildConfigField 'int', 'AIType','2'
buildConfigField 'int', 'AIType', '2'
buildConfigField 'boolean', 'AI_ASSIST_ACTIVE_STAUTS', 'true'
// 车机类型主要用于区分自研车机还是别人家的车机自研车机类型为0
buildConfigField 'int', 'CAR_MACHINE_TYPE', '0'
buildConfigField 'boolean', 'ROAD_EVENT_ANIMATED', 'true'
manifestPlaceholders = [AMAP_API_VALUE: rootProject.ext.android.commonLauncherAmapApiValue]
// 是否使用高德sdk自定义导航
buildConfigField 'boolean', 'USE_CUSTOM_NAVI', 'false'
}
// e系列-2+32对标D系列2+32采用独立app的形式
em3 {
applicationId rootProject.ext.android.independentApplicationId
dimension "product"
// 使用思必驰语音
buildConfigField 'int', 'AIType', '2'
@@ -146,9 +154,13 @@ android {
// 车机类型主要用于区分自研车机还是别人家的车机自研车机类型为0
buildConfigField 'int', 'CAR_MACHINE_TYPE', '0'
buildConfigField 'boolean', 'ROAD_EVENT_ANIMATED', 'false'
manifestPlaceholders = [AMAP_API_VALUE: rootProject.ext.android.commonIndependentAmapApiValue]
// 是否使用高德sdk自定义导航
buildConfigField 'boolean', 'USE_CUSTOM_NAVI', 'false'
}
// e系列-1+16对标D系列1+16采用独立app形式
em1 {
applicationId rootProject.ext.android.independentApplicationId
dimension "product"
// 使用思必驰语音
buildConfigField 'int', 'AIType', '2'
@@ -156,36 +168,51 @@ android {
// 车机类型主要用于区分自研车机还是别人家的车机自研车机类型为0
buildConfigField 'int', 'CAR_MACHINE_TYPE', '0'
buildConfigField 'boolean', 'ROAD_EVENT_ANIMATED', 'false'
manifestPlaceholders = [AMAP_API_VALUE: rootProject.ext.android.commonIndependentAmapApiValue]
// 是否使用高德sdk自定义导航
buildConfigField 'boolean', 'USE_CUSTOM_NAVI', 'false'
}
// d系列
d8xx {
applicationId rootProject.ext.android.independentApplicationId
dimension "product"
// 使用同行者语音
buildConfigField 'int', 'AIType','1'
buildConfigField 'int', 'AIType', '1'
buildConfigField 'boolean', 'AI_ASSIST_ACTIVE_STAUTS', 'true'
// 车机类型主要用于区分自研车机还是别人家的车机自研车机类型为0
buildConfigField 'int', 'CAR_MACHINE_TYPE', '0'
buildConfigField 'boolean', 'ROAD_EVENT_ANIMATED', 'false'
manifestPlaceholders = [AMAP_API_VALUE: rootProject.ext.android.commonIndependentAmapApiValue]
// 是否使用高德sdk自定义导航
buildConfigField 'boolean', 'USE_CUSTOM_NAVI', 'false'
}
// d系列 1+16 版本
d82x{
d82x {
applicationId rootProject.ext.android.independentApplicationId
dimension "product"
// 使用同行者语音
buildConfigField 'int', 'AIType','1'
buildConfigField 'int', 'AIType', '1'
buildConfigField 'boolean', 'AI_ASSIST_ACTIVE_STAUTS', 'true'
// 车机类型主要用于区分自研车机还是别人家的车机自研车机类型为0
buildConfigField 'int', 'CAR_MACHINE_TYPE', '0'
buildConfigField 'boolean', 'ROAD_EVENT_ANIMATED', 'false'
manifestPlaceholders = [AMAP_API_VALUE: rootProject.ext.android.commonIndependentAmapApiValue]
// 是否使用高德sdk自定义导航
buildConfigField 'boolean', 'USE_CUSTOM_NAVI', 'false'
}
// 比亚迪
bydauto{
bydauto {
applicationId rootProject.ext.android.bydautoIndependentApplicationId
dimension "product"
// 不使用语音
buildConfigField 'int', 'AIType','0'
buildConfigField 'int', 'AIType', '0'
buildConfigField 'boolean', 'AI_ASSIST_ACTIVE_STAUTS', 'false'
// 车机类型主要用于区分自研车机还是别人家的车机其他车机比亚迪定为1
buildConfigField 'int', 'CAR_MACHINE_TYPE', '1'
buildConfigField 'boolean', 'ROAD_EVENT_ANIMATED', 'true'
manifestPlaceholders = [AMAP_API_VALUE: rootProject.ext.android.bydautoIndependentApiValue]
// 是否使用高德sdk自定义导航
buildConfigField 'boolean', 'USE_CUSTOM_NAVI', 'true'
}
qa {
dimension "env"
@@ -206,42 +233,6 @@ android {
}
}
def generateVersionCode() {
String vn = rootProject.ext.android.versionName
String[] versions = vn.split("\\.")
if (versions.length == 3) {
int num1 = Integer.valueOf(versions[0])
int num2 = Integer.valueOf(versions[1])
int num3 = Integer.valueOf(versions[2])
if (num1 < 1 || num1 > 99){
throw new GradleException("版本号必须定义为 x.y.zx[1,99], y[0, 99], z[0, 99])")
}
if (num2 < 0 || num2 > 99){
throw new GradleException("版本号必须定义为 x.y.zx[1,99], y[0, 99], z[0, 99])")
}
if (num3 < 0 || num3 > 99){
throw new GradleException("版本号必须定义为 x.y.zx[1,99], y[0, 99], z[0, 99])")
}
StringBuilder builder = new StringBuilder()
builder.append(num1)
if( num2 > 9 ){
builder.append(num2)
} else {
builder.append("0").append(num2)
}
if( num3 > 9 ){
builder.append(num3)
} else {
builder.append("0").append(num3)
}
println("last versionCode ${builder}")
return Integer.valueOf(builder.toString())
} else {
throw new GradleException("版本号必须定义为 x.y.zx[1,99], y[0, 99], z[0, 99])")
}
}
repositories {
flatDir {
dirs 'libs'
@@ -262,9 +253,7 @@ dependencies {
implementation rootProject.ext.dependencies.carcallprovider
implementation rootProject.ext.dependencies.carcall
implementation rootProject.ext.dependencies.moduletanlu, {
exclude group: 'com.mogo.module', module: 'module-share'
}
implementation rootProject.ext.dependencies.mogologlib
if (Boolean.valueOf(RELEASE)) {
@@ -279,6 +268,13 @@ dependencies {
implementation rootProject.ext.dependencies.moduleservice
implementation rootProject.ext.dependencies.modulesplash
implementation rootProject.ext.dependencies.moduleV2x
implementation rootProject.ext.dependencies.moduletanlu, {
exclude group: 'com.mogo.module', module: 'module-share'
}
implementation rootProject.ext.dependencies.moduletanluapi
implementation rootProject.ext.dependencies.mogomonitor
implementation rootProject.ext.dependencies.mogomodulewidgets
implementation rootProject.ext.dependencies.mogomoduleback
} else {
launcherImplementation project(':main-extensions:mogo-module-main-launcher')
independentImplementation project(':main-extensions:mogo-module-main-independent')
@@ -290,22 +286,21 @@ dependencies {
implementation project(':modules:mogo-module-service')
implementation project(':modules:mogo-module-splash')
implementation project(':modules:mogo-module-v2x')
implementation project(':modules:mogo-module-tanlu'), {
exclude group: 'com.mogo.module', module: 'module-share'
}
implementation project(':libraries:mogo-tanlu-api')
implementation project(':modules:mogo-module-monitor')
implementation project(':modules:mogo-module-widgets')
implementation project(':modules:mogo-module-back')
}
apply from: "./functions/baseservices.gradle"
apply from: "./functions/socketpush.gradle"
apply from: "./functions/gpssimulator.gradle"
apply from: "./functions/eventpanel.gradle"
apply from: "./functions/leftpanel.gradle"
}
//android.applicationVariants.all { variant ->
// variant.outputs.all { //这里修改apk文件名
// outputFileName = "Launcher2.0_V${android.defaultConfig.versionName}_${getCurrentDate()}_${variant.name}_${getGitCommit()}.apk"
// println outputFileName
// }
//}
android.applicationVariants.all { variant ->
def buildTime = new Date().format("yyyyMMdd", TimeZone.getTimeZone("GMT+08:00"))
def flavor = variant.productFlavors.collect { it.name }.join('-')
@@ -321,64 +316,4 @@ android.applicationVariants.all { variant ->
}
}
def getMonthAndDay() {
SimpleDateFormat sdf = new SimpleDateFormat("MMddHH")
return sdf.format(new Date())
}
def getCurrentDate() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss")
return sdf.format(new Date())
}
def getGitCommit() {
def gitDir = new File("${new File("${rootDir}")}/.git")
if (!gitDir.isDirectory()) {
return 'non_git_build'
}
def cmd = 'git log --pretty=format:%h -1'
def gitCommit = cmd.execute().text.trim()
print gitCommit
def cmd2 = 'git status -s'
def gitStatus = cmd2.execute().text.trim()
println '---------'
print gitStatus
if (gitStatus != null && !gitStatus.isEmpty()) {
return 'local-build'
}
assert !gitCommit.isEmpty()
gitCommit
}
afterEvaluate {
it.getTasks().iterator().forEachRemaining {
def nameLowCase = it.name
if (nameLowCase.startsWith("assemble")
&& nameLowCase.contains("Independent")
&& (nameLowCase.endsWith("Debug") || nameLowCase.endsWith("Release"))) {
it.group = "assembleIndependent"
} else if (nameLowCase.startsWith("assemble")
&& nameLowCase.contains("Launcher")
&& (nameLowCase.endsWith("Debug") || nameLowCase.endsWith("Release"))) {
it.group = "assembleLauncher"
} else if (nameLowCase.startsWith("install")
&& nameLowCase.contains("Independent")
&& (nameLowCase.endsWith("Debug") || nameLowCase.endsWith("Release"))) {
it.group = "installIndependent"
} else if (nameLowCase.startsWith("install")
&& nameLowCase.contains("Launcher")
&& (nameLowCase.endsWith("Debug") || nameLowCase.endsWith("Release"))) {
it.group = "installLauncher"
}
}
}
apply from: "./regroup.gradle"

View File

@@ -1,25 +0,0 @@
// 道路事件操作面板
project.dependencies {
if (Boolean.valueOf(RELEASE)) {
d8xxImplementation rootProject.ext.dependencies.moduleventpanelnoop
d82xImplementation rootProject.ext.dependencies.moduleventpanelnoop
em1Implementation rootProject.ext.dependencies.moduleventpanelnoop
bydautoImplementation rootProject.ext.dependencies.moduleventpanelnoop
em4Implementation rootProject.ext.dependencies.moduleventpanelnoop
e8xxImplementation rootProject.ext.dependencies.moduleventpanelnoop
f8xxImplementation rootProject.ext.dependencies.moduleventpanelnoop
em3Implementation rootProject.ext.dependencies.moduleventpanelnoop
} else {
d8xxImplementation project(':modules:mogo-module-event-panel-noop')
d82xImplementation project(':modules:mogo-module-event-panel-noop')
em1Implementation project(':modules:mogo-module-event-panel-noop')
bydautoImplementation project(':modules:mogo-module-event-panel-noop')
em4Implementation project(':modules:mogo-module-event-panel-noop')
e8xxImplementation project(':modules:mogo-module-event-panel-noop')
f8xxImplementation project(':modules:mogo-module-event-panel-noop')
em3Implementation project(':modules:mogo-module-event-panel-noop')
}
}

45
app/regroup.gradle Normal file
View File

@@ -0,0 +1,45 @@
// 将 install 和 assemble 任务按功能分组
afterEvaluate {
def independent = ["em3", "em1", "d8xx", "d82x", "bydauto"]
def launcher = ["f8xx", "em4"]
it.getTasks().iterator().forEachRemaining {
def task = it
def taskName = task.name
if (taskName.startsWith("assemble")
&& taskName.contains("Independent")
&& (taskName.endsWith("Debug") || taskName.endsWith("Release"))) {
independent.forEach {
if( taskName.toLowerCase().contains(it) ){
task.group = "assembleIndependent"
}
}
} else if (taskName.startsWith("assemble")
&& taskName.contains("Launcher")
&& (taskName.endsWith("Debug") || taskName.endsWith("Release"))) {
launcher.forEach {
if( taskName.toLowerCase().contains(it) ){
task.group = "assembleLauncher"
}
}
} else if (taskName.startsWith("install")
&& taskName.contains("Independent")
&& (taskName.endsWith("Debug") || taskName.endsWith("Release"))) {
independent.forEach {
if( taskName.toLowerCase().contains(it) ){
task.group = "installIndependent"
}
}
} else if (taskName.startsWith("install")
&& taskName.contains("Launcher")
&& (taskName.endsWith("Debug") || taskName.endsWith("Release"))) {
launcher.forEach {
if( taskName.toLowerCase().contains(it) ){
task.group = "installLauncher"
}
}
}
}
}

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:sharedUserId="android.uid.system"
package="com.mogo.launcher">
</manifest>

View File

@@ -12,10 +12,10 @@
android:theme="@style/AppTheme.App"
tools:replace="android:label">
<!-- 高德地图 -->
<meta-data
tools:replace="android:value"
android:name="com.amap.api.v2.apikey"
android:value="1c3fbc5f5e183619ffb1e7bc01e6751f" />
<!-- <meta-data-->
<!-- tools:replace="android:value"-->
<!-- android:name="com.amap.api.v2.apikey"-->
<!-- android:value="1c3fbc5f5e183619ffb1e7bc01e6751f" />-->
</application>
<uses-permission android:name="android.permission.WAKE_LOCK" />

View File

@@ -18,10 +18,10 @@
<!-- android:authorities="com.zhidao.auto.personal.provider"-->
<!-- android:enabled="true"-->
<!-- android:exported="true" />-->
<meta-data
android:name="com.amap.api.v2.apikey"
android:value="a36b9f7b086fa3951bb35338a5a06dd3"
tools:replace="android:value" />
<!-- <meta-data-->
<!-- android:name="com.amap.api.v2.apikey"-->
<!-- android:value="a36b9f7b086fa3951bb35338a5a06dd3"-->
<!-- tools:replace="android:value" />-->
<!-- 高德地图 -->
</application>

View File

@@ -13,6 +13,9 @@
android:supportsRtl="true"
android:theme="@style/AppTheme.App"
tools:replace="android:label">
<meta-data
android:name="com.amap.api.v2.apikey"
android:value="${AMAP_API_VALUE}" />
<!--保活用-->
<service
android:name="com.zhidao.boot.persistent.lib.PersistentAliveService"

View File

@@ -11,8 +11,8 @@ import com.bytedance.boost_multidex.BoostMultiDex;
import com.mogo.commons.AbsMogoApplication;
import com.mogo.commons.debug.DebugConfig;
import com.mogo.commons.network.Utils;
import com.mogo.commons.storage.SpStorage;
import com.mogo.module.authorize.authprovider.invoke.AuthorizeConstant;
import com.mogo.module.back.BackToLauncherConst;
import com.mogo.module.carchatting.card.CallChatConstant;
import com.mogo.module.common.MogoModule;
import com.mogo.module.common.MogoModulePaths;
@@ -22,6 +22,7 @@ import com.mogo.module.push.base.PushUIConstants;
import com.mogo.module.service.ServiceConst;
import com.mogo.module.tanlu.constant.TanluConstants;
import com.mogo.module.v2x.V2XConst;
import com.mogo.module.widgets.MogoWidgetsProvider;
import com.mogo.service.IMogoServiceApis;
import com.mogo.service.MogoServicePaths;
import com.mogo.service.passport.IMogoTicketCallback;
@@ -29,8 +30,8 @@ import com.mogo.utils.UiThreadHandler;
import com.mogo.utils.logger.LogLevel;
import com.mogo.utils.logger.Logger;
import com.zhidao.boot.persistent.lib.PersistentManager;
import com.zhidao.mogo.module.event.panel.EventPanelConstants;
import com.zhidao.mogo.module.left.panel.LeftPanelConst;
import com.zhidao.mogo.tanlu.api.TanluApiConst;
/**
* @author congtaowang
@@ -57,23 +58,25 @@ public class MogoApplication extends AbsMogoApplication {
// MogoModulePaths.addModule(new MogoModule(PATH_GUIDE_FRAGMENT, PATH_GUIDE_MODULE_NAME));
MogoModulePaths.addModule( new MogoModule( MogoServicePaths.PATH_AGREEMENT, AuthorizeConstant.PATH_AGREEMENT_MODULE_NAME ) );
//运营位卡片,需要默认显示,放在第一个加载
if (DebugConfig.isLauncher() ) {
if ( DebugConfig.isLauncher() ) {
MogoModulePaths.addModule( new MogoModule( BackToLauncherConst.MODULE_PATH, BackToLauncherConst.MODULE_NAME ) );
MogoModulePaths.addModule( new MogoModule( MediaConstants.TAG, MediaConstants.MODULE_TYPE ) );
} else {
MogoModulePaths.addBaseModule( new MogoModule( MogoWidgetsProvider.PATH, MogoWidgetsProvider.NAME ) );
}
if(DebugConfig.getCarMachineType() != DebugConfig.CAR_MACHINE_TYPE_BYD){
if ( DebugConfig.getCarMachineType() != DebugConfig.CAR_MACHINE_TYPE_BYD ) {
MogoModulePaths.addModule( new MogoModule( CallChatConstant.PROVIDER, CallChatConstant.MODULE_NAME ) );
}
MogoModulePaths.addModule( new MogoModule( TanluApiConst.MODULE_PATH, TanluApiConst.MODULE_NAME ) );
MogoModulePaths.addModule( new MogoModule( TanluConstants.TAG, TanluConstants.MODEL_NAME ) );
MogoModulePaths.addModule( new MogoModule( MogoServicePaths.PATH_SHARE, "ShareControl" ) );
MogoModulePaths.addModule( new MogoModule( EventPanelConstants.PATH_NAME, EventPanelConstants.MODULE_NAME ) );
MogoModulePaths.addModule( new MogoModule( LeftPanelConst.PATH_NAME, LeftPanelConst.MODULE_NAME ) );
MogoModulePaths.addBaseModule( new MogoModule( ServiceConst.PATH_REFRESH_STRATEGY, ServiceConst.PATH_REFRESH_STRATEGY ) );
MogoModulePaths.addBaseModule( new MogoModule( V2XConst.PATH_V2X_UI, V2XConst.PATH_V2X_UI ) );
MogoModulePaths.addBaseModule( new MogoModule( V2XConst.PATH_V2X_UI, V2XConst.MODULE_NAME ) );
MogoModulePaths.addModule( new MogoModule( V2XConst.PATH_EVENT_PANEL, V2XConst.MODULE_NAME_EVENT_PANEL ) );
MogoModulePaths.addModule( new MogoModule( PushUIConstants.PATH, PushUIConstants.NAME ) );
MogoModulePaths.addModule(new MogoModule(MogoServicePaths.PATH_LOG_LIB, "LogLib"));
if ( !DebugConfig.isLauncher() ) {
PersistentManager.getInstance().initManager( this );
@@ -100,13 +103,13 @@ public class MogoApplication extends AbsMogoApplication {
@Override
protected void init() {
super.init();
prepareBaseService(2_000L);
prepareBaseService( 2_000L );
}
/**
* 基础服务passport、location、socket
*/
private void prepareBaseService(long delay) {
private void prepareBaseService( long delay ) {
UiThreadHandler.postDelayed( () -> {
final IMogoServiceApis apis = ARouter.getInstance().navigation( IMogoServiceApis.class );
// 第三方平台的sn是服务端生成的所以必须在返回后才能开启

View File

@@ -4,12 +4,18 @@ ext {
android = [
// applicationId : "com.mogo.launcher",
// zhidadoApplicationId: "com.zhidao.launcher",
launcherApplicationId : "com.mogo.launcher",
independentApplicationId: "com.mogo.launcher.app",
compileSdkVersion : 28,
buildToolsVersion : "29.0.2",
minSdkVersion : 19,
targetSdkVersion : 22,
launcherApplicationId : "com.mogo.launcher",
independentApplicationId : "com.mogo.launcher.app",
fLauncherApplicationId : "com.mogo.launcher.f",
bydautoIndependentApplicationId: "com.mogo.launcher.app.bydauto",
fLauncherAmapApiValue : "aa2c8a0547378ead2f9a37762d9dd179",
bydautoIndependentApiValue : "b1e1d527b02a493913c50985827c943a",
commonLauncherAmapApiValue : "a36b9f7b086fa3951bb35338a5a06dd3",
commonIndependentAmapApiValue : "1c3fbc5f5e183619ffb1e7bc01e6751f",
compileSdkVersion : 28,
buildToolsVersion : "29.0.2",
minSdkVersion : 19,
targetSdkVersion : 22,
]
dependencies = [
@@ -21,6 +27,7 @@ targetSdkVersion : 22,
androidxviewpager2 : "androidx.viewpager2:viewpager2:1.0.0",
androidxrecyclerview : "androidx.recyclerview:recyclerview:1.1.0",
androidxcardview : "androidx.cardview:cardview:1.0.0",
localbroadcastmanager : "androidx.localbroadcastmanager:localbroadcastmanager:1.0.0",
// flexbox
flexbox : 'com.google.android:flexbox:2.0.1',
// 测试
@@ -76,11 +83,12 @@ targetSdkVersion : 22,
roomRxjava : "android.arch.persistence.room:rxjava2:1.1.1",
// material
material : 'com.google.android.material:material:1.0.0',
material : 'com.google.android.material:material:1.1.0',
// modules
moduletanlu : "com.mogo.module:module-tanlu:${MOGO_MODULE_TANLU_VERSION}",
moduletanluapi : "com.mogo.module:module-tanlu-api:${MOGO_TANLU_API_VERSION}",
moduleshare : "com.mogo.module:module-share:${MOGO_MODULE_SHARE_VERSION}",
mogocommons : "com.mogo.commons:mogo-commons:${MOGO_COMMONS_VERSION}",
mogoutils : "com.mogo.commons:mogo-utils:${MOGO_UTILS_VERSION}",
@@ -141,6 +149,7 @@ targetSdkVersion : 22,
videoarm64 : "com.shuyu:gsyVideoPlayer-arm64:7.1.2",
videojava : "com.shuyu:gsyVideoPlayer-java:7.1.2",
eventbus : "org.greenrobot:eventbus:3.1.1",
videoprocessor : "com.zhidao.video:video-processor:1.0.2.1",
coroutinescore : "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.1.1",
coroutinesandroid : "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.1.1",
@@ -175,6 +184,8 @@ targetSdkVersion : 22,
mogobaseserviceapk : "com.mogo.base:services-apk:${MOGO_BASE_SERVICES_APK_VERSION}",
// loglib
mogologlib : "com.mogo.module:module-loglib:${LOGLIB_VERSION}",
// monitor
mogomonitor : "com.mogo.module:module-monitor:${MOGO_MODULE_MONITOR_VERSION}",
// google
googlezxing : "com.google.zxing:core:3.3.3",
@@ -184,5 +195,8 @@ targetSdkVersion : 22,
androidxroomruntime : "androidx.room:room-runtime:2.2.3",
androidxroomcompiler : "androidx.room:room-compiler:2.2.3",
androidxroomktx : "androidx.room:room-ktx:2.2.3",
//
mogomodulewidgets : "com.mogo.module:module-widgets:${MOGO_MODULE_WIDGETS_VERSION}",
]
}

View File

@@ -37,8 +37,9 @@ public abstract class MvpFragment<V extends IView, P extends Presenter<V>> exten
mRootView = inflater.inflate( getLayoutId(), container, false );
} else {
ViewGroup viewGroup = ( ViewGroup ) mRootView.getParent();
if ( viewGroup != null )
if ( viewGroup != null ) {
viewGroup.removeView( mRootView );
}
}
mRootView = inflater.inflate(getLayoutId(), container, false);
return mRootView;
@@ -82,6 +83,10 @@ public abstract class MvpFragment<V extends IView, P extends Presenter<V>> exten
@NonNull
protected abstract P createPresenter();
public P getPresenter() {
return mPresenter;
}
@Nullable
protected <T extends View> T findViewById(int id) {
if (mRootView == null) {

View File

@@ -61,6 +61,7 @@ public class ParamsUtil {
STATIC_PARAMS.put( ServerParam.DEVICE_ID, DeviceIdUtils.getDeviceId( AbsMogoApplication.getApp() ) );
STATIC_PARAMS.put( ServerParam.IMEI, CommonUtils.getIMEI( AbsMogoApplication.getApp() ) );
STATIC_PARAMS.put( ServerParam.IMSI, CommonUtils.getIMSI( AbsMogoApplication.getApp() ) );
STATIC_PARAMS.put( ServerParam.FOTA_VERSION, Utils.getFotaVersion() );
STATIC_PARAMS.put( ServerParam.END_POINT, ServerParam.END_POINT_CAR );
}

View File

@@ -34,6 +34,8 @@ class ServerParam {
public static final String SN = "sn";
public static final String CHANNEL = "channel";
public static final String FOTA_VERSION = "fotaVersion";
public static final String USER_ID = "userId";
public static final String FILE = "file";
public static final String END_POINT = "endPoint";

View File

@@ -57,4 +57,34 @@ public class AppUtils {
return null;
}
}
public static boolean isAppForeground( Context context ) {
if ( context != null ) {
ActivityManager activityManager = ( ActivityManager ) context.getSystemService( Context.ACTIVITY_SERVICE );
List< ActivityManager.RunningAppProcessInfo > processes = activityManager.getRunningAppProcesses();
for ( ActivityManager.RunningAppProcessInfo processInfo : processes ) {
if ( processInfo.processName.equals( context.getPackageName() ) ) {
if ( processInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND ) {
return true;
}
}
}
}
return false;
}
public static boolean isAppForeground( Context context, String pkg ) {
if ( context != null ) {
ActivityManager activityManager = ( ActivityManager ) context.getSystemService( Context.ACTIVITY_SERVICE );
List< ActivityManager.RunningAppProcessInfo > processes = activityManager.getRunningAppProcesses();
for ( ActivityManager.RunningAppProcessInfo processInfo : processes ) {
if ( processInfo.processName.equals( pkg ) ) {
if ( processInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND ) {
return true;
}
}
}
}
return false;
}
}

View File

@@ -443,8 +443,8 @@ public class DateTimeUtils {
* @param timestamp 时间戳
* @return
*/
public static String getTimeText( long timestamp) {
SimpleDateFormat format = new SimpleDateFormat(yyyy_Nian_MM_Yue_dd_Ri, Locale.US);
public static String getTimeText( long timestamp, String dateFormat) {
SimpleDateFormat format = new SimpleDateFormat(dateFormat, Locale.US);
String strStart = format.format(new Date(timestamp));
return strStart;
}

View File

@@ -1,432 +0,0 @@
package com.mogo.utils.sqlite
import android.content.ContentValues
import android.database.Cursor
import android.database.sqlite.SQLiteDatabase
import com.mogo.utils.logger.Logger
import com.mogo.utils.sqlite.annotation.DbField
import com.mogo.utils.sqlite.annotation.DbTable
import java.lang.reflect.Field
import java.util.*
/**
* <p>数据类操作实现</p>
*Created by donghongyu on 2019/9/6.
*/
//T必须指明上界是Any且不为null下面会用到反射获取对象实例默认是Any?
open class BaseDao<T : Any> : IBaseDao<T> {
private val TAG = "BaseDao"
//数据库操作的引用
private var sqLiteDatabase: SQLiteDatabase? = null
//要操作的数据实体的引用
private var entityClass: Class<T>? = null
//要操作的数据表名称
private var tableName: String? = null
//记录数据表是否存在
private var isInit = false
//因为反射会消耗时间,这里使用缓存,进行性能优化
//缓存空间key-字段名,标注的自定义注解 value-成员变量)
private var cacheField: HashMap<String, Field>? = null
override fun init(sqLiteDatabase: SQLiteDatabase, entityClass: Class<T>): Boolean {
this.sqLiteDatabase = sqLiteDatabase
this.entityClass = entityClass
//自动建表(只创建一次)
if (!isInit) {
//获取表名
tableName = entityClass.getAnnotation(DbTable::class.java).tableName
//如果数据库没有建立连接跳出操作防止异常信息
if (!sqLiteDatabase.isOpen) {
return false
}
//执行Sql进行自动建表
val createTableSql = getCreateTableSql()
Logger.d(TAG, "执行SQL$createTableSql")
sqLiteDatabase.execSQL(createTableSql)
//初始化缓存空间
cacheField = HashMap()
initCacheField()
//标记已经创建过数据表
isInit = true
}
return isInit
}
/**
* 初始化字段缓存
*/
private fun initCacheField() {
//1.取到所有的列名(查询一个空表获取表结构,不影响性能)
if (sqLiteDatabase != null && sqLiteDatabase!!.isOpen) {
val sqlQuery = "select * from $tableName limit 1,0"
val cursor = sqLiteDatabase!!.rawQuery(sqlQuery, null)
//获取所有的列名
val columnNames = cursor.columnNames
//关闭资源
cursor.close()
//2.取所有成员名
val columnFields = entityClass!!.declaredFields
//3.通过两层循环,进行对应关系建立
columnNames.forEach ColumnFor@{ columnName ->
columnFields.forEach FieldFor@{ columnField ->
if (columnName == columnField.getAnnotation(DbField::class.java).fieldName) {
columnField.isAccessible = true
cacheField!![columnName] = columnField
}
return@FieldFor
}
}
}
}
/**
* 拼装创建数据表的SQL语句
*/
private fun getCreateTableSql(): String? {
//create table if not exists tb_name(_id integer,name varchar2(20))
val sqlCreateTable = StringBuffer()
sqlCreateTable.append("create table if not exists ")
sqlCreateTable.append("$tableName (")
//反射获取所有的数据对象内的成员变量
val fields = entityClass!!.declaredFields
fields.forEachIndexed { index, field ->
//字段名称
val columnName = field.getAnnotation(DbField::class.java).fieldName
//获取成员变量数据类型
when (val fieldType = field.type) {
java.lang.String::class.java -> {
sqlCreateTable.append("$columnName TEXT,")
}
java.lang.Integer::class.java -> {
sqlCreateTable.append("$columnName INTEGER,")
}
java.lang.Long::class.java -> {
sqlCreateTable.append("$columnName BIGINT,")
}
java.lang.Double::class.java -> {
sqlCreateTable.append("$columnName DOUBLE,")
}
ByteArray::class.java -> {
sqlCreateTable.append("$columnName BLOB,")
}
else -> {
//未知类型
throw UnsupportedOperationException("未定义的数据类型fieldName= $columnName fieldType= $fieldType")
}
}
if (index == fields.size - 1) {
if (sqlCreateTable.endsWith(","))
sqlCreateTable.deleteCharAt(sqlCreateTable.length - 1)
}
}
sqlCreateTable.append(")")
return sqlCreateTable.toString()
}
/**
* 插入数据
*/
override fun insert(entity: T): Long {
//1、准备好ContentValues中的数据
//2、设置插入的内容
val values: ContentValues = getContentValuesForInsert(entity)
//3、执行插入
return if (sqLiteDatabase != null && sqLiteDatabase!!.isOpen) {
sqLiteDatabase!!.insert(tableName, null, values)
} else {
-1
}
}
/**
* 删除数据
*/
override fun delete(where: T): Int {
val condition = Condition(getContentValuesForQuery(where))
return if (sqLiteDatabase != null && sqLiteDatabase!!.isOpen) {
//受影响行数
sqLiteDatabase!!
.delete(
tableName,
condition.getWhereCause(),
condition.getWhereArgs()
)
} else {
-1
}
}
/**
* 更新数据
*/
override fun update(where: T, newEntity: T): Int {
val condition = Condition(getContentValuesForQuery(where))
return if (sqLiteDatabase != null && sqLiteDatabase!!.isOpen) {
//受影响行数
sqLiteDatabase!!
.update(
tableName,
getContentValuesForInsert(newEntity),
condition.getWhereCause(),
condition.getWhereArgs()
)
} else {
-1
}
}
/**
* 查询数据
* @param where 查询条件对象,同时也用来初始化对象使用
*/
override fun query(where: T): MutableList<T> {
return query(where, null, null, null)
}
/**
* 查询数据
* @param where 查询条件对象
* @param orderBy 排序规则
* @param startIndex 开始的位置
* @param limit 限制查询得到的数据个数
*/
fun query(where: T, orderBy: String?, startIndex: Int?, limit: Int?): MutableList<T> {
//拼接分页语句
var limitString: String? = null
if (startIndex != null && limit != null) {
limitString = "$startIndex,$limit"
}
val condition = Condition(getContentValuesForQuery(where))
var cursor: Cursor? = null
//定义查询结果
val result = mutableListOf<T>()
if (sqLiteDatabase != null && sqLiteDatabase!!.isOpen) {
try {
//查询数据库
cursor = sqLiteDatabase!!
.query(
tableName,
null,
condition.getWhereCause(),
condition.getWhereArgs(),
null,
null,
orderBy,
limitString
)
//将查到结果添加到返回集合中
result.addAll(getQueryResult(cursor, where))
} catch (e: Exception) {
e.printStackTrace()
} finally {
cursor?.close()
}
}
return result
}
/**
* 获取查询使用的ContentValues
*/
private fun getContentValuesForQuery(entity: T): ContentValues {
val contentValues = ContentValues()
try {
cacheField!!.forEach {
if (it.value.get(entity) == null) {
return@forEach
}
contentValues.put(it.key, it.value.get(entity).toString())
}
} catch (e: IllegalAccessError) {
e.printStackTrace()
}
return contentValues
}
/**
* 条件拼接
*/
class Condition(whereContent: ContentValues) {
/**
* 条件拼接
* _id=?&&name=?
*/
private var whereCause: String? = null
private var whereArgs: Array<String>? = null
//根据传入的contentValues转换成查询条件
init {
//记录后面填充到查询语句“?”上的数据参数
val argList = mutableListOf<String>()
//拼接查询语句
val whereCaseSb = StringBuilder()
/**
* 是为了链接下面的查询条件条件,也或者是替换没有查询条件的语句。
* 比如要把检索条件作为一个参数传递给SQL
* 那么当这个检索语句不存在的话就可以给它赋值为1=1.
* 这样就避免了SQL出错也就可以把加条件的SQL和不加条件的SQL合二为一。
*/
whereCaseSb.append(" 1=1 ")
val keys = whereContent.keySet()
val iterator = keys.iterator()
//因为使用了“1=1”所以即便是这里没有任何数据拼接也是可以正常
while (iterator.hasNext()) {
val key = iterator.next() as String
val valueObject = whereContent.get(key)
if (valueObject != null) {
val value = valueObject as String
//拼接查询条件语句
//1:1 and _id=? and name=?
whereCaseSb.append(" and $key =?")
//记录对应的value
argList.add(value)
}
}
//集合转成数组
this.whereArgs = argList.toTypedArray()
this.whereCause = whereCaseSb.toString()
}
fun getWhereCause(): String {
return this.whereCause!!
}
fun getWhereArgs(): Array<String> {
return this.whereArgs!!
}
}
/**
* 获取查询db结果
*/
private fun getQueryResult(cursor: Cursor, where: T): MutableList<T> {
//定义查询结果
val result = mutableListOf<T>()
//Cursor从头读到尾
//游标从头读到尾
cursor.moveToFirst()
//移动游标获取下一行数据
while (!cursor.isAfterLast) {
//通过反射构建一个查询结果对象
val item = where.javaClass.newInstance()
//拿到缓存的当前数据对象的成员变量与数据库的键值关系
val fieldIterator = cacheField!!.entries.iterator()
fieldIterator.forEach IteratorFor@{
//获取数据库字段名称
val columnName = it.key
//数据库字段名对应的数据对象的成员变量
val field = it.value
//获取指定列名对应的索引
val columnIndex = cursor.getColumnIndex(columnName)
//获取成员变量数据类型
val fieldType = field.type
if (columnIndex != -1) {
when (fieldType) {
java.lang.String::class.java -> {
field.set(item, cursor.getString(columnIndex))
}
java.lang.Integer::class.java -> {
field.set(item, cursor.getInt(columnIndex))
}
java.lang.Long::class.java -> {
field.set(item, cursor.getLong(columnIndex))
}
java.lang.Double::class.java -> {
field.set(item, cursor.getDouble(columnIndex))
}
ByteArray::class.java -> {
field.set(item, cursor.getBlob(columnIndex))
}
else -> {
//未知类型
throw UnsupportedOperationException("未定义的数据类型columnName= $columnName fieldType= $fieldType")
}
}
}
}
//添加到结果集
result.add(item)
//移动到下一个位置
cursor.moveToNext()
}
cursor.close()
return result
}
/**
* 获取插入使用的ContentValues
*/
private fun getContentValuesForInsert(entity: T): ContentValues {
val contentValues = ContentValues()
val fieldIterator = cacheField!!.entries.iterator()
fieldIterator.forEach IteratorFor@{
try {
//获取变量的值
val valueObject = it.value.get(entity) ?: return@IteratorFor
//获取列名
val columnName = it.key
//获取成员变量数据类型
val fieldType = it.value.type
when (fieldType) {
java.lang.String::class.java -> {
contentValues.put(columnName, valueObject as String)
}
java.lang.Integer::class.java -> {
contentValues.put(columnName, valueObject as Int)
}
java.lang.Long::class.java -> {
contentValues.put(columnName, valueObject as Long)
}
java.lang.Double::class.java -> {
contentValues.put(columnName, valueObject as Double)
}
ByteArray::class.java -> {
contentValues.put(columnName, valueObject as ByteArray)
}
else -> {
//未知类型
throw UnsupportedOperationException("未定义的数据类型columnName= $columnName fieldType= $fieldType")
}
}
} catch (e: IllegalArgumentException) {
e.printStackTrace()
}
}
Logger.d(TAG, "contentValues:$contentValues")
return contentValues
}
}

View File

@@ -1,35 +0,0 @@
package com.mogo.utils.sqlite
import android.database.sqlite.SQLiteDatabase
/**
* <p>操作SQLite数据库的顶层接口</p>
* Created by donghongyu on 2019/9/6.
*/
interface IBaseDao<T> {
/**
* 初始化数据库连接
*/
fun init(sqLiteDatabase: SQLiteDatabase, entityClass: Class<T>): Boolean
/**
* 将 [entity] 进行数据插入
*/
fun insert(entity: T): Long
/**
* 根据条件 [where] 进行数据删除
*/
fun delete(where: T): Int
/**
* 根据条件 [where] 进行数据更新,如果[where]==null 则代表删除所有数据
*/
fun update(where: T, newEntity: T): Int
/**
* 根据条件 [where] 进行数据查询,如果[where]==null 则代表查询所有数据
*/
fun query(where: T): MutableList<T>
}

View File

@@ -0,0 +1,488 @@
package com.mogo.utils.sqlite;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import com.mogo.utils.logger.Logger;
import com.mogo.utils.sqlite.annotation.DbField;
import com.mogo.utils.sqlite.annotation.DbTable;
import org.jetbrains.annotations.NotNull;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* 数据库操作
*
* @param <T>
* @author donghongyu
*/
public class SQLBaseDao<T> implements SQLIDao<T> {
private String TAG = "SQLBaseDao";
//数据库操作的引用
private SQLiteDatabase sqLiteDatabase;
//要操作的数据实体的引用
private Class<T> entityClass;
//要操作的数据表名称
private String tableName;
//记录数据表是否存在
private boolean isInit = false;
//因为反射会消耗时间,这里使用缓存,进行性能优化
//缓存空间key-字段名,标注的自定义注解 value-成员变量)
private HashMap<String, Field> cacheField;
@Override
public boolean init(@NotNull SQLiteDatabase sqLiteDatabase, @NotNull Class<T> entityClass) {
this.sqLiteDatabase = sqLiteDatabase;
this.entityClass = entityClass;
//自动建表(只创建一次)
if (!isInit) {
//获取表名
tableName = entityClass.getAnnotation(DbTable.class).tableName();
//如果数据库没有建立连接跳出操作防止异常信息
if (!sqLiteDatabase.isOpen()) {
return false;
}
//执行Sql进行自动建表
String createTableSql = getCreateTableSql();
Logger.d(TAG, "执行SQL" + createTableSql);
sqLiteDatabase.execSQL(createTableSql);
//初始化缓存空间
cacheField = new HashMap();
initCacheField();
//标记已经创建过数据表
isInit = true;
}
return isInit;
}
/**
* 插入数据
*
* @param entity 要插入数据的数据对象爱你那个
* @return 插入结果ID如果发生错误则为-1
*/
@Override
public long insert(T entity) {
//1、准备好ContentValues中的数据
//2、设置插入的内容
ContentValues values = getContentValuesForInsert(entity);
//3、执行插入
long result;
if (sqLiteDatabase != null && sqLiteDatabase.isOpen()) {
result = sqLiteDatabase.insert(tableName, null, values);
} else {
result = -1;
}
return result;
}
/**
* 删除数据
*
* @param where 要删除的对象,对象的字段如果赋值,则代表查询条件
* @return 受影响行数
*/
@Override
public int delete(T where) {
// 拼接查询条件
Condition condition = new Condition(getContentValuesForQuery(where));
int result;
if (sqLiteDatabase != null && sqLiteDatabase.isOpen()) {
//受影响行数
result = sqLiteDatabase.delete(
tableName,
condition.getWhereCause(),
condition.getWhereArgs()
);
} else {
result = -1;
}
return result;
}
/**
* 更新数据
*
* @param where 要更新的对象,对象的字段如果赋值,则代表查询条件
* @param newEntity 新的数据内容
* @return 受影响行数
*/
@Override
public int update(T where, T newEntity) {
Condition condition = new Condition(getContentValuesForQuery(where));
int result;
if (sqLiteDatabase != null && sqLiteDatabase.isOpen()) {
//受影响行数
result = sqLiteDatabase.update(
tableName,
getContentValuesForInsert(newEntity),
condition.getWhereCause(),
condition.getWhereArgs()
);
} else {
result = -1;
}
return result;
}
/**
* 查询数据
*
* @param where 查询条件对象,同时也用来初始化对象使用
*/
@NotNull
@Override
public List<T> query(T where) {
return query(where, null, null, null);
}
/**
* 查询数据
*
* @param where 查询条件对象,同时也用来初始化对象使用
*/
@NotNull
@Override
public List<T> query(T where, String orderBy) {
return query(where, orderBy, null, null);
}
/**
* @param where 根据条件 [where] 进行数据查询,如果[where]==只初始化不赋值 则代表查询所有数据
* @param orderBy 排序字段
* @param isOrderDESC 是否倒序 true-倒序false-正序
* @return 结果集合
*/
@Override
public List<T> query(T where, String orderBy, boolean isOrderDESC) {
if (isOrderDESC) {
orderBy = orderBy + " DESC";
}
return query(where, orderBy, null, null);
}
/**
* 查询数据
*
* @param where 查询条件对象
* @param orderBy 排序规则
* @param startIndex 开始的位置
* @param limit 限制查询得到的数据个数
*/
public ArrayList<T> query(T where, String orderBy, Integer startIndex, Integer limit) {
//拼接分页语句
String limitString = null;
if (startIndex != null && limit != null) {
limitString = startIndex + "," + limit;
}
Condition condition = new Condition(getContentValuesForQuery(where));
Cursor cursor = null;
//定义查询结果
ArrayList<T> result = new ArrayList<>();
if (sqLiteDatabase != null && sqLiteDatabase.isOpen()) {
try {
//查询数据库
cursor = sqLiteDatabase.query(
tableName,
null,
condition.getWhereCause(),
condition.getWhereArgs(),
null,
null,
orderBy,
limitString
);
//将查到结果添加到返回集合中
result.addAll(getQueryResult(cursor, where));
} catch (Exception e) {
e.printStackTrace();
} finally {
if (cursor != null) {
cursor.close();
}
}
}
return result;
}
/**
* 获取查询db结果
*/
private ArrayList<T> getQueryResult(Cursor cursor, T where) {
//定义查询结果
ArrayList<T> result = new ArrayList<>();
//Cursor从头读到尾
//游标从头读到尾
cursor.moveToFirst();
try {
if (where != null) {
//移动游标获取下一行数据
while (!cursor.isAfterLast()) {
//通过反射构建一个查询结果对象
T item = (T) where.getClass().newInstance();
Set<Map.Entry<String, Field>> fieldIterator = cacheField.entrySet();
for (Map.Entry<String, Field> stringFieldEntry : fieldIterator) {
//获取数据库字段名称
String columnName = stringFieldEntry.getKey();
//数据库字段名对应的数据对象的成员变量
Field field = stringFieldEntry.getValue();
//获取指定列名对应的索引
int columnIndex = cursor.getColumnIndex(columnName);
//获取成员变量数据类型
Class fieldType = field.getType();
if (columnIndex != -1) {
if (fieldType == (String.class)) {
field.set(item, cursor.getString(columnIndex));
} else if (fieldType == (Integer.class)) {
field.set(item, cursor.getInt(columnIndex));
} else if (fieldType == (Long.class)) {
field.set(item, cursor.getLong(columnIndex));
} else if (fieldType == (Double.class)) {
field.set(item, cursor.getDouble(columnIndex));
} else if (fieldType == (Boolean.class)) {
int value = cursor.getInt(columnIndex);
if (value == 0) {
field.set(item, false);
} else {
field.set(item, true);
}
} else if (fieldType == (byte[].class)) {
field.set(item, cursor.getBlob(columnIndex));
} else {
//未知类型
throw new UnsupportedOperationException("未定义的数据类型fieldName= " + columnName + " fieldType= " + fieldType);
}
}
}
//添加到结果集
result.add(item);
//移动到下一个位置
cursor.moveToNext();
}
}
} catch (IllegalAccessException | InstantiationException e) {
e.printStackTrace();
}
cursor.close();
return result;
}
/**
* 初始化字段缓存
*/
private void initCacheField() {
//1.取到所有的列名(查询一个空表获取表结构,不影响性能)
if (sqLiteDatabase != null && sqLiteDatabase.isOpen()) {
String sqlQuery = "select * from " + tableName + " limit 1,0";
Cursor cursor = sqLiteDatabase.rawQuery(sqlQuery, null);
//获取所有的列名
String[] columnNames = cursor.getColumnNames();
//关闭资源
cursor.close();
//2.取所有成员名
Field[] columnFields = entityClass.getDeclaredFields();
//3.通过两层循环,进行对应关系建立
for (String columnName : columnNames) {
for (Field columnField : columnFields) {
if (columnName.equals(columnField.getAnnotation(DbField.class).fieldName())) {
columnField.setAccessible(true);
cacheField.put(columnName, columnField);
}
}
}
}
}
/**
* 拼装创建数据表的SQL语句
*/
private String getCreateTableSql() {
//create table if not exists tb_name(_id integer,name varchar2(20))
StringBuffer sqlCreateTable = new StringBuffer();
sqlCreateTable.append("create table if not exists ");
sqlCreateTable.append(tableName + " (");
//反射获取所有的数据对象内的成员变量
Field[] fields = entityClass.getDeclaredFields();
for (int index = 0; index < fields.length; index++) {
//字段名称
String columnName = fields[index].getAnnotation(DbField.class).fieldName();
//获取成员变量数据类型
Class fieldType = fields[index].getType();
if (fieldType == (String.class)) {
sqlCreateTable.append(columnName).append(" TEXT,");
} else if (fieldType == (Integer.class)) {
sqlCreateTable.append(columnName).append(" INTEGER,");
} else if (fieldType == (Long.class)) {
sqlCreateTable.append(columnName).append(" BIGINT,");
} else if (fieldType == (Double.class)) {
sqlCreateTable.append(columnName).append(" DOUBLE,");
} else if (fieldType == (Boolean.class)) {
sqlCreateTable.append(columnName).append(" INTEGER,");
} else if (fieldType == (byte[].class)) {
sqlCreateTable.append(columnName).append(" BLOB,");
} else {
//未知类型
throw new UnsupportedOperationException("未定义的数据类型fieldName= " + columnName + " fieldType= " + fieldType);
}
if (index == fields.length - 1) {
if (sqlCreateTable.toString().endsWith(",")) {
sqlCreateTable.deleteCharAt(sqlCreateTable.length() - 1);
}
}
}
sqlCreateTable.append(")");
return sqlCreateTable.toString();
}
/**
* 获取插入使用的ContentValues
*/
private ContentValues getContentValuesForInsert(T entity) {
ContentValues contentValues = new ContentValues();
Set<Map.Entry<String, Field>> fieldIterator = cacheField.entrySet();
for (Map.Entry<String, Field> stringFieldEntry : fieldIterator) {
try {
//获取变量的值
Object valueObject = stringFieldEntry.getValue().get(entity);
//获取列名
String columnName = stringFieldEntry.getKey();
//获取成员变量数据类型
Class fieldType = stringFieldEntry.getValue().getType();
if (fieldType == (String.class)) {
contentValues.put(columnName, (String) valueObject);
} else if (fieldType == (Integer.class)) {
contentValues.put(columnName, (Integer) valueObject);
} else if (fieldType == (Long.class)) {
contentValues.put(columnName, (Long) valueObject);
} else if (fieldType == (Double.class)) {
contentValues.put(columnName, (Double) valueObject);
} else if (fieldType == (Boolean.class)) {
if (((boolean) valueObject)) {
contentValues.put(columnName, 1);
} else {
contentValues.put(columnName, 0);
}
} else if (fieldType == (byte[].class)) {
contentValues.put(columnName, (byte[]) valueObject);
} else {
//未知类型
throw new UnsupportedOperationException("未定义的数据类型fieldName= " + columnName + " fieldType= " + fieldType);
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
Logger.d(TAG, "contentValues:" + contentValues);
return contentValues;
}
/**
* 获取查询使用的 条件值对象
*/
private ContentValues getContentValuesForQuery(T entity) {
ContentValues contentValues = new ContentValues();
try {
if (entity != null) {
Set<Map.Entry<String, Field>> fieldIterator = cacheField.entrySet();
for (Map.Entry<String, Field> stringFieldEntry : fieldIterator) {
if (stringFieldEntry.getValue().get(entity) != null) {
// 针对Boolean类型进行处理
if (stringFieldEntry.getValue().get(entity) instanceof Boolean) {
if ((boolean) stringFieldEntry.getValue().get(entity)) {
contentValues.put(stringFieldEntry.getKey(), "1");
} else {
contentValues.put(stringFieldEntry.getKey(), "0");
}
}
// 其它数据类型处理
else {
contentValues.put(stringFieldEntry.getKey(),
stringFieldEntry.getValue().get(entity).toString());
}
}
}
}
} catch (IllegalAccessError | IllegalAccessException e) {
e.printStackTrace();
}
return contentValues;
}
/**
* 条件拼接对象
*/
static class Condition {
/**
* 条件key拼接
* _id=?&&name=?
*/
private String whereCause;
/**
* 条件值,与 whereCause 顺序一致
*/
private String[] whereArgs;
//根据传入的contentValues转换成查询条件
public Condition(ContentValues whereContent) {
//记录后面填充到查询语句“?”上的数据参数
ArrayList<String> argList = new ArrayList<>();
//拼接查询语句
StringBuilder whereCaseSb = new StringBuilder();
/*
* 是为了链接下面的查询条件条件,也或者是替换没有查询条件的语句。
* 比如要把检索条件作为一个参数传递给SQL
* 那么当这个检索语句不存在的话就可以给它赋值为1=1.
* 这样就避免了SQL出错也就可以把加条件的SQL和不加条件的SQL合二为一。
*/
whereCaseSb.append(" 1=1 ");
Set<String> keys = whereContent.keySet();
//因为使用了“1=1”所以即便是这里没有任何数据拼接也是可以正常
for (String key : keys) {
Object valueObject = whereContent.get(key);
if (valueObject != null) {
String value = (String) valueObject;
//拼接查询条件语句
//1:1 and _id=? and name=?
whereCaseSb.append(" and " + key + " =?");
//记录对应的value
argList.add(value);
}
}
//集合转成数组
this.whereArgs = argList.toArray(new String[argList.size()]);
this.whereCause = whereCaseSb.toString();
}
public String getWhereCause() {
return this.whereCause;
}
public String[] getWhereArgs() {
return this.whereArgs;
}
}
}

View File

@@ -10,14 +10,14 @@ import com.mogo.utils.sqlite.proxy.BaseDaoProxyLog
* <p>数据库处理工厂</p>
* Created by donghongyu on 2019/9/6.
*/
open class BaseDaoFactory {
open class SQLDaoFactory {
//默认数据库名称
private var dbName = "MoGoSQLite.db"
companion object {
//单利工厂
private var baseDaoFactory: BaseDaoFactory? = null
private var sqlDaoFactory: SQLDaoFactory? = null
//数据库存储路径
private lateinit var sqLiteDatabasePath: String
@@ -25,21 +25,21 @@ open class BaseDaoFactory {
//数据库操作类
private var sqLiteDatabase: SQLiteDatabase? = null
fun getInstance(): BaseDaoFactory {
if (baseDaoFactory == null) {
synchronized(BaseDaoFactory::class.java) {
if (baseDaoFactory == null) {
baseDaoFactory = BaseDaoFactory()
fun getInstance(): SQLDaoFactory {
if (sqlDaoFactory == null) {
synchronized(SQLDaoFactory::class.java) {
if (sqlDaoFactory == null) {
sqlDaoFactory = SQLDaoFactory()
}
}
}
return baseDaoFactory!!
return sqlDaoFactory!!
}
}
//获取数据库操作对象
fun <T : Any> getBaseDao(context: Context, entityClass: Class<T>): IBaseDao<T>? {
var baseDao: IBaseDao<T>? = null
fun <T : Any> getBaseDao(context: Context, entityClass: Class<T>): SQLIDao<T>? {
var baseDao: SQLIDao<T>? = null
try {
//获取数据库名称,如果没有设置则使用默认名称
@@ -49,12 +49,14 @@ open class BaseDaoFactory {
}
//openOrCreateDatabase 如果不存在则先创建再打开数据库,如果存在则直接打开。
sqLiteDatabasePath =
"${context.getDir("database", Context.MODE_APPEND).path}/$dbName"
"${context.getDir("database", Context.MODE_APPEND).path}/$dbName"
sqLiteDatabase = SQLiteDatabase.openOrCreateDatabase(sqLiteDatabasePath, null)
// 这里为了演示,添加了日志工具的叠加使用,根据需要可以自己修改
// baseDao = BaseDaoProxyShow().bind(BaseDaoProxyLog().bind(BaseDao<T>())) as IBaseDao<T>
baseDao = BaseDaoProxyLog().bind(BaseDao<T>()) as IBaseDao<T>
// baseDao = BaseDaoProxyLog().bind(SQLBaseDao<T>()) as IBaseDao<T>
// baseDao = BaseDao<T>()
baseDao = BaseDaoProxyLog().bind(SQLBaseDao<T>()) as SQLIDao<T>
baseDao.init(sqLiteDatabase!!, entityClass)
} catch (e: Exception) {

View File

@@ -0,0 +1,53 @@
package com.mogo.utils.sqlite;
import android.database.sqlite.SQLiteDatabase;
import java.util.List;
/**
* 数据库操作接口
*
* @author donghongyu
*/
public interface SQLIDao<T> {
/**
* 初始化数据库连接
*/
boolean init(SQLiteDatabase sqLiteDatabase, Class<T> entityClass);
/**
* 将 [entity] 进行数据插入
*/
long insert(T entity);
/**
* 根据条件 [where] 进行数据删除,如果[where]==只初始化不赋值 则代表删除所有数据
*/
int delete(T where);
/**
* 根据条件 [where] 进行数据更新,如果[where]==只初始化不赋值 则代表删除所有数据
*/
int update(T where, T newEntity);
/**
* @param where 根据条件 [where] 进行数据查询,如果[where]==只初始化不赋值 则代表查询所有数据
* @return 结果集合
*/
List<T> query(T where);
/**
* @param where 根据条件 [where] 进行数据查询,如果[where]==只初始化不赋值 则代表查询所有数据
* @param orderBy 排序字段
* @return 结果集合
*/
List<T> query(T where, String orderBy);
/**
* @param where 根据条件 [where] 进行数据查询,如果[where]==只初始化不赋值 则代表查询所有数据
* @param orderBy 排序字段
* @param isOrderDESC 是否倒序
* @return 结果集合
*/
List<T> query(T where, String orderBy, boolean isOrderDESC);
}

View File

@@ -1,51 +0,0 @@
package com.mogo.utils.sqlite.proxy
import android.database.sqlite.SQLiteDatabase
import android.util.Log
import com.mogo.utils.sqlite.IBaseDao
/**
* <p>静态代理数据库操作类,用来记录日志</P>
* Created by donghongyu on 2019/9/6.
*/
class BaseDaoProxy<T>(var iBaseDao: IBaseDao<T>) : IBaseDao<T> by iBaseDao {
override fun init(sqLiteDatabase: SQLiteDatabase, entityClass: Class<T>): Boolean {
Log.i("数据库代理", "初始化数据库连接……")
val isInitSuccess = iBaseDao.init(sqLiteDatabase, entityClass)
if (isInitSuccess) {
Log.i("数据库代理", "数据库连接成功……")
} else {
Log.e("数据库代理", "数据库连接失败……")
}
return isInitSuccess
}
override fun insert(entity: T): Long {
Log.i("数据库代理", "开始插入数据……")
val result = iBaseDao.insert(entity)
Log.i("数据库代理", "插入数据索引位置> $result <……")
return result
}
override fun delete(where: T): Int {
Log.i("数据库代理", "开始删除数据……")
val result = iBaseDao.delete(where)
Log.i("数据库代理", "删除了> $result <条数据……")
return result
}
override fun update(where: T, newEntity: T): Int {
Log.i("数据库代理", "开始更新数据……")
val result = iBaseDao.update(where, newEntity)
Log.i("数据库代理", "更新了> $result <条数据……")
return result
}
override fun query(where: T): MutableList<T> {
Log.i("数据库代理", "开始查询数据……")
val result = iBaseDao.query(where)
Log.i("数据库代理", "查询到> ${result.size} <条数据……")
return result
}
}

View File

@@ -1,6 +1,7 @@
package com.mogo.utils.sqlite.proxy
import android.util.Log
import com.mogo.utils.logger.Logger
import com.mogo.utils.network.utils.GsonUtil
import java.lang.reflect.InvocationHandler
import java.lang.reflect.Method
import java.lang.reflect.Proxy
@@ -24,8 +25,8 @@ class BaseDaoProxyLog : InvocationHandler {
this.target = target
//取得代理对象
return Proxy.newProxyInstance(
target.javaClass.classLoader,
target.javaClass.interfaces, this
target.javaClass.classLoader,
target.javaClass.interfaces, this
)
}
@@ -40,11 +41,11 @@ class BaseDaoProxyLog : InvocationHandler {
override fun invoke(proxy: Any, method: Method, args: Array<Any>): Any? {
var result: Any? = null
//反射方法前调用
Log.i("数据库", "当前执行>>${method.name}")
Logger.i("SQL数据库", "当前执行>>${method.name}>>")
//反射执行方法 相当于调用target.sayHelllo;
result = method.invoke(target, *args)
//反射方法后调用.
Log.i("数据库", "执行结果>>$result")
Logger.i("SQL数据库", "执行结果>>$result")
return result
}
}

View File

@@ -1,49 +0,0 @@
package com.mogo.utils.sqlite.proxy
import android.util.Log
import java.lang.reflect.InvocationHandler
import java.lang.reflect.Method
import java.lang.reflect.Proxy
/**
* 使用java实现动态代理数据库操作类,加入更多额外的功能,来测试代理类的叠加功能
* Created by donghongyu on 2019/9/6.
*/
class BaseDaoProxyShow : InvocationHandler {
private var target: Any? = null
/**
* 绑定委托对象并返回一个【代理占位】
*
* @param target 真实对象
* @return 代理对象【占位】
*/
fun bind(target: Any): Any {
this.target = target
//取得代理对象
return Proxy.newProxyInstance(
target.javaClass.classLoader,
target.javaClass.interfaces, this
)
}
/**
* 同过代理对象调用方法首先进入这个方法.
*
* @param proxy --代理对象
* @param method -- 方法,被调用方法.
* @param args -- 方法的参数
*/
@Throws(Throwable::class)
override fun invoke(proxy: Any, method: Method, args: Array<Any>): Any? {
var result: Any? = null
//反射方法前调用
Log.i("数据库代理", "显示执行>>${method.name}")
//反射执行方法 相当于调用target.sayHelllo;
result = method.invoke(target, *args)
//反射方法后调用.
Log.i("数据库代理", "执行结果>>$result")
return result
}
}

View File

@@ -64,30 +64,35 @@ MAP_CUSTOM_VERSION=1.2.1.9
## 工程外部模块
# 探路
MOGO_MODULE_TANLU_VERSION=1.3.1.20
MOGO_MODULE_TANLU_VERSION=1.3.1.24
# 车聊聊
CARCHATTING_VERSION=1.4.8
CARCHATTING_VERSION=1.5.1
# 车聊聊接口
CARCHATTINGPROVIDER_VERSION=1.3.4
# 视频引导
MOGO_MODULE_GUIDESHOW_VERSION=1.0.2-SNAPSHOT
# 视频引导接口
MOGO_MODULE_GUIDESHOW_PROVIDER_VERSION=1.0.2-SNAPSHOT
# 在线车辆F
MOGO_MODULE_ONLINECAR_VERSION=1.0.3.2
# 推送
MOGO_MODULE_PUSH_VERSION=1.1.6.1
MOGO_MODULE_PUSH_VERSION=1.1.6.2
MOGO_MODULE_PUSH_BASE_VERSION=1.1.5.5
MOGO_MODULE_PUSH_NOOP_VERSION=1.1.5.6
# 广告资源位
MOGO_MODULE_AD_CARD_VERSION=1.0.1
# 探路上报和分享模块
TANLULIB_VERSION=1.3.1.20
TANLULIB_VERSION=1.3.1.24
MOGO_TANLU_API_VERSION = 1.0.0-SNAPSHOT
MOGO_MODULE_EVENT_PANEL_VERSION = 1.0.0-SNAPSHOT
MOGO_MODULE_EVENT_PANEL_NOOP_VERSION = 1.0.0-SNAPSHOT
#左侧面板模块
MOGO_MODULE_LEFT_PANEL_VERSION = 1.2.1.10-SNAPSHOT
MOGO_MODULE_LEFT_PANEL_NOOP_VERSION = 1.2.1.10-SNAPSHOT
# 小控件
MOGO_MODULE_WIDGETS_VERSION = 1.2.1.10-SNAPSHOT
# Boost分包
BOOST_MULTIDEX_VERSION=1.0.0
@@ -102,7 +107,9 @@ MOGO_MODULE_SPLASH_VERSION = 1.0.0-SNAPSHOT
MOGO_MODULE_SPLASH_NOOP_VERSION = 1.0.0-SNAPSHOT
# loglib
LOGLIB_VERSION = 1.0.0-SNAPSHOT
LOGLIB_VERSION = 1.0.4
# monitor
MOGO_MODULE_MONITOR_VERSION = 1.0.0-SNAPSHOT
## 产品库必备配置产品库自动对versionCode和versionName版本进行升级
applicationId=com.mogo.launcer

View File

@@ -522,6 +522,9 @@ public class AMapNaviViewWrapper implements IMogoMapView,
}
if ( checkAMapView() ) {
MyLocationStyle style = mMapView.getMap().getMyLocationStyle();
if ( style == null ) {
style = new MyLocationStyle();
}
style.showMyLocation( visible );
if ( visible ) {
// 强制刷新一遍车标
@@ -541,6 +544,9 @@ public class AMapNaviViewWrapper implements IMogoMapView,
}
if ( checkAMapView() ) {
MyLocationStyle style = mMapView.getMap().getMyLocationStyle();
if ( style == null ) {
style = new MyLocationStyle();
}
style.showMyLocation( true );
style.myLocationIcon( BitmapDescriptorFactory.fromView( view ) );
mMapView.getMap().setMyLocationStyle( style );

View File

@@ -12,7 +12,7 @@ public interface IMogoMarkerClickListener {
* 事件是否继续往下传递
*
* @param marker
* @return true - 时间已经处理完毕不继续往下传,否则继续往下传
* @return true - 事件已经处理完毕不继续往下传,否则继续往下传
*/
boolean onMarkerClicked( IMogoMarker marker );
}

View File

@@ -75,7 +75,10 @@ class MogoMapDelegateFactory {
}
public static IMogoNavi getNaviDelegate( Context context ) {
if ( AppUtils.isAppInstalled( context, "com.autonavi.amapauto" ) ) {
if ( DebugConfig.isUseCustomNavi() ) {
return NaviClient.getInstance( context );
} else if ( AppUtils.isAppInstalled( context, "com.autonavi.amapauto" ) ) {
return AutoNaviClient.getInstance( context );
} else {
if ( DebugConfig.isUseCustomMap() ) {

View File

@@ -6,6 +6,7 @@ import android.content.pm.PackageManager;
import android.graphics.Rect;
import android.location.Location;
import com.mogo.commons.debug.DebugConfig;
import com.mogo.map.impl.amap.navi.NaviClient;
import com.mogo.map.impl.automap.navi.AutoNaviClient;
import com.mogo.map.navi.IMogoCarLocationChangedListener2;

View File

@@ -1,7 +1,4 @@
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
apply plugin: 'com.alibaba.arouter'
android {
@@ -39,16 +36,16 @@ android {
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation rootProject.ext.dependencies.androidxccorektx
implementation rootProject.ext.dependencies.kotlinstdlibjdk7
implementation rootProject.ext.dependencies.androidxappcompat
implementation rootProject.ext.dependencies.arouter
kapt rootProject.ext.dependencies.aroutercompiler
annotationProcessor rootProject.ext.dependencies.aroutercompiler
if (Boolean.valueOf(RELEASE)) {
compileOnly rootProject.ext.dependencies.modulecommon
implementation rootProject.ext.dependencies.tanluupload
} else {
compileOnly project(':modules:mogo-module-common')
implementation project(':libraries:tanlulib')
}
}

View File

@@ -1,3 +1,3 @@
GROUP=com.mogo.module
POM_ARTIFACT_ID=module-event-panel
POM_ARTIFACT_ID=module-tanlu-api
VERSION_CODE=1

View File

@@ -18,6 +18,4 @@
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
-keep class com.zhidao.mogo.module.event.panel.EventPanelConstants
#-renamesourcefileattribute SourceFile

View File

@@ -1,4 +1,4 @@
package com.mogo.base.services.apk;
package com.zhidao.mogo.tanlu.api;
import android.content.Context;
@@ -15,12 +15,12 @@ import static org.junit.Assert.*;
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith( AndroidJUnit4.class )
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
assertEquals( "com.mogo.base.services.apk.test", appContext.getPackageName() );
assertEquals("com.zhidao.mogo.tanlu.api.test", appContext.getPackageName());
}
}

View File

@@ -1,5 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.zhidao.mogo.module.event.panel">
package="com.zhidao.mogo.tanlu.api">
/
</manifest>

View File

@@ -0,0 +1,64 @@
package com.zhidao.mogo.tanlu.api;
import android.content.Context;
import android.util.ArrayMap;
import com.alibaba.android.arouter.facade.annotation.Route;
import com.alibaba.android.arouter.launcher.ARouter;
import com.mogo.commons.analytics.AnalyticsUtils;
import com.mogo.service.IMogoServiceApis;
import com.mogo.service.MogoServicePaths;
import com.mogo.service.tanlu.IMogoTanluProvider;
import com.mogo.service.tanlu.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.mogo.tanlu.api.TanluApiConst.MODULE_NAME;
import static com.zhidao.mogo.tanlu.api.TanluApiConst.MODULE_PATH;
/**
* 探路api
*
* @author tongchenfei
*/
@Route(path = MODULE_PATH)
public class MogoTanluApiProvider implements IMogoTanluProvider {
private Context context;
/**
* 上传情报
*
* @param params 情报类型
*/
@Override
public void uploadRoadCondition(TanluUploadParams params) {
if(params!=null) {
Logger.d(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);
MainService.Companion.launchService(context, new UploadParams(params.getEventType(),
params.getFromType(), params.getDuration()));
}else{
throw new IllegalArgumentException("TanluUploadParams 不允许为空");
}
}
/**
* 开启探路业务服务
*/
@Override
public void startTanluService() {
Logger.d(MODULE_NAME, "startTanluService");
MainService.Companion.launchService(context, null);
}
@Override
public void init(Context context) {
this.context = context;
}
}

View File

@@ -0,0 +1,10 @@
package com.zhidao.mogo.tanlu.api;
/**
* 探路api相关常量
* @author tongchenfei
*/
public class TanluApiConst {
public static final String MODULE_NAME = "MogoTanluApi";
public static final String MODULE_PATH = "/tanlulib/api";
}

View File

@@ -1,4 +1,4 @@
package com.mogo.base.services.apk;
package com.zhidao.mogo.tanlu.api;
import org.junit.Test;
@@ -12,6 +12,6 @@ import static org.junit.Assert.*;
public class ExampleUnitTest {
@Test
public void addition_isCorrect() {
assertEquals( 4, 2 + 2 );
assertEquals(4, 2 + 2);
}
}

1
libraries/tanlulib/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/build

View File

@@ -0,0 +1,70 @@
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
apply plugin: 'android-aspectjx'
android {
compileSdkVersion 29
buildToolsVersion "29.0.2"
defaultConfig {
minSdkVersion 19
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles 'consumer-rules.pro'
}
buildTypes {
release {
minifyEnabled false
zipAlignEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.core:core-ktx:1.3.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
implementation 'com.zhidaoauto.common:service:1.0.4.10'
implementation 'com.squareup.okhttp3:okhttp:3.10.0'
implementation 'com.squareup.okhttp3:logging-interceptor:3.6.0'
implementation 'com.zhidaoauto.controller:api:1.0.8'
implementation 'com.zhidao.cosupload:cosuploadsdk:1.1.6'
implementation 'com.zhidao.video:video-processor:1.0.2.1'
implementation 'com.foundation.utils:common-utils:1.0.7'
compileOnly rootProject.ext.dependencies.mogocommons
implementation rootProject.ext.dependencies.eventbus
implementation rootProject.ext.dependencies.coroutinescore
implementation rootProject.ext.dependencies.coroutinesandroid
implementation rootProject.ext.dependencies.retrofit
implementation rootProject.ext.dependencies.retrofitconvertergson
implementation rootProject.ext.dependencies.gson
implementation rootProject.ext.dependencies.rxjava
implementation rootProject.ext.dependencies.rxandroid
compileOnly rootProject.ext.dependencies.mogomap
implementation rootProject.ext.dependencies.aspectj
implementation rootProject.ext.dependencies.analytics
}
apply from: new File(rootProject.rootDir, "gradle/upload.gradle").toString()

View File

@@ -0,0 +1,4 @@
-keep class com.zhidao.roadcondition.model.**{*;}
-keep class com.zhidao.roadcondition.service.*{*;}
-keep class com.zhidao.roadcondition.ShareRoadReceiver.*{*;}

View File

@@ -0,0 +1,3 @@
GROUP=com.mogo.module
POM_ARTIFACT_ID=module-tanlu-upload
VERSION_CODE=1

View File

@@ -19,9 +19,9 @@
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
-keep class com.zhidao.roadcondition.model.**{*;}
-keep class com.zhidao.roadcondition.service.*{*;}
-keep class com.zhidao.roadcondition.ShareRoadReceiver.*{*;}
#-----EventPanel------
-keep class com.zhidao.mogo.module.event.panel.bean.** {*;}
-keep class com.zhidao.mogo.module.event.panel.dao.** {*;}
-keep class com.zhidao.mogo.module.event.panel.listener.** {*;}
-keep class com.zhidao.mogo.module.event.panel.EventPanelConstants

View File

@@ -0,0 +1,25 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.zhidao.roadcondition">
<application>
<!--分享相关广播-->
<receiver
android:name=".receiver.ShareRoadReceiver"
android:exported="true">
<intent-filter>
<action android:name="com.zhidao.share.roadcondition.action" />
<category android:name="android.intent.category.HOME" />
</intent-filter>
</receiver>
<!--获取图片和视频的服务-->
<service
android:name=".service.MainService"
android:enabled="true" />
<service
android:name=".service.DelayService"
android:exported="false" />
</application>
</manifest>

View File

@@ -0,0 +1,29 @@
package com.zhidao.roadcondition.aspect
import android.util.Log
import com.zhidao.roadcondition.util.CarNet_Alive
import com.zhidao.roadcondition.util.trackNormalEvent
import org.aspectj.lang.annotation.*
@Aspect
class DAUAspectj {
companion object {
const val TAG = "DAUAspectj"
}
@Pointcut("execution(* com.zhidao.roadcondition.splash_module.SplashActivity.initView())")
fun callDAU() {
}
@Pointcut("execution(* com.zhidao.roadcondition.service.RoadInfoService.launchMainActivity(..))")
fun callDAUOther(){
}
@Before("callDAU() || callDAUOther()")
fun trackDAU() {
Log.i(TAG, "trackDAU")
trackNormalEvent(CarNet_Alive, null)
}
}

View File

@@ -0,0 +1,14 @@
package com.zhidao.roadcondition.aspect;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Target({TYPE, METHOD, CONSTRUCTOR})
@Retention(RUNTIME)
public @interface DebugLog {
}

View File

@@ -0,0 +1,112 @@
package com.zhidao.roadcondition.aspect
import android.os.Looper
import android.os.Trace
import android.util.Log
import com.zhidao.roadcondition.BuildConfig
import org.aspectj.lang.ProceedingJoinPoint
import org.aspectj.lang.annotation.Around
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.Pointcut
import org.aspectj.lang.reflect.CodeSignature
import org.aspectj.lang.reflect.MethodSignature
import java.util.concurrent.TimeUnit
@Aspect
class LogAspectj {
companion object {
@Volatile
private var enable: Boolean = BuildConfig.DEBUG
}
@Pointcut("within(@com.zhidao.roadcondition.aspect.DebugLog *)")
fun withinAnnotatedClass() {}
@Pointcut("execution(!synthetic * *(..))&& withinAnnotatedClass()")
fun methodInsideAnnotatedType() {}
@Pointcut("execution(!synthetic *.new(..))&& withinAnnotatedClass()")
fun constructorInsideAnnotatedType() {}
@Pointcut("execution(@com.zhidao.roadcondition.aspect.DebugLog * *(..))|| methodInsideAnnotatedType()")
fun method() {}
@Pointcut("execution(@com.zhidao.roadcondition.aspect.DebugLog *.new(..))|| constructorInsideAnnotatedType()")
fun constructor() {}
@Around("method() || constructor()")
fun logExecute(joinPoint: ProceedingJoinPoint) {
enterMethod(joinPoint)
var startNanos = System.nanoTime()
var result = joinPoint.proceed()
var stopNanos = System.nanoTime()
var lengthMill = TimeUnit.NANOSECONDS.toMillis(stopNanos - startNanos)
exitMethod(joinPoint, result as Any?, lengthMill)
}
private fun enterMethod(joinPoint: ProceedingJoinPoint) {
if (!enable) return
val signature = joinPoint.signature as CodeSignature
val cls = signature.declaringType
val methodName = signature.name
val parameterNames = signature.parameterNames
val parameterValues = joinPoint.args
var builder = StringBuilder("\u21E2 ")
builder.append(methodName).append('(')
parameterValues.forEachIndexed { index, any ->
if (index > 0) {
builder.append(",")
}
builder.append(parameterNames[index]).append('=')
builder.append(parameterValues[index])
}
builder.append(')')
if (Looper.myLooper() != Looper.getMainLooper()) {
builder.append("[Thread:\"").append(Thread.currentThread().name).append("\"]")
}
Log.i(asTag(cls), builder.toString())
val section = builder.toString().substring(2)
Trace.beginSection(section)
}
private fun exitMethod(joinPoint: ProceedingJoinPoint, result: Any?, lengthMill: Long) {
if (!enable) return
Trace.endSection()
val signature = joinPoint.signature
val cls = signature.declaringType
val methodName = signature.name
var hasReturnType = signature is MethodSignature
&& signature.returnType != Void.TYPE
var builder = StringBuilder("\u21E0 ")
.append(methodName)
.append("[")
.append(lengthMill)
.append("ms]")
if (hasReturnType) {
builder.append(" = ")
builder.append(result.let {
result.toString()
})
}
Log.i(asTag(cls), builder.toString())
}
private fun asTag(cls: Class<*>): String {
// if (cls.isAnonymousClass) {
// return asTag(cls.enclosingClass)
// }
return cls.simpleName
}
}

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,79 @@
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"
/**
* 默认视频抓取时长,单位是秒
*/
const val DEFAULT_VIDEO_DURATION = 10
const val DEF_NEWS_LABEL = "拥堵"
const val DEF_NEWS_VALUE = "traffic_jam"
const val DEF_NEWS_TYPE = "0"

View File

@@ -0,0 +1,34 @@
package com.zhidao.roadcondition.constant
import com.mogo.commons.debug.DebugConfig
import com.zhidao.roadcondition.BuildConfig
class HttpConstants {
companion object {
const val DEV_BASE_URL_OWNER = "http://dzt-test.zhidaohulian.com/"
const val RELEASE_BASE_URL_OWNER = "http://dzt.zhidaohulian.com/"
const val SHOW_BASE_URL_OWNER = "http://dzt-show.zhidaohulian.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
}
}
// fun getBaseUrl1(): String {
// return when (BuildConfig.BUILD_TYPE) {
// "debug" -> DEV_BASE_URL_OWNER
// "qa" -> DEV_BASE_URL_OWNER
// "release" -> RELEASE_BASE_URL_OWNER
// "show" -> SHOW_BASE_URL_OWNER
// else -> RELEASE_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,97 @@
package com.zhidao.roadcondition.model
class CityStrategy {
var video: Video
var pic: Pic
constructor(video: Video, pic: Pic) {
this.video = video
this.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,55 @@
package com.zhidao.roadcondition.model
fun CommonConfig.isActiveNonNull():Boolean{
return active!=null
}
//fun CommonConfig.isPromotionNonNull():Boolean{
// return promotion!=null
//}
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,25 @@
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
)

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,69 @@
package com.zhidao.roadcondition.model
import android.os.Parcel
import android.os.Parcelable
import com.amap.api.maps.model.LatLng
fun Informations.toLatLng(): LatLng {
return LatLng(lat, lon)
}
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,62 @@
package com.zhidao.roadcondition.model
import com.amap.api.maps.model.LatLng
import com.amap.api.services.core.LatLonPoint
fun LocationInfo.toLatLng(): LatLng {
return LatLng(latitude,longitude)
}
fun LocationInfo.toLatLngPoint():LatLonPoint{
return LatLonPoint(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,8 @@
package com.zhidao.roadcondition.model
/**
* 城市策略实体
*/
class Results(var cityStrategy: CityStrategy) {
}

View File

@@ -0,0 +1,68 @@
package com.zhidao.roadcondition.model
import android.util.Log
import com.google.gson.Gson
import com.mogo.commons.network.Utils
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 {
var map = hashMapOf<String, String>()
map["sn"] = Utils.getSn()
val locInfo = LocationUtil.getInstance().getLocationInfo()
map["data"] = Gson().toJson(
StrategyRequest(
locInfo.longitude,
locInfo.latitude,
locInfo.cityCode
)
)
HttpClient.getInstance().getHttpApi().getCityStrategy(map)
}
}
// suspend fun getAuthorization(): BaseResponse<Any> {
// return apiCall {
// HttpClient.getInstance().getHttpApi().getAuthorization(getSn())
// }
// }
//
// suspend fun getAllConfig(): BaseResponse<CommonConfig> {
// return apiCall {
// var map = hashMapOf<String, String>()
// map["sn"] = getSn()
// HttpClient.getInstance().getHttpApi()
// .getAllCommonConfig(map)
// }
// }
//
// suspend fun getSplashConfig(): BaseResponse<SplashConfig> {
// return apiCall {
// var splashConfigRequest =
// SplashConfigRequest("1", "1")
// var splashBodyStr = Gson().toJson(splashConfigRequest)
//
// HttpClient.getInstance().getHttpApi()
// .getSplashConfig(
// mapOf(
// "sn" to getSn(),
// "data" to splashBodyStr
// )
// )
//
// }
// }
suspend fun uploadInformation(informationBody: InformationBody): BaseResponse<Any> {
return apiCall {
var informationBodyStr = Gson().toJson(informationBody)
Log.d("MainServiceController", "uploadInformation informationBody = " + informationBodyStr)
HttpClient.getInstance().getHttpApi()
.uploadInformation(mapOf("sn" to Utils.getSn(), "data" to informationBodyStr))
}
}
}

View File

@@ -0,0 +1,22 @@
package com.zhidao.roadcondition.model.proxy
import androidx.annotation.IntDef
import java.lang.annotation.Retention
import java.lang.annotation.RetentionPolicy
const val INFO_TYPE_GONE = 0
const val INFO_TYPE_SHOW = 1
@IntDef(INFO_TYPE_GONE, INFO_TYPE_SHOW)
@Retention(RetentionPolicy.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,16 @@
package com.zhidao.roadcondition.model.proxy
import androidx.annotation.IntDef
import java.lang.annotation.Retention
import java.lang.annotation.RetentionPolicy
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(RetentionPolicy.SOURCE)
annotation class InformationsType

View File

@@ -0,0 +1,17 @@
package com.zhidao.roadcondition.net
import android.util.Log
import okhttp3.Interceptor
import okhttp3.Response
class CommonInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val oldRequest = chain.request()
val newRequest = oldRequest.newBuilder()
Log.d("CommonInterceptor","addHeader")
newRequest.header("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
return chain.proceed(newRequest.build())
}
}

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)
}
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,28 @@
package com.zhidao.roadcondition.net
import com.zhidao.roadcondition.model.BaseResponse
import com.zhidao.roadcondition.model.CommonConfig
import com.zhidao.roadcondition.model.Results
import okhttp3.MultipartBody
import okhttp3.ResponseBody
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<Any>
//获取所有配置
@GET("dataService/car/customConfig/no/getAll/v1")
suspend fun getAllCommonConfig(@QueryMap commonConfigBody: Map<String, String>):BaseResponse<CommonConfig>
}

View File

@@ -0,0 +1,87 @@
package com.zhidao.roadcondition.net
import com.mogo.commons.AbsMogoApplication
import com.zhidao.roadcondition.constant.HttpConstants
import okhttp3.Cache
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 {
companion object {
const val DEFAULT_CONNECT_TIME = 30L
const val DEFAULT_WRITE_TIMEOUT = 30L
const val DEFAULT_READ_TIMEOUT = 30L
const val COMMON_PARAM_SN = "sn"
private var baseUrlClientMap = HashMap<String, HttpClient>()
fun getInstance(): HttpClient {
var 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 lateinit var retrofit: Retrofit
private lateinit var httpApi: HttpApi
private constructor(baseUrl: String) {
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 httpLoggingInterceptor = HttpLoggingInterceptor()
httpLoggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
val cacheFile = File(AbsMogoApplication.getApp().applicationContext.cacheDir, "cache")
val cache = Cache(cacheFile, 1024 * 1024 * 50)
return OkHttpClient.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,43 @@
package com.zhidao.roadcondition.net
import androidx.annotation.NonNull
import okhttp3.MediaType
import okhttp3.RequestBody
import okhttp3.internal.Util
import okio.BufferedSink
class PostCommonBody : RequestBody {
companion object {
val CONTENT_TYPE = MediaType.parse("application/x-www-form-urlencoded; charset=UTF-8")
val charset = Util.UTF_8
fun create(@NonNull content: String): RequestBody {
return PostCommonBody(content)
}
}
private var content: String
constructor(@NonNull content: String) {
this.content = content
}
fun getContent(): String {
return content
}
override fun contentType(): MediaType? {
return CONTENT_TYPE
}
override fun writeTo(sink: BufferedSink) {
if (content.isNullOrEmpty()) {
throw NullPointerException("content == null")
}
val bytes = content.toByteArray(charset)
Util.checkOffsetAndCount(bytes!!.size.toLong(), 0, bytes!!.size.toLong())
sink.write(bytes, 0, bytes!!.size)
}
}

View File

@@ -0,0 +1,28 @@
package com.zhidao.roadcondition.net
import com.zhidao.roadcondition.constant.HttpConstants
import com.zhidao.roadcondition.exception.ApiException
import com.zhidao.roadcondition.model.BaseResponse
import io.reactivex.functions.Function
class ResponseFunction<T> : Function<BaseResponse<T>, T> {
private var baseUrl: String = ""
constructor() {
this.baseUrl = HttpConstants.getBaseUrl()
}
constructor(baseUrl: String) {
this.baseUrl = baseUrl
}
override fun apply(t: BaseResponse<T>): T {
if (t == null || !t.isSuccess(baseUrl)) {
throw ApiException(t.code, t.msg)
}
return t.result
}
}

View File

@@ -0,0 +1,19 @@
package com.zhidao.roadcondition.receiver
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.util.Log
import com.zhidao.roadcondition.service.MainService
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") {
var type = intent.getStringExtra("type")
Log.e("MainService", "ShareRoadReceiver type ----> $type ----> 此方法已经废弃了,无法调起服务")
// MainService.launchService(context, type)
}
}
}

View File

@@ -0,0 +1,415 @@
package com.zhidao.roadcondition.service
import android.text.TextUtils
import android.util.Log
import com.hw.videoprocessor.VideoProcessor
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.BuildConfig
import com.zhidao.roadcondition.constant.*
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 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
/**
* @description 记录仪相关操作
*
* @author lixiaopeng
* @since 2019-10-30
*/
object CarCorderController : TakePhotoCallback, TakeVideoCallback {
private lateinit var zdCarCoderController: ZdCarCoderController
val TAG: String = this.javaClass.simpleName
var outputVideoPath: String = ""
var mType: String = "" //1 上报拥堵, 2 封路和查车
var mainInfoId: Long = 0
var mFromType: 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() {
zdCarCoderController =
ZdCarCoderController.getInstance(AbsMogoApplication.getApp().applicationContext)
zdCarCoderController.addCallback(this)
zdCarCoderController.addVideoCallback(this)
zdCarCoderController.init()
}
fun takePhoto(
photoType: Int,
cameraId: Int,
haveVoice: Boolean,
isCustom: Boolean = false,
type: String,
mainInfoId: Long,
fromType: String
) {
Log.d(TAG, "takePhoto ---------- type = $type --- mType = $mType ---fromType = $fromType")
this.mType = type
this.mainInfoId = mainInfoId
this.mFromType = fromType
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
) {
Log.d(TAG, "takeVideo -------- type = $type")
Log.d(TAG, "takeVideo -------- isCustom = $isCustom ---mFromType = $mFromType")
this.mType = type
this.mainInfoId = mainInfoId
this.mFromType = fromType
if (DebugConfig.getCarMachineType() == 0) { //自研车机
outputVideoPath = getCompressVideoPath()
CustomStatusHandler.offerVideoStatus(TakeEntity(isCustom, id))
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 {
//失败了,传空地址,发起请求
val entity = TakeEntity(isCustom, id)
videoAndThumbMap["video"] = ""
videoAndThumbMap["thumb"] = ""
CosStatusController().sendInformationDirectly(
INFO_TYPE_VIDEO,
videoAndThumbMap,
mType,
entity,
mainInfoId
)
//地图上打点
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()
val entity = TakeEntity(isCustom, 0L)
Log.e(TAG, "onTakePhotoFail -----mType = $mType --- isCustom = $isCustom")
//语音播报 1上报路况2交通检查3封路 默认 mType 应该为null
if (mType.equals(TANLU_ROAD_CONGESTION) || mType.equals(TANLU_TRAFFIC_CHECK) || mType.equals(
TANLU_ROAD_CLOSURE
)
|| mType.equals(TANLU_ROAD_CURRENT) || mType.equals(TANLU_ROAD_PONDING) || mType.equals(
TANLU_ROAD_ICING
)
|| mType.equals(TANLU_ROAD_HEAVY_FOG) || mType.equals(TANLU_ROAD_ACCIDENT) || mType.equals(
TANLU_ROAD_WORK
)
) {
taskAsync(1_500) {
try {
// VoiceController.speakVoice("上报失败")
// if (isCustom) {
// sendGetInfoFailedReceiver(mType)
// }
} catch (e: java.lang.Exception) {
e.printStackTrace()
}
}
//获取图片失败也上报,图片不打点
if (isCustom) {
CosStatusController().sendInformationDirectly(
INFO_TYPE_IMG,
mutableMapOf("pic" to "" as String),
mType,
entity,
mainInfoId
);
}
}
}
//拍照成功回调返回图片本地路径
override fun onTakePhotoSuccess(photoType: Int, camera: Int, photoPath: String?) {
trackGetPhoto(2)
Log.d(TAG, "onTakePhotoSuccess -----mType = $mType --- mainInfoId = $mainInfoId")
val isCustom = CustomStatusHandler.pollPhotoStatus()
val entity = TakeEntity(isCustom, 0L)
if (!isCustom) {
trackNormalEvent(CarNet_auto_upload, null)
}
var interceptor = false
interceptors.forEach {
interceptor = it?.intercept()
it?.onTakePhotoSuccess(photoType, camera, photoPath)
}
if (interceptor) {
return
}
var minSpeedPic =
getStrategyMinSpeed(
AbsMogoApplication.getApp().applicationContext,
"pic",
getStrategyType("pic")
)
var maxSpeedPic =
getStrategyMaxSpeed(
AbsMogoApplication.getApp().applicationContext,
"pic",
getStrategyType("pic")
)
var speed = LocationUtil.getInstance().getSpeed()
if (!TextUtils.isEmpty(mType)) {
Log.d(TAG, "onTakePhotoSuccess mType != null")
CosStatusController().uploadFile(mutableListOf(photoPath as String), entity, mType, mainInfoId, mFromType)
} else {
Log.d(TAG, "onTakePhotoSuccess mType == null")
Log.d(
TAG,
"onTakePhotoSuccess maxSpeedPic = $maxSpeedPic ---> speed = $speed ---->minSpeedPic= $minSpeedPic"
)
if (maxSpeedPic == -1) {
if (speed >= (Math.abs(minSpeedPic) / 3.6f)) {
Log.d(TAG, "onTakePhotoSuccess abs =" + (Math.abs(minSpeedPic) / 3.6f))
CosStatusController().uploadFile(
mutableListOf(photoPath as String),
entity,
mType,
mainInfoId,
mFromType
)
}
}
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
)
}
}
}
}
//获取视频成功
override fun onTakeVideoSuccess(camera: Int, videoPath: String?) {
var thumbnailPath =
AbsMogoApplication.getApp().applicationContext.filesDir.parent + File.separator + "Thumbnail${System.currentTimeMillis()}.jpg"
var isSuccess = getVideoThumbnail(videoPath!!, thumbnailPath)
Log.d(
TAG,
"getVideo onTakeVideoSuccess===$videoPath -----> isSuccess= $isSuccess ----> mType = $mType --- mainInfoId = $mainInfoId"
)
val entity = CustomStatusHandler.pollVideoStatus()
if (!entity?.isCustom) {
trackNormalEvent(CarNet_auto_upload_video, null)
}
if (isSuccess) {
var minSpeedVideo = getStrategyMinSpeed(
AbsMogoApplication.getApp().applicationContext, "video",
getStrategyType("video")
)
var maxSpeedVideo = getStrategyMaxSpeed(
AbsMogoApplication.getApp().applicationContext, "video",
getStrategyType("video")
)
var speed = LocationUtil.getInstance().getSpeed()
//TODO
if (BuildConfig.DEBUG) {
//获取视频以及缩略图成功,开始上报
compressVideo(videoPath, thumbnailPath, entity)
return
}
if (entity?.isCustom) {
compressVideo(videoPath, thumbnailPath, entity)
} else {
Log.d(
TAG,
"onTakeVideoSuccess maxSpeedVideo = $maxSpeedVideo --->speed= $speed + minSpeedVideo = $minSpeedVideo"
)
if (maxSpeedVideo == -1) {
Log.d(TAG, "onTakeVideoSuccess 111 abs =" + (Math.abs(minSpeedVideo) / 3.6f))
if (speed >= (Math.abs(minSpeedVideo) / 3.6f)) {
//获取视频以及缩略图成功,开始上报
compressVideo(videoPath, thumbnailPath, entity)
}
}
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, entity)
}
}
}
trackGetVideo(2)
}
}
var videoAndThumbMap: MutableMap<String, String> = mutableMapOf()
//获取视频失败
override fun onTakeVideoFail(camera: Int) {
trackGetVideo(3)
Log.e(TAG, "getVideo onTakeVideoFail")
getVideoFailed?.invoke()
val entity = CustomStatusHandler.pollVideoStatus()
InformationUploadController.release(entity?.id)
Log.e(TAG, "getVideo onTakeVideoFail entity?.isCustom =" + entity?.isCustom)
if (entity?.isCustom) {
// sendGetInfoFailedReceiver(mType)
}
//失败了,传空地址,发起请求
videoAndThumbMap["video"] = ""
videoAndThumbMap["thumb"] = ""
CosStatusController().sendInformationDirectly(
INFO_TYPE_VIDEO,
videoAndThumbMap,
mType,
entity,
mainInfoId
)
//地图上打点
taskAsync(3_000) {
try {
LatLngStickyEventBus.getInstance().postSticky(GetImageSuccessEvent("", mType))
} catch (e: java.lang.Exception) {
e.printStackTrace()
}
}
}
/**
* 压缩视频并且上传
*/
fun compressVideo(videoPath: String, thumbnailPath: String, entity: TakeEntity) {
Log.d(
TAG,
"outputVideoPath = " + outputVideoPath + ">> videoPath=" + videoPath + " isCustom = ${entity?.isCustom}"
)
var 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
)
//删除压缩前的视频
deletePicFile(videoPath)
} catch (e: Exception) {
Log.e(TAG, "compressVideo e = $e")
//删除压缩前的视频
deletePicFile(videoPath)
e.printStackTrace()
}
}).start()
}
override fun onTakePhotoCancel(photoType: Int, camera: Int) {
Log.d(TAG, "onTakePhotoCancel -----photoType = $photoType")
}
override fun onTakeVideoCancel(camera: Int) {
Log.d(TAG, "onTakeVideoCancel -----camera = $camera")
}
//获取图片
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,90 @@
package com.zhidao.roadcondition.service
import android.util.Log
import com.zhidao.cosupload.callback.CosStatusCallback
import com.zhidao.cosupload.callback.CosStatusCallbackManager
/**
* @author congtaowang
* @since 2019-11-25
*
*/
object CosCallbackMapController : CosStatusCallback {
val TAG = "CosCallbackMapController"
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[path] = 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]?.let {
it.onStartUpload(eventId, localPath)
}
}
override fun uploadCosFailed(cosPath: String?, eventId: String?, localPath: String?) {
map[localPath]?.let {
it.uploadCosFailed(cosPath, eventId, localPath)
}
}
override fun uploadCosCompleted(
cosPath: String?,
eventId: String?,
downloadUrl: String?,
localPath: String?
) {
map[localPath]?.let {
it.uploadCosCompleted(cosPath, eventId, downloadUrl, localPath)
}
}
override fun onProgress(localPath: String?, progress: Float) {
map[localPath]?.let {
it.onProgress(localPath, progress)
}
}
fun release() {
CosStatusCallbackManager.getInstance().unregister(this)
map.clear()
uploadFailed = null
}
}

View File

@@ -0,0 +1,222 @@
package com.zhidao.roadcondition.service
import android.content.Intent
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.constant.*
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.CarNet_Cos_Upload
import com.zhidao.roadcondition.util.LocationUtil
import com.zhidao.roadcondition.util.deletePicFile
import com.zhidao.roadcondition.util.trackNormalEvent
/**
* @description cos上传操作
*
* @author lixiaopeng
* @since 2019-10-30
*/
class CosStatusController : CosStatusCallback {
val TAG: String = this.javaClass.simpleName
//存储单次请求的视频和缩略图url
var videoAndThumbMap: MutableMap<String, String> = mutableMapOf()
//图片上传的eventId
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 = ""
//上传文件
fun uploadFile(picPath: MutableList<String>, entity: TakeEntity, type: String, mainInfoId: Long, fromType: String) {
CosCallbackMapController.registerCallback(picPath, this)
// CosLogger.setLogStatus(true)
this.entity = entity
this.mType = type
this.mainInfoId = mainInfoId
this.mFromType = fromType;
Log.d(TAG, "uploadFile type===$type ---- mainInfoId =$mainInfoId ----mFromType = $mFromType ---- picPath = $picPath ")
trackUploadCos(3)
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 ------>")
var 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 (CarCorderController.mType.equals(TANLU_ROAD_CONGESTION) || CarCorderController.mType.equals(
TANLU_TRAFFIC_CHECK
) || CarCorderController.mType.equals(TANLU_ROAD_CLOSURE)
|| CarCorderController.mType.equals(TANLU_ROAD_CURRENT) || CarCorderController.mType.equals(
TANLU_ROAD_PONDING
) || CarCorderController.mType.equals(TANLU_ROAD_ICING)
|| CarCorderController.mType.equals(TANLU_ROAD_HEAVY_FOG) || CarCorderController.mType.equals(
TANLU_ROAD_ACCIDENT
) || CarCorderController.mType.equals(TANLU_ROAD_WORK)
) {
// VoiceController.speakVoice("cos上报失败")
Log.d(TAG, "uploadCosFailed mType = $mType")
if (entity?.isCustom && mFromType != UPLOAD_FROM_STRATEGY_ACCIDENT_AUTO) {
sendGetInfoFailedReceiver("100")
}
}
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)
}
}
override fun uploadCosCompleted(
cosPath: String?,
eventId: String?,
downloadUrl: String?,
localPath: String?
) {
Log.d(TAG, "uploadFile mType= $mType")
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) {
Log.d(TAG, "videoAndThumbMap.size == 2 ")
var locationInfo = LocationUtil.getInstance().getLocationInfo()
val locationStr: String = Gson().toJson(locationInfo)
Log.d(TAG, "locationStr = " + locationStr)
//如果失败,需要提示失败弹框
if (locationInfo.address.isNullOrEmpty() || locationInfo.cityName.isNullOrEmpty() || locationInfo.cityCode.isNullOrEmpty()
|| locationInfo.latitude == 0.0 || locationInfo.areaName.isNullOrEmpty() || locationInfo.street.isNullOrEmpty()
|| locationInfo.areaCode.isNullOrEmpty() || locationInfo.provinceName.isNullOrEmpty()
) {
if (entity?.isCustom && mFromType != UPLOAD_FROM_STRATEGY_ACCIDENT_AUTO) {
sendGetInfoFailedReceiver("100")
}
} else {
//分享成功并打点如果是上报拥堵需要takeVideo
LatLngStickyEventBus.getInstance()
.postSticky(GetImageSuccessEvent(downloadUrl, mType))
//上传录像以及缩略图成功
sendInformation(INFO_TYPE_VIDEO, videoAndThumbMap)
}
}
} else {
//上传图片成功, 如果是上报路况直接上传TODO
Log.d(TAG, "uploadCosCompleted 分享成功 ----mType = $mType")
// if (mType.equals("1") || mType.equals("-1")) {
sendInformationDirectly(
INFO_TYPE_IMG,
mutableMapOf("pic" to downloadUrl as String),
mType,
entity,
mainInfoId
)
// }
}
Log.d(TAG, "delete file: ${localPath!!}")
CosCallbackMapController.unregisterCallback(localPath)
deletePicFile(localPath)
}
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)
// }
}
/**
* 被动上报时直接上报,不用等待、选择情报类型
*/
fun sendInformationDirectly(
type: Int,
map: Map<String, String>,
poiType: String,
entity: TakeEntity,
mainInfoId: Long
) {
Log.d(TAG, "sendInformationDirectly isCustomSend = ${entity?.isCustom}")
Log.d(TAG, "sendInformationDirectly poiType= $poiType ---- mainInfoId= $mainInfoId")
mainServiceHttpModel.sendInformationMessage(
type = type,
url = map,
isCustom = entity?.isCustom,
poiType = poiType,
mainInfoId = mainInfoId
) { success ->
CosCallbackMapController.unregisterCallbacks(map)
CosCallbackMapController.mainService?.let {
CosCallbackMapController.mainService?.sendCustomResult(success)
}
}
}
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 {
val takePhotoStatusQueue: Queue<Boolean> = ArrayDeque(5)
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
)

View File

@@ -0,0 +1,70 @@
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 kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
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,126 @@
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 {
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?.let {
it.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,379 @@
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.elegant.analytics.Analytics
import com.mogo.commons.AbsMogoApplication
import com.mogo.commons.debug.DebugConfig
import com.zhidao.cosupload.manager.CosUploadManagerImpl
import com.zhidao.roadcondition.BuildConfig
import com.zhidao.roadcondition.constant.*
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 io.reactivex.functions.Consumer
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
val TAG: String = this.javaClass.simpleName
//1是一次性2是周期性
var picType: Int = 0
var videoType: Int = 0
var shareType: String = "type"
var fromType: String = ""
var isCustom: Boolean = false
var params: UploadParams? = null
var mainInfoId: Long = 0
//发送主动上报结果广播给外部
fun sendCustomResult(result: Boolean) {
var intent = Intent(customResultAction)
intent.putExtra("isSuccess", result)
sendBroadcast(intent)
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Log.d(TAG, "onStartCommand -----------> ")
//清理多媒体资源和sp策略数据
clearStrategyType(this)
//初始化埋点
Analytics.getInstance().start(this)
//参数说明appKey: app唯一标识比如包名
CosUploadManagerImpl.getInstance(AbsMogoApplication.getApp().applicationContext)
.init(BuildConfig.APPLICATION_ID, 2)
//初始化语音
VoiceController.initVoice()
if (intent != null) {
params = intent.getParcelableExtra("params")
params?.let {
shareType = it.eventType
fromType = it.fromType
mainInfoId = 0 //TODO
Log.d(TAG, "onStartCommand shareType = $shareType")
if (shareType == TANLU_ROAD_CONGESTION || shareType == TANLU_TRAFFIC_CHECK || shareType == TANLU_ROAD_CLOSURE || shareType == TANLU_ROAD_CURRENT
|| shareType == TANLU_ROAD_PONDING || shareType == TANLU_ROAD_ICING || shareType == TANLU_ROAD_HEAVY_FOG
|| shareType == TANLU_ROAD_ACCIDENT || shareType == TANLU_ROAD_WORK
) {
// takePhoto(1, false, true)
if (!fromType.equals(UPLOAD_FROM_STRATEGY_ACCIDENT_AUTO)) { // 是策略触发,不提示
takeVideo(it.duration, isCustom = true, id = id)
} else {
takeVideo(it.duration)
}
}
}
} else {
Log.e(TAG, "intent == null ")
}
return super.onStartCommand(intent, flags, startId)
}
//定时任务回调广播
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) {
//拍照
var number = p1.getLongExtra("number", 1)
takePhoto(number, true)
} else {
//录像
var 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(10, 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()
}
var id: Long = System.currentTimeMillis()
/**
* 获取图片成功
*/
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true, priority = 98)
fun getImageEvent(getImageSuccessEvent: GetImageSuccessEvent) {
var info = LocationUtil.getInstance().getLocationInfo()
Log.e("MainService", "getImageEvent long = ${info.longitude} ----> lat= ${info.latitude}")
Log.d(
"MainService",
"getImageEvent url = " + getImageSuccessEvent.getImageUrl() + ">>>>type =" + getImageSuccessEvent.getType()
)
sendMarkerInfoReceiver(
info.latitude,
info.longitude,
getImageSuccessEvent.getImageUrl(),
getImageSuccessEvent.getType()
)
}
private fun sendMarkerInfoReceiver(lat: Double, lon: Double, imageUrl: String?, type: String?) {
Log.e("MainService", "sendMarkerInfoReceiver ------> type = $type ")
Log.e("MainService", "sendMarkerInfoReceiver ------> fromType = $fromType ")
Log.e("MainService", "sendMarkerInfoReceiver ------> isCustom = $isCustom ")
var intent = Intent()
intent.action = "com.zhidao.roadcondition.marker.info"
intent.putExtra("type", type)
intent.putExtra("imageUrl", imageUrl)
intent.putExtra("lat", lat)
intent.putExtra("lon", lon)
intent.putExtra("custom", isCustom)
intent.putExtra("fromType", fromType)
sendBroadcast(intent)
}
/**
* 初始化定位监听
*/
private fun initLocationListener() {
//开始开始监听速度,只要超过一次5公里每小时则即开始获取策略进行本地上报 只有release才加此判断qa环境方便测试
LocationUtil.getInstance().setonSpeedlistenner(object : LocationUtil.SpeedListener {
override fun onSpeedGet(speed: Float) {
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")
if (picType == 1) {
takePhoto(1)
} else if (picType == 2) {
postPhotoAlarmTask(true)
}
if (videoType == 1) {
takeVideo(10)
} 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(picNum: Long, isInterval: Boolean = false, isCustom: Boolean = false) {
Log.d("MainService", "takePhoto -----1----->")
//判断是否授权
// if (isAuthorization(BaseApplication.getAppContext())) {
// log(TAG, "takePhoto -----2----->")
//目前不支持连拍只能定时2秒拍一张 第一期每次只拍一张
Observable.intervalRange(0, 1, 0, 2_000, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(Consumer {
CarCorderController.takePhoto(1, 1, false, isCustom, TANLU_ROAD_CURRENT, mainInfoId, fromType)
})
// }
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---->")
// if (isAuthorization(BaseApplication.getAppContext())) {
// log(TAG, "takeVideo --------2---->")
CarCorderController.takeVideo(1, duration, isCustom, id, if (isCustom) shareType else TANLU_ROAD_CURRENT, mainInfoId, fromType)
// }
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)
var 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)
var 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,309 @@
package com.zhidao.roadcondition.service
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 {
var TAG = this.javaClass.name
}
//逆地理编码是否重试
private var geoRetryed = false
private val strategyeModel by lazy { StrategyServiceModel() }
fun initStrategies(initFinish: (() -> Unit)? = null) {
request<BaseResponse<Results>> {
loader {
strategyeModel.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中
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 getAuthorization() {
// request<BaseResponse<Any>> {
// loader {
// strategyeModel.getAuthorization()
// }
// onSuccess {
// it.let {
// var jsonObject = JSONObject(it.result.toString())
// setAuthorization(
// AbsMogoApplication.getApp().applicationContext,
// jsonObject.getBoolean("enable")
// )
// Log.d("MainServiceController", "MainServiceController getAuthorization onSuccess enable=" + jsonObject.getBoolean("enable"))
// }
// }
// onError {
// Log.d("MainServiceController", "getAuthorization error")
// }
// }
// }
// fun getNeedAuth(authStatus: ((Boolean) -> Unit)? = null) {
// request<BaseResponse<CommonConfig>> {
// loader {
// strategyeModel.getAllConfig()
// }
// onSuccess {
// it.let {
// var config = it.result
// config.auth?.let { auth ->
// authStatus?.invoke(auth.isNeedAuth == 1)
// }
// if (config.isActiveNonNull()) {
// //保存运营活动webUrl、imgUrl SP
// setActiveWebUrl(config.active.webUrl)
// setActiveImgUrl(config.active.imageUrl)
// setActiveStatus(config.active.status)
// } else {
// //清除运营活动配置SP
// clearActiveInfo()
// }
//
// }
// }
// onError {
// Log.d("MainServiceController", "getAuthorization error")
// }
// }
//
// //获取加载页面配置
// getSplashConfig()
// }
/**
* 获取加载页面配置
*/
// fun getSplashConfig() {
// request<BaseResponse<SplashConfig>> {
// loader {
// strategyeModel.getSplashConfig()
// }
// onSuccess {
// it.let {
// var splashConfig = it.result
// val timeFlag = getLong(TIME_QUANTUM, 0)
// if (splashConfig != null && !TextUtils.isEmpty(splashConfig.image)) {
// //保存倒计时时间
// Log.d(TAG, "splashConfig.displayTime =" + splashConfig.displayTime)
// putInt(COUNT_DOWN_TIME, splashConfig.displayTime)
// //保存语音播报
// putCommonString(VOICE_TEXT, splashConfig.content)
// //下载图片
// if (!TextUtils.isEmpty(splashConfig.image)) {
// Log.d(TAG, "splashConfig.effectiveFlag =" + splashConfig.effectiveFlag + "-->timeFlag=" + timeFlag)
// if (timeFlag != splashConfig.effectiveFlag) {
// ImageFactory.downloadPic(splashConfig.image)
// }
// }
//
// //保存时间戳
// putLong(TIME_QUANTUM, splashConfig.effectiveFlag)
// } else {
// putCommonString(VOICE_TEXT, "")
// putInt(COUNT_DOWN_TIME, 0)
// putCommonBoolean(IS_SAVE_SPLASH, false)
// }
// }
// }
// onError {
// Log.d(TAG, "getSplashConfig error it.printStackTrace()=" + it.printStackTrace())
// }
// }
// }
//上传情报数据
fun sendInformationMessage(
type: Int,
url: Map<String, String>,
isCustom: Boolean = false,
trafficInfoType:String = "",
isShare:Boolean = false,
poiType:String,
mainInfoId:Long,
customSend: ((Boolean) -> Unit)? = null
) {
Log.d(TAG, " sendInformationMessage poiType = $poiType");
//删除测试数据
var locationInfo = LocationUtil.getInstance().getLocationInfo();
if (locationInfo.address.isNullOrEmpty()) {
geoLocation(type, url, locationInfo, isCustom,trafficInfoType, isShare, customSend,poiType,mainInfoId)
} else {
postInformationMessage(
getInformationBody(type, url, locationInfo, isCustom, trafficInfoType, isShare,poiType,mainInfoId),
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
) {
Log.d(TAG, " geoLocation -- poiType = $poiType");
LocationUtil.getInstance()
.geoCodeLocation(locationInfo.toLatLngPoint(), { locInfo: LocationInfo ->
postInformationMessage(
getInformationBody(type, url, locationInfo, isCustom, trafficInfoType, isShare, poiType,mainInfoId),
customSend
)
}, {
//转换失败的情况下再重试一次
geoRetryed = if (!geoRetryed) {
geoLocation(type, url, locationInfo, isCustom, trafficInfoType, isShare, customSend, poiType,mainInfoId)
true
} else {
false
}
})
}
private fun postInformationMessage(
informationBody: InformationBody,
customSend: ((Boolean) -> Unit)? = null
) {
trackUploadServer(3)
request<BaseResponse<Any>> {
loader {
strategyeModel.uploadInformation(informationBody)
}
onSuccess {
Log.i(TAG, "upload message success ")
trackUploadServer(1)
// CosStatusController().videoAndThumbMap.clear()
customSend?.invoke(true)
}
onError {
Log.i(TAG, "$it upload message ${it.message}")
trackUploadServer(2)
// CosStatusController().videoAndThumbMap.clear()
customSend?.invoke(false)
}
}
}
//上传服务器
private fun trackUploadServer(type: Int) {
trackNormalEvent(
CarNet_Servers_Upload, 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,37 @@
package com.zhidao.roadcondition.service
import android.os.Parcel
import android.os.Parcelable
class UploadParams(val eventType:String,val fromType:String,val duration:Int) : Parcelable {
constructor(parcel: Parcel) : this(
parcel.readString()!!,
parcel.readString()!!,
parcel.readInt())
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeString(eventType)
parcel.writeString(fromType)
parcel.writeInt(duration)
}
override fun describeContents(): Int {
return 0
}
override fun toString(): String {
return "UploadParams(eventType='$eventType', fromType='$fromType', duration=$duration)"
}
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,58 @@
package com.zhidao.roadcondition.service
import com.mogo.commons.AbsMogoApplication
import com.zhidao.auto.platform.voice.VoiceClient
/**
* @description 声音控制类
*
* @author lixiaopeng
* @since 2019-11-01
*/
object VoiceController {
private lateinit var voiceClient: VoiceClient
fun initVoice() {
voiceClient = VoiceClient(AbsMogoApplication.getApp().applicationContext)
}
/**
* 设置语音命令回调接口
* @param callBack
*/
fun setCallBack(callBack: VoiceClient.VoiceCmdCallBack) {
voiceClient.setCallBack(callBack)
}
/**
* @param speakText 语音播报内容
*/
fun speakVoice(speakText: String) {
voiceClient.speakDefault(speakText)
}
/**
* 注册免唤醒命令
* @param customType 命令
* @param customWakeupCmd 命令对应的唤醒词集合
*/
fun registerCustomWakeupCmd(customType: String, customWakeupCmd: Array<String>) {
voiceClient.registerCustomWakeupCmd(customType, customWakeupCmd)
}
/**
* 取消免唤醒命令
* @param customType 命令
*/
fun unRegisterCustomWakeupCmd(customType: String) {
voiceClient.unRegisterCustomWakeupCmd(customType)
}
/**
* 释放资源(界面销毁的时候调用)
*/
fun release() {
voiceClient.release()
}
}

View File

@@ -0,0 +1,54 @@
package com.zhidao.roadcondition.util
import android.content.Context
import com.elegant.analytics.Analytics
import com.mogo.commons.AbsMogoApplication
import com.mogo.commons.network.Utils
import com.zhidao.roadcondition.aspect.DebugLog
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 开始
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_live_broadcast = "CarNet_live_broadcast" // 地图页面点击直播(在线可直播车机)
//自定义埋点
@DebugLog
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"] = getSystemVersion(context)
localParams["sn"] = Utils.getSn()
// Log.d("liyz", "data =" + data.toString() + ">>>>event=" + event)
Analytics.getInstance().setCustomParams(localParams)
Analytics.getInstance().track(event, data)
}

View File

@@ -0,0 +1,40 @@
@file:JvmName("DeviceInfo")
package com.zhidao.roadcondition.util
import android.content.Context
import java.lang.reflect.InvocationTargetException
private const val SN_INFO: String = "gsm.serial"
fun getSn(): String {
var serial = ""
try {
var cls = Class.forName("android.os.SystemProperties")
var method = cls.getMethod("get", String::class.java)
serial = method.invoke(cls, SN_INFO) as String
} catch (var4: ClassNotFoundException) {
var4.printStackTrace()
} catch (var5: NoSuchMethodException) {
var5.printStackTrace()
} catch (var6: InvocationTargetException) {
var6.printStackTrace()
} catch (var7: IllegalAccessException) {
var7.printStackTrace()
}
return serial
}
fun getSystemVersion(context: Context): String {
try {
var cls = context.classLoader
var systemProperties = cls.loadClass("android.os.SystemProperties")
var paramTypes = String::class.java
val get = systemProperties.getMethod("get", paramTypes)
val version = get.invoke(systemProperties, *arrayOf<Any>("ro.fota.version")) as String
return if (!version.trim().isNullOrEmpty()) version.trim() else ""
} catch (e: Exception) {
e.printStackTrace()
}
return ""
}

View File

@@ -0,0 +1,184 @@
package com.zhidao.roadcondition.util
import android.graphics.Bitmap
import android.media.MediaMetadataRetriever
import android.os.Environment
import android.util.Log
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? {
var file = File(fileUrl)
if (file.exists()) {
var requestBody = RequestBody.create(MediaType.parse("image/jpg"), file)
return MultipartBody.Part.createFormData("file", file.name, requestBody)
}
return null
}
fun deletePicFile(filePath: String?): Boolean {
var file = File(filePath)
if (file.exists()) {
//如果图片地址包含此路径则是C上面的拍照需要再删除后摄图片
if (filePath!!.contains("usbotg-1-1.1")) {
//将地址替换成后摄图片地址
var 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()) {
Log.e("liyz", "文件删除失败,请检查文件路径是否正确")
return
}
//取得这个目录下的所有子文件对象
val files: Array<File> = file.listFiles()
//遍历该目录下的文件对象
for (f in files) {
val name: String = file.getName()
Log.e("liyz", name)
//判断子目录是否存在子目录,如果是文件则删除
if (f.isDirectory()) {
deleteAllFile(f)
} else {
f.delete()
}
}
//删除空文件夹 for循环已经把上一层节点的目录清空。
// file.delete()
}
//根据本地视频文件生成缩略图文件
fun getVideoThumbnail(filePath: String, picPath: String): Boolean {
var b: Bitmap? = null
var 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)
var 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 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
}
/**
* 保存图片到本地
*/
fun saveImageToSdcard(bmp: Bitmap): Boolean {
var currentFile: File
lateinit var fos: FileOutputStream
var picFile = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
picFile.mkdirs()
val builder = StringBuilder()
builder.append("splash_bg")
val filePrefix = builder.toString()
val fileOut = ".jpg"
currentFile = File(picFile, filePrefix + fileOut)
try {
fos = FileOutputStream(currentFile)
bmp.compress(Bitmap.CompressFormat.JPEG, 100, fos)
fos.flush()
} catch (e: FileNotFoundException) {
e.printStackTrace()
return false
} catch (e: IOException) {
e.printStackTrace()
return false
} finally {
try {
if (fos != null) {
fos.close()
}
} catch (e: IOException) {
e.printStackTrace()
return false
}
}
return true
}
/**
* 获取本地保存的图片地址
*/
fun getImagePath(): String {
return "/mnt/sdcard/Pictures/splash_bg.jpg"
}

View File

@@ -0,0 +1,213 @@
package com.zhidao.roadcondition.util
import android.util.Log
import com.amap.api.location.AMapLocation
import com.amap.api.location.AMapLocationClient
import com.amap.api.location.AMapLocationClientOption
import com.amap.api.location.AMapLocationListener
import com.amap.api.services.core.LatLonPoint
import com.amap.api.services.geocoder.*
import com.mogo.commons.AbsMogoApplication
import com.zhidao.roadcondition.model.LocationInfo
private fun RegeocodeAddress.toLocInfo(
address: RegeocodeAddress,
latlngPoint: LatLonPoint
): LocationInfo {
return LocationInfo(
address.province,
address.city,
address.cityCode,
address.district,
address.adCode,
address.roads[0].name,
latlngPoint.longitude,
latlngPoint.latitude,
address.formatAddress,
System.currentTimeMillis()
)
}
private fun AMapLocation.toLocInfo(location: AMapLocation): LocationInfo {
return LocationInfo(
location.province,
location.city,
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 locationClient: AMapLocationClient? = null
private var locationOption: AMapLocationClientOption? = null
private var speedListener: SpeedListener? = null
private var locationInfo: LocationInfo = LocationInfo()
private var speed: Float = 0.0f
fun initLocation() {
//初始化client
locationOption = getDefaultOption()
locationClient = AMapLocationClient(mContext)
//设置定位参数
locationClient!!.setLocationOption(locationOption)
// 设置定位监听
locationClient!!.setLocationListener(locationListener)
init = true
startLocation()
}
private fun getDefaultOption(): AMapLocationClientOption {
val mOption = AMapLocationClientOption()
mOption.locationMode =
AMapLocationClientOption.AMapLocationMode.Hight_Accuracy//可选,设置定位模式,可选的模式有高精度、仅设备、仅网络。默认为高精度模式
mOption.isGpsFirst = true//可选设置是否gps优先只在高精度模式下有效。默认关闭
mOption.httpTimeOut = 30000//可选设置网络请求超时时间。默认为30秒。在仅设备模式下无效
mOption.interval = 5000//可选设置定位间隔。默认为2秒
mOption.isNeedAddress = true//可选设置是否返回逆地理地址信息。默认是true
mOption.isOnceLocation = false//可选设置是否单次定位。默认是false
mOption.isOnceLocationLatest = false//可选设置是否等待wifi刷新默认为false.如果设置为true,会自动变为单次定位,持续定位时不要使用
AMapLocationClientOption.setLocationProtocol(AMapLocationClientOption.AMapLocationProtocol.HTTP)//可选, 设置网络请求的协议。可选HTTP或者HTTPS。默认为HTTP
mOption.isSensorEnable = true//可选设置是否使用传感器。默认是false
mOption.isWifiScan =
true //可选设置是否开启wifi扫描。默认为true如果设置为false会同时停止主动刷新停止以后完全依赖于系统刷新定位位置可能存在误差
mOption.isLocationCacheEnable = false //可选设置是否使用缓存定位默认为true
mOption.geoLanguage =
AMapLocationClientOption.GeoLanguage.DEFAULT//可选,设置逆地理信息的语言,默认值为默认语言(根据所在地区选择语言)
return mOption
}
private fun startLocation() {
if (checkInit()) {
// 设置定位参数
locationClient!!.setLocationOption(locationOption)
// 启动定位
locationClient!!.startLocation()
}
}
fun setOnMapLocationChangedListener(listener: OnMapLocationChangedListener) {
this.listener = listener
}
/**
* 定位监听
*/
private var locationListener: AMapLocationListener = AMapLocationListener { location ->
if (null != location) {
locationInfo.longitude = location.longitude
locationInfo.latitude = location.latitude
locationInfo.address = location.address
locationInfo.time = location.time
locationInfo.provinceName = location.province
locationInfo.cityName = location.city
locationInfo.cityCode = location.cityCode
locationInfo.areaName = location.district
locationInfo.areaCode = location.adCode
locationInfo.street = location.street
locationInfo.direction = location.bearing
listener?.let {
it.onChanged(locationInfo)
}
speed = location.speed
speedListener?.let {
it.onSpeedGet(location.speed)
}
} else {
Log.d(TAG, "定位失败 -> location is null")
}
}
private fun checkInit(): Boolean {
return if (!init) {
initLocation()
init
} else {
true
}
}
fun getLocationInfo(): LocationInfo {
return if (null != locationInfo) {
locationInfo
} else {
if (null == locationClient) {
locationClient = AMapLocationClient(mContext)
this.locationClient = locationClient
}
var location = locationClient!!.lastKnownLocation
location.toLocInfo(location)
}
}
//如果获取到的location address为空可以通过高德逆地理编码获得
fun geoCodeLocation(
latlngPoint: LatLonPoint,
locGeoCode: (((locInfo: LocationInfo) -> Unit)),
onError: ((msg: String) -> Unit)
) {
var geocoderSearch = GeocodeSearch(AbsMogoApplication.getApp().applicationContext)
var regeocodeQuery = RegeocodeQuery(latlngPoint, 200f, GeocodeSearch.AMAP)
geocoderSearch.getFromLocationAsyn(regeocodeQuery)
geocoderSearch.setOnGeocodeSearchListener(object : GeocodeSearch.OnGeocodeSearchListener {
override fun onRegeocodeSearched(p0: RegeocodeResult?, p1: Int) {
if (p1 == 1000) {
var regeocodeAddress = p0?.regeocodeAddress
regeocodeAddress?.let {
var locInfo = regeocodeAddress.toLocInfo(regeocodeAddress, latlngPoint)
locGeoCode.invoke(locInfo)
}
} else {
onError.invoke("geoCode -> $p1")
}
}
override fun onGeocodeSearched(p0: GeocodeResult?, p1: Int) {
}
})
}
fun getSpeed():Float{
return speed
}
fun setonSpeedlistenner(speedListener: SpeedListener) {
this.speedListener = speedListener
}
interface SpeedListener {
fun onSpeedGet(speed: Float)
}
}

View File

@@ -0,0 +1,80 @@
package com.zhidao.roadcondition.util
import com.google.gson.JsonArray
import com.google.gson.JsonObject
import com.mogo.commons.AbsMogoApplication
import com.mogo.commons.network.Utils
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
var array: Array<LocationInfo> = arrayOf(
LocationInfo(116.40320588562773, 39.96661385462713, "测试数据1", 123123213),
LocationInfo(116.41309250805662, 39.970285228355976, "测试数据2", 123123213),
LocationInfo(116.40749205563353, 39.97317943101997, "测试数据3", 12321321321),
LocationInfo(116.39925230953978, 39.97314654304226, "测试数据4", 123123123),
LocationInfo(116.439366, 39.868472, "测试数据5", 123123123),
LocationInfo(116.288164, 39.910547, "测试数据6", 123123123),
LocationInfo(116.330132, 39.989311, "测试数据7", 123123123)
)
fun getInformationBody(
types: Int,
urls: Map<String, String>,
locationInfo: LocationInfo,
isCustom: Boolean,
trafficInfoType:String = "",
isShare:Boolean,
poiType: String,
mainInfoId: Long
): InformationBody {
var jsonArray = JsonArray()
var type: Int
type = if (types == INFO_TYPE_VIDEO) {
var videoObject = JsonObject()
videoObject.addProperty("thumbnail", urls["thumb"])
videoObject.addProperty("url", urls["video"])
jsonArray.add(videoObject)
INFO_TYPE_VIDEO
} else {
var urlObject = JsonObject()
urlObject.addProperty("url", urls["pic"])
jsonArray.add(urlObject)
INFO_TYPE_IMG
}
var infoType = if (isCustom) 1 else 0
return InformationBody(
jsonArray.toString(),
locationInfo.address,
locationInfo.areaCode,
locationInfo.areaName,
locationInfo.cityCode,
locationInfo.cityName,
System.currentTimeMillis(),
locationInfo.latitude,
locationInfo.longitude,
locationInfo.provinceName,
Utils.getSn(),
locationInfo.street,
type,
0,
infoType,
getStrategyValidity(AbsMogoApplication.getApp().applicationContext, convertUploadTypeOfSP(type), getStrategyType(convertUploadTypeOfSP(type))),
trafficInfoType,
isShare,
locationInfo.direction,
poiType,
mainInfoId
)
}
private fun convertUploadTypeOfSP(type: Int): String {
return when (type) {
INFO_TYPE_IMG -> "pic"
INFO_TYPE_VIDEO -> "video"
else -> "pic"
}
}

View File

@@ -0,0 +1,171 @@
package com.zhidao.roadcondition.util
import android.content.Context
import android.util.Log
import androidx.core.content.edit
import com.mogo.commons.AbsMogoApplication
import com.zhidao.roadcondition.aspect.DebugLog
const val FILE_NAME = "settings_data"
const val FILE_NAME_NAVI = "settings_navi_data"
const val FILE_NAME_ACTIVE = "settings_active_data"
const val NAVI_INFO = "NAVI_INFO" //沿途导航数据存储
const val NAVI_INFO_STATUS = "NAVI_INFO_STATUS" //沿途导航路线是否存在
const val PARAM_AUTHORIZATION = "PARAM_AUTHORIZATION"
const val PARAM_PROMOTION_CONTENT = "PARAM_PROMOTION_CONTENT"
const val PARAM_ACTIVE_WEB_URL = "PARAM_ACTIVE_WEB_URL"
const val PARAM_ACTIVE_IMG_URL = "PARAM_ACTIVE_IMG_URL"
const val PARAM_ACTIVE_STATUS = "PARAM_ACTIVE_STATUS"
const val LAST_ENTER_TIME = "LAST_ENTER_TIME"
const val PIC_NUMBER = "PIC_NUMBER" //图片拍摄张数
const val VIDEO_DURATION_DEFAULT = 15_000L //视频拍摄时长
const val PIC_NUMBER_DEFAULT = 1L //图片拍摄张数
const val TIME_QUANTUM = "time_quantum" //时间段
const val COUNT_DOWN_TIME = "count_down" //倒计时
const val VOICE_TEXT = "voice_text" //语音播报内容
const val IS_SAVE_SPLASH = "is_save_splash" //是否保存了
//播放开始时间
const val PLAYER_START_TIME = "first_time"
fun clearActiveInfo(context: Context = AbsMogoApplication.getApp().applicationContext) {
var sharedPreferences = context.getSharedPreferences(FILE_NAME_ACTIVE, Context.MODE_PRIVATE)
sharedPreferences.edit {
clear()
val clearActiveInfo = commit()
Log.d(FILE_NAME_NAVI, "clearActiveInfo = $clearActiveInfo")
}
}
fun setActiveWebUrl(content: String, context: Context = AbsMogoApplication.getApp().applicationContext) {
var sharedPreferences = context.getSharedPreferences(FILE_NAME_ACTIVE, Context.MODE_PRIVATE)
sharedPreferences.edit {
putString(PARAM_ACTIVE_WEB_URL, content)
.commit()
}
}
fun setActiveStatus(status: Int, context: Context = AbsMogoApplication.getApp().applicationContext) {
var sharedPreferences = context.getSharedPreferences(FILE_NAME_ACTIVE, Context.MODE_PRIVATE)
sharedPreferences.edit {
putInt(PARAM_ACTIVE_STATUS, status)
.commit()
}
}
fun getActiveStatus(context: Context): Int {
return context.getSharedPreferences(FILE_NAME_ACTIVE, Context.MODE_PRIVATE)
.getInt(PARAM_ACTIVE_STATUS, 0)
}
fun setPromotionContent(content: String, context: Context = AbsMogoApplication.getApp().applicationContext) {
var sharedPreferences = context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE)
sharedPreferences.edit {
putString(PARAM_PROMOTION_CONTENT, content)
.commit()
}
}
fun isAuthorization(context: Context): Boolean {
return context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE)
.getBoolean(PARAM_AUTHORIZATION, false)
}
@DebugLog
fun setAuthorization(context: Context, authorization: Boolean) {
var sharedPreferences = context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE)
sharedPreferences.edit {
putBoolean(PARAM_AUTHORIZATION, authorization)
.commit()
}
}
@DebugLog
fun setNaviInfo(context: Context, naviInfo: String) {
var sharedPreferences = context.getSharedPreferences(FILE_NAME_NAVI, Context.MODE_PRIVATE)
sharedPreferences.edit {
val naviInfoSave = putString(NAVI_INFO, naviInfo)
.commit()
Log.d(FILE_NAME_NAVI, "naviInfoSave = $naviInfoSave")
}
}
@DebugLog
fun setNaviInfoStatus(context: Context, naviInfoStatus: Boolean) {
var sharedPreferences = context.getSharedPreferences(FILE_NAME_NAVI, Context.MODE_PRIVATE)
sharedPreferences.edit {
val naviInfoSaveStatus = putBoolean(NAVI_INFO_STATUS, naviInfoStatus)
.commit()
Log.d(FILE_NAME_NAVI, "naviInfoSaveStatus = $naviInfoSaveStatus")
}
}
fun clearAllNaviInfo(context: Context) {
var sharedPreferences = context.getSharedPreferences(FILE_NAME_NAVI, Context.MODE_PRIVATE)
sharedPreferences.edit {
clear()
val naviInfoAllClear = commit()
Log.d(FILE_NAME_NAVI, "naviInfoAllClear = $naviInfoAllClear")
}
}
fun putLong(key: String, value: Long) {
var 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) {
var 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) {
var sharedPreferences =
AbsMogoApplication.getApp().applicationContext.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE)
sharedPreferences.edit {
putString(key, value).apply()
}
}
//fun getCommonString(key: String?, defaultValue: String?): String {
// return AbsMogoApplication.getApp().applicationContext.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE)
// .getString(key, defaultValue)
//}
//
//fun putCommonBoolean(key: String, value: Boolean) {
// var sharedPreferences =
// AbsMogoApplication.getApp().applicationContext.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE)
// sharedPreferences.edit {
// putBoolean(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) {
var 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) {
var 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
) {
var 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
) {
var 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
) {
var 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
) {
var 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
) {
var 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,17 @@
package com.zhidao.roadcondition.util
import android.text.format.DateFormat
const val format = "yy/MM/dd kk:mm:ss"
fun formatDate(time: Long): String {
return DateFormat.format(format, time).toString()
}
fun convertVar(param: Any?): Any? {
return if (param is String) {
"\"$param\""
} else {
param
}
}

View File

@@ -0,0 +1,12 @@
package com.zhidao.roadcondition.util
import android.content.Context
import android.content.Intent
fun isReiceverRegist(context: Context, intentAction: String): Boolean {
val intent = Intent()
intent.action = intentAction
val pm = context.packageManager
val resolveInfos = pm.queryBroadcastReceivers(intent, 0)
return resolveInfos != null && resolveInfos.isNotEmpty()
}

View File

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

Some files were not shown because too many files have changed in this diff Show More