fix: 合并代码

This commit is contained in:
xiaoyan 2023-04-25 15:40:23 +08:00
commit 67ce6f11b9
34 changed files with 560 additions and 188 deletions

View File

@ -12,12 +12,16 @@ android {
defaultConfig {
applicationId "com.navinfo.omqs"
minSdk 26
targetSdk 33
minSdk 21
targetSdk 21
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
multiDexEnabled true
ndk {
abiFilters "armeabi-v7a", "armeabi", "mips"
}
}
buildTypes {
@ -37,7 +41,18 @@ android {
viewBinding true
dataBinding true
}
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
debug.setRoot('build-types/debug')
release.setRoot('build-types/release')
}
//ok了
lintOptions {
checkReleaseBuilds false
abortOnError false
}
}
@ -46,7 +61,6 @@ dependencies {
implementation project(':collect-library')
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
@ -72,8 +86,8 @@ dependencies {
kapt "androidx.room:room-ktx:2.5.1"
//excel word等文件
implementation 'org.apache.poi:poi:5.2.3'
implementation 'org.apache.poi:poi-ooxml:5.2.3'
implementation 'org.apache.poi:poi:4.0.0'
implementation 'org.apache.poi:poi-ooxml:4.0.0'
// spatialite文件
implementation 'com.github.sevar83:android-spatialite:2.0.1'

View File

@ -16,7 +16,6 @@ class OMQSApplication : Application() {
Util.getInstance().init(applicationContext)
NetUtils.getInstance().init(this)
TakePhotoManager.getInstance().init(this, 1)
FileManager.initRootDir(this)
}
private fun getKey(inputString: String): String {

View File

@ -5,6 +5,7 @@ import com.navinfo.omqs.Constant
import com.navinfo.omqs.tools.FileManager.Companion.FileDownloadStatus
import io.realm.RealmList
import io.realm.RealmObject
import io.realm.annotations.Ignore
import io.realm.annotations.PrimaryKey
import io.realm.annotations.RealmClass
@ -55,9 +56,11 @@ open class TaskBean @JvmOverloads constructor(
/**
* 当前下载状态
*/
var status: Int = FileDownloadStatus.NONE
) : RealmObject(){
fun getDownLoadUrl():String{
var status: Int = FileDownloadStatus.NONE,
@Ignore
var message: String = ""
) : RealmObject() {
fun getDownLoadUrl(): String {
return "${Constant.SERVER_ADDRESS}devcp/download?fileStr=26"
}
}

View File

@ -30,13 +30,24 @@ import kotlin.streams.toList
/**
* 导入omdb数据的帮助类
* */
class ImportOMDBHelper @AssistedInject constructor(@Assisted("context") val context: Context,@Assisted("omdbFile") val omdbFile: File) {
class ImportOMDBHelper @AssistedInject constructor(
@Assisted("context") val context: Context,
@Assisted("omdbFile") val omdbFile: File
) {
@Inject
lateinit var omdbHiltFactory: OMDBDataBaseHiltFactory
@Inject
lateinit var gson: Gson
private val database by lazy { omdbHiltFactory.obtainOmdbDataBaseHelper(context, omdbFile.absolutePath, 1).writableDatabase }
private val configFile: File = File("${Constant.USER_DATA_PATH}", Constant.OMDB_CONFIG)
private val database by lazy {
omdbHiltFactory.obtainOmdbDataBaseHelper(
context,
omdbFile.absolutePath,
1
).writableDatabase
}
private val configFile: File =
File("${Constant.DATA_PATH}/${Constant.CURRENT_USER_ID}", Constant.OMDB_CONFIG)
/**
* 读取config的配置文件
@ -67,8 +78,10 @@ class ImportOMDBHelper @AssistedInject constructor(@Assisted("context") val cont
}
}.toList()
val cursor = database.query(table, finalColumns.toTypedArray(), "1=1",
mutableListOf<String>().toTypedArray(), null, null, null, null)
val cursor = database.query(
table, finalColumns.toTypedArray(), "1=1",
mutableListOf<String>().toTypedArray(), null, null, null, null
)
with(cursor) {
if (moveToFirst()) {
while (moveToNext()) {
@ -76,13 +89,17 @@ class ImportOMDBHelper @AssistedInject constructor(@Assisted("context") val cont
for (columnIndex in 0 until columnCount) {
var columnName = getColumnName(columnIndex)
if (columnName.startsWith("ST_AsText(")) {
columnName = columnName.replace("ST_AsText(", "").substringBeforeLast(")")
columnName = columnName.replace("ST_AsText(", "")
.substringBeforeLast(")")
}
when(getType(columnIndex)) {
when (getType(columnIndex)) {
FIELD_TYPE_NULL -> rowMap[columnName] = ""
FIELD_TYPE_INTEGER -> rowMap[columnName] = getInt(columnIndex)
FIELD_TYPE_FLOAT -> rowMap[columnName] = getFloat(columnIndex)
FIELD_TYPE_BLOB -> rowMap[columnName] = String(getBlob(columnIndex), Charsets.UTF_8)
FIELD_TYPE_INTEGER -> rowMap[columnName] =
getInt(columnIndex)
FIELD_TYPE_FLOAT -> rowMap[columnName] =
getFloat(columnIndex)
FIELD_TYPE_BLOB -> rowMap[columnName] =
String(getBlob(columnIndex), Charsets.UTF_8)
else -> rowMap[columnName] = getString(columnIndex)
}
}
@ -104,7 +121,7 @@ class ImportOMDBHelper @AssistedInject constructor(@Assisted("context") val cont
suspend fun importOmdbZipFile(omdbZipFile: File): Flow<String> = withContext(Dispatchers.IO) {
val importConfig = openConfigFile()
val unZipFolder = File(omdbZipFile.parentFile, "result")
flow<String> {
flow {
if (unZipFolder.exists()) {
unZipFolder.deleteRecursively()
}
@ -122,7 +139,7 @@ class ImportOMDBHelper @AssistedInject constructor(@Assisted("context") val cont
val listResult = mutableListOf<Map<String, Any?>>()
currentConfig?.let {
val list = FileIOUtils.readFile2List(txtFile, "UTF-8")
if (list!=null) {
if (list != null) {
// 将list数据转换为map
for (line in list) {
val map = gson.fromJson<Map<String, Any?>>(line, object:TypeToken<Map<String, Any?>>(){}.getType())
@ -154,10 +171,10 @@ class ImportOMDBHelper @AssistedInject constructor(@Assisted("context") val cont
Realm.getDefaultInstance().insert(renderEntity)
}
// 1个文件发送一次flow流
emit("${index+1}/${importConfig.tables.size}")
emit("${index + 1}/${importConfig.tables.size}")
}
Realm.getDefaultInstance().commitTransaction()
emit("finish")
emit("OK")
}
}
@ -166,7 +183,15 @@ class ImportOMDBHelper @AssistedInject constructor(@Assisted("context") val cont
val columns = mutableListOf<String>()
// 查询 sqlite_master 表获取指定数据表的元数据信息
val cursor = db.query("sqlite_master", arrayOf("sql"), "type='table' AND name=?", arrayOf(tableName), null, null, null)
val cursor = db.query(
"sqlite_master",
arrayOf("sql"),
"type='table' AND name=?",
arrayOf(tableName),
null,
null,
null
)
// 从元数据信息中解析出列名
if (cursor.moveToFirst()) {

View File

@ -1,9 +1,9 @@
package com.navinfo.omqs.db
import com.navinfo.collect.library.data.entity.QsRecordBean
import com.navinfo.omqs.bean.HadLinkDvoBean
import com.navinfo.omqs.bean.TaskBean
import io.realm.annotations.RealmModule
@io.realm.annotations.RealmModule(classes = [TaskBean::class, HadLinkDvoBean::class])
@RealmModule(classes = [TaskBean::class, HadLinkDvoBean::class])
class MyRealmModule {
}

View File

@ -0,0 +1,66 @@
package com.navinfo.omqs.hilt
import android.content.Context
import com.navinfo.collect.library.map.NIMapController
import com.navinfo.omqs.db.RealmOperateHelper
import com.navinfo.omqs.db.RoomAppDatabase
import com.navinfo.omqs.http.RetrofitNetworkServiceAPI
import com.navinfo.omqs.http.offlinemapdownload.OfflineMapDownloadManager
import com.navinfo.omqs.http.taskdownload.TaskDownloadManager
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)
@Module
class ActivityModule {
/**
* 注入地图控制器在activity范围内使用单例
*/
@ActivityRetainedScoped
@Provides
fun providesMapController(): NIMapController = NIMapController()
/**
* 注入离线地图下载管理在activity范围内使用单例
*/
@ActivityRetainedScoped
@Provides
fun providesOfflineMapDownloadManager(
networkServiceAPI: RetrofitNetworkServiceAPI,
roomAppDatabase: RoomAppDatabase,
mapController: NIMapController
): OfflineMapDownloadManager =
OfflineMapDownloadManager(networkServiceAPI, roomAppDatabase, mapController)
/**
* 注入任务下载
*/
@ActivityRetainedScoped
@Provides
fun providesTaskListDownloadManager(
networkServiceAPI: RetrofitNetworkServiceAPI,
importFactory: ImportOMDBHiltFactory,
): TaskDownloadManager =
TaskDownloadManager(importFactory, networkServiceAPI)
/**
* 实验失败这样创建viewmodel不会在activity销毁的时候同时销毁
* 4-14:因为没有传入activity的 owner,无法检测生命周期
*/
// @ActivityRetainedScoped
// @Provides
// fun providesMainViewModel(mapController: NIMapController): MainViewModel {
// return MainViewModel(mapController)
// }
@ActivityRetainedScoped
@Provides
fun providesRealmOperateHelper(): RealmOperateHelper {
return RealmOperateHelper()
}
}

View File

@ -17,6 +17,7 @@ import dagger.Lazy
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import io.realm.Realm
import kotlinx.coroutines.*
@ -36,7 +37,7 @@ class GlobalModule {
@Singleton
@Provides
fun provideApplication(application: Application): OMQSApplication {
fun provideApplication(@ApplicationContext application: Application): OMQSApplication {
return application as OMQSApplication
}

View File

@ -1,63 +1,21 @@
package com.navinfo.omqs.hilt
import android.content.Context
import com.navinfo.collect.library.map.NIMapController
import com.navinfo.omqs.db.RealmOperateHelper
import com.navinfo.omqs.db.RoomAppDatabase
import com.navinfo.omqs.http.RetrofitNetworkServiceAPI
import com.navinfo.omqs.http.offlinemapdownload.OfflineMapDownloadManager
import com.navinfo.omqs.http.taskdownload.TaskDownloadManager
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.components.ActivityRetainedComponent
import dagger.hilt.android.scopes.ActivityRetainedScoped
import dagger.hilt.android.components.ActivityComponent
import dagger.hilt.android.qualifiers.ActivityContext
import dagger.hilt.android.scopes.ActivityScoped
@InstallIn(ActivityRetainedComponent::class)
@InstallIn(ActivityComponent::class)
@Module
class MainActivityModule {
/**
* 注入地图控制器在activity范围内使用单例
*/
@ActivityRetainedScoped
@ActivityScoped
@Provides
fun providesMapController(): NIMapController = NIMapController()
/**
* 注入离线地图下载管理在activity范围内使用单例
*/
@ActivityRetainedScoped
@Provides
fun providesOfflineMapDownloadManager(
networkServiceAPI: RetrofitNetworkServiceAPI,
roomAppDatabase: RoomAppDatabase,
mapController: NIMapController
): OfflineMapDownloadManager =
OfflineMapDownloadManager(networkServiceAPI, roomAppDatabase, mapController)
/**
* 注入任务下载
*/
@ActivityRetainedScoped
@Provides
fun providesTaskListDownloadManager(
networkServiceAPI: RetrofitNetworkServiceAPI,
): TaskDownloadManager =
TaskDownloadManager(networkServiceAPI)
/**
* 实验失败这样创建viewmodel不会在activity销毁的时候同时销毁
* 4-14:因为没有传入activity的 owner,无法检测生命周期
*/
// @ActivityRetainedScoped
// @Provides
// fun providesMainViewModel(mapController: NIMapController): MainViewModel {
// return MainViewModel(mapController)
// }
@ActivityRetainedScoped
@Provides
fun providesRealmOperateHelper(): RealmOperateHelper {
return RealmOperateHelper()
}
fun providesContext(@ActivityContext context: Context): Context = context
}

View File

@ -0,0 +1,7 @@
//package com.navinfo.omqs.hilt
//
//import javax.inject.Qualifier
//
//@Qualifier
//@Retention(AnnotationRetention.RUNTIME)
//annotation class ActivityContext

View File

@ -1,23 +1,32 @@
package com.navinfo.omqs.http.taskdownload
import android.content.Context
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.Observer
import com.navinfo.omqs.bean.TaskBean
import com.navinfo.omqs.hilt.ImportOMDBHiltFactory
import com.navinfo.omqs.hilt.OMDBDataBaseHiltFactory
import com.navinfo.omqs.http.RetrofitNetworkServiceAPI
import dagger.hilt.android.qualifiers.ActivityContext
import java.util.concurrent.ConcurrentHashMap
import javax.inject.Inject
/**
* 管理任务数据下载
*/
class TaskDownloadManager(
class TaskDownloadManager constructor(
val importFactory: ImportOMDBHiltFactory,
val netApi: RetrofitNetworkServiceAPI,
) {
lateinit var context: Context
/**
* 最多同时下载数量
*/
private val MAX_SCOPE = 3
private val MAX_SCOPE = 1
/**
* 存储有哪些城市需要下载的队列
@ -33,6 +42,9 @@ class TaskDownloadManager(
ConcurrentHashMap<Int, TaskDownloadScope>()
}
fun init(context: Context) {
this.context = context
}
/**
* 启动下载任务
@ -92,7 +104,7 @@ class TaskDownloadManager(
fun addTask(taskBean: TaskBean) {
if (!scopeMap.containsKey(taskBean.id)) {
scopeMap[taskBean.id] = TaskDownloadScope(this, taskBean)
scopeMap[taskBean.id] = TaskDownloadScope( this, taskBean)
}
}

View File

@ -1,23 +1,34 @@
package com.navinfo.omqs.http.taskdownload
import android.content.Context
import android.util.Log
import androidx.core.content.ContentProviderCompat.requireContext
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Observer
import com.navinfo.omqs.Constant
import com.navinfo.omqs.bean.TaskBean
import com.navinfo.omqs.db.ImportOMDBHelper
import com.navinfo.omqs.hilt.ImportOMDBHiltFactory
import com.navinfo.omqs.tools.FileManager
import com.navinfo.omqs.tools.FileManager.Companion.FileDownloadStatus
import io.realm.Realm
import kotlinx.coroutines.*
import java.io.File
import java.io.IOException
import java.io.InputStream
import java.io.RandomAccessFile
import javax.inject.Inject
class TaskDownloadScope(
private val downloadManager: TaskDownloadManager,
val taskBean: TaskBean,
) :
CoroutineScope by CoroutineScope(Dispatchers.IO + CoroutineName("OfflineMapDownLoad")) {
@Inject
lateinit var importOMDBHiltFactory: ImportOMDBHiltFactory
/**
*下载任务用来取消的
*/
@ -59,7 +70,12 @@ class TaskDownloadScope(
*/
fun launch() {
downloadJob = launch() {
download()
FileManager.checkOMDBFileInfo(taskBean)
if (taskBean.status == FileDownloadStatus.IMPORT) {
importData()
} else {
download()
}
downloadManager.launchNext(taskBean.id)
}
}
@ -76,14 +92,17 @@ class TaskDownloadScope(
* 更新任务
* @param status [OfflineMapCityBean.Status]
*/
private fun change(status: Int) {
if (taskBean.status != status || status == FileDownloadStatus.LOADING) {
private fun change(status: Int, message: String = "") {
if (taskBean.status != status || status == FileDownloadStatus.LOADING || status == FileDownloadStatus.IMPORTING) {
taskBean.status = status
taskBean.message = message
downloadData.postValue(taskBean)
launch(Dispatchers.IO) {
// downloadManager.roomDatabase.getOfflineMapDao().update(taskBean)
launch {
val realm = Realm.getDefaultInstance()
realm.executeTransaction {
it.copyToRealmOrUpdate(taskBean)
}
}
}
}
@ -96,36 +115,75 @@ class TaskDownloadScope(
downloadData.observe(owner, ob)
}
/**
* 导入数据
*/
private suspend fun importData(file: File? = null) {
try {
Log.e("jingo", "importData SSS")
change(FileDownloadStatus.IMPORTING)
var fileNew = file
?: File("${Constant.DOWNLOAD_PATH}${taskBean.evaluationTaskName}_${taskBean.dataVersion}.zip")
val importOMDBHelper: ImportOMDBHelper =
downloadManager.importFactory.obtainImportOMDBHelper(
downloadManager.context,
fileNew
)
importOMDBHelper.importOmdbZipFile(importOMDBHelper.omdbFile).collect {
Log.e("jingo", "数据安装 $it")
if (it == "OK") {
change(FileDownloadStatus.DONE)
} else {
change(FileDownloadStatus.IMPORTING, it)
}
}
} catch (e: Exception) {
Log.e("jingo", "数据安装失败 ${e.toString()}")
change(FileDownloadStatus.ERROR)
}
Log.e("jingo", "importData EEE")
}
/**
* 下载文件
*/
private suspend fun download() {
//如果文件下载安装已经完毕
if (taskBean.status == FileDownloadStatus.DONE) {
return
}
var inputStream: InputStream? = null
var randomAccessFile: RandomAccessFile? = null
try {
//创建离线地图 下载文件夹,.map文件夹的下一级
val fileDir = File("${Constant.DOWNLOAD_PATH}download")
val fileDir = File("${Constant.DOWNLOAD_PATH}")
if (!fileDir.exists()) {
fileDir.mkdirs()
}
val fileTemp =
File("${Constant.DOWNLOAD_PATH}${taskBean.id}_${taskBean.dataVersion}")
File("${Constant.DOWNLOAD_PATH}${taskBean.evaluationTaskName}_${taskBean.dataVersion}.zip")
val startPosition = taskBean.currentSize
//验证断点有效性
if (startPosition < 0) throw IOException("jingo Start position less than zero")
if (fileTemp.length() > 0 && taskBean.fileSize > 0 && fileTemp.length() == taskBean.fileSize) {
importData(fileTemp)
return
}
val response = downloadManager.netApi.retrofitDownLoadFile(
start = "bytes=$startPosition-",
url = taskBean.getDownLoadUrl()
)
val responseBody = response.body()
change(FileDownloadStatus.LOADING)
responseBody ?: throw IOException("jingo ResponseBody is null")
responseBody ?: throw IOException("jingo ResponseBody is null")
if (startPosition == 0L) {
taskBean.fileSize = responseBody.contentLength()
Log.e("jingo", "当前文件大小 ${taskBean.fileSize}")
}
change(FileDownloadStatus.LOADING)
//写入文件
randomAccessFile = RandomAccessFile(fileTemp, "rwd")
randomAccessFile.seek(startPosition)
@ -146,17 +204,14 @@ class TaskDownloadScope(
}
}
Log.e("jingo", "文件下载完成 ${taskBean.currentSize} == ${taskBean.fileSize}")
if (taskBean.currentSize == taskBean.fileSize) {
val res =
fileTemp.renameTo(File("${Constant.DOWNLOAD_PATH}${taskBean.evaluationTaskName}.zip"))
Log.e("jingo", "文件下载完成 修改文件 $res")
change(FileDownloadStatus.DONE)
importData(fileTemp)
} else {
change(FileDownloadStatus.PAUSE)
}
} catch (e: Throwable) {
change(FileDownloadStatus.ERROR)
Log.e("jingo","数据下载出错 ${e.message}")
} finally {
inputStream?.close()
randomAccessFile?.close()

View File

@ -16,8 +16,10 @@ class FileManager {
const val LOADING = 2 //下载中
const val PAUSE = 3 //暂停
const val ERROR = 4 //错误
const val DONE = 5 //完成
const val UPDATE = 6 //有新版本要更新
const val IMPORT = 5 //安装
const val IMPORTING = 6 //安装中
const val UPDATE = 7 //有新版本要更新
const val DONE = 8 //完成
}
//初始化数据文件夹
@ -109,60 +111,28 @@ class FileManager {
* 检查离线地图文件
*/
suspend fun checkOMDBFileInfo(taskBean: TaskBean) {
if (taskBean.status == FileDownloadStatus.DONE)
return
//访问离线地图文件夹
val fileDir = File("${Constant.DOWNLOAD_PATH}")
//如果连本地文件夹还没有,就不用修改任何数据了
if (!fileDir.exists()) {
return
}
//访问离线地图临时下载文件夹
val fileTempDir = File(Constant.DOWNLOAD_PATH)
//是否有一份.map文件了
var mapFile: File? = null
//文件夹里文件挨个访问
for (item in fileDir.listFiles()) {
//先找到对应的省市文件例如540000_西藏自治区_20230401195018.map",以id开头
if (item.isFile && item.name.startsWith("${taskBean.id}_")) {
//如果本地文件与从网络获取到版本号一致,表示这个文件已经下载完毕,不用处理了
if (item.name == "${taskBean.id}_${taskBean.dataVersion}") {
taskBean.status = FileDownloadStatus.DONE
return
}
//文件存在,版本号不对应,留给下面流程处理
mapFile = item
break
}
}
//临时下载文件夹
if (fileTempDir.exists()) {
for (item in fileTempDir.listFiles()) {
//先找到对应的省市文件例如540000_20230401195018",以id开头
if (item.isFile && item.name.startsWith("${taskBean.id}_")) {
//如果本地文件与从网络获取到版本号一致,表示这个文件已经在下载列表中
if (item.name == "${taskBean.id}_${taskBean.dataVersion}") {
//如果这个临时文件的大小和下载大小是一致的,说明已经下载完了,但是在下载环节没有更名移动成功,需要重命名和移动文件夹
if (item.length() == taskBean.fileSize) {
//移动更名文件后删除旧数据,修改状态
if (item.renameTo(File("${Constant.OFFLINE_MAP_PATH}${taskBean.evaluationTaskName}.zip"))) {
//删除旧版本数据
mapFile?.delete()
taskBean.status = FileDownloadStatus.DONE
return
}
} else { // 临时文件大小和目标不一致,说明下载了一半
taskBean.status = FileDownloadStatus.PAUSE
taskBean.currentSize = item.length()
return
}
} else { //虽然省市id开头一致但是版本号不一致说明之前版本下载了一部分现在要更新了原来下载的文件直接删除
taskBean.status = FileDownloadStatus.UPDATE
item.delete()
return
}
break
if (item.isFile && item.name == "${taskBean.evaluationTaskName}_${taskBean.dataVersion}.zip") {
taskBean.currentSize = item.length()
if (taskBean.fileSize > 0 && taskBean.fileSize == item.length()) {
taskBean.status = FileDownloadStatus.IMPORT
} else {
taskBean.status = FileDownloadStatus.PAUSE
}
return
}
}
taskBean.status = FileDownloadStatus.NONE
}
}
}

View File

@ -0,0 +1,204 @@
/**
*
*/
package com.navinfo.omqs.ui.activity;
import android.Manifest;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.view.KeyEvent;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
/**
* 继承了Activity实现Android6.0的运行时权限检测
* 需要进行运行时权限检测的Activity可以继承这个类
*/
public class CheckPermissionsActivity extends BaseActivity {
//是否需要检测后台定位权限设置为true时如果用户没有给予后台定位权限会弹窗提示
private boolean needCheckBackLocation = false;
//如果设置了target > 28需要增加这个权限否则不会弹出"始终允许"这个选择框
private static String BACKGROUND_LOCATION_PERMISSION = "android.permission.ACCESS_BACKGROUND_LOCATION";
/**
* 需要进行检测的权限数组
*/
protected String[] needPermissions = {
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE,
};
private static final int PERMISSON_REQUESTCODE = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(Build.VERSION.SDK_INT > 28
&& getApplicationContext().getApplicationInfo().targetSdkVersion > 28) {
needPermissions = new String[] {
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE,
BACKGROUND_LOCATION_PERMISSION
};
}
}
/**
* 判断是否需要检测防止不停的弹框
*/
private boolean isNeedCheck = true;
@Override
protected void onResume() {
super.onResume();
if (Build.VERSION.SDK_INT >= 23
&& getApplicationInfo().targetSdkVersion >= 23) {
if (isNeedCheck) {
checkPermissions(needPermissions);
}
}
}
/**
*
* @param permissions
*
*/
private void checkPermissions(String... permissions) {
try {
if (Build.VERSION.SDK_INT >= 23
&& getApplicationInfo().targetSdkVersion >= 23) {
List<String> needRequestPermissonList = findDeniedPermissions(permissions);
if (null != needRequestPermissonList
&& needRequestPermissonList.size() > 0) {
String[] array = needRequestPermissonList.toArray(new String[needRequestPermissonList.size()]);
Method method = getClass().getMethod("requestPermissions", new Class[]{String[].class,
int.class});
method.invoke(this, array, PERMISSON_REQUESTCODE);
}
}
} catch (Throwable e) {
}
}
/**
* 获取权限集中需要申请权限的列表
*
* @param permissions
*
*/
private List<String> findDeniedPermissions(String[] permissions) {
List<String> needRequestPermissonList = new ArrayList<String>();
if (Build.VERSION.SDK_INT >= 23
&& getApplicationInfo().targetSdkVersion >= 23){
try {
for (String perm : permissions) {
Method checkSelfMethod = getClass().getMethod("checkSelfPermission", String.class);
Method shouldShowRequestPermissionRationaleMethod = getClass().getMethod("shouldShowRequestPermissionRationale",
String.class);
if ((Integer)checkSelfMethod.invoke(this, perm) != PackageManager.PERMISSION_GRANTED
|| (Boolean)shouldShowRequestPermissionRationaleMethod.invoke(this, perm)) {
if(!needCheckBackLocation
&& BACKGROUND_LOCATION_PERMISSION.equals(perm)) {
continue;
}
needRequestPermissonList.add(perm);
}
}
} catch (Throwable e) {
}
}
return needRequestPermissonList;
}
/**
* 检测是否所有的权限都已经授权
*
*/
private boolean verifyPermissions(int[] grantResults) {
for (int result : grantResults) {
if (result != PackageManager.PERMISSION_GRANTED) {
return false;
}
}
return true;
}
@TargetApi(23)
public void onRequestPermissionsResult(int requestCode,
String[] permissions, int[] paramArrayOfInt) {
if (requestCode == PERMISSON_REQUESTCODE) {
if (!verifyPermissions(paramArrayOfInt)) {
showMissingPermissionDialog();
isNeedCheck = false;
}
}
}
/**
* 显示提示信息
*
*/
private void showMissingPermissionDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("提示");
builder.setMessage("当前应用缺少必要权限。\\n\\n请点击\\\"设置\\\"-\\\"权限\\\"-打开所需权限。");
// 拒绝, 退出应用
builder.setNegativeButton("取消",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
finish();
}
});
builder.setPositiveButton("设置",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
startAppSettings();
}
});
builder.setCancelable(false);
builder.show();
}
/**
* 启动应用的设置
*
*/
private void startAppSettings() {
Intent intent = new Intent(
Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.setData(Uri.parse("package:" + getPackageName()));
startActivity(intent);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if(keyCode == KeyEvent.KEYCODE_BACK){
this.finish();
return true;
}
return super.onKeyDown(keyCode, event);
}
}

View File

@ -33,7 +33,7 @@ open abstract class PermissionsActivity : BaseActivity() {
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) {
// permissionList.add(Permission.ACCESS_BACKGROUND_LOCATION)
}
XXPermissions.with(this)
/* XXPermissions.with(this)
// 申请单个权限
.permission(permissionList)
// 设置权限请求拦截器(局部设置)
@ -73,7 +73,7 @@ open abstract class PermissionsActivity : BaseActivity() {
onPermissionsDenied()
}
}
})
})*/
}
/**

View File

@ -11,6 +11,7 @@ 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.CheckPermissionsActivity
import com.navinfo.omqs.ui.activity.PermissionsActivity
import com.navinfo.omqs.ui.activity.map.MainActivity
import dagger.hilt.android.AndroidEntryPoint
@ -20,7 +21,7 @@ import dagger.hilt.android.AndroidEntryPoint
*/
@AndroidEntryPoint
class LoginActivity : PermissionsActivity() {
class LoginActivity : CheckPermissionsActivity() {
private lateinit var binding: ActivityLoginBinding
private val viewModel by viewModels<LoginViewModel>()
@ -97,15 +98,6 @@ class LoginActivity : PermissionsActivity() {
}
}
//进应用根本不调用,待查
override fun onPermissionsGranted() {
Log.e("jingo", "调用了吗")
}
override fun onPermissionsDenied() {
}
override fun onDestroy() {
super.onDestroy()
}

View File

@ -10,6 +10,7 @@ import androidx.lifecycle.viewModelScope
import com.blankj.utilcode.util.ResourceUtils
import com.navinfo.omqs.Constant
import com.navinfo.omqs.bean.LoginUserBean
import com.navinfo.omqs.db.MyRealmModule
import com.navinfo.omqs.db.RoomAppDatabase
import com.navinfo.omqs.http.NetResult
import com.navinfo.omqs.http.NetworkService
@ -170,7 +171,7 @@ class LoginViewModel @Inject constructor(
.directory(userFolder)
.name("OMQS.realm")
.encryptionKey(password)
// .modules(Realm.getDefaultModule(), MyRealmModule())
.modules(Realm.getDefaultModule(), MyRealmModule())
.schemaVersion(1)
.build()
Realm.setDefaultConfiguration(config)

View File

@ -4,10 +4,8 @@ import android.os.Bundle
import androidx.activity.viewModels
import androidx.core.view.WindowCompat
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.ViewModelProvider
import com.blankj.utilcode.util.ToastUtils
import androidx.lifecycle.lifecycleScope
import androidx.navigation.findNavController
import com.blankj.utilcode.util.ToastUtils
import com.navinfo.collect.library.map.NIMapController
import com.navinfo.collect.library.map.handler.NiLocationListener
import com.navinfo.omqs.Constant
@ -34,10 +32,10 @@ class MainActivity : BaseActivity() {
@Inject
lateinit var offlineMapDownloadManager: OfflineMapDownloadManager
override fun onCreate(savedInstanceState: Bundle?) {
WindowCompat.setDecorFitsSystemWindows(window, false)
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
//初始化地图
mapController.init(

View File

@ -3,38 +3,24 @@ 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.lifecycle.lifecycleScope
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.collect.library.data.RealmUtils
import com.navinfo.collect.library.data.entity.OMDBEntity
import com.navinfo.collect.library.map.NIMapController
import com.navinfo.omqs.R
import com.navinfo.omqs.databinding.FragmentPersonalCenterBinding
import com.navinfo.omqs.db.ImportOMDBHelper
import com.navinfo.omqs.hilt.ImportOMDBHiltFactory
import com.navinfo.omqs.tools.CoroutineUtils
import com.navinfo.omqs.ui.activity.BaseActivity
import com.navinfo.omqs.ui.fragment.BaseFragment
import dagger.hilt.android.AndroidEntryPoint
import io.realm.Realm
import io.realm.RealmDictionary
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.FlowCollector
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.oscim.core.GeoPoint
import java.io.File
import java.util.UUID
import javax.inject.Inject
/**

View File

@ -28,6 +28,7 @@ class QsRecordListAdapter(
private val context: Context
) : BaseRecyclerViewAdapter<QsRecordBean>() {
private var itemClickListener: IKotlinItemClickListener? = null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder {
val viewBinding =
@ -46,6 +47,10 @@ class QsRecordListAdapter(
val qsRecordBean = data[position]
//tag 方便onclick里拿到数据
holder.tag = qsRecordBean.id.toString()
// 点击事件
holder.itemView.setOnClickListener {
itemClickListener!!.onItemClickListener(position)
}
changeViews(binding, qsRecordBean)
}
@ -59,6 +64,16 @@ class QsRecordListAdapter(
override fun getItemViewRes(position: Int): Int {
return R.layout.adapter_qs_record_list
}
// 提供set方法
fun setOnKotlinItemClickListener(itemClickListener: IKotlinItemClickListener) {
this.itemClickListener = itemClickListener
}
//自定义接口
interface IKotlinItemClickListener {
fun onItemClickListener(position: Int)
}
}

View File

@ -5,13 +5,17 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView.VERTICAL
import com.blankj.utilcode.util.ToastUtils
import com.navinfo.omqs.R
import com.navinfo.omqs.databinding.FragmentQsRecordListBinding
import com.navinfo.omqs.ui.activity.map.MainActivity
import com.navinfo.omqs.ui.fragment.BaseFragment
import com.navinfo.omqs.ui.fragment.tasklist.QsRecordListAdapter
import com.navinfo.omqs.ui.other.BaseToast.makeText
import com.navinfo.omqs.ui.widget.RecycleViewDivider
import dagger.hilt.android.AndroidEntryPoint
import org.apache.poi.xwpf.usermodel.VerticalAlign
@ -51,6 +55,18 @@ class QsRecordListFragment : BaseFragment(){
itemDecoration.setDrawable(resources.getDrawable(R.drawable.separator))
binding.qsRecyclerview.addItemDecoration(itemDecoration)
viewModel.getList(requireContext())
// itemClick
adapter!!.setOnKotlinItemClickListener(object : QsRecordListAdapter.IKotlinItemClickListener {
override fun onItemClickListener(position: Int) {
viewModel.onItemClickListener(activity as MainActivity,position)
findNavController().popBackStack()
}
})
}
override fun onResume() {
super.onResume()
viewModel.getList(requireContext())
}
override fun onDestroyView() {

View File

@ -1,11 +1,16 @@
package com.navinfo.omqs.ui.fragment.qsrecordlist
import android.content.Context
import android.os.Bundle
import android.util.Log
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.navigation.findNavController
import com.blankj.utilcode.util.ToastUtils
import com.navinfo.collect.library.data.entity.QsRecordBean
import com.navinfo.omqs.R
import com.navinfo.omqs.ui.activity.map.MainActivity
import dagger.hilt.android.lifecycle.HiltViewModel
import io.realm.Realm
import kotlinx.coroutines.Dispatchers
@ -27,4 +32,11 @@ class QsRecordListViewModel @Inject constructor(
}
}
fun onItemClickListener(activity: MainActivity, position :Int){
val naviController = activity.findNavController(R.id.main_activity_right_fragment)
val bundle = Bundle()
bundle.putString("QsId", liveDataQSList.value?.get(position)?.id)
naviController.navigate(R.id.EvaluationResultFragment, bundle)
ToastUtils.showLong(liveDataQSList.value?.get(position)?.classType)
}
}

View File

@ -13,6 +13,8 @@ import com.navinfo.omqs.http.taskdownload.TaskDownloadManager
import com.navinfo.omqs.tools.FileManager.Companion.FileDownloadStatus
import com.navinfo.omqs.ui.other.BaseRecyclerViewAdapter
import com.navinfo.omqs.ui.other.BaseViewHolder
import java.io.File
import javax.inject.Inject
/**
* 离线地图城市列表 RecyclerView 适配器
@ -31,7 +33,7 @@ class TaskListAdapter(
if (it.tag != null) {
val taskBean = data[it.tag as Int]
when (taskBean.status) {
FileDownloadStatus.NONE, FileDownloadStatus.UPDATE, FileDownloadStatus.PAUSE, FileDownloadStatus.ERROR -> {
FileDownloadStatus.NONE, FileDownloadStatus.UPDATE, FileDownloadStatus.PAUSE, FileDownloadStatus.IMPORT, FileDownloadStatus.ERROR -> {
Log.e("jingo", "开始下载 ${taskBean.status}")
downloadManager.start(taskBean.id)
}
@ -125,6 +127,29 @@ class TaskListAdapter(
View.INVISIBLE
binding.taskDownloadBtn.text = "更新"
}
FileDownloadStatus.IMPORTING -> {
if (binding.taskProgress.visibility != View.VISIBLE) binding.taskProgress.visibility =
View.VISIBLE
binding.taskDownloadBtn.text = "安装中"
val split = taskBean.message.split("/")
if (split.size == 2) {
try {
val index = split[0].toInt()
val count = split[1].toInt()
binding.taskProgress.progress =
index * 100 / count
} catch (e: Exception) {
Log.e("jingo", "更新进度条 $e")
}
} else {
binding.taskProgress.progress = 0
}
}
FileDownloadStatus.IMPORT -> {
if (binding.taskProgress.visibility != View.VISIBLE) binding.taskProgress.visibility =
View.INVISIBLE
binding.taskDownloadBtn.text = "安装"
}
}
}

View File

@ -30,6 +30,7 @@ class TaskListFragment : BaseFragment(){
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
downloadManager.init(requireContext())
_binding = FragmentTaskListBinding.inflate(inflater, container, false)
return binding.root

View File

@ -27,12 +27,28 @@ class TaskListViewModel @Inject constructor(
fun getTaskList(context: Context) {
viewModelScope.launch(Dispatchers.IO) {
val realm = Realm.getDefaultInstance()
Log.e("jingo","realm hashCOde ${realm.hashCode()}")
Log.e("jingo", "realm hashCOde ${realm.hashCode()}")
var taskList: List<TaskBean> = mutableListOf()
when (val result = networkService.getTaskList("02911")) {
is NetResult.Success -> {
if (result.data != null) {
realm.executeTransaction {
realm.copyToRealmOrUpdate(result.data.obj)
result.data.obj?.let { list ->
for (task in list) {
val item = realm.where(TaskBean::class.java).equalTo(
"id", task.id
).findFirst()
if (item != null) {
task.fileSize = item.fileSize
Log.e("jingo", "当前文件大小 ${task.fileSize}")
task.status = item.status
task.currentSize = item.currentSize
}
realm.copyToRealmOrUpdate(task)
}
}
val objects = realm.where(TaskBean::class.java).findAll()
taskList = realm.copyFromRealm(objects)
}
}
}
@ -51,9 +67,8 @@ class TaskListViewModel @Inject constructor(
is NetResult.Loading -> {}
else -> {}
}
val objects = realm.where(TaskBean::class.java).findAll()
val taskList = realm.copyFromRealm(objects)
for(item in taskList){
for (item in taskList) {
FileManager.checkOMDBFileInfo(item)
}
liveDataTaskList.postValue(taskList)

View File

@ -8,6 +8,7 @@
android:paddingLeft="10dp"
android:paddingTop="5dp"
android:paddingRight="10dp"
android:paddingBottom="5dp"
tools:context="com.navinfo.omqs.ui.fragment.tasklist.TaskListAdapter">
<TextView

View File

@ -28,9 +28,7 @@ android {
arguments = ["room.schemaLocation": "$projectDir/schemas".toString()]
}
}
ndk {
abiFilters "armeabi", "armeabi-v7a", "arm64-v8a"
}
}
buildTypes {
@ -60,7 +58,6 @@ dependencies {
api fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
api files('libs/BaiduLBS_Android.jar')
//
api 'androidx.core:core-ktx:1.9.0'
api 'androidx.appcompat:appcompat:1.6.1'
api 'com.google.android.material:material:1.8.0'

View File

@ -4,7 +4,6 @@ import com.navinfo.collect.library.utils.GeometryToolsKt
import io.realm.RealmObject
import io.realm.RealmSet
import io.realm.annotations.PrimaryKey
import io.realm.annotations.RealmClass
/**

View File

@ -75,7 +75,7 @@ class LocationLayerHandler(context: AppCompatActivity, mapView: NIMapView) : Bas
//可选默认gcj02设置返回的定位结果坐标系如果配合百度地图使用建议设置为bd09ll;
locationOption.setCoorType("gcj02")
//可选默认0即仅定位一次设置发起连续定位请求的间隔需要大于等于1000ms才是有效的
locationOption.setScanSpan(1000)
locationOption.setScanSpan(1200)
//可选,设置是否需要地址信息,默认不需要
locationOption.setIsNeedAddress(false)
//可选,设置是否需要地址描述
@ -97,11 +97,11 @@ class LocationLayerHandler(context: AppCompatActivity, mapView: NIMapView) : Bas
//可选默认false设置定位时是否需要海拔信息默认不需要除基础定位版本都可用
locationOption.setIsNeedAltitude(true)
//设置打开自动回调位置模式该开关打开后期间只要定位SDK检测到位置变化就会主动回调给开发者该模式下开发者无需再关心定位间隔是多少定位SDK本身发现位置变化就会及时回调给开发者
// locationOption.setOpenAutoNotifyMode()
locationOption.setOpenAutoNotifyMode()
//设置打开自动回调位置模式该开关打开后期间只要定位SDK检测到位置变化就会主动回调给开发者
locationOption.setOpenAutoNotifyMode(
/* locationOption.setOpenAutoNotifyMode(
5, 1, LocationClientOption.LOC_SENSITIVITY_HIGHT
)
)*/
//需将配置好的LocationClientOption对象通过setLocOption方法传递给LocationClient对象使用
locationClient.locOption = locationOption
} catch (e: Throwable) {