增加realm 在离线地图中的使用

This commit is contained in:
squallzhjch
2023-04-06 10:46:19 +08:00
parent dbb1572688
commit fa4ad254a5
29 changed files with 422 additions and 384 deletions

View File

@@ -8,8 +8,20 @@ class Constant {
* sd卡根目录 * sd卡根目录
*/ */
lateinit var ROOT_PATH: String lateinit var ROOT_PATH: String
/**
* 地图目录
*/
lateinit var MAP_PATH: String lateinit var MAP_PATH: String
/**
* 数据目录
*/
lateinit var DATA_PATH: String lateinit var DATA_PATH: String
/**
* 离线地图目录
*/
lateinit var OFFLINE_MAP_PATH: String lateinit var OFFLINE_MAP_PATH: String
/** /**

View File

@@ -1,13 +1,18 @@
package com.navinfo.omqs package com.navinfo.omqs
import android.app.Application import android.app.Application
import android.util.Log
import com.navinfo.omqs.tools.FileManager
import dagger.hilt.android.HiltAndroidApp import dagger.hilt.android.HiltAndroidApp
import io.realm.Realm import io.realm.Realm
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import java.io.File
@HiltAndroidApp @HiltAndroidApp
class OMQSApplication : Application() { class OMQSApplication : Application() {
override fun onCreate() { override fun onCreate() {
FileManager.initRootDir(this)
super.onCreate() super.onCreate()
Realm.init(this)
} }
} }

View File

@@ -1,45 +0,0 @@
package com.navinfo.omqs.bean
import io.realm.RealmObject
enum class StatusEnum(val status: Int) {
NONE(0), WAITING(1), LOADING(2), PAUSE(3),
ERROR(4), DONE(5), UPDATE(6)
}
open class OfflineMapCityBean{
var id: String = ""
var fileName: String = ""
var name: String = ""
var url: String = ""
var version: Long = 0L
var fileSize: Long = 0L
var currentSize:Long = 0L
var status: Int = StatusEnum.NONE.status
// status的转换对象
var statusEnum:StatusEnum
get() {
return try {
StatusEnum.values().find { it.status == status }!!
} catch (e: IllegalArgumentException) {
StatusEnum.NONE
}
}
set(value) {
status = value.status
}
constructor() : super()
fun getFileSizeText(): String {
return if (fileSize < 1024.0)
"$fileSize B"
else if (fileSize < 1048576.0)
"%.2f K".format(fileSize / 1024.0)
else if (fileSize < 1073741824.0)
"%.2f M".format(fileSize / 1048576.0)
else
"%.2f M".format(fileSize / 1073741824.0)
}
}

View File

@@ -1,17 +0,0 @@
package com.navinfo.omqs.bean
import io.realm.RealmObject
import io.realm.annotations.PrimaryKey
open class OfflineMapCityRealmObject(){
@PrimaryKey
var id: String = ""
var fileName: String=""
var name: String = ""
var url: String = ""
var version: Long = 0
var fileSize: Long = 0
var currentSize:Long = 0
var status:Int = 0
}

View File

@@ -6,11 +6,13 @@ import com.google.gson.Gson
import com.navinfo.omqs.Constant import com.navinfo.omqs.Constant
import com.navinfo.omqs.OMQSApplication import com.navinfo.omqs.OMQSApplication
import com.navinfo.omqs.http.RetrofitNetworkServiceAPI import com.navinfo.omqs.http.RetrofitNetworkServiceAPI
import com.navinfo.omqs.tools.RealmCoroutineScope
import dagger.Lazy import dagger.Lazy
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import dagger.hilt.InstallIn import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent import dagger.hilt.components.SingletonComponent
import kotlinx.coroutines.*
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit import retrofit2.Retrofit
@@ -25,11 +27,11 @@ import javax.inject.Singleton
@InstallIn(SingletonComponent::class) @InstallIn(SingletonComponent::class)
class GlobalModule { class GlobalModule {
// @Singleton @Singleton
// @Provides @Provides
// fun provideApplication(application: Application): OMQSApplication { fun provideApplication(application: Application): OMQSApplication {
// return application as OMQSApplication return application as OMQSApplication
// } }
/** /**
* 注入 网络OKHttp 对象 * 注入 网络OKHttp 对象
@@ -92,4 +94,13 @@ class GlobalModule {
fun provideNetworkService(retrofit: Retrofit): RetrofitNetworkServiceAPI { fun provideNetworkService(retrofit: Retrofit): RetrofitNetworkServiceAPI {
return retrofit.create(RetrofitNetworkServiceAPI::class.java) return retrofit.create(RetrofitNetworkServiceAPI::class.java)
} }
/**
* realm 注册
*/
@Provides
@Singleton
fun provideRealmService(context: Application): RealmCoroutineScope {
return RealmCoroutineScope(context)
}
} }

View File

