Merge branch 'master' into 'master'
Master See merge request qiji4215/OneMapQS!1
This commit is contained in:
commit
82aa4ab2c0
@ -3,8 +3,8 @@ plugins {
|
||||
id 'org.jetbrains.kotlin.android'
|
||||
id 'kotlin-kapt'
|
||||
id 'com.google.dagger.hilt.android'
|
||||
id 'realm-android'
|
||||
}
|
||||
|
||||
android {
|
||||
namespace 'com.navinfo.omqs'
|
||||
compileSdk 33
|
||||
@ -26,8 +26,8 @@ android {
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
sourceCompatibility JavaVersion.VERSION_11
|
||||
targetCompatibility JavaVersion.VERSION_11
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = '1.8'
|
||||
@ -50,8 +50,12 @@ dependencies {
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
|
||||
//权限管理
|
||||
implementation 'com.github.getActivity:XXPermissions:16.5'
|
||||
//权限管理 https://github.com/getActivity/XXPermissions
|
||||
implementation 'com.github.getActivity:XXPermissions:16.8'
|
||||
// 文件管理 https://github.com/K1rakishou/Fuck-Storage-Access-Framework
|
||||
implementation 'com.github.K1rakishou:Fuck-Storage-Access-Framework:v1.1.3'
|
||||
// Android工具类库 https://blankj.com/2016/07/31/android-utils-code/
|
||||
implementation 'com.blankj:utilcodex:1.30.1'
|
||||
//依赖注入
|
||||
//hilt
|
||||
implementation "com.google.dagger:hilt-android:2.44"
|
||||
|
@ -26,6 +26,7 @@
|
||||
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
|
||||
<!-- 读取缓存数据 -->
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<!--android:largeHeap="true" 大内存 128M -->
|
||||
<application
|
||||
android:name=".OMQSApplication"
|
||||
android:allowBackup="true"
|
||||
@ -33,10 +34,10 @@
|
||||
android:fullBackupContent="@xml/backup_rules"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:requestLegacyExternalStorage="true"
|
||||
android:supportsRtl="true"
|
||||
android:largeHeap="true"
|
||||
android:theme="@style/Theme.OMQualityInspection"
|
||||
tools:targetApi="31">
|
||||
android:requestLegacyExternalStorage="true">
|
||||
<activity
|
||||
android:name=".ui.activity.login.LoginActivity"
|
||||
android:exported="true"
|
||||
@ -56,6 +57,7 @@
|
||||
android:screenOrientation="landscape"
|
||||
android:theme="@style/Theme.OMQualityInspection" />
|
||||
|
||||
<meta-data android:name="ScopedStorage" android:value="true" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
95
app/src/main/assets/MergeOMDB.py
Normal file
95
app/src/main/assets/MergeOMDB.py
Normal file
@ -0,0 +1,95 @@
|
||||
# coding:utf-8
|
||||
# 合并指定目录下的omdb(sqlite)数据
|
||||
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import sqlite3
|
||||
|
||||
|
||||
# 定义遍历目录的函数
|
||||
def traverse_dir(path):
|
||||
fileList = list()
|
||||
for root, dirs, files in os.walk(path):
|
||||
for file in files:
|
||||
if str(file).endswith(".omdb"):
|
||||
# 文件的完整路径
|
||||
file_path = os.path.join(root, file)
|
||||
# 处理文件,例如读取文件内容等
|
||||
print(file_path)
|
||||
fileList.append(file_path)
|
||||
return fileList
|
||||
|
||||
|
||||
# 打开配置文件,读取用户配置的
|
||||
def openConfigJson(path):
|
||||
# 读取json配置,获取要抽取的表名
|
||||
with open(path, "r") as f:
|
||||
configMap = json.load(f)
|
||||
return configMap
|
||||
|
||||
|
||||
# 按照tableList中指定的表名合并多个源数据库到指定目标数据库中
|
||||
def mergeSqliteData(originSqliteList, destSqlite, tableList):
|
||||
destConn = sqlite3.connect(destSqlite)
|
||||
destCursor = destConn.cursor()
|
||||
|
||||
for originSqlite in originSqliteList:
|
||||
originConn = sqlite3.connect(originSqlite)
|
||||
originCursor = originConn.cursor()
|
||||
# 从源数据库中遍历取出表list中的数据
|
||||
for table in tableList:
|
||||
# 检查目标数据库中是否存在指定的表
|
||||
containsTable = destCursor.execute(
|
||||
"SELECT sql FROM sqlite_master WHERE type='table' AND name='%s'" % (table)).fetchall()
|
||||
if not containsTable or len(containsTable) <= 0:
|
||||
# 复制表结构
|
||||
originCursor.execute("SELECT sql FROM sqlite_master WHERE type='table' AND name='%s'" % (table))
|
||||
createTableSql = originCursor.fetchone()[0]
|
||||
destCursor.execute(createTableSql)
|
||||
destConn.commit()
|
||||
|
||||
originCursor.execute("Select * From " + table)
|
||||
# 获取到源数据库中该表的所有数据
|
||||
originData = originCursor.fetchall()
|
||||
# 获取一行数据中包含多少列,以此动态设置sql语句中的?个数
|
||||
if originData and len(originData)>0:
|
||||
num_cols = len(originData[0])
|
||||
placeholders = ",".join(["?"] * num_cols)
|
||||
for row in originData:
|
||||
destCursor.execute("INSERT INTO "+table+" VALUES ({})".format(placeholders), row)
|
||||
|
||||
print("{}数据已导入!".format(originSqlite))
|
||||
originCursor.close()
|
||||
originConn.close()
|
||||
destConn.commit()
|
||||
destCursor.close()
|
||||
destConn.close()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
params = sys.argv[1:] # 截取参数
|
||||
if params:
|
||||
if not params[0]:
|
||||
print("请输入要合并的omdb数据的文件夹")
|
||||
raise AttributeError("请输入要合并的omdb数据的文件夹")
|
||||
# 获取导出文件的表配置
|
||||
jsonPath = params[0] + "/config.json"
|
||||
if not os.path.exists(jsonPath):
|
||||
raise AttributeError("指定目录下缺少config.json配置文件")
|
||||
omdbDir = params[0]
|
||||
originSqliteList = traverse_dir(omdbDir) # 获取到所有的omdb数据库的路径
|
||||
|
||||
tableNameList = list()
|
||||
configMap = openConfigJson(jsonPath)
|
||||
if configMap["tables"] and len(configMap["tables"]) > 0:
|
||||
for tableName in set(configMap["tables"]):
|
||||
tableNameList.append(tableName)
|
||||
print(tableNameList)
|
||||
else:
|
||||
raise AttributeError("config.json文件中没有配置抽取数据的表名")
|
||||
|
||||
# 开始分别连接Sqlite数据库,按照指定表名合并数据
|
||||
mergeSqliteData(originSqliteList, params[0]+"/output.sqlite", tableNameList)
|
||||
else:
|
||||
raise AttributeError("缺少参数:请输入要合并的omdb数据的文件夹")
|
@ -1,13 +1,29 @@
|
||||
package com.navinfo.omqs
|
||||
|
||||
import io.realm.Realm
|
||||
|
||||
class Constant {
|
||||
companion object {
|
||||
/**
|
||||
* sd卡根目录
|
||||
*/
|
||||
lateinit var ROOT_PATH: String
|
||||
|
||||
/**
|
||||
* 地图目录
|
||||
*/
|
||||
lateinit var MAP_PATH: String
|
||||
|
||||
/**
|
||||
* 数据目录
|
||||
*/
|
||||
lateinit var DATA_PATH: String
|
||||
|
||||
/**
|
||||
* 离线地图目录
|
||||
*/
|
||||
lateinit var OFFLINE_MAP_PATH: String
|
||||
|
||||
/**
|
||||
* 服务器地址
|
||||
*/
|
||||
@ -20,7 +36,7 @@ class Constant {
|
||||
const val message_version_right_off = "1" //立即发送
|
||||
|
||||
const val MESSAGE_PAGE_SIZE = 30 //消息列表一页最多数量
|
||||
|
||||
lateinit var realm: Realm
|
||||
}
|
||||
|
||||
}
|
@ -1,8 +1,18 @@
|
||||
package com.navinfo.omqs
|
||||
|
||||
import android.app.Application
|
||||
import android.util.Log
|
||||
import com.navinfo.omqs.tools.FileManager
|
||||
import dagger.hilt.android.HiltAndroidApp
|
||||
import io.realm.Realm
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.File
|
||||
|
||||
@HiltAndroidApp
|
||||
class OMQSApplication : Application() {
|
||||
override fun onCreate() {
|
||||
FileManager.initRootDir(this)
|
||||
super.onCreate()
|
||||
}
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
package com.navinfo.omqs.bean
|
||||
|
||||
data class OfflineMapCityBean(
|
||||
val id: String,
|
||||
val fileName: String,
|
||||
val name: String,
|
||||
val url: String,
|
||||
val version: Long,
|
||||
val fileSize: Long,
|
||||
var currentSize:Long = 0,
|
||||
var status:Int = NONE
|
||||
) {
|
||||
companion object Status{
|
||||
const val NONE = 0 //无状态
|
||||
const val WAITING = 1 //等待中
|
||||
const val LOADING = 2 //下载中
|
||||
const val PAUSE = 3 //暂停
|
||||
const val ERROR = 4 //错误
|
||||
const val DONE = 5 //完成
|
||||
const val UPDATE = 6 //有新版本要更新
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package com.navinfo.omqs.data.process
|
||||
|
||||
/**
|
||||
* 数据处理引擎,数据导入、导出及转换处理
|
||||
* */
|
||||
class DataEngine {
|
||||
|
||||
}
|
@ -6,11 +6,13 @@ import com.google.gson.Gson
|
||||
import com.navinfo.omqs.Constant
|
||||
import com.navinfo.omqs.OMQSApplication
|
||||
import com.navinfo.omqs.http.RetrofitNetworkServiceAPI
|
||||
import com.navinfo.omqs.tools.RealmCoroutineScope
|
||||
import dagger.Lazy
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import kotlinx.coroutines.*
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.logging.HttpLoggingInterceptor
|
||||
import retrofit2.Retrofit
|
||||
@ -25,11 +27,11 @@ import javax.inject.Singleton
|
||||
@InstallIn(SingletonComponent::class)
|
||||
class GlobalModule {
|
||||
|
||||
// @Singleton
|
||||
// @Provides
|
||||
// fun provideApplication(application: Application): OMQSApplication {
|
||||
// return application as OMQSApplication
|
||||
// }
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideApplication(application: Application): OMQSApplication {
|
||||
return application as OMQSApplication
|
||||
}
|
||||
|
||||
/**
|
||||
* 注入 网络OKHttp 对象
|
||||
@ -54,7 +56,8 @@ class GlobalModule {
|
||||
}
|
||||
}.apply {
|
||||
level = if (Constant.DEBUG) {
|
||||
HttpLoggingInterceptor.Level.BODY
|
||||
//坑 !!!! 下载文件时打印log 内存不足
|
||||
HttpLoggingInterceptor.Level.HEADERS
|
||||
} else {
|
||||
HttpLoggingInterceptor.Level.NONE
|
||||
}
|
||||
@ -91,4 +94,13 @@ class GlobalModule {
|
||||
fun provideNetworkService(retrofit: Retrofit): RetrofitNetworkServiceAPI {
|
||||
return retrofit.create(RetrofitNetworkServiceAPI::class.java)
|
||||
}
|
||||
|
||||
/**
|
||||
* realm 注册
|
||||
*/
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideRealmService(context: Application): RealmCoroutineScope {
|
||||
return RealmCoroutineScope(context)
|
||||
}
|
||||
}
|
@ -1,13 +1,13 @@
|
||||
package com.navinfo.omqs.hilt
|
||||
|
||||
import android.content.Context
|
||||
import com.navinfo.collect.library.map.NIMapController
|
||||
import com.navinfo.omqs.http.RetrofitNetworkServiceAPI
|
||||
import com.navinfo.omqs.http.offlinemapdownload.OfflineMapDownloadManager
|
||||
import com.navinfo.omqs.tools.RealmCoroutineScope
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.components.ActivityRetainedComponent
|
||||
import dagger.hilt.android.qualifiers.ActivityContext
|
||||
import dagger.hilt.android.scopes.ActivityRetainedScoped
|
||||
|
||||
@InstallIn(ActivityRetainedComponent::class)
|
||||
@ -26,8 +26,11 @@ class MainActivityModule {
|
||||
*/
|
||||
@ActivityRetainedScoped
|
||||
@Provides
|
||||
fun providesOfflineMapDownloadManager(@ActivityContext context: Context): OfflineMapDownloadManager =
|
||||
OfflineMapDownloadManager(context)
|
||||
fun providesOfflineMapDownloadManager(
|
||||
networkServiceAPI: RetrofitNetworkServiceAPI,
|
||||
realmManager: RealmCoroutineScope
|
||||
): OfflineMapDownloadManager =
|
||||
OfflineMapDownloadManager(networkServiceAPI, realmManager)
|
||||
|
||||
/**
|
||||
* 实验失败,这样创建,viewmodel不会在activity销毁的时候同时销毁
|
||||
@ -35,7 +38,6 @@ class MainActivityModule {
|
||||
// @ActivityRetainedScoped
|
||||
// @Provides
|
||||
// fun providesMainViewModel(mapController: NIMapController): MainViewModel {
|
||||
// Log.e("jingo", "MainViewModel 被创建")
|
||||
// return MainViewModel(mapController)
|
||||
// }
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
package com.navinfo.omqs.http
|
||||
|
||||
import com.navinfo.omqs.bean.OfflineMapCityBean
|
||||
import com.navinfo.collect.library.data.entity.OfflineMapCityBean
|
||||
|
||||
|
||||
/**
|
||||
* 网络访问 业务接口
|
||||
|
@ -1,6 +1,6 @@
|
||||
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.withContext
|
||||
import javax.inject.Inject
|
||||
|
@ -1,13 +1,12 @@
|
||||
package com.navinfo.omqs.http
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import com.navinfo.omqs.bean.OfflineMapCityBean
|
||||
import com.navinfo.collect.library.data.entity.OfflineMapCityBean
|
||||
import okhttp3.ResponseBody
|
||||
import retrofit2.Response
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.Header
|
||||
import retrofit2.http.Streaming
|
||||
import retrofit2.http.Url
|
||||
import java.util.concurrent.Flow
|
||||
|
||||
/**
|
||||
* retrofit2 网络请求接口
|
||||
@ -47,7 +46,7 @@ interface RetrofitNetworkServiceAPI {
|
||||
*/
|
||||
@Streaming
|
||||
@GET
|
||||
suspend fun retrofitDownLoadFile(@Url url: String):Response<ResponseBody>
|
||||
suspend fun retrofitDownLoadFile(@Header("RANGE") start: String? = "0", @Url url: String):Response<ResponseBody>
|
||||
|
||||
|
||||
/**
|
||||
|
@ -1,48 +1,114 @@
|
||||
package com.navinfo.omqs.http.offlinemapdownload
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Environment
|
||||
import android.text.TextUtils
|
||||
import com.navinfo.omqs.Constant
|
||||
import com.navinfo.omqs.bean.OfflineMapCityBean
|
||||
import dagger.hilt.android.qualifiers.ActivityContext
|
||||
import java.io.Serializable
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.Observer
|
||||
import com.navinfo.collect.library.data.entity.OfflineMapCityBean
|
||||
import com.navinfo.omqs.http.RetrofitNetworkServiceAPI
|
||||
import com.navinfo.omqs.tools.RealmCoroutineScope
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* 管理离线地图下载
|
||||
*/
|
||||
class OfflineMapDownloadManager @Inject constructor(@ActivityContext context: Context) {
|
||||
class OfflineMapDownloadManager(
|
||||
val netApi: RetrofitNetworkServiceAPI, val realmManager: RealmCoroutineScope
|
||||
) {
|
||||
/**
|
||||
* 最多同时下载数量
|
||||
*/
|
||||
private val MAX_SCOPE = 5
|
||||
private val MAX_SCOPE = 3
|
||||
|
||||
/**
|
||||
* 存储有哪些城市需要下载
|
||||
* 存储有哪些城市需要下载的队列
|
||||
*/
|
||||
private val scopeMap: ConcurrentHashMap<String, OfflineMapDownloadScope> by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
|
||||
ConcurrentHashMap<String, OfflineMapDownloadScope>()
|
||||
}
|
||||
|
||||
val downloadFolder: String? by lazy {
|
||||
Constant.MAP_PATH + "/offline/"
|
||||
/**
|
||||
* 存储正在下载的城市队列
|
||||
*/
|
||||
private val taskScopeMap: ConcurrentHashMap<String, OfflineMapDownloadScope> by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
|
||||
ConcurrentHashMap<String, OfflineMapDownloadScope>()
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 启动下载任务
|
||||
* 请不要直接使用此方法启动下载任务,它是交由[OfflineMapDownloadScope]进行调用
|
||||
*/
|
||||
fun launchScope(scope: OfflineMapDownloadScope) {
|
||||
if (taskScopeMap.size >= MAX_SCOPE) {
|
||||
return
|
||||
}
|
||||
if (taskScopeMap.contains(scope.cityBean.id)) {
|
||||
return
|
||||
}
|
||||
taskScopeMap[scope.cityBean.id] = scope
|
||||
scope.launch()
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动下一个任务,如果有正在等待中的任务的话
|
||||
* 请不要直接使用此方法启动下载任务,它是交由[OfflineMapDownloadScope]进行调用
|
||||
* @param previousUrl 上一个下载任务的下载连接
|
||||
*/
|
||||
fun launchNext(previousUrl: String) {
|
||||
taskScopeMap.remove(previousUrl)
|
||||
for (entrySet in scopeMap) {
|
||||
val downloadScope = entrySet.value
|
||||
if (downloadScope.isWaiting()) {
|
||||
launchScope(downloadScope)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 暂停任务
|
||||
* 只有等待中的任务和正在下载中的任务才可以进行暂停操作
|
||||
*/
|
||||
fun pause(id: String) {
|
||||
if (taskScopeMap.containsKey(id)) {
|
||||
val downloadScope = taskScopeMap[id]
|
||||
downloadScope?.let {
|
||||
downloadScope.pause()
|
||||
}
|
||||
launchNext(id)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 将下载任务加入到协程作用域的下载队列里
|
||||
* 请求一个下载任务[OfflineMapDownloadScope]
|
||||
* 这是创建[OfflineMapDownloadScope]的唯一途径,请不要通过其他方式创建[OfflineMapDownloadScope]
|
||||
*/
|
||||
fun request(cityBean: OfflineMapCityBean): OfflineMapDownloadScope? {
|
||||
//没有下载连接的不能下载
|
||||
if (TextUtils.isEmpty(cityBean.url)) return null
|
||||
// if(scopeMap.containsKey())
|
||||
var downloadScope = scopeMap[cityBean.id]
|
||||
if (downloadScope == null) {
|
||||
scopeMap[cityBean.id] = OfflineMapDownloadScope(cityBean)
|
||||
}
|
||||
return downloadScope
|
||||
fun start(id: String) {
|
||||
scopeMap[id]?.start()
|
||||
}
|
||||
|
||||
|
||||
fun addTask(cityBean: OfflineMapCityBean) {
|
||||
if (!scopeMap.containsKey(cityBean.id)) {
|
||||
scopeMap[cityBean.id] = OfflineMapDownloadScope(this, cityBean)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun observer(
|
||||
id: String, lifecycleOwner: LifecycleOwner, observer: Observer<OfflineMapCityBean>
|
||||
) {
|
||||
if (scopeMap.containsKey(id)) {
|
||||
scopeMap[id]!!.observer(lifecycleOwner, observer)
|
||||
}
|
||||
}
|
||||
|
||||
fun removeObserver(id: String) {
|
||||
if (scopeMap.containsKey(id)) {
|
||||
scopeMap[id]!!.removeObserver()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,26 +1,176 @@
|
||||
package com.navinfo.omqs.http.offlinemapdownload
|
||||
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.navinfo.omqs.bean.OfflineMapCityBean
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlin.coroutines.EmptyCoroutineContext
|
||||
import androidx.lifecycle.Observer
|
||||
import com.navinfo.collect.library.data.entity.OfflineMapCityBean
|
||||
import com.navinfo.omqs.Constant
|
||||
import kotlinx.coroutines.*
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.io.RandomAccessFile
|
||||
|
||||
/**
|
||||
* 代表一个下载任务
|
||||
* [OfflineMapCityBean.id]将做为下载任务的唯一标识
|
||||
* 不要直接在外部直接创建此对象,那样就可能无法统一管理下载任务,请通过[OfflineMapDownloadManager.download]获取此对象
|
||||
* 不要直接在外部直接创建此对象,那样就可能无法统一管理下载任务,请通过[OfflineMapDownloadManager.request]获取此对象
|
||||
* 这是一个协程作用域,
|
||||
* EmptyCoroutineContext 表示一个不包含任何元素的协程上下文,它通常用于创建新的协程上下文,或者作为协程上下文的基础。
|
||||
*/
|
||||
class OfflineMapDownloadScope(cityBean: OfflineMapCityBean) : CoroutineScope by CoroutineScope(EmptyCoroutineContext) {
|
||||
class OfflineMapDownloadScope(
|
||||
private val downloadManager: OfflineMapDownloadManager,
|
||||
val cityBean: OfflineMapCityBean,
|
||||
) :
|
||||
CoroutineScope by CoroutineScope(Dispatchers.IO) {
|
||||
/**
|
||||
*
|
||||
*下载任务,用来取消的
|
||||
*/
|
||||
private var downloadJob: Job? = null
|
||||
|
||||
/**
|
||||
*
|
||||
* 管理观察者,同时只有一个就行了
|
||||
*/
|
||||
// private var observer: Observer<OfflineMapCityBean>? = null
|
||||
private var lifecycleOwner: LifecycleOwner? = null
|
||||
|
||||
/**
|
||||
*通知UI更新
|
||||
*/
|
||||
private val downloadData = MutableLiveData<OfflineMapCityBean>()
|
||||
|
||||
init {
|
||||
downloadData.value = cityBean
|
||||
}
|
||||
|
||||
//改进的代码
|
||||
fun start() {
|
||||
change(OfflineMapCityBean.WAITING)
|
||||
downloadManager.launchScope(this@OfflineMapDownloadScope)
|
||||
}
|
||||
|
||||
/**
|
||||
* 暂停任务
|
||||
* 其实就是取消任务,移除监听
|
||||
*/
|
||||
fun pause() {
|
||||
downloadJob?.cancel("pause")
|
||||
change(OfflineMapCityBean.PAUSE)
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动协程进行下载
|
||||
* 请不要尝试在外部调用此方法,那样会脱离[OfflineMapDownloadManager]的管理
|
||||
*/
|
||||
fun launch() {
|
||||
downloadJob = launch() {
|
||||
Log.e("jingo", "启动下载1")
|
||||
download()
|
||||
Log.e("jingo", "启动下载2")
|
||||
downloadManager.launchNext(cityBean.id)
|
||||
Log.e("jingo", "启动下载3")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 是否是等待任务
|
||||
*/
|
||||
fun isWaiting(): Boolean {
|
||||
return cityBean.status == OfflineMapCityBean.WAITING
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新任务
|
||||
* @param status [OfflineMapCityBean.Status]
|
||||
*/
|
||||
private fun change(status: Int) {
|
||||
if (cityBean.status != status || status == OfflineMapCityBean.LOADING) {
|
||||
cityBean.status = status
|
||||
downloadData.postValue(cityBean)
|
||||
|
||||
downloadManager.realmManager.launch {
|
||||
downloadManager.realmManager.insertOrUpdate(cityBean)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加下载任务观察者
|
||||
*/
|
||||
fun observer(owner: LifecycleOwner, ob: Observer<OfflineMapCityBean>) {
|
||||
removeObserver()
|
||||
this.lifecycleOwner = owner
|
||||
downloadData.observe(owner, ob)
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载文件
|
||||
*/
|
||||
private suspend fun download() {
|
||||
var inputStream: InputStream? = null
|
||||
var randomAccessFile: RandomAccessFile? = null
|
||||
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
|
||||
while (downloadJob?.isActive == true) {
|
||||
readLength = inputStream.read(buffer)
|
||||
if (readLength != -1) {
|
||||
randomAccessFile.write(buffer, 0, readLength)
|
||||
cityBean.currentSize += readLength
|
||||
change(OfflineMapCityBean.LOADING)
|
||||
} else {
|
||||
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 {
|
||||
inputStream?.close()
|
||||
randomAccessFile?.close()
|
||||
}
|
||||
}
|
||||
|
||||
fun removeObserver() {
|
||||
lifecycleOwner?.let {
|
||||
downloadData.removeObservers(it)
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
95
app/src/main/java/com/navinfo/omqs/tools/FileManager.kt
Normal file
95
app/src/main/java/com/navinfo/omqs/tools/FileManager.kt
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
84
app/src/main/java/com/navinfo/omqs/ui/MainActivity.kt
Normal file
84
app/src/main/java/com/navinfo/omqs/ui/MainActivity.kt
Normal file
@ -0,0 +1,84 @@
|
||||
package com.navinfo.omqs.ui
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.navigation.ui.AppBarConfiguration
|
||||
import com.github.k1rakishou.fsaf.FileChooser
|
||||
import com.github.k1rakishou.fsaf.callback.FSAFActivityCallbacks
|
||||
import com.navinfo.omqs.databinding.ActivityMainBinding
|
||||
import com.navinfo.omqs.ui.activity.PermissionsActivity
|
||||
|
||||
class MainActivity : PermissionsActivity(), FSAFActivityCallbacks {
|
||||
|
||||
private lateinit var appBarConfiguration: AppBarConfiguration
|
||||
private lateinit var binding: ActivityMainBinding
|
||||
private val fileChooser by lazy { FileChooser(this@MainActivity) }
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
binding = ActivityMainBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
// val navController = findNavController(R.id.nav_host_fragment_content_main)
|
||||
// appBarConfiguration = AppBarConfiguration(navController.graph)
|
||||
// setupActionBarWithNavController(navController, appBarConfiguration)
|
||||
|
||||
fileChooser.setCallbacks(this@MainActivity)
|
||||
// binding.fab.setOnClickListener { view ->
|
||||
// Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
|
||||
// .setAnchorView(R.id.fab)
|
||||
// .setAction("Action", null).show()
|
||||
// // 开始数据导入功能
|
||||
// fileChooser.openChooseFileDialog(object: FileChooserCallback() {
|
||||
// override fun onCancel(reason: String) {
|
||||
// }
|
||||
//
|
||||
// override fun onResult(uri: Uri) {
|
||||
// val file = UriUtils.uri2File(uri)
|
||||
// Snackbar.make(view, "文件大小为:${file.length()}", Snackbar.LENGTH_LONG)
|
||||
// .show()
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
}
|
||||
|
||||
override fun onPermissionsGranted() {
|
||||
}
|
||||
|
||||
override fun onPermissionsDenied() {
|
||||
}
|
||||
|
||||
// override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
// // Inflate the menu; this adds items to the action bar if it is present.
|
||||
// menuInflater.inflate(R.menu.menu_main, menu)
|
||||
// return true
|
||||
// }
|
||||
|
||||
// override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
// // Handle action bar item clicks here. The action bar will
|
||||
// // automatically handle clicks on the Home/Up button, so long
|
||||
// // as you specify a parent activity in AndroidManifest.xml.
|
||||
// return when (item.itemId) {
|
||||
// R.id.action_settings -> true
|
||||
// else -> super.onOptionsItemSelected(item)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// override fun onSupportNavigateUp(): Boolean {
|
||||
// val navController = findNavController(R.id.nav_host_fragment_content_main)
|
||||
// return navController.navigateUp(appBarConfiguration)
|
||||
// || super.onSupportNavigateUp()
|
||||
// }
|
||||
|
||||
override fun fsafStartActivityForResult(intent: Intent, requestCode: Int) {
|
||||
startActivityForResult(intent, requestCode)
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
fileChooser.onActivityResult(requestCode, resultCode, data)
|
||||
}
|
||||
}
|
@ -16,9 +16,10 @@ open class PermissionsActivity : BaseActivity() {
|
||||
val permissionList = mutableListOf<String>()
|
||||
if (applicationInfo.targetSdkVersion >= Build.VERSION_CODES.TIRAMISU) {
|
||||
//文件读写
|
||||
permissionList.add(Permission.READ_MEDIA_IMAGES)
|
||||
permissionList.add(Permission.READ_MEDIA_AUDIO)
|
||||
permissionList.add(Permission.READ_MEDIA_VIDEO)
|
||||
// permissionList.add(Permission.READ_MEDIA_IMAGES)
|
||||
// permissionList.add(Permission.READ_MEDIA_AUDIO)
|
||||
// permissionList.add(Permission.READ_MEDIA_VIDEO)
|
||||
permissionList.add(Permission.MANAGE_EXTERNAL_STORAGE)
|
||||
} else {
|
||||
//文件读写
|
||||
permissionList.add(Permission.WRITE_EXTERNAL_STORAGE)
|
||||
|
@ -7,15 +7,19 @@ import android.widget.Toast
|
||||
import androidx.activity.viewModels
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import androidx.lifecycle.Observer
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.navinfo.omqs.R
|
||||
import com.navinfo.omqs.databinding.ActivityLoginBinding
|
||||
import com.navinfo.omqs.ui.activity.PermissionsActivity
|
||||
import com.navinfo.omqs.ui.activity.map.MainActivity
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
||||
/**
|
||||
* 登陆页面
|
||||
*/
|
||||
|
||||
@AndroidEntryPoint
|
||||
class LoginActivity : PermissionsActivity() {
|
||||
|
||||
private lateinit var binding: ActivityLoginBinding
|
||||
@ -30,41 +34,52 @@ class LoginActivity : PermissionsActivity() {
|
||||
initView()
|
||||
}
|
||||
|
||||
private fun initView() {
|
||||
//登录校验,初始化成功
|
||||
viewModel.loginStatus.observe(this) {
|
||||
when (it) {
|
||||
LoginStatus.LOGIN_STATUS_NET_LOADING -> {
|
||||
loginDialog("验证用户信息...")
|
||||
}
|
||||
LoginStatus.LOGIN_STATUS_FOLDER_INIT -> {
|
||||
loginDialog("检查本地数据...")
|
||||
}
|
||||
LoginStatus.LOGIN_STATUS_FOLDER_FAILURE -> {
|
||||
Toast.makeText(this, "文件夹初始化失败", Toast.LENGTH_SHORT).show()
|
||||
loginDialog?.dismiss()
|
||||
loginDialog = null
|
||||
}
|
||||
LoginStatus.LOGIN_STATUS_NET_FAILURE -> {
|
||||
Toast.makeText(this, "网络访问失败", Toast.LENGTH_SHORT).show()
|
||||
loginDialog?.dismiss()
|
||||
loginDialog = null
|
||||
}
|
||||
LoginStatus.LOGIN_STATUS_SUCCESS -> {
|
||||
val intent = Intent(this@LoginActivity, MainActivity::class.java)
|
||||
startActivity(intent)
|
||||
/**
|
||||
* 观察登录状态,把Observer提出来是为了防止每次数据变化都会有新的observer创建
|
||||
* 还有为了方便释放(需不需要手动释放?不清楚)不需要释放,当viewmodel观察到activity/fragment 的生命周期时会自动释放
|
||||
* PS:不要在 observer 中修改 LiveData 的值的数据,会影响其他 observer
|
||||
*/
|
||||
private val loginObserve = Observer<LoginStatus> {
|
||||
when (it) {
|
||||
LoginStatus.LOGIN_STATUS_NET_LOADING -> {
|
||||
loginDialog("验证用户信息...")
|
||||
}
|
||||
LoginStatus.LOGIN_STATUS_FOLDER_INIT -> {
|
||||
loginDialog("检查本地数据...")
|
||||
}
|
||||
LoginStatus.LOGIN_STATUS_FOLDER_FAILURE -> {
|
||||
Toast.makeText(this, "文件夹初始化失败", Toast.LENGTH_SHORT).show()
|
||||
loginDialog?.dismiss()
|
||||
loginDialog = null
|
||||
}
|
||||
LoginStatus.LOGIN_STATUS_NET_FAILURE -> {
|
||||
Toast.makeText(this, "网络访问失败", Toast.LENGTH_SHORT).show()
|
||||
loginDialog?.dismiss()
|
||||
loginDialog = null
|
||||
}
|
||||
LoginStatus.LOGIN_STATUS_SUCCESS -> {
|
||||
val intent = Intent(this@LoginActivity, MainActivity::class.java)
|
||||
startActivity(intent)
|
||||
// finish()
|
||||
loginDialog?.dismiss()
|
||||
loginDialog = null
|
||||
}
|
||||
LoginStatus.LOGIN_STATUS_CANCEL -> {
|
||||
loginDialog?.dismiss()
|
||||
loginDialog = null
|
||||
}
|
||||
loginDialog?.dismiss()
|
||||
loginDialog = null
|
||||
}
|
||||
LoginStatus.LOGIN_STATUS_CANCEL -> {
|
||||
loginDialog?.dismiss()
|
||||
loginDialog = null
|
||||
}
|
||||
LoginStatus.LOGIN_STATUS_NET_OFFLINE_MAP -> {
|
||||
loginDialog("检查离线地图...")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun initView() {
|
||||
//登录校验,初始化成功
|
||||
viewModel.loginStatus.observe(this, loginObserve)
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录dialog
|
||||
*/
|
||||
@ -73,7 +88,7 @@ class LoginActivity : PermissionsActivity() {
|
||||
loginDialog = MaterialAlertDialogBuilder(
|
||||
this, com.google.android.material.R.style.MaterialAlertDialog_Material3
|
||||
).setTitle("登录").setMessage(message).show()
|
||||
loginDialog!!.setCanceledOnTouchOutside(true)
|
||||
loginDialog!!.setCanceledOnTouchOutside(false)
|
||||
loginDialog!!.setOnCancelListener {
|
||||
viewModel.cancelLogin()
|
||||
}
|
||||
@ -84,13 +99,17 @@ class LoginActivity : PermissionsActivity() {
|
||||
|
||||
//进应用根本不调用,待查
|
||||
override fun onPermissionsGranted() {
|
||||
Log.e("jingo","调用了吗")
|
||||
Log.e("jingo", "调用了吗")
|
||||
|
||||
}
|
||||
|
||||
override fun onPermissionsDenied() {
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理登录按钮
|
||||
*/
|
||||
|
@ -7,11 +7,15 @@ import android.widget.Toast
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.navinfo.omqs.Constant
|
||||
import com.navinfo.omqs.bean.LoginUserBean
|
||||
import com.navinfo.omqs.http.NetResult
|
||||
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 okio.IOException
|
||||
import java.io.File
|
||||
import javax.inject.Inject
|
||||
|
||||
enum class LoginStatus {
|
||||
/**
|
||||
@ -19,6 +23,11 @@ enum class LoginStatus {
|
||||
*/
|
||||
LOGIN_STATUS_NET_LOADING,
|
||||
|
||||
/**
|
||||
* 访问离线地图列表
|
||||
*/
|
||||
LOGIN_STATUS_NET_OFFLINE_MAP,
|
||||
|
||||
/**
|
||||
* 初始化文件夹
|
||||
*/
|
||||
@ -45,7 +54,10 @@ enum class LoginStatus {
|
||||
LOGIN_STATUS_CANCEL,
|
||||
}
|
||||
|
||||
class LoginViewModel(
|
||||
@HiltViewModel
|
||||
class LoginViewModel @Inject constructor(
|
||||
private val networkService: NetworkService,
|
||||
private val realmManager: RealmCoroutineScope
|
||||
) : ViewModel() {
|
||||
//用户信息
|
||||
val loginUser: MutableLiveData<LoginUserBean> = MutableLiveData()
|
||||
@ -65,7 +77,7 @@ class LoginViewModel(
|
||||
*/
|
||||
fun onClick(view: View) {
|
||||
loginUser.value!!.username = "admin2"
|
||||
loginUser.postValue(loginUser.value)
|
||||
loginUser.value = loginUser.value
|
||||
}
|
||||
|
||||
/**
|
||||
@ -81,7 +93,6 @@ class LoginViewModel(
|
||||
//不指定IO,会在主线程里运行
|
||||
jobLogin = viewModelScope.launch(Dispatchers.IO) {
|
||||
loginCheck(context, userName, password)
|
||||
Log.e("jingo", "运行完了1?${Thread.currentThread().name}")
|
||||
}
|
||||
}
|
||||
|
||||
@ -90,52 +101,68 @@ class LoginViewModel(
|
||||
*/
|
||||
|
||||
private suspend fun loginCheck(context: Context, userName: String, password: String) {
|
||||
Log.e("jingo", "我在哪个线程里?${Thread.currentThread().name}")
|
||||
//上面调用了线程切换,这里不用调用,即使调用了还是在同一个线程中,除非自定义协程域?(待验证)
|
||||
// withContext(Dispatchers.IO) {
|
||||
Log.e("jingo", "delay之前?${Thread.currentThread().name}")
|
||||
//网络访问
|
||||
loginStatus.postValue(LoginStatus.LOGIN_STATUS_NET_LOADING)
|
||||
//假装网络访问,等待3秒
|
||||
delay(3000)
|
||||
//假装网络访问,等待2秒
|
||||
delay(1000)
|
||||
//文件夹初始化
|
||||
try {
|
||||
loginStatus.postValue(LoginStatus.LOGIN_STATUS_FOLDER_INIT)
|
||||
createRootFolder(context)
|
||||
createUserFolder(context)
|
||||
// 初始化Realm
|
||||
} catch (e: IOException) {
|
||||
loginStatus.postValue(LoginStatus.LOGIN_STATUS_FOLDER_FAILURE)
|
||||
}
|
||||
|
||||
//假装解压文件等
|
||||
delay(1000)
|
||||
loginStatus.postValue(LoginStatus.LOGIN_STATUS_SUCCESS)
|
||||
Log.e("jingo", "delay之后?${Thread.currentThread().name}")
|
||||
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)
|
||||
private fun createRootFolder(context: Context) {
|
||||
// 在SD卡创建项目目录
|
||||
val sdCardPath = context.getExternalFilesDir(null)
|
||||
sdCardPath?.let {
|
||||
Constant.ROOT_PATH = sdCardPath.absolutePath
|
||||
Constant.MAP_PATH = Constant.ROOT_PATH + "/map/"
|
||||
val file = File(Constant.MAP_PATH)
|
||||
if (!file.exists()) {
|
||||
file.mkdirs()
|
||||
}
|
||||
}
|
||||
private fun createUserFolder(context: Context) {
|
||||
// 在SD卡创建用户目录,解压资源等
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消登录
|
||||
*/
|
||||
fun cancelLogin() {
|
||||
Log.e("jingo", "取消了?${Thread.currentThread().name}")
|
||||
jobLogin?.let {
|
||||
it.cancel()
|
||||
loginStatus.postValue(LoginStatus.LOGIN_STATUS_CANCEL)
|
||||
loginStatus.value = LoginStatus.LOGIN_STATUS_CANCEL
|
||||
}
|
||||
}
|
||||
|
||||
@ -143,6 +170,4 @@ class LoginViewModel(
|
||||
super.onCleared()
|
||||
cancelLogin()
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -11,6 +11,7 @@ import com.navinfo.collect.library.map.NIMapController
|
||||
import com.navinfo.omqs.Constant
|
||||
import com.navinfo.omqs.R
|
||||
import com.navinfo.omqs.databinding.ActivityMainBinding
|
||||
import com.navinfo.omqs.http.offlinemapdownload.OfflineMapDownloadManager
|
||||
import com.navinfo.omqs.ui.activity.BaseActivity
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import javax.inject.Inject
|
||||
@ -27,6 +28,8 @@ class MainActivity : BaseActivity() {
|
||||
//注入地图控制器
|
||||
@Inject
|
||||
lateinit var mapController: NIMapController
|
||||
@Inject
|
||||
lateinit var offlineMapDownloadManager: OfflineMapDownloadManager
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
@ -66,7 +69,6 @@ class MainActivity : BaseActivity() {
|
||||
super.onDestroy()
|
||||
mapController.mMapView.onDestroy()
|
||||
mapController.locationLayerHandler.stopLocation()
|
||||
Log.e("jingo", "MainActivity 销毁")
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
|
@ -25,7 +25,6 @@ class MainViewModel @Inject constructor(
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
Log.e("jingo","MainViewModel 被释放了")
|
||||
super.onCleared()
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@ package com.navinfo.omqs.ui.fragment.offlinemap
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||
import dagger.hilt.EntryPoint
|
||||
|
||||
/**
|
||||
* 离线地图主页面,viewpage适配器
|
||||
|
@ -1,33 +1,134 @@
|
||||
package com.navinfo.omqs.ui.fragment.offlinemap
|
||||
|
||||
import androidx.databinding.ViewDataBinding
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.lifecycle.Observer
|
||||
import com.navinfo.collect.library.data.entity.OfflineMapCityBean
|
||||
import com.navinfo.omqs.R
|
||||
import com.navinfo.omqs.BR
|
||||
import com.navinfo.omqs.bean.OfflineMapCityBean
|
||||
import com.navinfo.omqs.databinding.AdapterOfflineMapCityBinding
|
||||
import com.navinfo.omqs.http.offlinemapdownload.OfflineMapDownloadManager
|
||||
import com.navinfo.omqs.ui.other.BaseRecyclerViewAdapter
|
||||
import com.navinfo.omqs.ui.other.BaseViewHolder
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* 离线地图城市列表 RecyclerView 适配器
|
||||
*
|
||||
* 在 RecycleView 的 ViewHolder 中监听 ViewModel 的 LiveData,然后此时传递的 lifecycleOwner 是对应的 Fragment。由于 ViewHolder 的生命周期是比 Fragment 短的,所以当 ViewHolder 销毁时,由于 Fragment 的 Lifecycle 还没有结束,此时 ViewHolder 会发生内存泄露(监听的 LiveData 没有解绑)
|
||||
* 这种场景下有两种解决办法:
|
||||
*使用 LiveData 的 observeForever 然后在 ViewHolder 销毁前手动调用 removeObserver
|
||||
*使用 LifecycleRegistry 给 ViewHolder 分发生命周期(这里使用了这个)
|
||||
*/
|
||||
class OfflineMapCityListAdapter @Inject constructor(
|
||||
private val downloadManager: OfflineMapDownloadManager, private val context: Context
|
||||
) : BaseRecyclerViewAdapter<OfflineMapCityBean>() {
|
||||
|
||||
class OfflineMapCityListAdapter @Inject constructor() :
|
||||
BaseRecyclerViewAdapter<OfflineMapCityBean>() {
|
||||
override fun onBindViewHolder(holder: BaseViewHolder, position: Int) {
|
||||
var binding: ViewDataBinding = holder.dataBinding
|
||||
//立刻刷新UI,解决闪烁
|
||||
// binding.executePendingBindings()
|
||||
binding.setVariable(BR.cityBean, data[position])
|
||||
(binding as AdapterOfflineMapCityBinding).offlineMapDownloadBtn.setOnClickListener {
|
||||
|
||||
private val downloadBtnClick = View.OnClickListener() {
|
||||
if (it.tag != null) {
|
||||
val cityBean = data[it.tag as Int]
|
||||
when (cityBean.status) {
|
||||
OfflineMapCityBean.NONE, OfflineMapCityBean.UPDATE, OfflineMapCityBean.PAUSE, OfflineMapCityBean.ERROR -> {
|
||||
Log.e("jingo", "开始下载 ${cityBean.status}")
|
||||
downloadManager.start(cityBean.id)
|
||||
}
|
||||
OfflineMapCityBean.LOADING, OfflineMapCityBean.WAITING -> {
|
||||
Log.e("jingo", "暂停 ${cityBean.status}")
|
||||
downloadManager.pause(cityBean.id)
|
||||
}
|
||||
else -> {
|
||||
Log.e("jingo", "暂停 ${cityBean.status}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder {
|
||||
val viewBinding =
|
||||
AdapterOfflineMapCityBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||
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) {
|
||||
val binding: AdapterOfflineMapCityBinding =
|
||||
holder.viewBinding as AdapterOfflineMapCityBinding
|
||||
//牺牲性能立刻刷新UI,解决闪烁 这里不用
|
||||
// binding.executePendingBindings()
|
||||
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.setOnClickListener(downloadBtnClick)
|
||||
binding.offlineMapCityName.text = cityBean.name
|
||||
binding.offlineMapCitySize.text = cityBean.getFileSizeText()
|
||||
}
|
||||
|
||||
inner class DownloadObserver(val id: String, val binding: AdapterOfflineMapCityBinding) :
|
||||
Observer<OfflineMapCityBean> {
|
||||
override fun onChanged(t: OfflineMapCityBean?) {
|
||||
if (id == t?.id)
|
||||
changeViews(binding, t)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun changeViews(binding: AdapterOfflineMapCityBinding, cityBean: OfflineMapCityBean) {
|
||||
binding.offlineMapProgress.progress =
|
||||
(cityBean.currentSize * 100 / cityBean.fileSize).toInt()
|
||||
when (cityBean.status) {
|
||||
OfflineMapCityBean.NONE -> {
|
||||
if (binding.offlineMapProgress.visibility == View.VISIBLE) binding.offlineMapProgress.visibility =
|
||||
View.INVISIBLE
|
||||
binding.offlineMapDownloadBtn.text = "下载"
|
||||
}
|
||||
OfflineMapCityBean.WAITING -> {
|
||||
if (binding.offlineMapProgress.visibility != View.VISIBLE) binding.offlineMapProgress.visibility =
|
||||
View.VISIBLE
|
||||
binding.offlineMapDownloadBtn.text = "等待中"
|
||||
}
|
||||
OfflineMapCityBean.LOADING -> {
|
||||
if (binding.offlineMapProgress.visibility != View.VISIBLE) binding.offlineMapProgress.visibility =
|
||||
View.VISIBLE
|
||||
binding.offlineMapDownloadBtn.text = "暂停"
|
||||
}
|
||||
OfflineMapCityBean.PAUSE -> {
|
||||
if (binding.offlineMapProgress.visibility != View.VISIBLE) binding.offlineMapProgress.visibility =
|
||||
View.VISIBLE
|
||||
binding.offlineMapDownloadBtn.text = "继续"
|
||||
}
|
||||
OfflineMapCityBean.ERROR -> {
|
||||
if (binding.offlineMapProgress.visibility != View.VISIBLE) binding.offlineMapProgress.visibility =
|
||||
View.VISIBLE
|
||||
binding.offlineMapDownloadBtn.text = "重试"
|
||||
}
|
||||
OfflineMapCityBean.DONE -> {
|
||||
if (binding.offlineMapProgress.visibility == View.VISIBLE) binding.offlineMapProgress.visibility =
|
||||
View.INVISIBLE
|
||||
binding.offlineMapDownloadBtn.text = "已完成"
|
||||
}
|
||||
OfflineMapCityBean.UPDATE -> {
|
||||
if (binding.offlineMapProgress.visibility == View.VISIBLE) binding.offlineMapProgress.visibility =
|
||||
View.INVISIBLE
|
||||
binding.offlineMapDownloadBtn.text = "更新"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
return R.layout.adapter_offline_map_city
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -10,17 +10,28 @@ import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.navinfo.omqs.databinding.FragmentOfflineMapCityListBinding
|
||||
import com.navinfo.omqs.http.RetrofitNetworkServiceAPI
|
||||
import com.navinfo.omqs.http.offlinemapdownload.OfflineMapDownloadManager
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* 离线地图城市列表
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
class OfflineMapCityListFragment : Fragment() {
|
||||
@Inject
|
||||
lateinit var downloadManager: OfflineMapDownloadManager
|
||||
private var _binding: FragmentOfflineMapCityListBinding? = null
|
||||
private val viewModel by viewModels<OfflineMapCityListViewModel>()
|
||||
private val binding get() = _binding!!
|
||||
private val adapter: OfflineMapCityListAdapter by lazy { OfflineMapCityListAdapter() }
|
||||
private val adapter: OfflineMapCityListAdapter by lazy {
|
||||
OfflineMapCityListAdapter(
|
||||
downloadManager,
|
||||
requireContext()
|
||||
)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
@ -33,8 +44,10 @@ class OfflineMapCityListFragment : Fragment() {
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
val layoutManager = LinearLayoutManager(context)
|
||||
_binding!!.offlineMapCityListRecyclerview.layoutManager = layoutManager
|
||||
_binding!!.offlineMapCityListRecyclerview.adapter = adapter
|
||||
//// 设置 RecyclerView 的固定大小,避免在滚动时重新计算视图大小和布局,提高性能
|
||||
binding.offlineMapCityListRecyclerview.setHasFixedSize(true)
|
||||
binding.offlineMapCityListRecyclerview.layoutManager = layoutManager
|
||||
binding.offlineMapCityListRecyclerview.adapter = adapter
|
||||
viewModel.cityListLiveData.observe(viewLifecycleOwner) {
|
||||
adapter.refreshData(it)
|
||||
}
|
||||
@ -44,6 +57,5 @@ class OfflineMapCityListFragment : Fragment() {
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
_binding = null
|
||||
Log.e("jingo","OfflineMapCityListFragment onDestroyView")
|
||||
}
|
||||
}
|
@ -1,16 +1,18 @@
|
||||
package com.navinfo.omqs.ui.fragment.offlinemap
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import android.widget.Toast
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.navinfo.omqs.http.NetResult
|
||||
import com.navinfo.omqs.http.NetworkService
|
||||
import com.navinfo.omqs.bean.OfflineMapCityBean
|
||||
import com.navinfo.collect.library.data.entity.OfflineMapCityBean
|
||||
import com.navinfo.omqs.tools.FileManager
|
||||
import com.navinfo.omqs.tools.RealmCoroutineScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
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 javax.inject.Inject
|
||||
|
||||
@ -19,8 +21,7 @@ import javax.inject.Inject
|
||||
*/
|
||||
@HiltViewModel
|
||||
class OfflineMapCityListViewModel @Inject constructor(
|
||||
private val networkService: NetworkService,
|
||||
@ApplicationContext val context: Context
|
||||
@ApplicationContext val context: Context,
|
||||
) : ViewModel() {
|
||||
|
||||
val cityListLiveData = MutableLiveData<List<OfflineMapCityBean>>()
|
||||
@ -29,21 +30,15 @@ class OfflineMapCityListViewModel @Inject constructor(
|
||||
* 去获取离线地图列表
|
||||
*/
|
||||
fun getCityList() {
|
||||
viewModelScope.launch {
|
||||
when (val result = networkService.getOfflineMapCityList()) {
|
||||
is NetResult.Success -> {
|
||||
cityListLiveData.postValue(result.data!!)
|
||||
}
|
||||
is NetResult.Error -> {
|
||||
Toast.makeText(context, "${result.exception.message}", Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
}
|
||||
is NetResult.Failure -> {
|
||||
Toast.makeText(context, "${result.code}:${result.msg}", Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
}
|
||||
NetResult.Loading -> {}
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
val realm = Realm.getDefaultInstance()
|
||||
val objects = realm.where<OfflineMapCityBean>().findAll().sort("id", Sort.ASCENDING)
|
||||
val list = realm.copyFromRealm(objects)
|
||||
realm.close()
|
||||
for (item in list) {
|
||||
FileManager.checkOfflineMapFileInfo(item)
|
||||
}
|
||||
cityListLiveData.postValue(list)
|
||||
}
|
||||
}
|
||||
}
|
@ -61,6 +61,5 @@ class OfflineMapFragment : Fragment() {
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
_binding = null
|
||||
Log.e("jingo","OfflineMapFragment onDestroyView")
|
||||
}
|
||||
}
|
@ -33,6 +33,5 @@ class OfflineMapStateListFragment : Fragment() {
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
_binding = null
|
||||
Log.e("jingo","OfflineMapStateListFragment onDestroyView")
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package com.navinfo.omqs.ui.fragment.offlinemap
|
||||
|
||||
import com.navinfo.omqs.databinding.AdapterOfflineMapCityBinding
|
||||
import com.navinfo.omqs.ui.other.BaseViewHolder
|
||||
|
||||
class OfflineMapViewHolder(dataBinding: AdapterOfflineMapCityBinding) : BaseViewHolder(dataBinding) {
|
||||
init{
|
||||
dataBinding.offlineMapDownloadBtn
|
||||
}
|
||||
}
|
@ -1,22 +1,30 @@
|
||||
package com.navinfo.omqs.ui.fragment.personalcenter
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import com.blankj.utilcode.util.UriUtils
|
||||
import com.github.k1rakishou.fsaf.FileChooser
|
||||
import com.github.k1rakishou.fsaf.callback.FSAFActivityCallbacks
|
||||
import com.github.k1rakishou.fsaf.callback.FileChooserCallback
|
||||
import com.navinfo.omqs.R
|
||||
import com.navinfo.omqs.databinding.FragmentPersonalCenterBinding
|
||||
|
||||
/**
|
||||
* 个人中心
|
||||
*/
|
||||
class PersonalCenterFragment : Fragment() {
|
||||
class PersonalCenterFragment : Fragment(), FSAFActivityCallbacks {
|
||||
|
||||
private var _binding: FragmentPersonalCenterBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
private val fileChooser by lazy { FileChooser(requireContext()) }
|
||||
private val viewModel by lazy { viewModels<PersonalCenterViewModel>().value }
|
||||
|
||||
|
||||
override fun onCreateView(
|
||||
@ -25,23 +33,45 @@ class PersonalCenterFragment : Fragment() {
|
||||
): View {
|
||||
_binding = FragmentPersonalCenterBinding.inflate(inflater, container, false)
|
||||
return binding.root
|
||||
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
Log.e("jingo", "NIMapController PersonalCenterFragment onViewCreated")
|
||||
binding.root.setNavigationItemSelectedListener {
|
||||
when (it.itemId) {
|
||||
R.id.personal_center_menu_offline_map ->
|
||||
findNavController().navigate(R.id.action_FirstFragment_to_SecondFragment)
|
||||
R.id.personal_center_menu_import_data -> {
|
||||
// 用户选中导入数据,打开文件选择器,用户选择导入的数据文件目录
|
||||
fileChooser.openChooseFileDialog(object: FileChooserCallback() {
|
||||
override fun onCancel(reason: String) {
|
||||
}
|
||||
|
||||
override fun onResult(uri: Uri) {
|
||||
val file = UriUtils.uri2File(uri)
|
||||
// 开始导入数据
|
||||
viewModel.importOmdbData(file)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
fileChooser.setCallbacks(this@PersonalCenterFragment)
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
_binding = null
|
||||
}
|
||||
|
||||
override fun fsafStartActivityForResult(intent: Intent, requestCode: Int) {
|
||||
startActivityForResult(intent, requestCode)
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
fileChooser.onActivityResult(requestCode, resultCode, data)
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package com.navinfo.omqs.ui.fragment.personalcenter
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import java.io.File
|
||||
|
||||
class PersonalCenterViewModel: ViewModel() {
|
||||
fun importOmdbData(omdbFile: File) {
|
||||
// 检查File是否为sqlite数据库
|
||||
if (omdbFile == null || omdbFile.exists()) {
|
||||
throw Exception("文件不存在")
|
||||
}
|
||||
if (!omdbFile.name.endsWith(".sqlite") and !omdbFile.name.endsWith("db")) {
|
||||
throw Exception("文件不存在")
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
package com.navinfo.omqs.ui.other
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View.OnClickListener
|
||||
import android.view.ViewGroup
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
@ -10,23 +11,51 @@ import androidx.recyclerview.widget.RecyclerView
|
||||
*/
|
||||
abstract class BaseRecyclerViewAdapter<T>(var data: List<T> = listOf()) :
|
||||
RecyclerView.Adapter<BaseViewHolder>() {
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder {
|
||||
return BaseViewHolder(
|
||||
DataBindingUtil.inflate(
|
||||
LayoutInflater.from(parent.context),
|
||||
viewType,
|
||||
parent,
|
||||
false
|
||||
)
|
||||
)
|
||||
}
|
||||
// private var recyclerView: RecyclerView? = null
|
||||
// override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder {
|
||||
//
|
||||
//
|
||||
//
|
||||
// return BaseViewHolder(
|
||||
// DataBindingUtil.inflate(
|
||||
// LayoutInflater.from(parent.context),
|
||||
// viewType,
|
||||
// parent,
|
||||
// false
|
||||
// )
|
||||
// )
|
||||
// }
|
||||
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return data.size
|
||||
}
|
||||
|
||||
fun refreshData(newData:List<T>){
|
||||
fun refreshData(newData: List<T>) {
|
||||
this.data = newData
|
||||
this.notifyDataSetChanged()
|
||||
}
|
||||
|
||||
|
||||
override fun onViewAttachedToWindow(holder: BaseViewHolder) {
|
||||
super.onViewAttachedToWindow(holder)
|
||||
holder.onStart()
|
||||
}
|
||||
|
||||
override fun onViewDetachedFromWindow(holder: BaseViewHolder) {
|
||||
super.onViewDetachedFromWindow(holder)
|
||||
holder.apply {
|
||||
onStop()
|
||||
}
|
||||
}
|
||||
//
|
||||
// override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
|
||||
// super.onAttachedToRecyclerView(recyclerView)
|
||||
// this.recyclerView = recyclerView
|
||||
// }
|
||||
//
|
||||
// override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) {
|
||||
// super.onDetachedFromRecyclerView(recyclerView)
|
||||
// this.recyclerView = null
|
||||
// }
|
||||
}
|
@ -1,11 +1,55 @@
|
||||
package com.navinfo.omqs.ui.other
|
||||
|
||||
import androidx.databinding.ViewDataBinding
|
||||
import android.view.View
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.LifecycleRegistry
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.viewbinding.ViewBinding
|
||||
|
||||
/**
|
||||
* dataBinding viewHolder 基类
|
||||
* LifecycleRegistry 这是一个生命周期注册器,继承自 Lifecycle,LifecycleOwner 通过这个类来分发生命周期事件,并在 getLifecycle() 中返回
|
||||
*/
|
||||
open class BaseViewHolder(var dataBinding: ViewDataBinding) :
|
||||
RecyclerView.ViewHolder(dataBinding.root) {
|
||||
open class BaseViewHolder(val viewBinding: ViewBinding) :
|
||||
RecyclerView.ViewHolder(viewBinding.root), LifecycleOwner {
|
||||
private val lifecycleRegistry = LifecycleRegistry(this)
|
||||
var tag = ""
|
||||
|
||||
init {
|
||||
// dataBinding.lifecycleOwner = this
|
||||
lifecycleRegistry.currentState = Lifecycle.State.INITIALIZED
|
||||
lifecycleRegistry.currentState = Lifecycle.State.CREATED
|
||||
itemView.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {
|
||||
// View onDetached 的时候回调 onDestroy()
|
||||
override fun onViewDetachedFromWindow(v: View) {
|
||||
itemView.removeOnAttachStateChangeListener(this)
|
||||
onDestroy()
|
||||
}
|
||||
|
||||
// View onAttached 的时候回调 onCreate()
|
||||
override fun onViewAttachedToWindow(v: View) {
|
||||
onStart()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun onStart() {
|
||||
lifecycleRegistry.currentState = Lifecycle.State.STARTED //
|
||||
lifecycleRegistry.currentState = Lifecycle.State.RESUMED // ON_RESUME EVENT
|
||||
}
|
||||
|
||||
fun onStop() {
|
||||
lifecycleRegistry.currentState = Lifecycle.State.STARTED //
|
||||
lifecycleRegistry.currentState = Lifecycle.State.CREATED // ON_STOP EVENT
|
||||
}
|
||||
|
||||
fun onDestroy() {
|
||||
lifecycleRegistry.currentState = Lifecycle.State.DESTROYED /// ON_DESTROY EVENT
|
||||
}
|
||||
|
||||
|
||||
override fun getLifecycle(): Lifecycle {
|
||||
return lifecycleRegistry
|
||||
}
|
||||
}
|
@ -5,10 +5,13 @@ import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import android.graphics.Paint
|
||||
import android.graphics.Rect
|
||||
import android.opengl.ETC1.getHeight
|
||||
import android.opengl.ETC1.getWidth
|
||||
import android.util.AttributeSet
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.ProgressBar
|
||||
import com.navinfo.omqs.R
|
||||
|
||||
|
||||
/**
|
||||
@ -18,8 +21,13 @@ class MyProgressBar : ProgressBar {
|
||||
private lateinit var mPaint: Paint
|
||||
private var text: String = ""
|
||||
private var rate = 0f
|
||||
private lateinit var bar: ProgressBar
|
||||
|
||||
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {
|
||||
// LayoutInflater.from(context).inflate(
|
||||
// R.layout.my_projressbar, this,
|
||||
// true
|
||||
// );
|
||||
initView()
|
||||
}
|
||||
|
||||
@ -33,6 +41,7 @@ class MyProgressBar : ProgressBar {
|
||||
mPaint.color = Color.BLUE
|
||||
}
|
||||
|
||||
|
||||
@Synchronized
|
||||
override fun setProgress(progress: Int) {
|
||||
setText(progress)
|
||||
@ -40,7 +49,7 @@ class MyProgressBar : ProgressBar {
|
||||
}
|
||||
|
||||
private fun setText(progress: Int) {
|
||||
rate = progress * 1.0f / this.getMax()
|
||||
rate = progress * 1.0f / this.max
|
||||
val i = (rate * 100).toInt()
|
||||
text = "$i%"
|
||||
}
|
||||
@ -53,12 +62,14 @@ class MyProgressBar : ProgressBar {
|
||||
// int x = (getWidth()/2) - rect.centerX();
|
||||
// int y = (getHeight()/2) - rect.centerY();
|
||||
var x = (width * rate).toInt()
|
||||
if (x == width) {
|
||||
val dx = width - rect.right
|
||||
if (x > dx) {
|
||||
// 如果为百分之百则在左边绘制。
|
||||
x = width - rect.right
|
||||
x = dx
|
||||
}
|
||||
val y: Int = 0 - rect.top
|
||||
mPaint.textSize = 22f
|
||||
mPaint.textSize = 24f
|
||||
val y: Int = 10 - rect.top
|
||||
|
||||
canvas.drawText(text, x.toFloat(), y.toFloat(), mPaint)
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
<vector android:height="24dp" android:tint="#747D8C"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M9,3L5,6.99h3L8,14h2L10,6.99h3L9,3zM16,17.01L16,10h-2v7.01h-3L15,21l4,-3.99h-3z"/>
|
||||
</vector>
|
21
app/src/main/res/drawable/progress_bg.xml
Normal file
21
app/src/main/res/drawable/progress_bg.xml
Normal file
@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!--设置ProgressBar背景色-->
|
||||
<item android:id="@android:id/background">
|
||||
<shape>
|
||||
<!--设置ProgressBar进度条圆角半径-->
|
||||
<corners android:radius="4dp" />
|
||||
<solid android:color="@color/cv_gray_153" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
<!--设置ProgressBar进度条颜色-->
|
||||
<item android:id="@android:id/progress">
|
||||
<clip android:clipOrientation="horizontal">
|
||||
<shape>
|
||||
<corners android:radius="4dp" />
|
||||
<solid android:color="@color/default_blue" />
|
||||
</shape>
|
||||
</clip>
|
||||
</item>
|
||||
</layer-list>
|
@ -17,8 +17,7 @@
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
>
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/login_fragment_logo"
|
||||
|
9
app/src/main/res/layout/activity_map_test.xml
Normal file
9
app/src/main/res/layout/activity_map_test.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<com.navinfo.collect.library.map.NIMapView
|
||||
android:id="@+id/main_activity_map1"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -1,82 +1,67 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/cv_bg_color"
|
||||
android:paddingLeft="10dp"
|
||||
android:paddingRight="10dp"
|
||||
android:paddingTop="5dp"
|
||||
tools:context="com.navinfo.omqs.ui.fragment.offlinemap.OfflineMapCityListAdapter">
|
||||
|
||||
<data>
|
||||
|
||||
<import type="com.navinfo.omqs.R" />
|
||||
|
||||
<variable
|
||||
name="cityBean"
|
||||
type="com.navinfo.omqs.bean.OfflineMapCityBean" />
|
||||
</data>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
<TextView
|
||||
android:id="@+id/offline_map_city_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="10dp"
|
||||
android:background="@color/cv_bg_color">
|
||||
android:text="省市名称"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="@dimen/default_font_size" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/offline_map_city_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@{cityBean.name}"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="@dimen/default_font_size"
|
||||
app:layout_constraintLeft_toLeftOf="@id/offline_map_city_size"
|
||||
app:layout_constraintRight_toRightOf="@id/offline_map_city_size"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
<TextView
|
||||
android:id="@+id/offline_map_city_size"
|
||||
style="@style/map_size_font_style"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/offline_map_city_name"
|
||||
android:drawableLeft="@mipmap/point_blue"
|
||||
android:layout_marginTop="5dp"
|
||||
android:text="文件大小"
|
||||
android:textSize="@dimen/card_title_font_3size" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/offline_map_city_size"
|
||||
style="@style/map_size_font_style"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawableLeft="@mipmap/point_blue"
|
||||
android:textSize="@dimen/card_title_font_3size"
|
||||
android:text="@{cityBean.getFileSizeText()}"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/offline_map_city_name" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_city_list_status"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:clickable="true"
|
||||
android:focusable="false"
|
||||
android:shadowColor="@android:color/transparent"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="@dimen/card_title_font_2size"
|
||||
app:layout_constraintBottom_toBottomOf="@id/offline_map_download_btn"
|
||||
app:layout_constraintRight_toLeftOf="@id/offline_map_download_btn"
|
||||
app:layout_constraintTop_toTopOf="@id/offline_map_download_btn" />
|
||||
<TextView
|
||||
android:id="@+id/offline_map_download_btn"
|
||||
style="@style/map_download_style_btn"
|
||||
android:layout_width="60dp"
|
||||
android:layout_alignTop="@id/offline_map_city_name"
|
||||
android:layout_alignBottom="@id/offline_map_city_size"
|
||||
android:layout_alignParentRight="true"
|
||||
android:shadowColor="@android:color/transparent"
|
||||
android:text="下载"
|
||||
android:textColor="@color/btn_blue_solid"
|
||||
android:textSize="@dimen/card_title_font_2size" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/offline_map_download_btn"
|
||||
style="@style/map_download_style_btn"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:shadowColor="@android:color/transparent"
|
||||
android:text="下载"
|
||||
android:textColor="@color/btn_blue_solid"
|
||||
android:textSize="@dimen/card_title_font_2size"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@id/offline_map_progress"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
<TextView
|
||||
android:id="@+id/tv_city_list_status"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:layout_toLeftOf="@id/offline_map_download_btn"
|
||||
android:clickable="true"
|
||||
android:focusable="false"
|
||||
android:shadowColor="@android:color/transparent"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="@dimen/card_title_font_2size" />
|
||||
|
||||
<com.navinfo.omqs.ui.widget.MyProgressBar
|
||||
android:layout_marginTop="5dp"
|
||||
android:visibility="gone"
|
||||
android:id="@+id/offline_map_progress"
|
||||
style="?android:attr/progressBarStyleHorizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:max="100"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</layout>
|
||||
<com.navinfo.omqs.ui.widget.MyProgressBar
|
||||
android:id="@+id/offline_map_progress"
|
||||
style="?android:attr/progressBarStyleHorizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="16dp"
|
||||
android:layout_below="@id/offline_map_download_btn"
|
||||
android:progressDrawable="@drawable/progress_bg"
|
||||
android:paddingTop="10dp"
|
||||
android:visibility="invisible" />
|
||||
</RelativeLayout>
|
@ -11,9 +11,9 @@
|
||||
android:icon="@drawable/baseline_map_24"
|
||||
android:title="离线地图" />
|
||||
<item
|
||||
android:id="@+id/personal_center_menu_offline_map1"
|
||||
android:icon="@drawable/baseline_person_24"
|
||||
android:title="menu_gallery" />
|
||||
android:id="@+id/personal_center_menu_import_data"
|
||||
android:icon="@drawable/ic_baseline_import_export_24"
|
||||
android:title="导入数据" />
|
||||
<item
|
||||
android:id="@+id/personal_center_menu_offline_map2"
|
||||
android:icon="@drawable/baseline_person_24"
|
||||
|
@ -2,13 +2,12 @@
|
||||
|
||||
buildscript {
|
||||
dependencies {
|
||||
classpath "io.realm:realm-gradle-plugin:10.10.1"
|
||||
classpath "io.realm:realm-gradle-plugin:10.11.1"
|
||||
}
|
||||
}
|
||||
plugins {
|
||||
id 'com.android.application' version '7.3.1' apply false
|
||||
id 'com.android.library' version '7.3.1' apply false
|
||||
id 'org.jetbrains.kotlin.android' version '1.8.0' apply false
|
||||
id 'io.realm.kotlin' version '0.10.0' apply false
|
||||
id 'com.google.dagger.hilt.android' version '2.44' apply false
|
||||
}
|
||||
|
@ -0,0 +1,57 @@
|
||||
package com.navinfo.collect.library.data.entity
|
||||
|
||||
import io.realm.RealmObject
|
||||
import io.realm.annotations.PrimaryKey
|
||||
|
||||
//enum class StatusEnum(val status: Int) {
|
||||
// NONE(0), WAITING(1), LOADING(2), PAUSE(3),
|
||||
// ERROR(4), DONE(5), UPDATE(6)
|
||||
//}
|
||||
|
||||
open class OfflineMapCityBean @JvmOverloads constructor(
|
||||
@PrimaryKey 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 = NONE
|
||||
) : RealmObject() {
|
||||
|
||||
companion object Status{
|
||||
const val NONE = 0 //无状态
|
||||
const val WAITING = 1 //等待中
|
||||
const val LOADING = 2 //下载中
|
||||
const val PAUSE = 3 //暂停
|
||||
const val ERROR = 4 //错误
|
||||
const val DONE = 5 //完成
|
||||
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 {
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
package com.navinfo.collect.library.data.entity
|
||||
|
||||
import io.realm.RealmModel
|
||||
import io.realm.RealmObject
|
||||
import io.realm.annotations.PrimaryKey
|
||||
import io.realm.annotations.RealmClass
|
||||
|
||||
@RealmClass
|
||||
open class OfflineMapCityRealmObject: RealmModel {
|
||||
@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
|
||||
|
||||
constructor(){
|
||||
|
||||
}
|
||||
|
||||
constructor(
|
||||
id: String,
|
||||
fileName: String,
|
||||
name: String,
|
||||
url: String,
|
||||
version: Long,
|
||||
fileSize: Long,
|
||||
currentSize: Long,
|
||||
status: Int
|
||||
) {
|
||||
this.id = id
|
||||
this.fileName = fileName
|
||||
this.name = name
|
||||
this.url = url
|
||||
this.version = version
|
||||
this.fileSize = fileSize
|
||||
this.currentSize = currentSize
|
||||
this.status = status
|
||||
}
|
||||
}
|
@ -91,7 +91,6 @@ DataNiLocationHandler(context: Context, dataBase: MapLifeDataBase) :
|
||||
)
|
||||
mDataBase.niLocationDao.delete(niLocation);
|
||||
} catch (e: Throwable) {
|
||||
Log.e("jingo", "删除数据报错 ${e.message}");
|
||||
Handler(Looper.getMainLooper()).post {
|
||||
callback.invoke(false, "${e.message}")
|
||||
}
|
||||
|
@ -37,8 +37,5 @@ class NIMapController {
|
||||
mapView.setOptions(options)
|
||||
}
|
||||
|
||||
fun print() {
|
||||
Log.e("jingo", "NIMapController 哈希code ${hashCode()}")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -429,7 +429,6 @@ public class GeometryTools {
|
||||
dList.add(lt + dis);
|
||||
total += dis;
|
||||
}
|
||||
Log.e("jingo", "line lengh =" + total);
|
||||
total = total / 2;
|
||||
for (int i = 0; i < dList.size(); i++) {
|
||||
double a = dList.get(i);
|
||||
@ -495,7 +494,6 @@ public class GeometryTools {
|
||||
dList.add(lt + dis);
|
||||
total += dis;
|
||||
}
|
||||
Log.e("jingo", "line lengh =" + total);
|
||||
total = total / 2;
|
||||
for (int i = 0; i < dList.size(); i++) {
|
||||
double a = dList.get(i);
|
||||
|
Loading…
x
Reference in New Issue
Block a user