@@ -1,14 +1,13 @@
package com.navinfo.omqs.hilt package com.navinfo.omqs.hilt
import android.content.Context
import com.navinfo.collect.library.map.NIMapController import com.navinfo.collect.library.map.NIMapController
import com.navinfo.omqs.http.RetrofitNetworkServiceAPI import com.navinfo.omqs.http.RetrofitNetworkServiceAPI
import com.navinfo.omqs.http.offlinemapdownload.OfflineMapDownloadManager import com.navinfo.omqs.http.offlinemapdownload.OfflineMapDownloadManager
import com.navinfo.omqs.tools.RealmCoroutineScope
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import dagger.hilt.InstallIn import dagger.hilt.InstallIn
import dagger.hilt.android.components.ActivityRetainedComponent import dagger.hilt.android.components.ActivityRetainedComponent
import dagger.hilt.android.qualifiers.ActivityContext
import dagger.hilt.android.scopes.ActivityRetainedScoped import dagger.hilt.android.scopes.ActivityRetainedScoped
@InstallIn(ActivityRetainedComponent::class) @InstallIn(ActivityRetainedComponent::class)
@@ -28,9 +27,10 @@ class MainActivityModule {
@ActivityRetainedScoped @ActivityRetainedScoped
@Provides @Provides
fun providesOfflineMapDownloadManager( fun providesOfflineMapDownloadManager(
networkServiceAPI: RetrofitNetworkServiceAPI networkServiceAPI: RetrofitNetworkServiceAPI,
realmManager: RealmCoroutineScope
): OfflineMapDownloadManager = ): OfflineMapDownloadManager =
OfflineMapDownloadManager( networkServiceAPI) OfflineMapDownloadManager(networkServiceAPI, realmManager)
/** /**
* 实验失败这样创建viewmodel不会在activity销毁的时候同时销毁 * 实验失败这样创建viewmodel不会在activity销毁的时候同时销毁

View File

@@ -1,6 +1,7 @@
package com.navinfo.omqs.http package com.navinfo.omqs.http
import com.navinfo.omqs.bean.OfflineMapCityBean import com.navinfo.collect.library.data.entity.OfflineMapCityBean
/** /**
* 网络访问 业务接口 * 网络访问 业务接口

View File

@@ -1,6 +1,6 @@
package com.navinfo.omqs.http package com.navinfo.omqs.http
import com.navinfo.omqs.bean.OfflineMapCityBean import com.navinfo.collect.library.data.entity.OfflineMapCityBean
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import javax.inject.Inject import javax.inject.Inject

View File

@@ -1,14 +1,12 @@
package com.navinfo.omqs.http package com.navinfo.omqs.http
import androidx.lifecycle.LiveData import com.navinfo.collect.library.data.entity.OfflineMapCityBean
import com.navinfo.omqs.bean.OfflineMapCityBean
import okhttp3.ResponseBody import okhttp3.ResponseBody
import retrofit2.Response import retrofit2.Response
import retrofit2.http.GET import retrofit2.http.GET
import retrofit2.http.Header import retrofit2.http.Header
import retrofit2.http.Streaming import retrofit2.http.Streaming
import retrofit2.http.Url import retrofit2.http.Url
import java.util.concurrent.Flow
/** /**
* retrofit2 网络请求接口 * retrofit2 网络请求接口

View File

@@ -1,23 +1,17 @@
package com.navinfo.omqs.http.offlinemapdownload package com.navinfo.omqs.http.offlinemapdownload
import android.content.Context
import android.text.TextUtils
import android.util.Log
import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import com.navinfo.omqs.Constant import com.navinfo.collect.library.data.entity.OfflineMapCityBean
import com.navinfo.omqs.bean.OfflineMapCityBean
import com.navinfo.omqs.http.RetrofitNetworkServiceAPI import com.navinfo.omqs.http.RetrofitNetworkServiceAPI
import dagger.hilt.android.qualifiers.ActivityContext import com.navinfo.omqs.tools.RealmCoroutineScope
import kotlinx.coroutines.cancel
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
import javax.inject.Inject
/** /**
* 管理离线地图下载 * 管理离线地图下载
*/ */
class OfflineMapDownloadManager @Inject constructor( class OfflineMapDownloadManager(
private val netApi: RetrofitNetworkServiceAPI val netApi: RetrofitNetworkServiceAPI, val realmManager: RealmCoroutineScope
) { ) {
/** /**
* 最多同时下载数量 * 最多同时下载数量
@@ -94,33 +88,27 @@ class OfflineMapDownloadManager @Inject constructor(
scopeMap[id]?.start() scopeMap[id]?.start()
} }
fun cancel(id: String) {
taskScopeMap.remove(id)
scopeMap[id]?.cancelTask()
}
fun addTask(cityBean: OfflineMapCityBean) { fun addTask(cityBean: OfflineMapCityBean) {
if (scopeMap.containsKey(cityBean.id)) { if (!scopeMap.containsKey(cityBean.id)) {
return scopeMap[cityBean.id] = OfflineMapDownloadScope(this, cityBean)
} else {
scopeMap[cityBean.id] = OfflineMapDownloadScope(this, netApi, cityBean)
} }
} }
fun observer( fun observer(
id: String, id: String, lifecycleOwner: LifecycleOwner, observer: Observer<OfflineMapCityBean>
lifecycleOwner: LifecycleOwner,
observer: Observer<OfflineMapCityBean>
) { ) {
if (scopeMap.containsKey(id)) { if (scopeMap.containsKey(id)) {
val downloadScope = scopeMap[id] scopeMap[id]!!.observer(lifecycleOwner, observer)
downloadScope?.let { }
downloadScope.observer(lifecycleOwner, observer) }
}
fun removeObserver(id: String) {
if (scopeMap.containsKey(id)) {
scopeMap[id]!!.removeObserver()
} }
} }
} }

View File

@@ -4,14 +4,13 @@ import android.util.Log
import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import com.navinfo.collect.library.data.entity.OfflineMapCityBean
import com.navinfo.omqs.Constant import com.navinfo.omqs.Constant
import com.navinfo.omqs.bean.OfflineMapCityBean
import com.navinfo.omqs.http.RetrofitNetworkServiceAPI
import kotlinx.coroutines.* import kotlinx.coroutines.*
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
import java.io.InputStream
import java.io.RandomAccessFile import java.io.RandomAccessFile
import kotlin.coroutines.EmptyCoroutineContext
/** /**
* 代表一个下载任务 * 代表一个下载任务
@@ -22,11 +21,9 @@ import kotlin.coroutines.EmptyCoroutineContext
*/ */
class OfflineMapDownloadScope( class OfflineMapDownloadScope(
private val downloadManager: OfflineMapDownloadManager, private val downloadManager: OfflineMapDownloadManager,
private val netApi: RetrofitNetworkServiceAPI, val cityBean: OfflineMapCityBean,
val cityBean: OfflineMapCityBean
) : ) :
CoroutineScope by CoroutineScope(EmptyCoroutineContext) { CoroutineScope by CoroutineScope(Dispatchers.IO) {
/** /**
*下载任务,用来取消的 *下载任务,用来取消的
*/ */
@@ -35,10 +32,11 @@ class OfflineMapDownloadScope(
/** /**
* 管理观察者,同时只有一个就行了 * 管理观察者,同时只有一个就行了
*/ */
private var observer: Observer<OfflineMapCityBean>? = null // private var observer: Observer<OfflineMapCityBean>? = null
private var lifecycleOwner: LifecycleOwner? = null
/** /**
* *通知UI更新
*/ */
private val downloadData = MutableLiveData<OfflineMapCityBean>() private val downloadData = MutableLiveData<OfflineMapCityBean>()
@@ -46,39 +44,6 @@ class OfflineMapDownloadScope(
downloadData.value = cityBean downloadData.value = cityBean
} }
/**
* 开始任务的下载
* [OfflineMapCityBean]是在协程中进行创建的,它的创建会优先从数据库和本地文件获取,但这种操作是异步的,详情请看init代码块
* 我们需要通过观察者观察[OfflineMapCityBean]来得知它是否已经创建完成,只有当他创建完成且不为空(如果创建完成,它一定不为空)
* 才可以交由[OfflineMapDownloadManager]进行下载任务的启动
* 任务的开始可能并不是立即的,任务会受到[OfflineMapDownloadManager]的管理
*
* 这段原来代码没看懂:要触发 Observer 得观察的对象[OfflineMapCityBean]发生变化才行原demo里没找到livedata的变化也触发了onChange这里根本触发不了
*
* 找到原因了:是[cityBean]根本没有设置到liveData中但是还是不用这样了因为cityBean是一定创建好了的
*/
//原代码
// fun start() {
// var observer: Observer<OfflineMapCityBean>? = null
// observer = Observer { cityBean ->
// Log.e("jingo","Observer 创建了bean 为null吗$cityBean")
// cityBean?.let {
// observer?.let {
// Log.e("jingo","Observer 这里为什么要解除观察?")
// downloadData.removeObserver(it)
// }
// Log.e("jingo","Observer 状态 ${cityBean.status} ")
// when (cityBean.status) {
//
// OfflineMapCityBean.PAUSE, OfflineMapCityBean.ERROR, OfflineMapCityBean.NONE -> {
// change(OfflineMapCityBean.WAITING)
// downloadManager.launchScope(this@OfflineMapDownloadScope)
// }
// }
// }
// }
// downloadData.observeForever(observer)
// }
//改进的代码 //改进的代码
fun start() { fun start() {
change(OfflineMapCityBean.WAITING) change(OfflineMapCityBean.WAITING)
@@ -91,6 +56,7 @@ class OfflineMapDownloadScope(
*/ */
fun pause() { fun pause() {
downloadJob?.cancel("pause") downloadJob?.cancel("pause")
change(OfflineMapCityBean.PAUSE)
} }
/** /**
@@ -98,20 +64,12 @@ class OfflineMapDownloadScope(
* 请不要尝试在外部调用此方法,那样会脱离[OfflineMapDownloadManager]的管理 * 请不要尝试在外部调用此方法,那样会脱离[OfflineMapDownloadManager]的管理
*/ */
fun launch() { fun launch() {
downloadJob = launch { downloadJob = launch() {
try { Log.e("jingo", "启动下载1")
download() download()
change(OfflineMapCityBean.DONE) Log.e("jingo", "启动下载2")
} catch (e: Throwable) { downloadManager.launchNext(cityBean.id)
Log.e("jingo DownloadScope", "error:${e.message}") Log.e("jingo", "启动下载3")
if (e.message == "pause") {
change(OfflineMapCityBean.PAUSE)
} else {
change(OfflineMapCityBean.ERROR)
}
} finally {
downloadManager.launchNext(cityBean.id)
}
} }
} }
@@ -120,9 +78,7 @@ class OfflineMapDownloadScope(
* 是否是等待任务 * 是否是等待任务
*/ */
fun isWaiting(): Boolean { fun isWaiting(): Boolean {
val downloadInfo = downloadData.value return cityBean.status == OfflineMapCityBean.WAITING
downloadInfo ?: return false
return downloadInfo.status == OfflineMapCityBean.WAITING
} }
/** /**
@@ -130,126 +86,91 @@ class OfflineMapDownloadScope(
* @param status [OfflineMapCityBean.Status] * @param status [OfflineMapCityBean.Status]
*/ */
private fun change(status: Int) { private fun change(status: Int) {
downloadData.value?.let { if (cityBean.status != status || status == OfflineMapCityBean.LOADING) {
it.status = status cityBean.status = status
downloadData.postValue(it) downloadData.postValue(cityBean)
downloadManager.realmManager.launch {
downloadManager.realmManager.insertOrUpdate(cityBean)
}
} }
} }
/** /**
* 添加下载任务观察者 * 添加下载任务观察者
*/ */
fun observer(lifecycleOwner: LifecycleOwner, ob: Observer<OfflineMapCityBean>) { fun observer(owner: LifecycleOwner, ob: Observer<OfflineMapCityBean>) {
if (observer != null) { removeObserver()
downloadData.removeObserver(observer!!) this.lifecycleOwner = owner
} downloadData.observe(owner, ob)
this.observer = ob
downloadData.observe(lifecycleOwner, observer!!)
} }
/** /**
* 下载文件 * 下载文件
*/ */
private suspend fun download() = withContext(context = Dispatchers.IO, block = { private suspend fun download() {
var inputStream: InputStream? = null
val downloadInfo = downloadData.value ?: throw IOException("jingo Download info is null") var randomAccessFile: RandomAccessFile? = null
//创建离线地图 下载文件夹,.map文件夹的下一级
val fileDir = File("${Constant.OFFLINE_MAP_PATH}download")
if (!fileDir.exists()) {
fileDir.mkdirs()
}
//遍历文件夹,找到对应的省市.map文件
val files = fileDir.listFiles()
for (item in files) {
//用id找到对应的文件
if (item.isFile && item.name.startsWith(downloadInfo.id)) {
//判断文件的版本号是否一致
if (item.name.contains("_${downloadInfo.version}.map")) {
//都一致,说明文件已经下载完成,不用再次下载
change(OfflineMapCityBean.DONE)
return@withContext
}else{
}
break
}
}
//查看下.map文件夹在不在
val fileMap = File("${Constant.OFFLINE_MAP_PATH}${downloadInfo.fileName}")
val fileTemp =
File("${Constant.OFFLINE_MAP_PATH}download/${downloadInfo.id}_${downloadInfo.version}")
if (fileTemp.exists()) {
}
if (!fileMap.exists()) {
}
change(OfflineMapCityBean.LOADING)
val startPosition = downloadInfo.currentSize
//验证断点有效性
if (startPosition < 0) throw IOException("jingo Start position less than zero")
//下载的文件是否已经被删除
// if (startPosition > 0 && !TextUtils.isEmpty(downloadInfo.path))
// if (!File(downloadInfo.path).exists()) throw IOException("File does not exist")
val response = netApi.retrofitDownLoadFile(
start = "bytes=$startPosition-",
url = downloadInfo.url
)
val responseBody = response.body()
responseBody ?: throw IOException("jingo ResponseBody is null")
//文件长度
downloadInfo.fileSize = responseBody.contentLength()
//保存的文件名称
// if (TextUtils.isEmpty(downloadInfo.fileName))
// downloadInfo.fileName = UrlUtils.getUrlFileName(downloadInfo.url)
// //验证下载完成的任务与实际文件的匹配度
// if (startPosition == downloadInfo.fileSize && startPosition > 0) {
// if (file.exists() && startPosition == file.length()) {
// change(OfflineMapCityBean.DONE)
// return@withContext
// } else throw IOException("jingo The content length is not the same as the file length")
// }
//写入文件
val randomAccessFile = RandomAccessFile(fileTemp, "rwd")
randomAccessFile.seek(startPosition)
// if (downloadInfo.currentSize == 0L) {
// randomAccessFile.setLength(downloadInfo.fileSize)
// }
downloadInfo.currentSize = startPosition
val inputStream = responseBody.byteStream()
val bufferSize = 1024 * 2
val buffer = ByteArray(bufferSize)
try { try {
//创建离线地图 下载文件夹,.map文件夹的下一级
val fileDir = File("${Constant.OFFLINE_MAP_PATH}download")
if (!fileDir.exists()) {
fileDir.mkdirs()
}
val fileTemp =
File("${Constant.OFFLINE_MAP_PATH}download/${cityBean.id}_${cityBean.version}")
val startPosition = cityBean.currentSize
//验证断点有效性
if (startPosition < 0) throw IOException("jingo Start position less than zero")
val response = downloadManager.netApi.retrofitDownLoadFile(
start = "bytes=$startPosition-",
url = cityBean.url
)
val responseBody = response.body()
change(OfflineMapCityBean.LOADING)
responseBody ?: throw IOException("jingo ResponseBody is null")
//写入文件
randomAccessFile = RandomAccessFile(fileTemp, "rwd")
randomAccessFile.seek(startPosition)
cityBean.currentSize = startPosition
inputStream = responseBody.byteStream()
val bufferSize = 1024 * 2
val buffer = ByteArray(bufferSize)
var readLength = 0 var readLength = 0
while (isActive) { while (downloadJob?.isActive == true) {
readLength = inputStream.read(buffer) readLength = inputStream.read(buffer)
if (readLength != -1) { if (readLength != -1) {
randomAccessFile.write(buffer, 0, readLength) randomAccessFile.write(buffer, 0, readLength)
downloadInfo.currentSize += readLength cityBean.currentSize += readLength
change(OfflineMapCityBean.LOADING) change(OfflineMapCityBean.LOADING)
} else { } else {
break break
} }
} }
Log.e("jingo", "文件下载完成 ${cityBean.currentSize} == ${cityBean.fileSize}")
if (cityBean.currentSize == cityBean.fileSize) {
val res =
fileTemp.renameTo(File("${Constant.OFFLINE_MAP_PATH}${cityBean.fileName}"))
Log.e("jingo", "文件下载完成 修改文件 $res")
change(OfflineMapCityBean.DONE)
} else {
change(OfflineMapCityBean.PAUSE)
}
} catch (e: Throwable) {
change(OfflineMapCityBean.ERROR)
} finally { } finally {
inputStream.close() inputStream?.close()
randomAccessFile.close() randomAccessFile?.close()
} }
})
/**
*
*/
private fun checkFile(){
} }
fun removeObserver() {
lifecycleOwner?.let {
downloadData.removeObservers(it)
null
}
}
} }

View File

@@ -0,0 +1,95 @@
package com.navinfo.omqs.tools
import android.content.Context
import android.util.Log
import com.navinfo.collect.library.data.entity.OfflineMapCityBean
import com.navinfo.omqs.Constant
import java.io.File
class FileManager {
companion object {
//初始化数据文件夹
fun initRootDir(context:Context){
// 在SD卡创建项目目录
val sdCardPath = context.getExternalFilesDir(null)
sdCardPath?.let {
Constant.ROOT_PATH = sdCardPath.absolutePath
Constant.MAP_PATH = Constant.ROOT_PATH + "/map/"
Constant.OFFLINE_MAP_PATH = Constant.MAP_PATH + "offline/"
val file = File(Constant.MAP_PATH)
if (!file.exists()) {
file.mkdirs()
Constant.DATA_PATH = Constant.ROOT_PATH + "/data/"
with(File(Constant.MAP_PATH)) {
if (!this.exists()) this.mkdirs()
}
with(File(Constant.DATA_PATH)) {
if (!this.exists()) this.mkdirs()
}
}else{
Constant.DATA_PATH = Constant.ROOT_PATH + "/data/"
}
}
}
/**
* 检查离线地图文件
*/
suspend fun checkOfflineMapFileInfo(cityBean: OfflineMapCityBean) {
//访问离线地图文件夹
val fileDir = File("${Constant.OFFLINE_MAP_PATH}")
//如果连本地文件夹还没有,就不用修改任何数据了
if (!fileDir.exists()) {
return
}
//访问离线地图临时下载文件夹
val fileTempDir = File("${Constant.OFFLINE_MAP_PATH}download/")
//是否有一份.map文件了
var mapFile: File? = null
//文件夹里文件挨个访问
for (item in fileDir.listFiles()) {
//先找到对应的省市文件例如540000_西藏自治区_20230401195018.map",以id开头
if (item.isFile && item.name.startsWith(cityBean.id)) {
//如果本地文件与从网络获取到版本号一致,表示这个文件已经下载完毕,不用处理了
if (item.name.contains("_${cityBean.version}.map")) {
cityBean.status = OfflineMapCityBean.DONE
return
}
//文件存在,版本号不对应,留给下面流程处理
mapFile = item
break
}
}
//临时下载文件夹
if (fileTempDir.exists()) {
for (item in fileTempDir.listFiles()) {
//先找到对应的省市文件例如540000_20230401195018",以id开头
if (item.isFile && item.name.startsWith(cityBean.id)) {
//如果本地文件与从网络获取到版本号一致,表示这个文件已经在下载列表中
if (item.name == "${cityBean.id}_${cityBean.version}") {
//如果这个临时文件的大小和下载大小是一致的,说明已经下载完了,但是在下载环节没有更名移动成功,需要重命名和移动文件夹
if (item.length() == cityBean.fileSize) {
//移动更名文件后删除旧数据,修改状态
if (item.renameTo(File("${Constant.OFFLINE_MAP_PATH}${cityBean.fileName}"))) {
//删除旧版本数据
mapFile?.delete()
cityBean.status = OfflineMapCityBean.DONE
return
}
} else { // 临时文件大小和目标不一致,说明下载了一半
cityBean.status = OfflineMapCityBean.PAUSE
cityBean.currentSize = item.length()
return
}
} else { //虽然省市id开头一致但是版本号不一致说明之前版本下载了一部分现在要更新了原来下载的文件直接删除
cityBean.status = OfflineMapCityBean.UPDATE
item.delete()
return
}
break
}
}
}
}
}
}

View File

@@ -0,0 +1,57 @@
package com.navinfo.omqs.tools
import android.app.Application
import com.navinfo.collect.library.data.entity.OfflineMapCityBean
import com.navinfo.omqs.Constant
import io.realm.Realm
import io.realm.RealmConfiguration
import io.realm.RealmModel
import io.realm.Sort
import io.realm.kotlin.where
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.newSingleThreadContext
import java.io.File
class RealmCoroutineScope(context: Application) :
CoroutineScope by CoroutineScope(newSingleThreadContext("RealmThread")) {
lateinit var realm: Realm
init {
launch {
Realm.init(context)
val password = "password".encodeToByteArray().copyInto(ByteArray(64))
// 1110000011000010111001101110011011101110110111101110010011001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
// Log.d("", "密码是: ${BigInteger(1, password).toString(2).padStart(64, '0')}")
val config = RealmConfiguration.Builder()
.directory(File(Constant.DATA_PATH))
.name("HDData")
// .encryptionKey(password)
.build()
Realm.setDefaultConfiguration(config)
realm = Realm.getDefaultInstance()
}
}
suspend fun getOfflineCityList(): List<OfflineMapCityBean> {
var list: List<OfflineMapCityBean> = mutableListOf()
realm.executeTransaction {
val objects = realm.where<OfflineMapCityBean>().findAll().sort("id", Sort.ASCENDING)
list = realm.copyFromRealm(objects)
}
return list
}
suspend fun insertOrUpdate(objects: Collection<RealmModel?>?) {
realm.executeTransaction {
realm.insertOrUpdate(objects)
}
}
suspend fun insertOrUpdate(realmModel: RealmModel?) {
realm.executeTransaction {
realm.insertOrUpdate(realmModel)
}
}
}

View File

@@ -3,15 +3,9 @@ package com.navinfo.omqs.ui
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import androidx.core.view.WindowCompat import androidx.core.view.WindowCompat
import androidx.navigation.findNavController
import androidx.navigation.ui.AppBarConfiguration import androidx.navigation.ui.AppBarConfiguration
import androidx.navigation.ui.navigateUp
import androidx.navigation.ui.setupActionBarWithNavController
import android.view.Menu
import android.view.MenuItem
import com.github.k1rakishou.fsaf.FileChooser import com.github.k1rakishou.fsaf.FileChooser
import com.github.k1rakishou.fsaf.callback.FSAFActivityCallbacks import com.github.k1rakishou.fsaf.callback.FSAFActivityCallbacks
import com.navinfo.omqs.R
import com.navinfo.omqs.databinding.ActivityMainBinding import com.navinfo.omqs.databinding.ActivityMainBinding
import com.navinfo.omqs.ui.activity.PermissionsActivity import com.navinfo.omqs.ui.activity.PermissionsActivity

View File

@@ -13,10 +13,13 @@ import com.navinfo.omqs.R
import com.navinfo.omqs.databinding.ActivityLoginBinding import com.navinfo.omqs.databinding.ActivityLoginBinding
import com.navinfo.omqs.ui.activity.PermissionsActivity import com.navinfo.omqs.ui.activity.PermissionsActivity
import com.navinfo.omqs.ui.activity.map.MainActivity import com.navinfo.omqs.ui.activity.map.MainActivity
import dagger.hilt.android.AndroidEntryPoint
/** /**
* 登陆页面 * 登陆页面
*/ */
@AndroidEntryPoint
class LoginActivity : PermissionsActivity() { class LoginActivity : PermissionsActivity() {
private lateinit var binding: ActivityLoginBinding private lateinit var binding: ActivityLoginBinding
@@ -65,6 +68,9 @@ class LoginActivity : PermissionsActivity() {
loginDialog?.dismiss() loginDialog?.dismiss()
loginDialog = null loginDialog = null
} }
LoginStatus.LOGIN_STATUS_NET_OFFLINE_MAP -> {
loginDialog("检查离线地图...")
}
} }
} }

View File

@@ -5,17 +5,17 @@ import android.util.Log
import android.view.View import android.view.View
import android.widget.Toast import android.widget.Toast
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.navinfo.omqs.Constant
import com.navinfo.omqs.bean.LoginUserBean import com.navinfo.omqs.bean.LoginUserBean
import io.realm.Realm import com.navinfo.omqs.http.NetResult
import io.realm.RealmConfiguration import com.navinfo.omqs.http.NetworkService
import com.navinfo.omqs.tools.FileManager
import com.navinfo.omqs.tools.RealmCoroutineScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.* import kotlinx.coroutines.*
import okio.IOException import okio.IOException
import java.io.File import javax.inject.Inject
import java.math.BigInteger
enum class LoginStatus { enum class LoginStatus {
/** /**
@@ -23,6 +23,11 @@ enum class LoginStatus {
*/ */
LOGIN_STATUS_NET_LOADING, LOGIN_STATUS_NET_LOADING,
/**
* 访问离线地图列表
*/
LOGIN_STATUS_NET_OFFLINE_MAP,
/** /**
* 初始化文件夹 * 初始化文件夹
*/ */
@@ -49,7 +54,10 @@ enum class LoginStatus {
LOGIN_STATUS_CANCEL, LOGIN_STATUS_CANCEL,
} }
class LoginViewModel( @HiltViewModel
class LoginViewModel @Inject constructor(
private val networkService: NetworkService,
private val realmManager: RealmCoroutineScope
) : ViewModel() { ) : ViewModel() {
//用户信息 //用户信息
val loginUser: MutableLiveData<LoginUserBean> = MutableLiveData() val loginUser: MutableLiveData<LoginUserBean> = MutableLiveData()
@@ -63,17 +71,6 @@ class LoginViewModel(
loginUser.value = LoginUserBean(username = "admin", password = "123456") loginUser.value = LoginUserBean(username = "admin", password = "123456")
} }
private fun initRealm() {
val password = "password".encodeToByteArray().copyInto(ByteArray(64))
// 1110000011000010111001101110011011101110110111101110010011001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
Log.d("", "密码是: ${BigInteger(1, password).toString(2).padStart(64, '0')}")
val config = RealmConfiguration.Builder()
.directory(File(Constant.DATA_PATH))
.name("HDData")
// .encryptionKey(password)
.build()
Constant.realm = Realm.getInstance(config)
}
/** /**
* 处理注册按钮 * 处理注册按钮
@@ -113,46 +110,56 @@ class LoginViewModel(
//文件夹初始化 //文件夹初始化
try { try {
loginStatus.postValue(LoginStatus.LOGIN_STATUS_FOLDER_INIT) loginStatus.postValue(LoginStatus.LOGIN_STATUS_FOLDER_INIT)
createRootFolder(context) createUserFolder(context)
// 初始化Realm // 初始化Realm
initRealm()
} catch (e: IOException) { } catch (e: IOException) {
loginStatus.postValue(LoginStatus.LOGIN_STATUS_FOLDER_FAILURE) loginStatus.postValue(LoginStatus.LOGIN_STATUS_FOLDER_FAILURE)
} }
//假装解压文件等 //假装解压文件等
delay(1000) delay(1000)
loginStatus.postValue(LoginStatus.LOGIN_STATUS_SUCCESS) loginStatus.postValue(LoginStatus.LOGIN_STATUS_NET_OFFLINE_MAP)
when (val result = networkService.getOfflineMapCityList()) {
is NetResult.Success -> {
// } if (result.data != null) {
for (cityBean in result.data) {
FileManager.checkOfflineMapFileInfo(cityBean)
}
realmManager.launch {
realmManager.insertOrUpdate(result.data)
}
}
}
is NetResult.Error -> {
withContext(Dispatchers.Main) {
Toast.makeText(context, "${result.exception.message}", Toast.LENGTH_SHORT)
.show()
}
}
is NetResult.Failure -> {
withContext(Dispatchers.Main) {
Toast.makeText(context, "${result.code}:${result.msg}", Toast.LENGTH_SHORT)
.show()
}
}
NetResult.Loading -> {}
}
loginStatus.postValue(LoginStatus.LOGIN_STATUS_SUCCESS)
} }
/**
* 创建用户目录
*/
@Throws(IOException::class) @Throws(IOException::class)
private fun createRootFolder(context: Context) { private fun createUserFolder(context: Context) {
// 在SD卡创建项目目录 // 在SD卡创建用户目录,解压资源等
val sdCardPath = context.getExternalFilesDir(null)
sdCardPath?.let {
Constant.ROOT_PATH = sdCardPath.absolutePath
Constant.MAP_PATH = Constant.ROOT_PATH + "/map/"
Constant.OFFLINE_MAP_PATH = Constant.MAP_PATH + "offline/"
val file = File(Constant.MAP_PATH)
if (!file.exists()) {
file.mkdirs()
Constant.DATA_PATH = Constant.ROOT_PATH + "/data/"
with(File(Constant.MAP_PATH)) {
if(!this.exists()) this.mkdirs()
}
with(File(Constant.DATA_PATH)) {
if(!this.exists()) this.mkdirs()
}
}
} }
/** /**
* 取消登录 * 取消登录
*/ */
fun cancelLogin() { fun cancelLogin() {
Log.e("jingo", "取消了?${Thread.currentThread().name}")
jobLogin?.let { jobLogin?.let {
it.cancel() it.cancel()
loginStatus.value = LoginStatus.LOGIN_STATUS_CANCEL loginStatus.value = LoginStatus.LOGIN_STATUS_CANCEL
@@ -163,6 +170,4 @@ class LoginViewModel(
super.onCleared() super.onCleared()
cancelLogin() cancelLogin()
} }
} }

View File

@@ -69,7 +69,6 @@ class MainActivity : BaseActivity() {
super.onDestroy() super.onDestroy()
mapController.mMapView.onDestroy() mapController.mMapView.onDestroy()
mapController.locationLayerHandler.stopLocation() mapController.locationLayerHandler.stopLocation()
Log.e("jingo", "MainActivity 销毁")
} }
override fun onResume() { override fun onResume() {

View File

@@ -25,7 +25,6 @@ class MainViewModel @Inject constructor(
} }
override fun onCleared() { override fun onCleared() {
Log.e("jingo","MainViewModel 被释放了")
super.onCleared() super.onCleared()
} }
} }

View File

@@ -6,8 +6,8 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import com.navinfo.collect.library.data.entity.OfflineMapCityBean
import com.navinfo.omqs.R import com.navinfo.omqs.R
import com.navinfo.omqs.bean.OfflineMapCityBean
import com.navinfo.omqs.databinding.AdapterOfflineMapCityBinding import com.navinfo.omqs.databinding.AdapterOfflineMapCityBinding
import com.navinfo.omqs.http.offlinemapdownload.OfflineMapDownloadManager import com.navinfo.omqs.http.offlinemapdownload.OfflineMapDownloadManager
import com.navinfo.omqs.ui.other.BaseRecyclerViewAdapter import com.navinfo.omqs.ui.other.BaseRecyclerViewAdapter
@@ -32,14 +32,16 @@ class OfflineMapCityListAdapter @Inject constructor(
val cityBean = data[it.tag as Int] val cityBean = data[it.tag as Int]
when (cityBean.status) { when (cityBean.status) {
OfflineMapCityBean.NONE, OfflineMapCityBean.UPDATE, OfflineMapCityBean.PAUSE, OfflineMapCityBean.ERROR -> { OfflineMapCityBean.NONE, OfflineMapCityBean.UPDATE, OfflineMapCityBean.PAUSE, OfflineMapCityBean.ERROR -> {
Log.e("jingo", "开始下载 ${cityBean.status}")
downloadManager.start(cityBean.id) downloadManager.start(cityBean.id)
} }
OfflineMapCityBean.LOADING, OfflineMapCityBean.WAITING -> { OfflineMapCityBean.LOADING, OfflineMapCityBean.WAITING -> {
Log.e("jingo", "暂停 ${cityBean.status}")
downloadManager.pause(cityBean.id) downloadManager.pause(cityBean.id)
} }
// OfflineMapCityBean.WAITING->{ else -> {
// downloadManager.cancel(cityBean.id) Log.e("jingo", "暂停 ${cityBean.status}")
// } }
} }
} }
} }
@@ -50,24 +52,38 @@ class OfflineMapCityListAdapter @Inject constructor(
return BaseViewHolder(viewBinding) return BaseViewHolder(viewBinding)
} }
override fun onViewRecycled(holder: BaseViewHolder) {
super.onViewRecycled(holder)
//页面滑动时会用holder重构页面但是对进度条的监听回调会一直返回扰乱UI所以当当前holder去重构的时候移除监听
downloadManager.removeObserver(holder.tag)
}
override fun onBindViewHolder(holder: BaseViewHolder, position: Int) { override fun onBindViewHolder(holder: BaseViewHolder, position: Int) {
val binding: AdapterOfflineMapCityBinding = val binding: AdapterOfflineMapCityBinding =
holder.viewBinding as AdapterOfflineMapCityBinding holder.viewBinding as AdapterOfflineMapCityBinding
//牺牲性能立刻刷新UI解决闪烁 这里不用 //牺牲性能立刻刷新UI解决闪烁 这里不用
// binding.executePendingBindings() // binding.executePendingBindings()
val cityBean = data[position] val cityBean = data[position]
//tag 方便onclick里拿到数据
holder.tag = cityBean.id
changeViews(binding, cityBean)
downloadManager.addTask(cityBean)
downloadManager.observer(cityBean.id, holder, DownloadObserver(cityBean.id, binding))
binding.offlineMapDownloadBtn.tag = position binding.offlineMapDownloadBtn.tag = position
binding.offlineMapDownloadBtn.setOnClickListener(downloadBtnClick) binding.offlineMapDownloadBtn.setOnClickListener(downloadBtnClick)
binding.offlineMapCityName.text = cityBean.name binding.offlineMapCityName.text = cityBean.name
binding.offlineMapCitySize.text = cityBean.getFileSizeText() binding.offlineMapCitySize.text = cityBean.getFileSizeText()
downloadManager.addTask(cityBean) }
changeViews(binding, cityBean)
downloadManager.observer(cityBean.id, holder) { inner class DownloadObserver(val id: String, val binding: AdapterOfflineMapCityBinding) :
if (cityBean.id == it.id) Observer<OfflineMapCityBean> {
changeViews(binding, it) override fun onChanged(t: OfflineMapCityBean?) {
if (id == t?.id)
changeViews(binding, t)
} }
} }
private fun changeViews(binding: AdapterOfflineMapCityBinding, cityBean: OfflineMapCityBean) { private fun changeViews(binding: AdapterOfflineMapCityBinding, cityBean: OfflineMapCityBean) {
binding.offlineMapProgress.progress = binding.offlineMapProgress.progress =
(cityBean.currentSize * 100 / cityBean.fileSize).toInt() (cityBean.currentSize * 100 / cityBean.fileSize).toInt()

View File

@@ -57,6 +57,5 @@ class OfflineMapCityListFragment : Fragment() {
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() super.onDestroyView()
_binding = null _binding = null
Log.e("jingo", "OfflineMapCityListFragment onDestroyView")
} }
} }

View File

@@ -1,16 +1,18 @@
package com.navinfo.omqs.ui.fragment.offlinemap package com.navinfo.omqs.ui.fragment.offlinemap
import android.app.Application
import android.content.Context import android.content.Context
import android.widget.Toast
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.navinfo.omqs.http.NetResult import com.navinfo.collect.library.data.entity.OfflineMapCityBean
import com.navinfo.omqs.http.NetworkService import com.navinfo.omqs.tools.FileManager
import com.navinfo.omqs.bean.OfflineMapCityBean import com.navinfo.omqs.tools.RealmCoroutineScope
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import io.realm.Realm
import io.realm.Sort
import io.realm.kotlin.where
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import javax.inject.Inject import javax.inject.Inject
@@ -19,8 +21,7 @@ import javax.inject.Inject
*/ */
@HiltViewModel @HiltViewModel
class OfflineMapCityListViewModel @Inject constructor( class OfflineMapCityListViewModel @Inject constructor(
private val networkService: NetworkService, @ApplicationContext val context: Context,
@ApplicationContext val context: Context
) : ViewModel() { ) : ViewModel() {
val cityListLiveData = MutableLiveData<List<OfflineMapCityBean>>() val cityListLiveData = MutableLiveData<List<OfflineMapCityBean>>()
@@ -29,21 +30,15 @@ class OfflineMapCityListViewModel @Inject constructor(
* 去获取离线地图列表 * 去获取离线地图列表
*/ */
fun getCityList() { fun getCityList() {
viewModelScope.launch { viewModelScope.launch(Dispatchers.IO) {
when (val result = networkService.getOfflineMapCityList()) { val realm = Realm.getDefaultInstance()
is NetResult.Success -> { val objects = realm.where<OfflineMapCityBean>().findAll().sort("id", Sort.ASCENDING)
cityListLiveData.postValue(result.data?.sortedBy { bean -> bean.id }) val list = realm.copyFromRealm(objects)
} realm.close()
is NetResult.Error -> { for (item in list) {
Toast.makeText(context, "${result.exception.message}", Toast.LENGTH_SHORT) FileManager.checkOfflineMapFileInfo(item)
.show()
}
is NetResult.Failure -> {
Toast.makeText(context, "${result.code}:${result.msg}", Toast.LENGTH_SHORT)
.show()
}
NetResult.Loading -> {}
} }
cityListLiveData.postValue(list)
} }
} }
} }

View File

@@ -61,6 +61,5 @@ class OfflineMapFragment : Fragment() {
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() super.onDestroyView()
_binding = null _binding = null
Log.e("jingo","OfflineMapFragment onDestroyView")
} }
} }

View File

@@ -33,6 +33,5 @@ class OfflineMapStateListFragment : Fragment() {
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() super.onDestroyView()
_binding = null _binding = null
Log.e("jingo","OfflineMapStateListFragment onDestroyView")
} }
} }

View File

@@ -3,21 +3,16 @@ package com.navinfo.omqs.ui.fragment.personalcenter
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.activity.viewModels
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.get
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import com.blankj.utilcode.util.UriUtils import com.blankj.utilcode.util.UriUtils
import com.github.k1rakishou.fsaf.FileChooser import com.github.k1rakishou.fsaf.FileChooser
import com.github.k1rakishou.fsaf.callback.FSAFActivityCallbacks import com.github.k1rakishou.fsaf.callback.FSAFActivityCallbacks
import com.github.k1rakishou.fsaf.callback.FileChooserCallback import com.github.k1rakishou.fsaf.callback.FileChooserCallback
import com.google.android.material.snackbar.Snackbar
import com.navinfo.omqs.R import com.navinfo.omqs.R
import com.navinfo.omqs.databinding.FragmentPersonalCenterBinding import com.navinfo.omqs.databinding.FragmentPersonalCenterBinding
@@ -42,7 +37,6 @@ class PersonalCenterFragment : Fragment(), FSAFActivityCallbacks {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
Log.e("jingo", "NIMapController PersonalCenterFragment onViewCreated")
binding.root.setNavigationItemSelectedListener { binding.root.setNavigationItemSelectedListener {
when (it.itemId) { when (it.itemId) {
R.id.personal_center_menu_offline_map -> R.id.personal_center_menu_offline_map ->

View File

@@ -14,6 +14,7 @@ import androidx.viewbinding.ViewBinding
open class BaseViewHolder(val viewBinding: ViewBinding) : open class BaseViewHolder(val viewBinding: ViewBinding) :
RecyclerView.ViewHolder(viewBinding.root), LifecycleOwner { RecyclerView.ViewHolder(viewBinding.root), LifecycleOwner {
private val lifecycleRegistry = LifecycleRegistry(this) private val lifecycleRegistry = LifecycleRegistry(this)
var tag = ""
init { init {
// dataBinding.lifecycleOwner = this // dataBinding.lifecycleOwner = this

View File

@@ -62,9 +62,10 @@ class MyProgressBar : ProgressBar {
// int x = (getWidth()/2) - rect.centerX(); // int x = (getWidth()/2) - rect.centerX();
// int y = (getHeight()/2) - rect.centerY(); // int y = (getHeight()/2) - rect.centerY();
var x = (width * rate).toInt() var x = (width * rate).toInt()
if (x == width) { val dx = width - rect.right
if (x > dx) {
// 如果为百分之百则在左边绘制。 // 如果为百分之百则在左边绘制。
x = width - rect.right x = dx
} }
mPaint.textSize = 24f mPaint.textSize = 24f
val y: Int = 10 - rect.top val y: Int = 10 - rect.top

View File

@@ -3,31 +3,44 @@ package com.navinfo.collect.library.data.entity
import io.realm.RealmObject import io.realm.RealmObject
import io.realm.annotations.PrimaryKey import io.realm.annotations.PrimaryKey
enum class StatusEnum(val status: Int) { //enum class StatusEnum(val status: Int) {
NONE(0), WAITING(1), LOADING(2), PAUSE(3), // NONE(0), WAITING(1), LOADING(2), PAUSE(3),
ERROR(4), DONE(5), UPDATE(6) // ERROR(4), DONE(5), UPDATE(6)
} //}
open class OfflineMapCityBean @JvmOverloads constructor(@PrimaryKey var id: String = "", open class OfflineMapCityBean @JvmOverloads constructor(
var fileName: String = "", @PrimaryKey var id: String = "",
var name: String = "", var fileName: String = "",
var url: String = "", var name: String = "",
var version: Long = 0L, var url: String = "",
var fileSize: Long = 0L, var version: Long = 0L,
var currentSize: Long = 0L, var fileSize: Long = 0L,
var status: Int =0) : RealmObject(){ var currentSize: Long = 0L,
// status的转换对象 var status: Int = NONE
var statusEnum:StatusEnum ) : RealmObject() {
get() {
return try { companion object Status{
StatusEnum.values().find { it.status == status }!! const val NONE = 0 //无状态
} catch (e: IllegalArgumentException) { const val WAITING = 1 //等待中
StatusEnum.NONE const val LOADING = 2 //下载中
} const val PAUSE = 3 //暂停
} const val ERROR = 4 //错误
set(value) { const val DONE = 5 //完成
status = value.status const val UPDATE = 6 //有新版本要更新
} }
// // status的转换对象
// var statusEnum: StatusEnum
// get() {
// return try {
// StatusEnum.values().find { it.status == status }!!
// } catch (e: IllegalArgumentException) {
// StatusEnum.NONE
// }
// }
// set(value) {
// status = value.status
// }
fun getFileSizeText(): String { fun getFileSizeText(): String {
return if (fileSize < 1024.0) return if (fileSize < 1024.0)
@@ -40,9 +53,5 @@ open class OfflineMapCityBean @JvmOverloads constructor(@PrimaryKey var id: Stri
"%.2f M".format(fileSize / 1073741824.0) "%.2f M".format(fileSize / 1073741824.0)
} }
// constructor(){
//
// }
//
} }

View File

@@ -91,7 +91,6 @@ DataNiLocationHandler(context: Context, dataBase: MapLifeDataBase) :
) )
mDataBase.niLocationDao.delete(niLocation); mDataBase.niLocationDao.delete(niLocation);
} catch (e: Throwable) { } catch (e: Throwable) {
Log.e("jingo", "删除数据报错 ${e.message}");
Handler(Looper.getMainLooper()).post { Handler(Looper.getMainLooper()).post {
callback.invoke(false, "${e.message}") callback.invoke(false, "${e.message}")
} }

View File

@@ -37,8 +37,5 @@ class NIMapController {
mapView.setOptions(options) mapView.setOptions(options)
} }
fun print() {
Log.e("jingo", "NIMapController 哈希code ${hashCode()}")
}
} }