diff --git a/app/build.gradle b/app/build.gradle index a6212aa7..798067aa 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -4,6 +4,7 @@ plugins { id 'kotlin-kapt' id 'com.google.dagger.hilt.android' id 'realm-android' + id 'kotlin-parcelize' // 序列化 } android { namespace 'com.navinfo.omqs' @@ -11,7 +12,7 @@ android { defaultConfig { applicationId "com.navinfo.omqs" - minSdk 24 + minSdk 26 targetSdk 33 versionCode 1 versionName "1.0" @@ -36,17 +37,16 @@ android { viewBinding true dataBinding true } + } + + dependencies { implementation project(':collect-library') - implementation 'androidx.core:core-ktx:1.7.0' - implementation 'androidx.appcompat:appcompat:1.6.1' - implementation 'com.google.android.material:material:1.5.0' - implementation 'androidx.constraintlayout:constraintlayout:2.1.4' - implementation 'androidx.navigation:navigation-fragment-ktx:2.5.3' - implementation 'androidx.navigation:navigation-ui-ktx:2.5.3' + + testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' @@ -55,17 +55,26 @@ dependencies { // 文件管理 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' + implementation 'com.blankj:utilcodex:1.31.1' //依赖注入 //hilt - implementation "com.google.dagger:hilt-android:2.44" - kapt "com.google.dagger:hilt-compiler:2.44" + implementation 'com.google.dagger:hilt-android:2.45' + kapt 'com.google.dagger:hilt-compiler:2.45' // Retrofit 网络请求相关 implementation("com.squareup.retrofit2:retrofit:2.9.0") implementation("com.squareup.retrofit2:converter-gson:2.9.0") - implementation("com.squareup.okhttp3:okhttp:4.9.0") - implementation("com.squareup.okhttp3:logging-interceptor:4.9.0") + implementation('com.squareup.okhttp3:okhttp:5.0.0-alpha.11') + implementation('com.squareup.okhttp3:logging-interceptor:5.0.0-alpha.11') + + kapt 'android.arch.persistence.room:compiler:1.1.1'// compiler 需要用 room 的 + kapt "androidx.room:room-compiler:2.5.1" + 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' + } //允许引用生成的代码 kapt { diff --git a/app/src/main/java/com/navinfo/omqs/OMQSApplication.kt b/app/src/main/java/com/navinfo/omqs/OMQSApplication.kt index 27e26fa3..a021fc6b 100644 --- a/app/src/main/java/com/navinfo/omqs/OMQSApplication.kt +++ b/app/src/main/java/com/navinfo/omqs/OMQSApplication.kt @@ -2,9 +2,11 @@ package com.navinfo.omqs import android.app.Application import android.util.Log +import com.navinfo.omqs.db.MyRealmModule import com.navinfo.omqs.tools.FileManager import dagger.hilt.android.HiltAndroidApp import io.realm.Realm +import io.realm.RealmConfiguration import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import java.io.File @@ -12,7 +14,19 @@ import java.io.File @HiltAndroidApp class OMQSApplication : Application() { override fun onCreate() { - FileManager.initRootDir(this) super.onCreate() + FileManager.initRootDir(this) + Realm.init(this) + 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") + .modules(Realm.getDefaultModule(), MyRealmModule()) + .schemaVersion(1) +// .encryptionKey(password) + .build() + Realm.setDefaultConfiguration(config) } } \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/bean/OfflineMapCityBean.kt b/app/src/main/java/com/navinfo/omqs/bean/OfflineMapCityBean.kt new file mode 100644 index 00000000..cb269e60 --- /dev/null +++ b/app/src/main/java/com/navinfo/omqs/bean/OfflineMapCityBean.kt @@ -0,0 +1,57 @@ +package com.navinfo.omqs.bean + +import android.os.Parcelable +import androidx.room.Entity +import androidx.room.PrimaryKey +import kotlinx.parcelize.Parcelize + + +@Entity(tableName = "OfflineMapCity") +@Parcelize +data 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 +) : Parcelable { + + 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) + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/bean/ScProblemTypeBean.kt b/app/src/main/java/com/navinfo/omqs/bean/ScProblemTypeBean.kt new file mode 100644 index 00000000..4ce71de7 --- /dev/null +++ b/app/src/main/java/com/navinfo/omqs/bean/ScProblemTypeBean.kt @@ -0,0 +1,30 @@ +package com.navinfo.omqs.bean + +import android.os.Parcelable +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import kotlinx.parcelize.Parcelize + +@Entity(tableName = "ScProblemType") +@Parcelize +data class ScProblemTypeBean( + @PrimaryKey(autoGenerate = true) + var id: Long = 0, + /** + * 问题分类 + */ + @ColumnInfo("CLASS_TYPE") + var classType: String = "", + /** + * 问题类型 + */ + @ColumnInfo("TYPE") + var problemType: String = "", + /** + * 问题现象 + */ + @ColumnInfo("PHENOMENON") + var phenomenon: String = "" + +) : Parcelable \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/bean/ScRootCauseAnalysisBean.kt b/app/src/main/java/com/navinfo/omqs/bean/ScRootCauseAnalysisBean.kt new file mode 100644 index 00000000..221aeb89 --- /dev/null +++ b/app/src/main/java/com/navinfo/omqs/bean/ScRootCauseAnalysisBean.kt @@ -0,0 +1,24 @@ +package com.navinfo.omqs.bean + +import android.os.Parcelable +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import kotlinx.parcelize.Parcelize + +@Entity(tableName = "ScRootCauseAnalysis") +@Parcelize +data class ScRootCauseAnalysisBean( + @PrimaryKey(autoGenerate = true) + var id: Long = 0, + /** + * 问题环节 + */ + @ColumnInfo("PROBLEM_LINK") + var problemLink: String = "", + /** + * 问题原因 + */ + @ColumnInfo("PROBLEM_CAUSE") + var problemCause: String = "", +) : Parcelable \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/db/MyRealmModule.kt b/app/src/main/java/com/navinfo/omqs/db/MyRealmModule.kt new file mode 100644 index 00000000..f3d98286 --- /dev/null +++ b/app/src/main/java/com/navinfo/omqs/db/MyRealmModule.kt @@ -0,0 +1,7 @@ +package com.navinfo.omqs.db + +import com.navinfo.collect.library.data.entity.QsRecordBean + +@io.realm.annotations.RealmModule(classes = [QsRecordBean::class]) +class MyRealmModule { +} \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/db/RoomAppDatabase.kt b/app/src/main/java/com/navinfo/omqs/db/RoomAppDatabase.kt new file mode 100644 index 00000000..260df186 --- /dev/null +++ b/app/src/main/java/com/navinfo/omqs/db/RoomAppDatabase.kt @@ -0,0 +1,21 @@ +package com.navinfo.omqs.db + +import androidx.room.Database +import androidx.room.RoomDatabase +import com.navinfo.omqs.bean.OfflineMapCityBean +import com.navinfo.omqs.bean.ScProblemTypeBean +import com.navinfo.omqs.bean.ScRootCauseAnalysisBean +import com.navinfo.omqs.db.dao.OfflineMapDao +import com.navinfo.omqs.db.dao.ScProblemTypeDao +import com.navinfo.omqs.db.dao.ScRootCauseAnalysisDao + +@Database( + entities = [OfflineMapCityBean::class, ScProblemTypeBean::class, ScRootCauseAnalysisBean::class], + version = 1, + exportSchema = false +) +abstract class RoomAppDatabase : RoomDatabase() { + abstract fun getOfflineMapDao(): OfflineMapDao + abstract fun getScProblemTypeDao(): ScProblemTypeDao + abstract fun getScRootCauseAnalysisDao(): ScRootCauseAnalysisDao +} \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/db/dao/OfflineMapDao.kt b/app/src/main/java/com/navinfo/omqs/db/dao/OfflineMapDao.kt new file mode 100644 index 00000000..f1256c9c --- /dev/null +++ b/app/src/main/java/com/navinfo/omqs/db/dao/OfflineMapDao.kt @@ -0,0 +1,23 @@ +package com.navinfo.omqs.db.dao + +import androidx.room.* +import com.navinfo.omqs.bean.OfflineMapCityBean + +@Dao +interface OfflineMapDao { + + @Insert + suspend fun insert(message: OfflineMapCityBean): Long + + @Update(onConflict = OnConflictStrategy.REPLACE) + suspend fun update(message: OfflineMapCityBean) + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insertOrUpdate(list: List) + + @Query("select * from OfflineMapCity order by id") + suspend fun getOfflineMapList(): List + + @Query("select * from OfflineMapCity where status != 0 order by id") + suspend fun getOfflineMapListWithOutNone(): List +} \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/db/dao/ScProblemTypeDao.kt b/app/src/main/java/com/navinfo/omqs/db/dao/ScProblemTypeDao.kt new file mode 100644 index 00000000..72c049ba --- /dev/null +++ b/app/src/main/java/com/navinfo/omqs/db/dao/ScProblemTypeDao.kt @@ -0,0 +1,48 @@ +package com.navinfo.omqs.db.dao + +import androidx.room.* +import com.navinfo.omqs.bean.ScProblemTypeBean + + +@Dao +interface ScProblemTypeDao { +// @Insert(onConflict = OnConflictStrategy.REPLACE) +// suspend fun insert(bean: ScProblemTypeBean): Long + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insertList(list: List) + + + @Query("delete from ScProblemType") + suspend fun deleteAll() + + /** + * 更新整个数据库表,由于没有 + */ + @Transaction + suspend fun insertOrUpdateList(list: List) { + //先删除 + deleteAll() + //后插入 + insertList(list) + } + + /** + * 获取问题分类,并去重 + */ + @Query("select DISTINCT CLASS_TYPE from ScProblemType order by CLASS_TYPE") + suspend fun findClassTypeList(): List? + + /** + * 获取问题类型,并去重 + */ + @Query("select * from ScProblemType where CLASS_TYPE=:type order by TYPE") + suspend fun findProblemTypeList(type: String): List? + +// /** +// * +// */ +// @Query("select PHENOMENON from ScProblemType where CLASS_TYPE=:classType and TYPE=:type order by PHENOMENON") +// suspend fun getPhenomenonList(classType: String, type: String): List + +} \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/db/dao/ScRootCauseAnalysisDao.kt b/app/src/main/java/com/navinfo/omqs/db/dao/ScRootCauseAnalysisDao.kt new file mode 100644 index 00000000..9fd40dc8 --- /dev/null +++ b/app/src/main/java/com/navinfo/omqs/db/dao/ScRootCauseAnalysisDao.kt @@ -0,0 +1,31 @@ +package com.navinfo.omqs.db.dao + +import androidx.room.* +import com.navinfo.omqs.bean.ScRootCauseAnalysisBean + + +@Dao +interface ScRootCauseAnalysisDao { + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insertList(list: List) + + + @Query("delete from ScRootCauseAnalysis") + suspend fun deleteAll() + + @Transaction + suspend fun insertOrUpdateList(list: List) { + //先删除 + deleteAll() + //后插入 + insertList(list) + } + + /** + * 获取问题环节数据 + */ + @Query("select * from ScRootCauseAnalysis order by PROBLEM_LINK") + suspend fun findAllData(): List? + +} \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/hilt/GlobalModule.kt b/app/src/main/java/com/navinfo/omqs/hilt/GlobalModule.kt index 4ace2b64..c3478149 100644 --- a/app/src/main/java/com/navinfo/omqs/hilt/GlobalModule.kt +++ b/app/src/main/java/com/navinfo/omqs/hilt/GlobalModule.kt @@ -1,12 +1,16 @@ package com.navinfo.omqs.hilt import android.app.Application +import android.content.Context import android.util.Log +import androidx.room.Room import com.google.gson.Gson import com.navinfo.omqs.Constant import com.navinfo.omqs.OMQSApplication +import com.navinfo.omqs.db.RoomAppDatabase import com.navinfo.omqs.http.RetrofitNetworkServiceAPI -import com.navinfo.omqs.tools.RealmCoroutineScope +import com.tencent.wcdb.database.SQLiteCipherSpec +import com.tencent.wcdb.room.db.WCDBOpenHelperFactory import dagger.Lazy import dagger.Module import dagger.Provides @@ -95,12 +99,41 @@ class GlobalModule { return retrofit.create(RetrofitNetworkServiceAPI::class.java) } - /** - * realm 注册 - */ - @Provides @Singleton - fun provideRealmService(context: Application): RealmCoroutineScope { - return RealmCoroutineScope(context) + @Provides + fun provideDatabase(context: Application): RoomAppDatabase { + val DB_PASSWORD = "123456"; + val cipherSpec = SQLiteCipherSpec() + .setPageSize(1024) + .setSQLCipherVersion(3) + val factory = WCDBOpenHelperFactory() +// .passphrase(DB_PASSWORD.toByteArray()) // passphrase to the database, remove this line for plain-text + .cipherSpec(cipherSpec) // cipher to use, remove for default settings + .writeAheadLoggingEnabled(true) // enable WAL mode, remove if not needed + .asyncCheckpointEnabled(true); // enable asynchronous checkpoint, remove if not needed + + return Room.databaseBuilder( + context, + RoomAppDatabase::class.java, + "${Constant.DATA_PATH}/omqs.db" + ) + + // [WCDB] Specify open helper to use WCDB database implementation instead + // of the Android framework. + .openHelperFactory(factory) + + // Wipes and rebuilds instead of migrating if no Migration object. + // Migration is not part of this codelab. +// .fallbackToDestructiveMigration().addCallback(sRoomDatabaseCallback) + .build(); } + +// /** +// * realm 注册 +// */ +// @Provides +// @Singleton +// fun provideRealmService(context: Application): RealmCoroutineScope { +// return RealmCoroutineScope(context) +// } } \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/hilt/MainActivityModule.kt b/app/src/main/java/com/navinfo/omqs/hilt/MainActivityModule.kt index 8560c38b..f5ba69a6 100644 --- a/app/src/main/java/com/navinfo/omqs/hilt/MainActivityModule.kt +++ b/app/src/main/java/com/navinfo/omqs/hilt/MainActivityModule.kt @@ -1,9 +1,9 @@ package com.navinfo.omqs.hilt import com.navinfo.collect.library.map.NIMapController +import com.navinfo.omqs.db.RoomAppDatabase 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 @@ -28,12 +28,14 @@ class MainActivityModule { @Provides fun providesOfflineMapDownloadManager( networkServiceAPI: RetrofitNetworkServiceAPI, - realmManager: RealmCoroutineScope + roomAppDatabase: RoomAppDatabase, + mapController: NIMapController ): OfflineMapDownloadManager = - OfflineMapDownloadManager(networkServiceAPI, realmManager) + OfflineMapDownloadManager(networkServiceAPI, roomAppDatabase, mapController) /** * 实验失败,这样创建,viewmodel不会在activity销毁的时候同时销毁 + * 4-14:因为没有传入activity的 owner,无法检测生命周期, */ // @ActivityRetainedScoped // @Provides diff --git a/app/src/main/java/com/navinfo/omqs/http/NetworkService.kt b/app/src/main/java/com/navinfo/omqs/http/NetworkService.kt index eb4a6103..fde1d736 100644 --- a/app/src/main/java/com/navinfo/omqs/http/NetworkService.kt +++ b/app/src/main/java/com/navinfo/omqs/http/NetworkService.kt @@ -1,6 +1,6 @@ package com.navinfo.omqs.http -import com.navinfo.collect.library.data.entity.OfflineMapCityBean +import com.navinfo.omqs.bean.OfflineMapCityBean /** diff --git a/app/src/main/java/com/navinfo/omqs/http/NetworkServiceImpl.kt b/app/src/main/java/com/navinfo/omqs/http/NetworkServiceImpl.kt index bbcafc3e..4a67bf83 100644 --- a/app/src/main/java/com/navinfo/omqs/http/NetworkServiceImpl.kt +++ b/app/src/main/java/com/navinfo/omqs/http/NetworkServiceImpl.kt @@ -1,6 +1,6 @@ package com.navinfo.omqs.http -import com.navinfo.collect.library.data.entity.OfflineMapCityBean +import com.navinfo.omqs.bean.OfflineMapCityBean import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import javax.inject.Inject diff --git a/app/src/main/java/com/navinfo/omqs/http/RetrofitNetworkServiceAPI.kt b/app/src/main/java/com/navinfo/omqs/http/RetrofitNetworkServiceAPI.kt index 4bea1858..c4463b6d 100644 --- a/app/src/main/java/com/navinfo/omqs/http/RetrofitNetworkServiceAPI.kt +++ b/app/src/main/java/com/navinfo/omqs/http/RetrofitNetworkServiceAPI.kt @@ -1,6 +1,6 @@ package com.navinfo.omqs.http -import com.navinfo.collect.library.data.entity.OfflineMapCityBean +import com.navinfo.omqs.bean.OfflineMapCityBean import okhttp3.ResponseBody import retrofit2.Response import retrofit2.http.GET diff --git a/app/src/main/java/com/navinfo/omqs/http/offlinemapdownload/OfflineMapDownloadManager.kt b/app/src/main/java/com/navinfo/omqs/http/offlinemapdownload/OfflineMapDownloadManager.kt index e4aa57e1..87907e6e 100644 --- a/app/src/main/java/com/navinfo/omqs/http/offlinemapdownload/OfflineMapDownloadManager.kt +++ b/app/src/main/java/com/navinfo/omqs/http/offlinemapdownload/OfflineMapDownloadManager.kt @@ -2,16 +2,19 @@ package com.navinfo.omqs.http.offlinemapdownload import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.Observer -import com.navinfo.collect.library.data.entity.OfflineMapCityBean +import com.navinfo.collect.library.map.NIMapController +import com.navinfo.omqs.db.RoomAppDatabase +import com.navinfo.omqs.bean.OfflineMapCityBean import com.navinfo.omqs.http.RetrofitNetworkServiceAPI -import com.navinfo.omqs.tools.RealmCoroutineScope import java.util.concurrent.ConcurrentHashMap /** * 管理离线地图下载 */ class OfflineMapDownloadManager( - val netApi: RetrofitNetworkServiceAPI, val realmManager: RealmCoroutineScope + val netApi: RetrofitNetworkServiceAPI, + val roomDatabase: RoomAppDatabase, + val mapController: NIMapController ) { /** * 最多同时下载数量 diff --git a/app/src/main/java/com/navinfo/omqs/http/offlinemapdownload/OfflineMapDownloadScope.kt b/app/src/main/java/com/navinfo/omqs/http/offlinemapdownload/OfflineMapDownloadScope.kt index 0821f8c0..0e133dff 100644 --- a/app/src/main/java/com/navinfo/omqs/http/offlinemapdownload/OfflineMapDownloadScope.kt +++ b/app/src/main/java/com/navinfo/omqs/http/offlinemapdownload/OfflineMapDownloadScope.kt @@ -4,8 +4,8 @@ import android.util.Log import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.MutableLiveData import androidx.lifecycle.Observer -import com.navinfo.collect.library.data.entity.OfflineMapCityBean import com.navinfo.omqs.Constant +import com.navinfo.omqs.bean.OfflineMapCityBean import kotlinx.coroutines.* import java.io.File import java.io.IOException @@ -32,8 +32,8 @@ class OfflineMapDownloadScope( /** * 管理观察者,同时只有一个就行了 */ -// private var observer: Observer? = null - private var lifecycleOwner: LifecycleOwner? = null + private val observer = Observer {} +// private var lifecycleOwner: LifecycleOwner? = null /** *通知UI更新 @@ -89,10 +89,10 @@ class OfflineMapDownloadScope( if (cityBean.status != status || status == OfflineMapCityBean.LOADING) { cityBean.status = status downloadData.postValue(cityBean) - - downloadManager.realmManager.launch { - downloadManager.realmManager.insertOrUpdate(cityBean) + launch(Dispatchers.IO) { + downloadManager.roomDatabase.getOfflineMapDao().update(cityBean) } + } } @@ -101,7 +101,7 @@ class OfflineMapDownloadScope( */ fun observer(owner: LifecycleOwner, ob: Observer) { removeObserver() - this.lifecycleOwner = owner +// this.lifecycleOwner = owner downloadData.observe(owner, ob) } @@ -156,6 +156,9 @@ class OfflineMapDownloadScope( fileTemp.renameTo(File("${Constant.OFFLINE_MAP_PATH}${cityBean.fileName}")) Log.e("jingo", "文件下载完成 修改文件 $res") change(OfflineMapCityBean.DONE) + withContext(Dispatchers.Main) { + downloadManager.mapController.layerManagerHandler.loadBaseMap() + } } else { change(OfflineMapCityBean.PAUSE) } @@ -168,9 +171,10 @@ class OfflineMapDownloadScope( } fun removeObserver() { - lifecycleOwner?.let { - downloadData.removeObservers(it) - null - } + downloadData.observeForever(observer) +// lifecycleOwner?.let { + downloadData.removeObserver(observer) +// null +// } } } \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/tools/FileManager.kt b/app/src/main/java/com/navinfo/omqs/tools/FileManager.kt index e40972ee..0f0c0244 100644 --- a/app/src/main/java/com/navinfo/omqs/tools/FileManager.kt +++ b/app/src/main/java/com/navinfo/omqs/tools/FileManager.kt @@ -1,9 +1,8 @@ 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 com.navinfo.omqs.bean.OfflineMapCityBean import java.io.File class FileManager { diff --git a/app/src/main/java/com/navinfo/omqs/tools/RealmCoroutineScope.kt b/app/src/main/java/com/navinfo/omqs/tools/RealmCoroutineScope.kt index 70880896..e3f6a76f 100644 --- a/app/src/main/java/com/navinfo/omqs/tools/RealmCoroutineScope.kt +++ b/app/src/main/java/com/navinfo/omqs/tools/RealmCoroutineScope.kt @@ -1,57 +1,47 @@ -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 { - var list: List = mutableListOf() - realm.executeTransaction { - val objects = realm.where().findAll().sort("id", Sort.ASCENDING) - list = realm.copyFromRealm(objects) - } - return list - } - - suspend fun insertOrUpdate(objects: Collection?) { - realm.executeTransaction { - realm.insertOrUpdate(objects) - } - } - - suspend fun insertOrUpdate(realmModel: RealmModel?) { - realm.executeTransaction { - realm.insertOrUpdate(realmModel) - } - } - -} \ No newline at end of file +//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 = Realm.getDefaultInstance() +// } +// } +// +// suspend fun getOfflineCityList(): List { +// var list: List = mutableListOf() +// realm.executeTransaction { +// val objects = realm.where().findAll().sort("id", Sort.ASCENDING) +// list = realm.copyFromRealm(objects) +// } +// return list +// } +// +// suspend fun insertOrUpdate(objects: Collection?) { +// realm.executeTransaction { +// realm.insertOrUpdate(objects) +// } +// } +// +// suspend fun insertOrUpdate(realmModel: RealmModel?) { +// realm.executeTransaction { +// realm.insertOrUpdate(realmModel) +// } +// } +// +//} \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/ui/MainActivity.kt b/app/src/main/java/com/navinfo/omqs/ui/MainActivity.kt deleted file mode 100644 index 6bad71e2..00000000 --- a/app/src/main/java/com/navinfo/omqs/ui/MainActivity.kt +++ /dev/null @@ -1,84 +0,0 @@ -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) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/ui/activity/login/LoginActivity.kt b/app/src/main/java/com/navinfo/omqs/ui/activity/login/LoginActivity.kt index 061fe8dc..b28368ba 100644 --- a/app/src/main/java/com/navinfo/omqs/ui/activity/login/LoginActivity.kt +++ b/app/src/main/java/com/navinfo/omqs/ui/activity/login/LoginActivity.kt @@ -58,11 +58,11 @@ class LoginActivity : PermissionsActivity() { loginDialog = null } LoginStatus.LOGIN_STATUS_SUCCESS -> { - val intent = Intent(this@LoginActivity, MainActivity::class.java) - startActivity(intent) -// finish() loginDialog?.dismiss() loginDialog = null + val intent = Intent(this@LoginActivity, MainActivity::class.java) + startActivity(intent) + finish() } LoginStatus.LOGIN_STATUS_CANCEL -> { loginDialog?.dismiss() diff --git a/app/src/main/java/com/navinfo/omqs/ui/activity/login/LoginViewModel.kt b/app/src/main/java/com/navinfo/omqs/ui/activity/login/LoginViewModel.kt index 76496b0d..d0bc2101 100644 --- a/app/src/main/java/com/navinfo/omqs/ui/activity/login/LoginViewModel.kt +++ b/app/src/main/java/com/navinfo/omqs/ui/activity/login/LoginViewModel.kt @@ -8,10 +8,10 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.navinfo.omqs.bean.LoginUserBean +import com.navinfo.omqs.db.RoomAppDatabase 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 @@ -57,7 +57,7 @@ enum class LoginStatus { @HiltViewModel class LoginViewModel @Inject constructor( private val networkService: NetworkService, - private val realmManager: RealmCoroutineScope + private val roomAppDatabase: RoomAppDatabase ) : ViewModel() { //用户信息 val loginUser: MutableLiveData = MutableLiveData() @@ -126,9 +126,7 @@ class LoginViewModel @Inject constructor( for (cityBean in result.data) { FileManager.checkOfflineMapFileInfo(cityBean) } - realmManager.launch { - realmManager.insertOrUpdate(result.data) - } + roomAppDatabase.getOfflineMapDao().insertOrUpdate(result.data) } } is NetResult.Error -> { @@ -143,7 +141,8 @@ class LoginViewModel @Inject constructor( .show() } } - NetResult.Loading -> {} + is NetResult.Loading -> {} + else -> {} } loginStatus.postValue(LoginStatus.LOGIN_STATUS_SUCCESS) } diff --git a/app/src/main/java/com/navinfo/omqs/ui/activity/map/MainActivity.kt b/app/src/main/java/com/navinfo/omqs/ui/activity/map/MainActivity.kt index 4126123b..b28d7c4f 100644 --- a/app/src/main/java/com/navinfo/omqs/ui/activity/map/MainActivity.kt +++ b/app/src/main/java/com/navinfo/omqs/ui/activity/map/MainActivity.kt @@ -6,7 +6,9 @@ import android.util.Log import androidx.activity.viewModels import androidx.core.view.WindowCompat import androidx.databinding.DataBindingUtil +import androidx.lifecycle.lifecycleScope import androidx.lifecycle.viewModelScope +import androidx.navigation.findNavController import com.navinfo.collect.library.map.NIMapController import com.navinfo.omqs.Constant import com.navinfo.omqs.R @@ -28,6 +30,7 @@ class MainActivity : BaseActivity() { //注入地图控制器 @Inject lateinit var mapController: NIMapController + @Inject lateinit var offlineMapDownloadManager: OfflineMapDownloadManager @@ -39,9 +42,9 @@ class MainActivity : BaseActivity() { //初始化地图 mapController.init( this, - binding.mapView.mainActivityMap, + binding.mainActivityMap, null, - Constant.ROOT_PATH + "/map/" + Constant.MAP_PATH ) //关联生命周期 binding.lifecycleOwner = this @@ -50,7 +53,7 @@ class MainActivity : BaseActivity() { //给xml传递viewModel对象 binding.viewModel = viewModel // lifecycle.addObserver(viewModel) - + lifecycleScope } override fun onStart() { @@ -83,4 +86,15 @@ class MainActivity : BaseActivity() { binding.mainActivityDrawer.open() } + /** + * 点击录音按钮 + */ + fun voiceOnclick() { + val naviController = findNavController(R.id.main_activity_right_fragment) + naviController.navigate(R.id.EvaluationResultFragment) + } + + override fun onBackPressed() { + super.onBackPressed() + } } \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/BaseFragment.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/BaseFragment.kt new file mode 100644 index 00000000..a0882082 --- /dev/null +++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/BaseFragment.kt @@ -0,0 +1,57 @@ +package com.navinfo.omqs.ui.fragment + +import android.os.Bundle +import android.view.KeyEvent +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.activity.OnBackPressedCallback +import androidx.fragment.app.Fragment +import androidx.navigation.fragment.findNavController + +abstract class BaseFragment : Fragment() { +// override fun onCreateView( +// inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? +// ): View { +// val view = OnCreateView(inflater, container, savedInstanceState) +// +// view.isFocusableInTouchMode = true; +// view.requestFocus(); +// view.setOnKeyListener { _, keyCode, event -> +// if (keyCode == KeyEvent.KEYCODE_BACK && event.action == KeyEvent.ACTION_DOWN) { +// onBackPressed() +// } +// false +// } +// +// +// return view +// } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + // 获取OnBackPressedDispatcher + val dispatcher = requireActivity().onBackPressedDispatcher + val callback = object : OnBackPressedCallback(true) { + override fun handleOnBackPressed() { + onBackPressed() + } + } + + // 添加返回键事件处理逻辑 + dispatcher.addCallback(this, callback) + } + +// abstract fun OnCreateView( +// inflater: LayoutInflater, +// container: ViewGroup?, +// savedInstanceState: Bundle? +// ): View + + fun onBackPressed(): Boolean{ + findNavController().navigateUp() + return true + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/empty/EmptyFragment.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/empty/EmptyFragment.kt new file mode 100644 index 00000000..9128a761 --- /dev/null +++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/empty/EmptyFragment.kt @@ -0,0 +1,32 @@ +package com.navinfo.omqs.ui.fragment.empty + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import com.navinfo.omqs.databinding.FragmentEmptyBinding + +class EmptyFragment :Fragment(){ + private var _binding: FragmentEmptyBinding? = null + + private val binding get() = _binding!! +// private val viewModel by lazy { viewModels().value} + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + _binding = FragmentEmptyBinding.inflate(inflater, container, false) + return binding.root + + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } +} \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/EvaluationResultFragment.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/EvaluationResultFragment.kt new file mode 100644 index 00000000..4e9013db --- /dev/null +++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/EvaluationResultFragment.kt @@ -0,0 +1,254 @@ +package com.navinfo.omqs.ui.fragment.evaluationresult + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.navigation.NavOptions +import androidx.navigation.findNavController +import com.navinfo.omqs.R +import com.navinfo.omqs.databinding.FragmentEvaluationResultBinding +import com.navinfo.omqs.ui.fragment.BaseFragment +import com.navinfo.omqs.ui.other.shareViewModels +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +class EvaluationResultFragment : BaseFragment(), View.OnClickListener { + private var _binding: FragmentEvaluationResultBinding? = null + private val binding get() = _binding!! + private val viewModel by shareViewModels("QsRecode") + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? + ): View { + _binding = FragmentEvaluationResultBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + liveDataObserve() + + /** + * 点击监听 + */ + binding.evaluationClassType.setOnClickListener(this) + binding.evaluationProblemType.setOnClickListener(this) + binding.evaluationPhenomenon.setOnClickListener(this) + binding.evaluationLink.setOnClickListener(this) + binding.evaluationCause.setOnClickListener(this) + + //返回按钮点击 + binding.evaluationBar.setNavigationOnClickListener { + onBackPressed() + } + //标题栏按钮 + binding.evaluationBar.setOnMenuItemClickListener { + when (it.itemId) { + R.id.save -> { + viewModel.saveData() + true + } + R.id.delete -> { + viewModel.deleteData() + true + } + else -> true + } + } + /** + * 读取元数据 + */ + viewModel.loadMetadata() +// //监听大分类数据变化 +// viewModel.liveDataClassTypeList.observe(viewLifecycleOwner) { +// if (it == null || it.isEmpty()) { +// Toast.makeText(requireContext(), "还没有导入元数据!", Toast.LENGTH_SHORT).show() +// } else { +// binding.evaluationClassType.adapter = +// ArrayAdapter(requireContext(), R.layout.text_item_select, it) +// } +// } +// +// viewModel.liveDataProblemTypeList.observe(viewLifecycleOwner){ +// if (it == null || it.isEmpty()) { +// Toast.makeText(requireContext(), "还没有导入元数据!", Toast.LENGTH_SHORT).show() +// }else{ +// binding.evaluationProblemType.adapter = +// ArrayAdapter(requireContext(), R.layout.text_item_select, it) +// } +// } + +// //选择问题分类的回调 +// binding.evaluationClassType.onItemSelectedListener = +// object : AdapterView.OnItemSelectedListener { +// override fun onItemSelected( +// parent: AdapterView<*>?, view: View?, position: Int, id: Long +// ) { +// viewModel.getProblemTypeList(position) +// } +// +// override fun onNothingSelected(parent: AdapterView<*>?) {} +// } +// /** +// * 监听联动选择的内容 +// */ +// viewModel.problemTypeListLiveData.observe(viewLifecycleOwner) { +// binding.evaluationClassTabLayout.let { tabLayout -> +// tabLayout.removeAllTabs() +// val fragmentList = mutableListOf() +// for (item in it) { +// val tab = tabLayout.newTab() +// tab.text = item +// tabLayout.addTab(tab) +// fragmentList.add(PhenomenonFragment(viewModel.currentClassType, item)) +// } +// phenomenonFragmentAdapter = +// activity?.let { a -> EvaluationResultAdapter(a, fragmentList) } +// binding.evaluationViewpager.adapter = phenomenonFragmentAdapter +// +// TabLayoutMediator( +// binding.evaluationClassTabLayout, +// binding.evaluationViewpager +// ) { tab, position -> +// tab.text = it[position] +// }.attach() +// updateHeight(0) +// } +// +// } + +// binding.evaluationViewpager.registerOnPageChangeCallback(object : +// ViewPager2.OnPageChangeCallback() { +// override fun onPageSelected(position: Int) { +// super.onPageSelected(position) +// updateHeight(position) +// } +// }) + } + + +// private fun updateHeight(position: Int) { +// phenomenonFragmentAdapter?.let { +// if (it.fragmentList.size > position) { +// val fragment: Fragment = it.fragmentList[position] +// if (fragment.view != null) { +// val viewWidth = View.MeasureSpec.makeMeasureSpec( +// fragment.requireView().width, View.MeasureSpec.EXACTLY +// ) +// val viewHeight = +// View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) +// fragment.requireView().measure(viewWidth, viewHeight) +// binding.evaluationViewpager.let { viewpager -> +// if (viewpager.layoutParams.height != fragment.requireView().measuredHeight) { +// //必须要用对象去接收,然后修改该对象再采用该对象,否则无法生效... +// val layoutParams: ViewGroup.LayoutParams = +// viewpager.layoutParams +// layoutParams.height = fragment.requireView().measuredHeight +// viewpager.layoutParams = layoutParams +// } +// } +// +// } +// } +// } +// +// } + + /** + * 监听liveData + */ + private fun liveDataObserve() { + + //监听问题分类,更新UI + viewModel.liveDataCurrentClassType.observe(viewLifecycleOwner) { + binding.evaluationClassType.text = it + } + //监听问题类型,更新UI + viewModel.liveDataCurrentProblemType.observe(viewLifecycleOwner) { + binding.evaluationProblemType.text = it + } + //监听问题现象,更新UI + viewModel.liveDataCurrentPhenomenon.observe(viewLifecycleOwner) { + binding.evaluationPhenomenon.text = it + } + //监听问题环节,更新UI + viewModel.liveDataCurrentProblemLink.observe(viewLifecycleOwner) { + binding.evaluationLink.text = it + } + //监听问题初步原因,更新UI + viewModel.liveDataCurrentCause.observe(viewLifecycleOwner) { + binding.evaluationCause.text = it + } + //监听是否退出当前页面 + viewModel.liveDataFinish.observe(viewLifecycleOwner) { + onBackPressed() + } + } + + override fun onDestroyView() { + activity?.apply { + findNavController(R.id.main_activity_middle_fragment).navigateUp() + } + super.onDestroyView() + _binding = null + } + + /** + * 处理点击事件 + */ + override fun onClick(v: View?) { + v?.let { + when (v.id) { + //上三项,打开面板 + R.id.evaluation_class_type, R.id.evaluation_problem_type, R.id.evaluation_phenomenon -> { + activity?.apply { + val controller = findNavController(R.id.main_activity_middle_fragment) + controller.currentDestination?.let { + //如果之前页面是空fragment,直接打开面板 + if (it.id == R.id.EmptyFragment) { + findNavController( + R.id.main_activity_middle_fragment + ).navigate(R.id.PhenomenonFragment) + } else if (it.id != R.id.PhenomenonFragment) {//不是空fragment,先弹出之前的fragment + findNavController( + R.id.main_activity_middle_fragment + ).navigate( + R.id.PhenomenonFragment, + null, + NavOptions.Builder() + .setPopUpTo(it.id, true).build() + ) + } + } + } + } + //下两项,打开面板 + R.id.evaluation_link, R.id.evaluation_cause -> { + activity?.apply { + val controller = findNavController(R.id.main_activity_middle_fragment) + controller.currentDestination?.let { + //如果之前页面是空fragment,直接打开面板 + if (it.id == R.id.EmptyFragment) { + findNavController( + R.id.main_activity_middle_fragment + ).navigate(R.id.ProblemLinkFragment) + } else if (it.id != R.id.ProblemLinkFragment) {//不是空fragment,先弹出之前的fragment + findNavController( + R.id.main_activity_middle_fragment + ).navigate( + R.id.ProblemLinkFragment, + null, + NavOptions.Builder() + .setPopUpTo(it.id, true).build() + ) + } + } + + } + } + else -> {} + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/EvaluationResultViewModel.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/EvaluationResultViewModel.kt new file mode 100644 index 00000000..a233fb64 --- /dev/null +++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/EvaluationResultViewModel.kt @@ -0,0 +1,242 @@ +package com.navinfo.omqs.ui.fragment.evaluationresult + +import android.util.Log +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.navinfo.collect.library.map.GeoPoint +import com.navinfo.collect.library.map.NIMapController +import com.navinfo.collect.library.data.entity.QsRecordBean +import com.navinfo.omqs.db.RoomAppDatabase +import dagger.hilt.android.lifecycle.HiltViewModel +import io.realm.Realm +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import java.util.* +import javax.inject.Inject + +@HiltViewModel +class EvaluationResultViewModel @Inject constructor( + private val roomAppDatabase: RoomAppDatabase, private val mapController: NIMapController +) : ViewModel() { + + private val markerTitle = "点选marker" + + /** + * 操作结束,销毁页面 + */ + val liveDataFinish = MutableLiveData() + + /** + * 问题分类 liveData,给[PhenomenonLeftAdapter]展示的数据 + */ + val liveDataClassTypeList = MutableLiveData>() + + /** + * 问题类型 liveData 给[PhenomenonMiddleAdapter]展示的数据 + */ + val liveDataProblemTypeList = MutableLiveData>() + + /** + * 问题现象 liveData 给[PhenomenonRightGroupHeaderAdapter]展示的数据 + */ + val liveDataPhenomenonRightList = MutableLiveData>() + + + /** + * 当前选择问题分类 给[EvaluationResultFragment]中 【问题分类】展示数据 + */ + var liveDataCurrentClassType = MutableLiveData() + + /** + * 当前选择的问题类型 给[EvaluationResultFragment]中 【问题类型】展示数据 + */ + var liveDataCurrentProblemType = MutableLiveData() + + /** + * 当前选择的问题现象 给[EvaluationResultFragment]中 【问题现象】展示数据 + */ + var liveDataCurrentPhenomenon = MutableLiveData() + + + /** + * 当前选择的问题环节 给[EvaluationResultFragment]中 【问题环节】展示数据 + */ + var liveDataCurrentProblemLink = MutableLiveData() + + /** + * 当前选择的问初步原因 给[EvaluationResultFragment]中 【初步原因】展示数据 + */ + var liveDataCurrentCause = MutableLiveData() + + var currentGeoPoint: GeoPoint? = null + + + init { + Log.e("jingo", "EvaluationResultViewModel 创建了 ${hashCode()}") + mapController.markerHandle.apply { + setOnMapClickListener { + currentGeoPoint = it + addMarker(it, markerTitle) + } + } + val geoPoint = mapController.locationLayerHandler.getCurrentGeoPoint() + geoPoint?.let { + currentGeoPoint = it + mapController.markerHandle.addMarker(geoPoint, markerTitle) + } + + } + + override fun onCleared() { + super.onCleared() + Log.e("jingo", "EvaluationResultViewModel 销毁了 ${hashCode()}") + mapController.markerHandle.removeMarker(markerTitle) + mapController.markerHandle.removeOnMapClickListener() + } + + + /** + * 查询数据库,获取问题分类 + */ + fun loadMetadata() { + viewModelScope.launch(Dispatchers.IO) { + getClassTypeList() + getProblemLinkList() + } + } + + /** + * //获取问题分类列表 + */ + fun getClassTypeList() { + viewModelScope.launch(Dispatchers.IO) { + val list = roomAppDatabase.getScProblemTypeDao().findClassTypeList() + list?.let { + //通知页面更新 + liveDataClassTypeList.postValue(it) + //如果右侧栏没数据,给个默认值 + if (liveDataCurrentClassType.value == null) { + liveDataCurrentClassType.postValue(it[0]) + } + getProblemList(it[0]) + } + } + + } + + /** + * 获取问题环节列表和初步问题 + */ + fun getProblemLinkList() { + viewModelScope.launch(Dispatchers.IO) { + val list = roomAppDatabase.getScRootCauseAnalysisDao().findAllData() + list?.let { tl -> + if (tl.isNotEmpty()) { + val typeTitleList = mutableListOf() + val phenomenonRightList = mutableListOf() + for (item in tl) { + if (!typeTitleList.contains(item.problemLink)) { + typeTitleList.add(item.problemLink) + } + phenomenonRightList.add( + PhenomenonMiddleBean( + title = item.problemLink, text = item.problemCause, isSelect = false + ) + ) + } + + if (liveDataCurrentProblemLink.value == null) { + liveDataCurrentProblemLink.postValue(phenomenonRightList[0].text) + } + if (liveDataCurrentCause.value == null) { + liveDataCurrentCause.postValue(typeTitleList[0]) + } + liveDataProblemTypeList.postValue(typeTitleList) + liveDataPhenomenonRightList.postValue(phenomenonRightList) + } + } + } + } + + /** + * 获取问题类型列表和问题现象 + */ + private suspend fun getProblemList(classType: String) { + val typeList = roomAppDatabase.getScProblemTypeDao().findProblemTypeList(classType) + typeList?.let { tl -> + if (tl.isNotEmpty()) { + val typeTitleList = mutableListOf() + val phenomenonRightList = mutableListOf() + for (item in tl) { + if (!typeTitleList.contains(item.problemType)) { + typeTitleList.add(item.problemType) + } + phenomenonRightList.add( + PhenomenonMiddleBean( + title = item.problemType, text = item.phenomenon, isSelect = false + ) + ) + } + if (liveDataCurrentPhenomenon.value == null) { + liveDataCurrentPhenomenon.postValue(phenomenonRightList[0].text) + } + if (liveDataCurrentProblemType.value == null) { + liveDataCurrentProblemType.postValue(typeTitleList[0]) + } + liveDataProblemTypeList.postValue(typeTitleList) + liveDataPhenomenonRightList.postValue(phenomenonRightList) + } + } + } + + /** + * 查询问题类型 + */ + fun getProblemTypeList(classType: String) { + viewModelScope.launch(Dispatchers.IO) { + liveDataCurrentClassType.postValue(classType) + getProblemList(classType) + } + } + + fun setPhenomenonMiddleBean(bean: PhenomenonMiddleBean) { + if (liveDataCurrentPhenomenon.value != bean.text) liveDataCurrentPhenomenon.value = + bean.text + if (liveDataCurrentProblemType.value != bean.title) liveDataCurrentProblemType.value = + bean.title + + } + + fun setProblemLinkMiddleBean(bean: PhenomenonMiddleBean) { + if (liveDataCurrentProblemLink.value != bean.text) liveDataCurrentProblemLink.value = + bean.text + if (liveDataCurrentCause.value != bean.title) liveDataCurrentCause.value = bean.title + + } + + fun saveData() { + viewModelScope.launch(Dispatchers.IO) { + val qsRecord = QsRecordBean( + id = UUID.randomUUID().toString(), + classType = liveDataCurrentClassType.value.toString(), + type = liveDataCurrentProblemType.value.toString(), + phenomenon = liveDataCurrentPhenomenon.value.toString(), + problemLink = liveDataCurrentProblemLink.value.toString(), + cause = liveDataCurrentCause.value.toString(), + ) + qsRecord.geometry = currentGeoPoint!!.toGeometry() + val realm = Realm.getDefaultInstance() + realm.executeTransaction { + it.copyToRealmOrUpdate(qsRecord) + } + realm.close() + mapController.mMapView.updateMap() + liveDataFinish.postValue(true) + } + } + + fun deleteData() { + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/PhenomenonFragment.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/PhenomenonFragment.kt new file mode 100644 index 00000000..9eef25da --- /dev/null +++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/PhenomenonFragment.kt @@ -0,0 +1,125 @@ +package com.navinfo.omqs.ui.fragment.evaluationresult + +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.navigation.findNavController +import androidx.navigation.fragment.findNavController +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import androidx.recyclerview.widget.RecyclerView.OnScrollListener +import com.navinfo.omqs.R +import com.navinfo.omqs.databinding.FragmentPhenomenonBinding +import com.navinfo.omqs.ui.fragment.BaseFragment +import com.navinfo.omqs.ui.other.shareViewModels +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +class PhenomenonFragment : + BaseFragment() { + private var _binding: FragmentPhenomenonBinding? = null + private val binding get() = _binding!! + private val viewModel: EvaluationResultViewModel by shareViewModels("QsRecode") + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + _binding = FragmentPhenomenonBinding.inflate(inflater, container, false) + Log.e("jingo", "PhenomenonFragment onCreateView ${hashCode()}") + return binding.root + } + + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + //左侧菜单 + binding.phenomenonLeftRecyclerview.setHasFixedSize(true) + binding.phenomenonLeftRecyclerview.layoutManager = LinearLayoutManager(requireContext()) + val leftAdapter = PhenomenonLeftAdapter { _, text -> + viewModel.getProblemTypeList(text) + } + binding.phenomenonLeftRecyclerview.adapter = leftAdapter + //左侧菜单查询结果监听 + viewModel.liveDataClassTypeList.observe(viewLifecycleOwner) { + leftAdapter.refreshData(it) + } + + //右侧菜单 + binding.phenomenonRightRecyclerview.setHasFixedSize(true) + var rightLayoutManager = LinearLayoutManager(requireContext()) + + binding.phenomenonRightRecyclerview.layoutManager = rightLayoutManager + val rightAdapter = PhenomenonRightGroupHeaderAdapter { _, bean -> + viewModel.setPhenomenonMiddleBean(bean) + } + binding.phenomenonRightRecyclerview.adapter = rightAdapter + //右侧菜单增加组标题 + binding.phenomenonRightRecyclerview.addItemDecoration( + PhenomenonRightGroupHeaderDecoration( + requireContext() + ) + ) + //右侧菜单查询数据监听 + viewModel.liveDataPhenomenonRightList.observe(viewLifecycleOwner) { + rightAdapter.refreshData(it) + } + + val middleAdapter = PhenomenonMiddleAdapter { _, title -> + rightLayoutManager.scrollToPositionWithOffset(rightAdapter.getGroupTopIndex(title), 0) + + } + + binding.phenomenonRightRecyclerview.addOnScrollListener(object : + OnScrollListener() { + override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { + super.onScrollStateChanged(recyclerView, newState) + } + + //findLastVisibleItemPosition() :最后一个可见位置 +// findFirstVisibleItemPosition() :第一个可见位置 +// findLastCompletelyVisibleItemPosition() :最后一个完全可见位置 +// findFirstCompletelyVisibleItemPosition() :第一个完全可见位置 + override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { + super.onScrolled(recyclerView, dx, dy) + val firstIndex = rightLayoutManager.findFirstVisibleItemPosition() + middleAdapter.setRightTitle(rightAdapter.data[firstIndex].title) + } + }) + + + //中间菜单 + binding.phenomenonMiddleRecyclerview.setHasFixedSize(true) + binding.phenomenonMiddleRecyclerview.layoutManager = LinearLayoutManager(requireContext()) + binding.phenomenonMiddleRecyclerview.adapter = middleAdapter + //中间侧菜单查询结果监听 + viewModel.liveDataProblemTypeList.observe(viewLifecycleOwner) { + middleAdapter.refreshData(it) + } + binding.phenomenonDrawer.setOnClickListener { + when (binding.group.visibility) { + View.INVISIBLE, View.GONE -> + binding.group.visibility = View.VISIBLE + else -> + binding.group.visibility = View.GONE + } + } + + viewModel.getClassTypeList() + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + Log.e("jingo", "PhenomenonFragment onDestroyView ${hashCode()}") + } + + override fun onResume() { + super.onResume() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/PhenomenonLeftAdapter.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/PhenomenonLeftAdapter.kt new file mode 100644 index 00000000..572a3c5f --- /dev/null +++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/PhenomenonLeftAdapter.kt @@ -0,0 +1,54 @@ +package com.navinfo.omqs.ui.fragment.evaluationresult + +import android.view.LayoutInflater +import android.view.ViewGroup +import com.navinfo.omqs.R +import com.navinfo.omqs.databinding.TextItemSelectBinding +import com.navinfo.omqs.ui.other.BaseRecyclerViewAdapter +import com.navinfo.omqs.ui.other.BaseViewHolder + +class PhenomenonLeftAdapter(private var itemListener: ((Int, String) -> Unit?)? = null) : + BaseRecyclerViewAdapter() { + private var selectTitle = "" + + override fun getItemViewRes(position: Int): Int { + return R.layout.text_item_select + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder { + val viewBinding = + TextItemSelectBinding.inflate(LayoutInflater.from(parent.context), parent, false) + return BaseViewHolder(viewBinding) + } + + override fun onBindViewHolder(holder: BaseViewHolder, position: Int) { + val bd = holder.viewBinding as TextItemSelectBinding + val title = data[position] + bd.itemId.text = title + if (selectTitle == title) { + bd.itemId.setBackgroundColor(holder.viewBinding.root.context.getColor(R.color.cv_gray_153)) + } else { + bd.itemId.setBackgroundColor(holder.viewBinding.root.context.getColor(R.color.white)) + } + bd.root.setOnClickListener { + if (selectTitle != title) { + selectTitle = title + notifyDataSetChanged() + } + itemListener?.invoke(position, title) + } + } + + override fun refreshData(newData: List) { + data = newData + selectTitle = newData[0] + notifyDataSetChanged() + } + + fun setRightTitle(title: String) { + if (title != selectTitle) { + selectTitle = title + notifyDataSetChanged() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/PhenomenonMiddleAdapter.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/PhenomenonMiddleAdapter.kt new file mode 100644 index 00000000..997b8b83 --- /dev/null +++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/PhenomenonMiddleAdapter.kt @@ -0,0 +1,54 @@ +package com.navinfo.omqs.ui.fragment.evaluationresult + +import android.view.LayoutInflater +import android.view.ViewGroup +import com.navinfo.omqs.R +import com.navinfo.omqs.databinding.TextItemSelectBinding +import com.navinfo.omqs.ui.other.BaseRecyclerViewAdapter +import com.navinfo.omqs.ui.other.BaseViewHolder + +class PhenomenonMiddleAdapter(private var itemListener: ((Int, String) -> Unit?)? = null) : + BaseRecyclerViewAdapter() { + private var selectTitle = "" + + override fun getItemViewRes(position: Int): Int { + return R.layout.text_item_select + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder { + val viewBinding = + TextItemSelectBinding.inflate(LayoutInflater.from(parent.context), parent, false) + return BaseViewHolder(viewBinding) + } + + override fun onBindViewHolder(holder: BaseViewHolder, position: Int) { + val bd = holder.viewBinding as TextItemSelectBinding + val title = data[position] + bd.itemId.text = title + if (selectTitle == title) { + bd.itemId.setBackgroundColor(holder.viewBinding.root.context.getColor(R.color.cv_gray_153)) + } else { + bd.itemId.setBackgroundColor(holder.viewBinding.root.context.getColor(R.color.white)) + } + bd.root.setOnClickListener { + if (selectTitle != title) { + selectTitle = title + notifyDataSetChanged() + } + itemListener?.invoke(position, title) + } + } + + override fun refreshData(newData: List) { + data = newData + selectTitle = newData[0] + notifyDataSetChanged() + } + + fun setRightTitle(title: String) { + if (title != selectTitle) { + selectTitle = title + notifyDataSetChanged() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/PhenomenonMiddleBean.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/PhenomenonMiddleBean.kt new file mode 100644 index 00000000..5f77e434 --- /dev/null +++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/PhenomenonMiddleBean.kt @@ -0,0 +1,6 @@ +package com.navinfo.omqs.ui.fragment.evaluationresult + +/** + * 问题现象列表 + */ +data class PhenomenonMiddleBean(val title: String, val text: String, var isSelect: Boolean = false) diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/PhenomenonRightGroupHeaderAdapter.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/PhenomenonRightGroupHeaderAdapter.kt new file mode 100644 index 00000000..1b57d518 --- /dev/null +++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/PhenomenonRightGroupHeaderAdapter.kt @@ -0,0 +1,103 @@ +package com.navinfo.omqs.ui.fragment.evaluationresult + +import android.view.LayoutInflater +import android.view.ViewGroup +import com.navinfo.omqs.R +import com.navinfo.omqs.databinding.TextItemSelectBinding +import com.navinfo.omqs.ui.other.BaseRecyclerViewAdapter +import com.navinfo.omqs.ui.other.BaseViewHolder + +class PhenomenonRightGroupHeaderAdapter(private var itemListener: ((Int, PhenomenonMiddleBean) -> Unit?)? = null) : + BaseRecyclerViewAdapter() { + private var groupTitleList = mutableListOf() + override fun getItemViewRes(position: Int): Int { + return R.layout.text_item_select + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder { + val viewBinding = + TextItemSelectBinding.inflate(LayoutInflater.from(parent.context), parent, false) + return BaseViewHolder(viewBinding) + } + + override fun onBindViewHolder(holder: BaseViewHolder, position: Int) { + val bd = holder.viewBinding as TextItemSelectBinding + bd.itemId.text = data[position].text + bd.root.setOnClickListener { + itemListener?.invoke(position, data[position]) + } + } + + /** + * 判断position对应的Item是否是组的第一项 + * + * @param position + * @return + */ + fun isItemHeader(position: Int): Boolean { + if (position >= data.size) + return false + return if (position == 0) { + true + } else { + val lastGroupName = data[position - 1].title + val currentGroupName = data[position].title + //判断上一个数据的组别和下一个数据的组别是否一致,如果不一致则是不同组,也就是为第一项(头部) + lastGroupName != currentGroupName + } + } + + fun isLastGroupTitle(position: Int): Boolean { + if (groupTitleList.isNotEmpty() && data[position].title == groupTitleList.last()) { + return true + } + return false + } + + /** + * 获取position对应的Item组名 + * + * @param position + * @return + */ + fun getGroupName(position: Int): String { + return data[position].title + } + + fun getGroupTopIndex(position: Int): Int { + var nowPosition = position + val title = data[position].title + for (i in data.size - 2 downTo 0) { + if (data[i].title == title) { + nowPosition = i + } else { + break + } + } + return nowPosition + } + + fun getGroupTopIndex(title: String): Int { + for (i in data.indices) { + if (data[i].title == title) + return i + } + return 0 + } + + override fun refreshData(newData: List) { + super.refreshData(newData) + groupTitleList.clear() + for (item in newData) { + if (groupTitleList.size > 0) { + if (groupTitleList.last() != item.title) { + groupTitleList.add(item.title) + } + } else { + groupTitleList.add(item.title) + } + } + } + + +} \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/PhenomenonRightGroupHeaderDecoration.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/PhenomenonRightGroupHeaderDecoration.kt new file mode 100644 index 00000000..23b60c68 --- /dev/null +++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/PhenomenonRightGroupHeaderDecoration.kt @@ -0,0 +1,205 @@ +package com.navinfo.omqs.ui.fragment.evaluationresult + +import android.content.Context +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.Paint +import android.graphics.Rect +import android.util.Log +import android.view.View +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import androidx.recyclerview.widget.RecyclerView.ItemDecoration + + +/** + * 自定义装饰器(实现分组+吸顶效果) + */ +class PhenomenonRightGroupHeaderDecoration(context: Context) : ItemDecoration() { + //头部的高 + private val mItemHeaderHeight: Int + private val mTextPaddingLeft: Int + + //画笔,绘制头部和分割线 + private val mItemHeaderPaint: Paint + private val mTextPaint: Paint + private val mLinePaint: Paint + private val mTextRect: Rect + private var lastGroupView: View? = null + + init { + mItemHeaderHeight = dp2px(context, 40f) + mTextPaddingLeft = dp2px(context, 6f) + mTextRect = Rect() + mItemHeaderPaint = Paint(Paint.ANTI_ALIAS_FLAG) + mItemHeaderPaint.color = Color.GRAY + mTextPaint = Paint(Paint.ANTI_ALIAS_FLAG) + mTextPaint.textSize = 46f + mTextPaint.color = Color.WHITE + mLinePaint = Paint(Paint.ANTI_ALIAS_FLAG) + mLinePaint.color = Color.GRAY + } + + /** + * 绘制Item的分割线和组头 + * + * @param c + * @param parent + * @param state + */ + override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) { + if (parent.adapter is PhenomenonRightGroupHeaderAdapter) { + val adapter = parent.adapter as PhenomenonRightGroupHeaderAdapter + val count = parent.childCount //获取可见范围内Item的总数 + for (i in 0 until count) { + val view: View = parent.getChildAt(i) + + val position = parent.getChildLayoutPosition(view) + val isHeader: Boolean = adapter.isItemHeader(position) + val left = parent.paddingLeft + val right = parent.width - parent.paddingRight + if (isHeader) { + c.drawRect( + left.toFloat(), + (view.top - mItemHeaderHeight).toFloat(), + right.toFloat(), + view.top.toFloat(), + mItemHeaderPaint + ) + val text = adapter.getGroupName(position) + mTextPaint.getTextBounds( + text, + 0, + text.length, + mTextRect + ) + c.drawText( + adapter.getGroupName(position), + (left + mTextPaddingLeft).toFloat(), + (view.top - mItemHeaderHeight + mItemHeaderHeight / 2 + mTextRect.height() / 2).toFloat(), + mTextPaint + ) + } else { + c.drawRect( + left.toFloat(), + (view.top - 1).toFloat(), right.toFloat(), + view.top.toFloat(), mLinePaint + ) + + } + } + } + } + + /** + * 绘制Item的顶部布局(吸顶效果) + * + * @param c + * @param parent + * @param state + */ + override fun onDrawOver(c: Canvas, parent: RecyclerView, state: RecyclerView.State) { + if (parent.adapter is PhenomenonRightGroupHeaderAdapter) { + val adapter = parent.adapter as PhenomenonRightGroupHeaderAdapter + val position = + (parent.layoutManager as LinearLayoutManager?)!!.findFirstVisibleItemPosition() + parent.findViewHolderForAdapterPosition(position)?.let { + val view: View = it.itemView + val isHeader: Boolean = adapter.isItemHeader(position + 1) + val top = parent.paddingTop + val left = parent.paddingLeft + val right = parent.width - parent.paddingRight + if (isHeader) { + val bottom = mItemHeaderHeight.coerceAtMost(view.bottom) + c.drawRect( + left.toFloat(), + (top + view.top - mItemHeaderHeight).toFloat(), + right.toFloat(), + (top + bottom).toFloat(), + mItemHeaderPaint + ) + val text = adapter.getGroupName(position) + mTextPaint.getTextBounds( + text, + 0, + text.length, + mTextRect + ) + c.drawText( + adapter.getGroupName(position), + (left + mTextPaddingLeft).toFloat(), + (top + mItemHeaderHeight / 2 + mTextRect.height() / 2 - (mItemHeaderHeight - bottom)).toFloat(), + mTextPaint + ) + } else { + c.drawRect( + left.toFloat(), + top.toFloat(), right.toFloat(), + (top + mItemHeaderHeight).toFloat(), mItemHeaderPaint + ) + val text = adapter.getGroupName(position) + mTextPaint.getTextBounds( + text, + 0, + text.length, + mTextRect + ) + c.drawText( + adapter.getGroupName(position), (left + mTextPaddingLeft).toFloat(), + (top + mItemHeaderHeight / 2 + mTextRect.height() / 2).toFloat(), mTextPaint + ) + + } + } + c.save() + } + } + + /** + * 设置Item的间距 + * + * @param outRect + * @param view + * @param parent + * @param state + */ + override fun getItemOffsets( + outRect: Rect, + view: View, + parent: RecyclerView, + state: RecyclerView.State + ) { + if (parent.adapter is PhenomenonRightGroupHeaderAdapter) { + val adapter = parent.adapter as PhenomenonRightGroupHeaderAdapter + //获取当前view在整个列表中的位置 + val position = parent.getChildLayoutPosition(view) + //是不是改组的第一个 + val isHeader: Boolean = adapter.isItemHeader(position) + if (isHeader) { + outRect.top = mItemHeaderHeight + if (adapter.isLastGroupTitle(position)) { + lastGroupView = view + } + } else if (position == (parent.adapter as PhenomenonRightGroupHeaderAdapter).itemCount - 1) { + //判断这条是不是最后一条 + //如果是最后一个,找到他所在组的第一个 + lastGroupView?.let { + if (it.top > 0) { + outRect.bottom = it.top - it.height * 2 + } + } + outRect.top = 1 + } else { + outRect.top = 1 + } + } + } + + /** + * dp转换成px + */ + private fun dp2px(context: Context, dpValue: Float): Int { + val scale: Float = context.resources.displayMetrics.density + return (dpValue * scale + 0.5f).toInt() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/ProblemLinkFragment.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/ProblemLinkFragment.kt new file mode 100644 index 00000000..d10fe1ef --- /dev/null +++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/ProblemLinkFragment.kt @@ -0,0 +1,99 @@ +package com.navinfo.omqs.ui.fragment.evaluationresult + +import android.os.Bundle +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.navinfo.omqs.databinding.FragmentProblemLinkBinding +import com.navinfo.omqs.ui.fragment.BaseFragment +import com.navinfo.omqs.ui.other.shareViewModels + +class ProblemLinkFragment : BaseFragment() { + private var _binding: FragmentProblemLinkBinding? = null + private val binding get() = _binding!! + private val viewModel: EvaluationResultViewModel by shareViewModels("QsRecode") + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + _binding = FragmentProblemLinkBinding.inflate(inflater, container, false) + Log.e("jingo", "linkFragment onCreateView ${hashCode()}") + return binding.root + } + + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + //右侧菜单 + binding.linkRightRecyclerview.setHasFixedSize(true) + var rightLayoutManager = LinearLayoutManager(requireContext()) + + binding.linkRightRecyclerview.layoutManager = rightLayoutManager + val rightAdapter = PhenomenonRightGroupHeaderAdapter { _, bean -> + viewModel.setProblemLinkMiddleBean(bean) + } + binding.linkRightRecyclerview.adapter = rightAdapter + //右侧菜单增加组标题 + binding.linkRightRecyclerview.addItemDecoration( + PhenomenonRightGroupHeaderDecoration( + requireContext() + ) + ) + //右侧菜单查询数据监听 + viewModel.liveDataPhenomenonRightList.observe(viewLifecycleOwner) { + rightAdapter.refreshData(it) + } + + val middleAdapter = PhenomenonMiddleAdapter { _, title -> + rightLayoutManager.scrollToPositionWithOffset(rightAdapter.getGroupTopIndex(title), 0) + } + + binding.linkRightRecyclerview.addOnScrollListener(object : + RecyclerView.OnScrollListener() { + override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { + super.onScrollStateChanged(recyclerView, newState) + } + + override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { + super.onScrolled(recyclerView, dx, dy) + val firstIndex = rightLayoutManager.findFirstVisibleItemPosition() + middleAdapter.setRightTitle(rightAdapter.data[firstIndex].title) + } + }) + + //中间菜单 + binding.linkMiddleRecyclerview.setHasFixedSize(true) + binding.linkMiddleRecyclerview.layoutManager = LinearLayoutManager(requireContext()) + binding.linkMiddleRecyclerview.adapter = middleAdapter + //中间侧菜单查询结果监听 + viewModel.liveDataProblemTypeList.observe(viewLifecycleOwner) { + middleAdapter.refreshData(it) + } + binding.linkDrawer.setOnClickListener { + when (binding.group.visibility) { + View.INVISIBLE, View.GONE -> + binding.group.visibility = View.VISIBLE + else -> + binding.group.visibility = View.GONE + } + } + + viewModel.getProblemLinkList() + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + Log.e("jingo", "linkFragment onDestroyView ${hashCode()}") + } + + override fun onResume() { + super.onResume() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/offlinemap/OfflineMapAdapter.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/offlinemap/OfflineMapAdapter.kt index f3c863a4..4c1e6786 100644 --- a/app/src/main/java/com/navinfo/omqs/ui/fragment/offlinemap/OfflineMapAdapter.kt +++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/offlinemap/OfflineMapAdapter.kt @@ -3,7 +3,6 @@ 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适配器 diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/offlinemap/OfflineMapCityListAdapter.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/offlinemap/OfflineMapCityListAdapter.kt index d27594bd..0800abce 100644 --- a/app/src/main/java/com/navinfo/omqs/ui/fragment/offlinemap/OfflineMapCityListAdapter.kt +++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/offlinemap/OfflineMapCityListAdapter.kt @@ -6,9 +6,9 @@ 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.databinding.AdapterOfflineMapCityBinding +import com.navinfo.omqs.bean.OfflineMapCityBean import com.navinfo.omqs.http.offlinemapdownload.OfflineMapDownloadManager import com.navinfo.omqs.ui.other.BaseRecyclerViewAdapter import com.navinfo.omqs.ui.other.BaseViewHolder @@ -22,7 +22,7 @@ import javax.inject.Inject *使用 LiveData 的 observeForever 然后在 ViewHolder 销毁前手动调用 removeObserver *使用 LifecycleRegistry 给 ViewHolder 分发生命周期(这里使用了这个) */ -class OfflineMapCityListAdapter @Inject constructor( +class OfflineMapCityListAdapter( private val downloadManager: OfflineMapDownloadManager, private val context: Context ) : BaseRecyclerViewAdapter() { @@ -126,7 +126,7 @@ class OfflineMapCityListAdapter @Inject constructor( } } - override fun getItemViewType(position: Int): Int { + override fun getItemViewRes(position: Int): Int { return R.layout.adapter_offline_map_city } } diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/offlinemap/OfflineMapCityListViewModel.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/offlinemap/OfflineMapCityListViewModel.kt index 1dbd04f0..245a82d7 100644 --- a/app/src/main/java/com/navinfo/omqs/ui/fragment/offlinemap/OfflineMapCityListViewModel.kt +++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/offlinemap/OfflineMapCityListViewModel.kt @@ -4,14 +4,10 @@ import android.content.Context import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.navinfo.collect.library.data.entity.OfflineMapCityBean -import com.navinfo.omqs.tools.FileManager -import com.navinfo.omqs.tools.RealmCoroutineScope +import com.navinfo.omqs.db.RoomAppDatabase +import com.navinfo.omqs.bean.OfflineMapCityBean 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 @@ -22,6 +18,7 @@ import javax.inject.Inject @HiltViewModel class OfflineMapCityListViewModel @Inject constructor( @ApplicationContext val context: Context, + private val roomDatabase: RoomAppDatabase ) : ViewModel() { val cityListLiveData = MutableLiveData>() @@ -31,13 +28,14 @@ class OfflineMapCityListViewModel @Inject constructor( */ fun getCityList() { viewModelScope.launch(Dispatchers.IO) { - val realm = Realm.getDefaultInstance() - val objects = realm.where().findAll().sort("id", Sort.ASCENDING) - val list = realm.copyFromRealm(objects) - realm.close() - for (item in list) { - FileManager.checkOfflineMapFileInfo(item) - } +// val realm = Realm.getDefaultInstance() +// val objects = realm.where().findAll().sort("id", Sort.ASCENDING) +// val list = realm.copyFromRealm(objects) +// realm.close() +// for (item in list) { +// FileManager.checkOfflineMapFileInfo(item) +// } + val list = roomDatabase.getOfflineMapDao().getOfflineMapList() cityListLiveData.postValue(list) } } diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/offlinemap/OfflineMapFragment.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/offlinemap/OfflineMapFragment.kt index 8f9da1e1..9755b2e4 100644 --- a/app/src/main/java/com/navinfo/omqs/ui/fragment/offlinemap/OfflineMapFragment.kt +++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/offlinemap/OfflineMapFragment.kt @@ -1,23 +1,16 @@ package com.navinfo.omqs.ui.fragment.offlinemap 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.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController -import com.google.android.material.tabs.TabLayout -import com.google.android.material.tabs.TabLayout.OnTabSelectedListener import com.google.android.material.tabs.TabLayoutMediator import com.navinfo.omqs.databinding.FragmentOfflineMapBinding -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext /** - * 离线地图 + * 离线地图总页面 */ class OfflineMapFragment : Fragment() { diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/offlinemap/OfflineMapStateListFragment.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/offlinemap/OfflineMapStateListFragment.kt index b4ae37f7..41c3b337 100644 --- a/app/src/main/java/com/navinfo/omqs/ui/fragment/offlinemap/OfflineMapStateListFragment.kt +++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/offlinemap/OfflineMapStateListFragment.kt @@ -6,16 +6,33 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment -import com.google.android.material.tabs.TabLayout -import com.google.android.material.tabs.TabLayoutMediator -import com.navinfo.omqs.databinding.FragmentOfflineMapBinding +import androidx.fragment.app.viewModels +import androidx.lifecycle.DefaultLifecycleObserver +import androidx.lifecycle.LifecycleOwner +import androidx.recyclerview.widget.LinearLayoutManager import com.navinfo.omqs.databinding.FragmentOfflineMapStateListBinding +import com.navinfo.omqs.http.offlinemapdownload.OfflineMapDownloadManager +import dagger.hilt.android.AndroidEntryPoint +import javax.inject.Inject - +/** + * 离线地图管理页面 + */ +@AndroidEntryPoint class OfflineMapStateListFragment : Fragment() { + @Inject + lateinit var downloadManager: OfflineMapDownloadManager private var _binding: FragmentOfflineMapStateListBinding? = null - + private val viewModel by viewModels() private val binding get() = _binding!! + + private val adapter: OfflineMapCityListAdapter by lazy { + OfflineMapCityListAdapter( + downloadManager, + requireContext() + ) + } + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? @@ -27,11 +44,24 @@ class OfflineMapStateListFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - + val layoutManager = LinearLayoutManager(context) + //// 设置 RecyclerView 的固定大小,避免在滚动时重新计算视图大小和布局,提高性能 + binding.offlineMapCityStateListRecyclerview.setHasFixedSize(true) + binding.offlineMapCityStateListRecyclerview.layoutManager = layoutManager + binding.offlineMapCityStateListRecyclerview.adapter = adapter + viewModel.cityListLiveData.observe(viewLifecycleOwner) { + adapter.refreshData(it) + } } override fun onDestroyView() { super.onDestroyView() _binding = null } + + override fun onResume() { + super.onResume() + viewModel.getCityList() + } + } \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/offlinemap/OfflineMapStateListViewModel.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/offlinemap/OfflineMapStateListViewModel.kt new file mode 100644 index 00000000..1490e33c --- /dev/null +++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/offlinemap/OfflineMapStateListViewModel.kt @@ -0,0 +1,40 @@ +package com.navinfo.omqs.ui.fragment.offlinemap + +import android.content.Context +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.navinfo.omqs.db.RoomAppDatabase +import com.navinfo.omqs.bean.OfflineMapCityBean +import dagger.hilt.android.lifecycle.HiltViewModel +import dagger.hilt.android.qualifiers.ApplicationContext +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import javax.inject.Inject + +/** + * 离线地图城市列表viewModel + */ +@HiltViewModel +class OfflineMapStateListViewModel @Inject constructor( + @ApplicationContext val context: Context, + private val roomDatabase: RoomAppDatabase +) : ViewModel() { + + val cityListLiveData = MutableLiveData>() + + /** + * 去获取正在下载或 已经下载的离线地图列表 + */ + fun getCityList() { + viewModelScope.launch(Dispatchers.IO) { + val list = roomDatabase.getOfflineMapDao().getOfflineMapListWithOutNone() + if (cityListLiveData.value != null) { + if (cityListLiveData.value!!.size != list.size) + cityListLiveData.postValue(list) + }else{ + cityListLiveData.postValue(list) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/offlinemap/OfflineMapViewHolder.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/offlinemap/OfflineMapViewHolder.kt deleted file mode 100644 index 02a03583..00000000 --- a/app/src/main/java/com/navinfo/omqs/ui/fragment/offlinemap/OfflineMapViewHolder.kt +++ /dev/null @@ -1,10 +0,0 @@ -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 - } -} \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/personalcenter/PersonalCenterFragment.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/personalcenter/PersonalCenterFragment.kt index e0d21450..e5799c2d 100644 --- a/app/src/main/java/com/navinfo/omqs/ui/fragment/personalcenter/PersonalCenterFragment.kt +++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/personalcenter/PersonalCenterFragment.kt @@ -15,10 +15,12 @@ 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 +import dagger.hilt.android.AndroidEntryPoint /** * 个人中心 */ +@AndroidEntryPoint class PersonalCenterFragment : Fragment(), FSAFActivityCallbacks { private var _binding: FragmentPersonalCenterBinding? = null @@ -54,6 +56,18 @@ class PersonalCenterFragment : Fragment(), FSAFActivityCallbacks { } }) } + R.id.personal_center_menu_import_yuan_data->{ + // 用户选中导入数据,打开文件选择器,用户选择导入的数据文件目录 + fileChooser.openChooseFileDialog(object: FileChooserCallback() { + override fun onCancel(reason: String) { + } + + override fun onResult(uri: Uri) { + viewModel.importScProblemData(uri) + + } + }) + } } true } diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/personalcenter/PersonalCenterViewModel.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/personalcenter/PersonalCenterViewModel.kt index a0217e7d..741c58b8 100644 --- a/app/src/main/java/com/navinfo/omqs/ui/fragment/personalcenter/PersonalCenterViewModel.kt +++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/personalcenter/PersonalCenterViewModel.kt @@ -1,16 +1,89 @@ package com.navinfo.omqs.ui.fragment.personalcenter +import android.net.Uri +import android.util.Log import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.blankj.utilcode.util.UriUtils +import com.navinfo.omqs.bean.ScProblemTypeBean +import com.navinfo.omqs.bean.ScRootCauseAnalysisBean +import com.navinfo.omqs.db.RoomAppDatabase +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import org.apache.poi.ss.usermodel.Cell +import org.apache.poi.ss.usermodel.Row +import org.apache.poi.ss.usermodel.Sheet +import org.apache.poi.ss.usermodel.WorkbookFactory import java.io.File +import java.io.FileInputStream +import java.io.IOException +import java.io.InputStream +import javax.inject.Inject -class PersonalCenterViewModel: ViewModel() { +@HiltViewModel +class PersonalCenterViewModel @Inject constructor( + private val roomAppDatabase: RoomAppDatabase +) : ViewModel() { fun importOmdbData(omdbFile: File) { // 检查File是否为sqlite数据库 - if (omdbFile == null || omdbFile.exists()) { + if (omdbFile == null || !omdbFile.exists()) { throw Exception("文件不存在") } if (!omdbFile.name.endsWith(".sqlite") and !omdbFile.name.endsWith("db")) { throw Exception("文件不存在") } } + + fun importScProblemData(uri: Uri) { + viewModelScope.launch(Dispatchers.IO) { + try { + val file = UriUtils.uri2File(uri) + val inputStream: InputStream = + FileInputStream(file) //getAssets().open("sample.xlsx") + val workbook = WorkbookFactory.create(inputStream) + //获取所有sheet + val sheet1 = workbook.getSheet("SC_PROBLEM_TYPE") + sheet1?.let { + val rowCount: Int = it.physicalNumberOfRows // 获取行数 + val list = mutableListOf() + for (i in 1 until rowCount) { + val row: Row = it.getRow(i) // 获取行 + val cellCount: Int = row.physicalNumberOfCells // 获取列数 + if (cellCount == 3) { + val bean = ScProblemTypeBean() + bean.classType = row.getCell(0).stringCellValue + bean.problemType = row.getCell(1).stringCellValue + bean.phenomenon = row.getCell(2).stringCellValue + list.add(bean) + Log.e("jingo", bean.toString()) + } + } + roomAppDatabase.getScProblemTypeDao().insertOrUpdateList(list) + } + val sheet2 = workbook.getSheet("SC_ROOT_CAUSE_ANALYSIS") + sheet2?.let { + val rowCount: Int = it.physicalNumberOfRows // 获取行数 + val list = mutableListOf() + for (i in 1 until rowCount) { + val row: Row = it.getRow(i) // 获取行 + val cellCount: Int = row.physicalNumberOfCells // 获取列数 + if (cellCount == 2) { + val bean = ScRootCauseAnalysisBean() + bean.problemLink = row.getCell(0).stringCellValue + bean.problemCause = row.getCell(1).stringCellValue + list.add(bean) + Log.e("jingo", bean.toString()) + } + } + roomAppDatabase.getScRootCauseAnalysisDao().insertOrUpdateList(list) + } + workbook.close() + } catch (e: IOException) { + e.printStackTrace() + Log.e("jingo", e.toString()) + } + } + + } } \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/ui/other/AdapterItemClickListener.kt b/app/src/main/java/com/navinfo/omqs/ui/other/AdapterItemClickListener.kt new file mode 100644 index 00000000..ce53ad6e --- /dev/null +++ b/app/src/main/java/com/navinfo/omqs/ui/other/AdapterItemClickListener.kt @@ -0,0 +1,5 @@ +package com.navinfo.omqs.ui.other + +interface AdapterItemClickListener { + fun onItemClick(position: Int) +} \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/ui/other/BaseRecyclerViewAdapter.kt b/app/src/main/java/com/navinfo/omqs/ui/other/BaseRecyclerViewAdapter.kt index fa964626..aed4ced8 100644 --- a/app/src/main/java/com/navinfo/omqs/ui/other/BaseRecyclerViewAdapter.kt +++ b/app/src/main/java/com/navinfo/omqs/ui/other/BaseRecyclerViewAdapter.kt @@ -5,6 +5,7 @@ import android.view.View.OnClickListener import android.view.ViewGroup import androidx.databinding.DataBindingUtil import androidx.recyclerview.widget.RecyclerView +import com.navinfo.omqs.R /** * RecyclerView 适配器基础类 @@ -26,32 +27,38 @@ abstract class BaseRecyclerViewAdapter(var data: List = listOf()) : // ) // } + abstract fun getItemViewRes(position: Int): Int + + override fun getItemViewType(position: Int): Int { + return getItemViewRes(position) + } override fun getItemCount(): Int { return data.size } - fun refreshData(newData: List) { - 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) { + + // this.recyclerView = recyclerView // super.onAttachedToRecyclerView(recyclerView) -// this.recyclerView = recyclerView +// override fun onAttachedToRecyclerView(recyclerView: RecyclerView) { + open fun refreshData(newData: List) { + this.data = newData + this.notifyDataSetChanged() + } // } // // override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) { diff --git a/app/src/main/java/com/navinfo/omqs/ui/other/ShareViewModel.kt b/app/src/main/java/com/navinfo/omqs/ui/other/ShareViewModel.kt new file mode 100644 index 00000000..57ffe9cc --- /dev/null +++ b/app/src/main/java/com/navinfo/omqs/ui/other/ShareViewModel.kt @@ -0,0 +1,107 @@ +package com.navinfo.omqs.ui.other + +import androidx.annotation.MainThread +import androidx.fragment.app.Fragment +import androidx.fragment.app.createViewModelLazy +import androidx.lifecycle.* +import androidx.lifecycle.viewmodel.CreationExtras + +/** + * 用来共享的viewModel + */ +val vMStores = HashMap() + + +@MainThread +inline fun Fragment.shareViewModels( + scopeName: String, + noinline ownerProducer: () -> ViewModelStoreOwner = { this }, + noinline factoryProducer: (() -> ViewModelProvider.Factory)? = null +): Lazy { + val owner by lazy(LazyThreadSafetyMode.NONE) { ownerProducer() } + + val store: ViewModelStoreOwner + if (vMStores.keys.contains(scopeName)) { + store = vMStores[scopeName]!! + } else { + vMStores[scopeName] = owner + store = owner + this.let { fragment -> + fragment.lifecycle.addObserver(object : LifecycleEventObserver { + override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) { + if (event == Lifecycle.Event.ON_DESTROY) { + fragment.lifecycle.removeObserver(this) + store.viewModelStore.clear() + vMStores.remove(scopeName) + } + } + }) + } + + + } +// store.register(this) + + return createViewModelLazy( + VM::class, + { store.viewModelStore }, + { + (store as? HasDefaultViewModelProviderFactory)?.defaultViewModelCreationExtras + ?: CreationExtras.Empty + }, + factoryProducer ?: { + (store as? HasDefaultViewModelProviderFactory)?.defaultViewModelProviderFactory + ?: defaultViewModelProviderFactory + }) +} + +// +//@MainThread +//inline fun LifecycleOwner.shareViewModels( +// scopeName: String, +// factory: ViewModelProvider.Factory? = null +//): Lazy { +// val store: VMStore +// if (vMStores.keys.contains(scopeName)) { +// store = vMStores[scopeName]!! +// } else { +// store = VMStore() +// vMStores[scopeName] = store +// } +// store.register(this) +// return ViewModelLazy(VM::class, +// { store.viewModelStore }, +// { factory ?: MyViewModelFactory() }) +//} + + +class MyViewModelFactory( +) : ViewModelProvider.Factory { + override fun create(modelClass: Class): T { + return modelClass.getConstructor().newInstance() + } +} + +class VMStore(val owner: ViewModelStoreOwner) { + +// private val bindTargets = ArrayList() +// fun register(host: LifecycleOwner) { +// if (!bindTargets.contains(host)) { +// bindTargets.add(host) +// host.lifecycle.addObserver(object : LifecycleEventObserver { +// override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) { +// if (event == Lifecycle.Event.ON_DESTROY) { +// host.lifecycle.removeObserver(this) +// bindTargets.remove(host) +// if (bindTargets.isEmpty()) {//如果当前商店没有关联对象,则释放资源 +// vMStores.entries.find { it.value == this@VMStore }?.also { +// owner.viewModelStore.clear() +// vMStores.remove(it.key) +// } +// } +// } +// } +// }) +// } +// } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/baseline_delete_forever_24.xml b/app/src/main/res/drawable/baseline_delete_forever_24.xml new file mode 100644 index 00000000..32fc924a --- /dev/null +++ b/app/src/main/res/drawable/baseline_delete_forever_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/baseline_keyboard_voice_24.xml b/app/src/main/res/drawable/baseline_keyboard_voice_24.xml new file mode 100644 index 00000000..50ea1620 --- /dev/null +++ b/app/src/main/res/drawable/baseline_keyboard_voice_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/baseline_save_24.xml b/app/src/main/res/drawable/baseline_save_24.xml new file mode 100644 index 00000000..d4ab6604 --- /dev/null +++ b/app/src/main/res/drawable/baseline_save_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml index 1d88e9f5..fba45e93 100644 --- a/app/src/main/res/layout/activity_login.xml +++ b/app/src/main/res/layout/activity_login.xml @@ -35,13 +35,14 @@ @@ -54,10 +55,12 @@ - + tools:context=".ui.activity.map.MainActivity"> + + + + + + + + + + + + + + + + app:layout_constraintHorizontal_bias="0.3" + app:navGraph="@navigation/left_drawer_nav_graph" /> + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_map_test.xml b/app/src/main/res/layout/activity_map_test.xml deleted file mode 100644 index 0d477f41..00000000 --- a/app/src/main/res/layout/activity_map_test.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_empty.xml b/app/src/main/res/layout/fragment_empty.xml new file mode 100644 index 00000000..f59a4d2f --- /dev/null +++ b/app/src/main/res/layout/fragment_empty.xml @@ -0,0 +1,7 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_evaluation_result.xml b/app/src/main/res/layout/fragment_evaluation_result.xml new file mode 100644 index 00000000..c9829571 --- /dev/null +++ b/app/src/main/res/layout/fragment_evaluation_result.xml @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_map_download.xml b/app/src/main/res/layout/fragment_map_download.xml deleted file mode 100644 index f5b4982e..00000000 --- a/app/src/main/res/layout/fragment_map_download.xml +++ /dev/null @@ -1,99 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_offline_map.xml b/app/src/main/res/layout/fragment_offline_map.xml index 03bf1190..798d5e1c 100644 --- a/app/src/main/res/layout/fragment_offline_map.xml +++ b/app/src/main/res/layout/fragment_offline_map.xml @@ -26,20 +26,13 @@ android:text="城市列表" /> - + app:layout_constraintTop_toBottomOf="@id/offline_map_tab_layout" /> + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_offline_map_city_list.xml b/app/src/main/res/layout/fragment_offline_map_city_list.xml index 0b0982b3..930bd830 100644 --- a/app/src/main/res/layout/fragment_offline_map_city_list.xml +++ b/app/src/main/res/layout/fragment_offline_map_city_list.xml @@ -6,8 +6,19 @@ android:layout_height="match_parent" tools:context=".ui.fragment.offlinemap.OfflineMapCityListFragment"> + + + android:layout_height="0dp" + app:layout_constraintTop_toBottomOf="@id/offline_map_search" /> \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_offline_map_state_list.xml b/app/src/main/res/layout/fragment_offline_map_state_list.xml index 5de6383b..5df608e9 100644 --- a/app/src/main/res/layout/fragment_offline_map_state_list.xml +++ b/app/src/main/res/layout/fragment_offline_map_state_list.xml @@ -4,15 +4,11 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@color/default_red" tools:context=".ui.fragment.offlinemap.OfflineMapStateListFragment"> - + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_personal_center.xml b/app/src/main/res/layout/fragment_personal_center.xml index 4d137cf7..8e36ac72 100644 --- a/app/src/main/res/layout/fragment_personal_center.xml +++ b/app/src/main/res/layout/fragment_personal_center.xml @@ -2,7 +2,7 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_problem_link.xml b/app/src/main/res/layout/fragment_problem_link.xml new file mode 100644 index 00000000..c0862ccb --- /dev/null +++ b/app/src/main/res/layout/fragment_problem_link.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/map_view.xml b/app/src/main/res/layout/map_view.xml deleted file mode 100644 index 0a7f8b09..00000000 --- a/app/src/main/res/layout/map_view.xml +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/temp.xml b/app/src/main/res/layout/temp.xml new file mode 100644 index 00000000..3bce645a --- /dev/null +++ b/app/src/main/res/layout/temp.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/text_item_select.xml b/app/src/main/res/layout/text_item_select.xml new file mode 100644 index 00000000..66dd87d4 --- /dev/null +++ b/app/src/main/res/layout/text_item_select.xml @@ -0,0 +1,11 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/evaluation_bar_mean.xml b/app/src/main/res/menu/evaluation_bar_mean.xml new file mode 100644 index 00000000..16c99b49 --- /dev/null +++ b/app/src/main/res/menu/evaluation_bar_mean.xml @@ -0,0 +1,18 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/personal_center_menu.xml b/app/src/main/res/menu/personal_center_menu.xml index 4e3502df..d8860fdb 100644 --- a/app/src/main/res/menu/personal_center_menu.xml +++ b/app/src/main/res/menu/personal_center_menu.xml @@ -15,9 +15,9 @@ android:icon="@drawable/ic_baseline_import_export_24" android:title="导入数据" /> + android:id="@+id/personal_center_menu_import_yuan_data" + android:icon="@drawable/ic_baseline_import_export_24" + android:title="导入元数据" /> + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/navigation/right_fragment_nav_graph.xml b/app/src/main/res/navigation/right_fragment_nav_graph.xml new file mode 100644 index 00000000..25383197 --- /dev/null +++ b/app/src/main/res/navigation/right_fragment_nav_graph.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/build.gradle b/build.gradle index da697ab5..63d6efae 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,6 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. + buildscript { dependencies { classpath "io.realm:realm-gradle-plugin:10.11.1" diff --git a/collect-library/build.gradle b/collect-library/build.gradle index 9cc1452d..3cca075f 100644 --- a/collect-library/build.gradle +++ b/collect-library/build.gradle @@ -1,11 +1,13 @@ plugins { id 'com.android.library' id 'org.jetbrains.kotlin.android' + id 'kotlin-parcelize' // 序列化 + id 'kotlin-android' + id 'kotlin-kapt' + id 'realm-android' } -apply plugin: 'kotlin-android' -apply plugin: 'kotlin-kapt' -apply plugin: 'realm-android' +ext.roomversion = "2.1.0" ext.vtmVersion = "0.18.0" ext.realm_version = '10.10.1' ext.appcompatVersion = "1.3.0" @@ -31,7 +33,7 @@ android { } } ndk { - abiFilters "armeabi", "armeabi-v7a" + abiFilters "armeabi", "armeabi-v7a", "arm64-v8a" } } @@ -53,20 +55,31 @@ android { } } - +//configurations.all { +// resolutionStrategy { +// force 'com.intellij:annotations:13.0' +// } +//} dependencies { api fileTree(dir: 'libs', include: ['*.jar', '*.aar']) - api files('libs/BaiduLBS_AndroidSDK_Lib.aar') - implementation "androidx.appcompat:appcompat:$appcompatVersion" - implementation "com.google.android.material:material:$materialVersion" + 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' + api 'androidx.constraintlayout:constraintlayout:2.1.4' + api 'androidx.navigation:navigation-fragment-ktx:2.5.3' + api 'androidx.navigation:navigation-ui-ktx:2.5.3' + testImplementation 'junit:junit:4.13.2' - androidTestImplementation 'androidx.test.ext:junit:1.1.3' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' - implementation 'com.yanzhenjie:kalle:0.1.7' + androidTestImplementation 'androidx.test.ext:junit:1.1.5' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' +// implementation 'com.yanzhenjie:kalle:0.1.7' // VTM依赖 implementation "net.sf.kxml:kxml2:2.3.0" - implementation "org.slf4j:slf4j-api:1.7.28" + implementation 'org.slf4j:slf4j-api:2.0.7' implementation "org.mapsforge:vtm:$vtmVersion" implementation "org.mapsforge:vtm-themes:$vtmVersion" implementation "org.mapsforge:vtm-android:$vtmVersion" @@ -94,30 +107,37 @@ dependencies { implementation "com.badlogicgames.gdx:gdx-backend-android:1.11.0" implementation "com.caverock:androidsvg:1.4" implementation "org.mapsforge:vtm-jts:$vtmVersion" - implementation "org.locationtech.jts:jts-core:1.18.2" - implementation "com.squareup.okhttp3:okhttp:3.12.13" - implementation "com.squareup.okio:okio:1.15.0" + implementation 'org.locationtech.jts:jts-core:1.19.0' + implementation 'com.squareup.okhttp3:okhttp:5.0.0-alpha.11' + implementation 'com.squareup.okio:okio:3.3.0' implementation "com.fasterxml.jackson.core:jackson-annotations:2.9.9" implementation "com.fasterxml.jackson.core:jackson-core:2.9.9" implementation "com.fasterxml.jackson.core:jackson-databind:2.9.9" - implementation 'com.tencent.wcdb:room:1.0.8' // 代替 room-runtime,同时也不需要再引用 wcdb-android - implementation "androidx.sqlite:sqlite:2.0.1" - implementation "androidx.room:room-runtime:2.1.0" - annotationProcessor "androidx.room:room-compiler:2.1.0" - kapt "androidx.room:room-compiler:2.1.0" - androidTestImplementation 'androidx.room:room-testing:2.0.0' - implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0' - kapt 'androidx.lifecycle:lifecycle-compiler:2.0.0' - implementation 'com.tencent.wcdb:wcdb-android:1.0.0' - implementation "androidx.core:core-ktx:1.8.0" - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - // 协程核心库 - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4' - // 协程Android支持库 - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4' - // 协程Java8支持库 - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:1.6.4' + //room 数据库相关 + api 'com.tencent.wcdb:room:1.1-19' // 代替 room-runtime,同时也不需要再引用 wcdb-android + api 'androidx.sqlite:sqlite-ktx:2.3.1' + api "androidx.room:room-runtime:2.5.1" + api "androidx.room:room-ktx:2.5.1" + annotationProcessor "androidx.room:room-compiler:2.5.1" + annotationProcessor 'android.arch.persistence.room:compiler:1.1.1' + kapt 'android.arch.persistence.room:compiler:1.1.1'// compiler 需要用 room 的 + kapt "androidx.room:room-compiler:2.5.1" + kapt "androidx.room:room-ktx:2.5.1" + +// //分页加载 +// api "androidx.room:room-paging:$roomversion" +// api "androidx.paging:paging-runtime-ktx:3.1.1" + + androidTestImplementation "android.arch.persistence.room:testing:1.1.1" + api 'com.tencent.wcdb:wcdb-android:1.1-19' + +// // 协程核心库 +// implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4' +// // 协程Android支持库 +// implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4' +// // 协程Java8支持库 +// implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:1.6.4' } \ No newline at end of file diff --git a/collect-library/libs/BaiduLBS_Android.jar b/collect-library/libs/BaiduLBS_Android.jar new file mode 100644 index 00000000..07327d76 Binary files /dev/null and b/collect-library/libs/BaiduLBS_Android.jar differ diff --git a/collect-library/libs/BaiduLBS_AndroidSDK_Lib.aar b/collect-library/libs/BaiduLBS_AndroidSDK_Lib.aar deleted file mode 100644 index 18d4fe80..00000000 Binary files a/collect-library/libs/BaiduLBS_AndroidSDK_Lib.aar and /dev/null differ diff --git a/collect-library/libs/arm64-v8a/libindoor.so b/collect-library/libs/arm64-v8a/libindoor.so new file mode 100644 index 00000000..8b55e4c9 Binary files /dev/null and b/collect-library/libs/arm64-v8a/libindoor.so differ diff --git a/collect-library/libs/arm64-v8a/liblocSDK8b.so b/collect-library/libs/arm64-v8a/liblocSDK8b.so new file mode 100644 index 00000000..5b0d7afe Binary files /dev/null and b/collect-library/libs/arm64-v8a/liblocSDK8b.so differ diff --git a/collect-library/libs/armeabi-v7a/libindoor.so b/collect-library/libs/armeabi-v7a/libindoor.so new file mode 100644 index 00000000..e14d583d Binary files /dev/null and b/collect-library/libs/armeabi-v7a/libindoor.so differ diff --git a/collect-library/libs/armeabi-v7a/liblocSDK8b.so b/collect-library/libs/armeabi-v7a/liblocSDK8b.so new file mode 100644 index 00000000..c14b9199 Binary files /dev/null and b/collect-library/libs/armeabi-v7a/liblocSDK8b.so differ diff --git a/collect-library/libs/x86/liblocSDK8b.so b/collect-library/libs/x86/liblocSDK8b.so new file mode 100644 index 00000000..62a9a604 Binary files /dev/null and b/collect-library/libs/x86/liblocSDK8b.so differ diff --git a/collect-library/libs/x86_64/liblocSDK8b.so b/collect-library/libs/x86_64/liblocSDK8b.so new file mode 100644 index 00000000..4fe4fe81 Binary files /dev/null and b/collect-library/libs/x86_64/liblocSDK8b.so differ diff --git a/collect-library/src/main/java/com/navinfo/collect/library/data/entity/OfflineMapCityBean.kt b/collect-library/src/main/java/com/navinfo/collect/library/data/entity/OfflineMapCityBean.kt index 9d1f54fe..3621578b 100644 --- a/collect-library/src/main/java/com/navinfo/collect/library/data/entity/OfflineMapCityBean.kt +++ b/collect-library/src/main/java/com/navinfo/collect/library/data/entity/OfflineMapCityBean.kt @@ -1,57 +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) - } - - -} \ No newline at end of file +//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) +// } +// +// +//} \ No newline at end of file diff --git a/collect-library/src/main/java/com/navinfo/collect/library/data/entity/OfflineMapCityRealmObject.kt b/collect-library/src/main/java/com/navinfo/collect/library/data/entity/OfflineMapCityRealmObject.kt index 5bd0e80a..f42ad705 100644 --- a/collect-library/src/main/java/com/navinfo/collect/library/data/entity/OfflineMapCityRealmObject.kt +++ b/collect-library/src/main/java/com/navinfo/collect/library/data/entity/OfflineMapCityRealmObject.kt @@ -1,43 +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 - } -} \ No newline at end of file +//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 +// } +//} \ No newline at end of file diff --git a/collect-library/src/main/java/com/navinfo/collect/library/data/entity/QsRecordBean.kt b/collect-library/src/main/java/com/navinfo/collect/library/data/entity/QsRecordBean.kt new file mode 100644 index 00000000..ed24f345 --- /dev/null +++ b/collect-library/src/main/java/com/navinfo/collect/library/data/entity/QsRecordBean.kt @@ -0,0 +1,102 @@ +package com.navinfo.collect.library.data.entity + +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 + + +/** + * @author zhjch + * @version V1.0 + * @ClassName: Rd_qcRecord + * @Date 2016/1/12 + * @Description: ${TODO}(质检对象) + */ +@RealmClass +open class QsRecordBean @JvmOverloads constructor( + /** + * id 主键 + * + */ + @PrimaryKey + var id: String = "", + /** + * 关联要素id + */ + var elementId: String = "", + /** + * linkPid 绑定的道路ID + */ + var linkId: String = "", + /** + *问题分类 + */ + var classType: String = "", + /** + * 问题类型 + */ + var type: String = "", + + /** + * 问题现象 + */ + var phenomenon: String = "", + /** + * 描述信息 + */ + var description: String = "", + /** + * 问题环节 + */ + var problemLink: String = "", + /** + * 问题原因 + * 根本原因(RCA) + */ + + var cause: String = "", + /** + * 质检员ID + */ + var checkUserId: String = "", + /** + * 质检日期 + */ + var checkTime: String = "", + /** + * 确认人 + */ + var confirmUserId: String = "", + /** + * 状态 0 无; 1 删除;2 更新;3 新增; + */ + + var t_lifecycle: Int = 3, + /** + * 问题记录提交状态 0 未提交;1 已提交; + */ + var t_status: Int = 0, + /** + * 显示坐标 + */ +// var geometry: String = "", + /** + * 显示坐标 + */ + var guideGeometry: String = "", + + ) : RealmObject() { + + + private val tileX = RealmSet() // x方向的tile编码 + private val tileY = RealmSet() // y方向的tile编码 + var geometry: String = "" + set(value) { + field = value + // 根据geometry自动计算当前要素的x-tile和y-tile + GeometryToolsKt.getTileXByGeometry(value, tileX) + GeometryToolsKt.getTileYByGeometry(value, tileY) + } +} \ No newline at end of file diff --git a/collect-library/src/main/java/com/navinfo/collect/library/map/GeoPoint.kt b/collect-library/src/main/java/com/navinfo/collect/library/map/GeoPoint.kt new file mode 100644 index 00000000..ed7115f8 --- /dev/null +++ b/collect-library/src/main/java/com/navinfo/collect/library/map/GeoPoint.kt @@ -0,0 +1,14 @@ +package com.navinfo.collect.library.map + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +@Parcelize +data class GeoPoint( + var latitude: Double = 0.0, + var longitude: Double = 0.0 +) : Parcelable { + fun toGeometry(): String { + return "POINT($longitude $latitude)" + } +} \ No newline at end of file diff --git a/collect-library/src/main/java/com/navinfo/collect/library/map/NIMapController.kt b/collect-library/src/main/java/com/navinfo/collect/library/map/NIMapController.kt index 72ccc8dc..c941e5ae 100644 --- a/collect-library/src/main/java/com/navinfo/collect/library/map/NIMapController.kt +++ b/collect-library/src/main/java/com/navinfo/collect/library/map/NIMapController.kt @@ -2,6 +2,7 @@ package com.navinfo.collect.library.map import android.content.Context import android.util.Log +import androidx.appcompat.app.AppCompatActivity import com.navinfo.collect.library.map.handler.* import com.navinfo.collect.library.map.maphandler.MeasureLayerHandler import com.navinfo.collect.library.map.handler.ViewportHandler @@ -23,7 +24,7 @@ class NIMapController { lateinit var measureLayerHandler: MeasureLayerHandler - fun init(context: Context, mapView: NIMapView, options: NIMapOptions? = null, mapPath: String) { + fun init(context: AppCompatActivity, mapView: NIMapView, options: NIMapOptions? = null, mapPath: String) { Constant.MAP_PATH = mapPath layerManagerHandler = LayerManagerHandler(context, mapView) locationLayerHandler = LocationLayerHandler(context, mapView) @@ -37,5 +38,6 @@ class NIMapController { mapView.setOptions(options) } + } diff --git a/collect-library/src/main/java/com/navinfo/collect/library/map/NIMapView.java b/collect-library/src/main/java/com/navinfo/collect/library/map/NIMapView.java index 02a0daea..d8fa38ca 100644 --- a/collect-library/src/main/java/com/navinfo/collect/library/map/NIMapView.java +++ b/collect-library/src/main/java/com/navinfo/collect/library/map/NIMapView.java @@ -3,7 +3,6 @@ package com.navinfo.collect.library.map; import android.content.Context; import android.os.Bundle; import android.util.AttributeSet; -import android.util.Log; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; @@ -15,37 +14,21 @@ import androidx.annotation.Nullable; import com.navinfo.collect.library.R; import com.navinfo.collect.library.map.layers.NaviMapScaleBar; -import com.navinfo.collect.library.map.source.MapLifeDBTileSource; -import com.navinfo.collect.library.map.source.MapLifeNiLocationTileDataSource; -import com.navinfo.collect.library.map.source.MapLifeNiLocationTileSource; -import com.navinfo.collect.library.system.Constant; import org.oscim.android.MapPreferences; import org.oscim.android.MapView; import org.oscim.core.GeoPoint; import org.oscim.android.theme.AssetsRenderTheme; import org.oscim.core.MapPosition; -import org.oscim.core.Tile; import org.oscim.event.Event; import org.oscim.event.Gesture; import org.oscim.event.GestureListener; import org.oscim.layers.GroupLayer; import org.oscim.layers.Layer; -import org.oscim.layers.TileGridLayer; -import org.oscim.layers.tile.buildings.BuildingLayer; -import org.oscim.layers.tile.vector.OsmTileLayer; import org.oscim.layers.tile.vector.VectorTileLayer; -import org.oscim.layers.tile.vector.labeling.LabelLayer; -import org.oscim.layers.tile.vector.labeling.LabelTileLoaderHook; import org.oscim.map.Map; import org.oscim.renderer.GLViewport; -import org.oscim.theme.IRenderTheme; -import org.oscim.theme.ThemeLoader; import org.oscim.theme.VtmThemes; -import org.oscim.tiling.source.mapfile.MapFileTileSource; -import org.oscim.tiling.source.mapfile.MultiMapFileTileSource; - -import java.io.File; /** @@ -125,14 +108,14 @@ public final class NIMapView extends RelativeLayout { * * @param point */ - void onMapClick(GeoPoint point); + void onMapClick(com.navinfo.collect.library.map.GeoPoint point); /** * 地图内 Poi 单击事件回调函数 * * @param poi */ - void onMapPoiClick(GeoPoint poi); +// void onMapPoiClick(GeoPoint poi); } /** @@ -218,10 +201,8 @@ public final class NIMapView extends RelativeLayout { this.mContext = context; mapView = rootView.findViewById(R.id.base_map_view); -// map = new NIMap(this); compassImage = rootView.findViewById(R.id.navinfo_map_compass); initMapGroup(); // 初始化图层组 -// mLayerManager = new NILayerManager(context, getVtmMap()); logoImage = rootView.findViewById(R.id.navinfo_map_logo); mRotateAnimation = new NIRotateAnimation(compassImage); @@ -251,6 +232,7 @@ public final class NIMapView extends RelativeLayout { } }); + // 增加比例尺图层 NaviMapScaleBar naviMapScaleBar = new NaviMapScaleBar(getVtmMap()); naviMapScaleBar.initScaleBarLayer(GLViewport.Position.BOTTOM_LEFT, 25, 60); @@ -302,6 +284,9 @@ public final class NIMapView extends RelativeLayout { zoomLayout = rootView.findViewById(R.id.navinfo_map_zoom_layer); switchTileVectorLayerTheme(MAP_THEME.DEFAULT); + + MapEventsReceiver mapEventReceiver = new MapEventsReceiver(mapView.map()); + getVtmMap().layers().add(mapEventReceiver); } @@ -923,7 +908,7 @@ public final class NIMapView extends RelativeLayout { GeoPoint geoPoint = mMap.viewport().fromScreenPoint(e.getX(), e.getY()); if (g instanceof Gesture.Tap) { // 单击事件 if (mapClickListener != null) { - mapClickListener.onMapClick(geoPoint); + mapClickListener.onMapClick(new com.navinfo.collect.library.map.GeoPoint(geoPoint.getLatitude(), geoPoint.getLongitude())); } } else if (g instanceof Gesture.DoubleTap) { // 双击 if (mapDoubleClickListener != null) { @@ -934,17 +919,6 @@ public final class NIMapView extends RelativeLayout { mapLongClickListener.onMapLongClick(geoPoint); } } - setOnMapClickListener(new OnMapClickListener() { - @Override - public void onMapClick(GeoPoint point) { - - } - - @Override - public void onMapPoiClick(GeoPoint poi) { - - } - }); return false; } } @@ -952,7 +926,7 @@ public final class NIMapView extends RelativeLayout { /** * 设置地图的点击事件 */ - public void setOnMapClickListener(@Nullable OnMapClickListener listener) { + public void setOnMapClickListener(OnMapClickListener listener) { this.mapClickListener = listener; } diff --git a/collect-library/src/main/java/com/navinfo/collect/library/map/handler/BaseHandler.kt b/collect-library/src/main/java/com/navinfo/collect/library/map/handler/BaseHandler.kt index b909f5b4..2d5bf2e5 100644 --- a/collect-library/src/main/java/com/navinfo/collect/library/map/handler/BaseHandler.kt +++ b/collect-library/src/main/java/com/navinfo/collect/library/map/handler/BaseHandler.kt @@ -1,11 +1,12 @@ package com.navinfo.collect.library.map.handler import android.content.Context +import androidx.appcompat.app.AppCompatActivity import com.navinfo.collect.library.map.NIMapView import org.oscim.layers.Layer -abstract class BaseHandler(context: Context, mapView: NIMapView) { - protected val mContext: Context = context +abstract class BaseHandler(context: AppCompatActivity, mapView: NIMapView) { + protected val mContext: AppCompatActivity = context protected val mMapView: NIMapView = mapView fun addLayer(layer: Layer, groupType: NIMapView.LAYER_GROUPS) { @@ -19,4 +20,11 @@ abstract class BaseHandler(context: Context, mapView: NIMapView) { mMapView.vtmMap.layers().remove(layer) } + fun setOnMapClickListener(listener: NIMapView.OnMapClickListener) { + mMapView.setOnMapClickListener(listener) + } + + fun removeOnMapClickListener() { + mMapView.setOnMapClickListener(null) + } } \ No newline at end of file diff --git a/collect-library/src/main/java/com/navinfo/collect/library/map/handler/LayerManagerHandler.kt b/collect-library/src/main/java/com/navinfo/collect/library/map/handler/LayerManagerHandler.kt index 3db6b728..aa709a97 100644 --- a/collect-library/src/main/java/com/navinfo/collect/library/map/handler/LayerManagerHandler.kt +++ b/collect-library/src/main/java/com/navinfo/collect/library/map/handler/LayerManagerHandler.kt @@ -1,25 +1,46 @@ package com.navinfo.collect.library.map.handler -import android.content.Context -import android.os.Environment +import android.graphics.Color +import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.lifecycleScope +import com.navinfo.collect.library.data.entity.QsRecordBean import com.navinfo.collect.library.map.NIMapView -import com.navinfo.collect.library.map.NIMapView.LAYER_GROUPS -import com.navinfo.collect.library.map.source.NavinfoMapRastorTileSource +import com.navinfo.collect.library.map.source.NavinfoMultiMapFileTileSource import com.navinfo.collect.library.system.Constant +import io.realm.Realm +import io.realm.kotlin.where +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import okhttp3.Cache import okhttp3.OkHttpClient -import org.oscim.layers.Layer -import org.oscim.layers.LocationLayer -import org.oscim.layers.tile.bitmap.BitmapTileLayer +import org.oscim.backend.CanvasAdapter +import org.oscim.backend.canvas.Paint +import org.oscim.layers.GroupLayer +import org.oscim.layers.tile.buildings.BuildingLayer +import org.oscim.layers.tile.vector.VectorTileLayer +import org.oscim.layers.tile.vector.labeling.LabelLayer import org.oscim.tiling.source.OkHttpEngine.OkHttpFactory +import org.oscim.tiling.source.mapfile.MapFileTileSource import java.io.File /** * Layer 操作 */ -class LayerManagerHandler(context: Context, mapView: NIMapView) : +class LayerManagerHandler(context: AppCompatActivity, mapView: NIMapView) : BaseHandler(context, mapView) { - private var baseRasterLayer: Layer? = null + private var baseGroupLayer // 用于盛放所有基础底图的图层组,便于统一管理 + : GroupLayer? = null + + /** + * 默认文字颜色 + */ + private val mDefaultTextColor = "#4E55AF" + + /** + * 文字画笔 + */ + + private lateinit var paint: Paint init { initMap() @@ -29,9 +50,12 @@ class LayerManagerHandler(context: Context, mapView: NIMapView) : * 初始化地图 */ private fun initMap() { - switchBaseMapType(BASE_MAP_TYPE.CYCLE_MAP) -// initVectorTileLayer() + loadBaseMap() + mMapView.switchTileVectorLayerTheme(NIMapView.MAP_THEME.DEFAULT) + //初始化之间数据图层 + initQsRecordDataLayer() + mMapView.vtmMap.updateMap() // initMapLifeSource() } @@ -39,40 +63,104 @@ class LayerManagerHandler(context: Context, mapView: NIMapView) : /** * 切换基础底图样式 */ - fun switchBaseMapType(type: BASE_MAP_TYPE) { - if (baseRasterLayer != null) { - mMapView.vtmMap.layers().remove(baseRasterLayer) - baseRasterLayer = null - mMapView.vtmMap.updateMap() + fun loadBaseMap() { + //给地图layer分组 + if (baseGroupLayer == null) { + baseGroupLayer = GroupLayer(mMapView.vtmMap) + addLayer(baseGroupLayer!!, NIMapView.LAYER_GROUPS.BASE) } - baseRasterLayer = getRasterTileLayer(type.url, type.tilePath, true) - addLayer(baseRasterLayer!!, LAYER_GROUPS.BASE) - mMapView.updateMap() - } + baseGroupLayer?.let { + for (layer in it.layers) { + removeLayer(layer) + } + it.layers.clear() + val builder = OkHttpClient.Builder() + val urlTileSource: NavinfoMultiMapFileTileSource = + NavinfoMultiMapFileTileSource.builder() + .httpFactory(OkHttpFactory(builder)) //.locale("en") + .build() - private fun getRasterTileLayer( - url: String?, - tilePath: String?, - useCache: Boolean - ): Layer { - val builder = OkHttpClient.Builder() - val mTileSource = - NavinfoMapRastorTileSource.builder(url).tilePath(tilePath) - .httpFactory(OkHttpFactory(builder)).build() - // 如果使用缓存 - if (useCache) { - val cacheDirectory: File = - File(Constant.MAP_PATH, "tiles-raster") - val cacheSize = 300 * 1024 * 1024 // 300 MB + // Cache the tiles into file system + val cacheDirectory = File(Constant.MAP_PATH, "cache") + val cacheSize = 200 * 1024 * 1024 // 10 MB val cache = Cache(cacheDirectory, cacheSize.toLong()) builder.cache(cache) - } -// mTileSource.setHttpEngine(new OkHttpEngine.OkHttpFactory(builder)); -// mTileSource.setHttpRequestHeaders(Collections.singletonMap("User-Agent", "vtm-android-example")); -// mTileSource.setCache(new TileCache(mContext, defaultDir, url.substring(url.indexOf(":")+1))); - return BitmapTileLayer(mMapView.vtmMap, mTileSource) +// val headerMap = HashMap() +// headerMap["token"] = ""//Constant.TOKEN +// urlTileSource.setHttpRequestHeaders(headerMap) + val baseLayer = VectorTileLayer(mMapView.vtmMap, urlTileSource) + + val baseMapFolder = File("${Constant.MAP_PATH}offline") + + if (baseMapFolder.exists()) { + val dirFileList = baseMapFolder.listFiles() + if (dirFileList != null && dirFileList.isNotEmpty()) { + for (mapFile in dirFileList) { + if (!mapFile.isFile || !mapFile.name.endsWith(".map")) { + continue + } + val mTileSource = MapFileTileSource() + mTileSource.setPreferredLanguage("zh") + if (mTileSource.setMapFile(mapFile.absolutePath)) { + urlTileSource.add(mTileSource) + } + } + } + baseLayer.tileSource = urlTileSource + //增加基础路网图层 + it.layers.add(baseLayer) + //增加建筑图层 + it.layers.add(BuildingLayer(mMapView.vtmMap, baseLayer)) + //增加文字图层 + it.layers.add(LabelLayer(mMapView.vtmMap, baseLayer)) + for (layer in it.layers) { + addLayer(layer, NIMapView.LAYER_GROUPS.BASE) + } + mMapView.updateMap() + } + } } + + private fun initQsRecordDataLayer() { + paint = CanvasAdapter.newPaint() + paint.setTypeface(Paint.FontFamily.DEFAULT, Paint.FontStyle.NORMAL) + paint.setTextSize(13 * CanvasAdapter.getScale()) + paint.strokeWidth = 2 * CanvasAdapter.getScale() + paint.color = Color.parseColor(mDefaultTextColor) + + mContext.lifecycleScope.launch(Dispatchers.IO) { + + + val realm = Realm.getDefaultInstance() + realm.executeTransaction { + val list = realm.where().findAll() + paint.setColor(Color.parseColor(mDefaultTextColor)) + } + realm.close() + } + } + +// private fun getRasterTileLayer( +// url: String?, +// tilePath: String?, +// useCache: Boolean +// ): Layer { +// val builder = OkHttpClient.Builder() +// val mTileSource = +// NavinfoMapRastorTileSource.builder(url).tilePath(tilePath) +// .httpFactory(OkHttpFactory(builder)).build() +// // 如果使用缓存 +// if (useCache) { +// val cacheDirectory = +// File(Constant.MAP_PATH, "cache") +// val cacheSize = 300 * 1024 * 1024 // 300 MB +// val cache = Cache(cacheDirectory, cacheSize.toLong()) +// builder.cache(cache) +// } +// +// return BitmapTileLayer(mMapView.vtmMap, mTileSource) +// } } /** diff --git a/collect-library/src/main/java/com/navinfo/collect/library/map/handler/LocationLayerHandler.kt b/collect-library/src/main/java/com/navinfo/collect/library/map/handler/LocationLayerHandler.kt index 7ca739be..e6558e75 100644 --- a/collect-library/src/main/java/com/navinfo/collect/library/map/handler/LocationLayerHandler.kt +++ b/collect-library/src/main/java/com/navinfo/collect/library/map/handler/LocationLayerHandler.kt @@ -8,12 +8,12 @@ import com.baidu.location.BDLocation import com.baidu.location.LocationClient import com.baidu.location.LocationClientOption import com.baidu.location.LocationClientOption.LocationMode +import com.navinfo.collect.library.map.GeoPoint import com.navinfo.collect.library.map.NIMapView import org.oscim.layers.LocationLayer -class LocationLayerHandler(context: Context, mapView: NIMapView) : - BaseHandler(context, mapView) { +class LocationLayerHandler(context: Context, mapView: NIMapView) : BaseHandler(context, mapView) { private var mCurrentLocation: BDLocation? = null private var bFirst = true @@ -55,13 +55,11 @@ class LocationLayerHandler(context: Context, mapView: NIMapView) : val errorCode = it.locType mCurrentLocation = it mLocationLayer.setPosition( - it.latitude, - it.longitude, - it.radius + it.latitude, it.longitude, it.radius ) //第一次定位成功显示当前位置 if (this.bFirst) { - animateToCurrentPosition() + animateToCurrentPosition(16.0) } } @@ -97,9 +95,7 @@ class LocationLayerHandler(context: Context, mapView: NIMapView) : // locationOption.setOpenAutoNotifyMode() //设置打开自动回调位置模式,该开关打开后,期间只要定位SDK检测到位置变化就会主动回调给开发者 locationOption.setOpenAutoNotifyMode( - 1000, - 1, - LocationClientOption.LOC_SENSITIVITY_HIGHT + 1000, 1, LocationClientOption.LOC_SENSITIVITY_HIGHT ) //需将配置好的LocationClientOption对象,通过setLocOption方法传递给LocationClient对象使用 locationClient.locOption = locationOption @@ -130,21 +126,36 @@ class LocationLayerHandler(context: Context, mapView: NIMapView) : /** * 回到当前位置 */ - fun animateToCurrentPosition() - { + fun animateToCurrentPosition(zoom: Double) { + mCurrentLocation?.run { - val mapPosition = mMapView.vtmMap.mapPosition; - mapPosition.setPosition(this.latitude, this.longitude); - mMapView.vtmMap.animator().animateTo(300, mapPosition); + val mapPosition = mMapView.vtmMap.mapPosition + mapPosition.zoom = zoom + mapPosition.setPosition(this.latitude, this.longitude) + mMapView.vtmMap.animator().animateTo(300, mapPosition) } } + + fun animateToCurrentPosition() { + mCurrentLocation?.run { + val mapPosition = mMapView.vtmMap.mapPosition + mapPosition.setPosition(this.latitude, this.longitude) + mMapView.vtmMap.animator().animateTo(300, mapPosition) + } + } + + fun getCurrentGeoPoint(): GeoPoint? { + mCurrentLocation?.let { + return GeoPoint(it.latitude, it.longitude) + } + return null + } } /** * 实现定位回调 */ -private class MyLocationListener(callback: (BDLocation) -> Unit) : - BDAbstractLocationListener() { +private class MyLocationListener(callback: (BDLocation) -> Unit) : BDAbstractLocationListener() { val call = callback; override fun onReceiveLocation(location: BDLocation) { call(location) diff --git a/collect-library/src/main/java/com/navinfo/collect/library/map/handler/MarkHandler.kt b/collect-library/src/main/java/com/navinfo/collect/library/map/handler/MarkHandler.kt index 8c86e51b..7fbee159 100644 --- a/collect-library/src/main/java/com/navinfo/collect/library/map/handler/MarkHandler.kt +++ b/collect-library/src/main/java/com/navinfo/collect/library/map/handler/MarkHandler.kt @@ -1,75 +1,101 @@ package com.navinfo.collect.library.map.handler import android.content.Context +import android.graphics.BitmapFactory +import com.navinfo.collect.library.R +import com.navinfo.collect.library.map.GeoPoint import com.navinfo.collect.library.map.NIMapView import com.navinfo.collect.library.utils.StringUtil -import org.oscim.core.GeoPoint +import org.oscim.android.canvas.AndroidBitmap +import org.oscim.layers.marker.ItemizedLayer import org.oscim.layers.marker.ItemizedLayer.OnItemGestureListener import org.oscim.layers.marker.MarkerInterface import org.oscim.layers.marker.MarkerItem +import org.oscim.layers.marker.MarkerSymbol /** * marker 操作 */ -open class MarkHandler(context: Context, mapView:NIMapView) : - BaseHandler(context, mapView), OnItemGestureListener { +class MarkHandler(context: Context, mapView: NIMapView) : + BaseHandler(context, mapView) { -// //增加marker -// fun addMarker( -// geoPoint: GeoPoint, -// title: String?, -// description: String? = "" -// ): MarkerItem { -// var marker: MarkerItem? = null -// for (e in mMapView.layerManager.defaultMarkerLayer.itemList) { -// if (e is MarkerItem && e.title == title) { -// marker = e; -// break; -// } -// } -// if (marker == null) { -// var tempTitle = title; -// if (tempTitle.isNullOrBlank()) { -// tempTitle = StringUtil.createUUID(); -// } -// val marker = MarkerItem( -// tempTitle, -// description, -// geoPoint -// ) -// mMapView.layerManager.defaultMarkerLayer.addItem(marker); -// mMapView.vtmMap.updateMap(true) -// return marker -// } else { -// marker.description = description -// marker.geoPoint = geoPoint -// mMapView.layerManager.defaultMarkerLayer.removeItem(marker) -// mMapView.layerManager.defaultMarkerLayer.addItem(marker) -// mMapView.vtmMap.updateMap(true) -// return marker -// } -// } -// -// fun removeMarker(title: String) { -// var marker: MarkerItem? = null -// for (e in mMapView.layerManager.defaultMarkerLayer.itemList) { -// if (e is MarkerItem && e.title == title) { -// marker = e; -// break; -// } -// } -// if (marker != null) { -// mMapView.layerManager.defaultMarkerLayer.removeItem(marker) -// mMapView.vtmMap.updateMap(true) -// } -// } -// - override fun onItemSingleTapUp(index: Int, item: MarkerInterface): Boolean { - return false + // //默认marker图层 + private var mDefaultMarkerLayer: ItemizedLayer + + init { + //新增marker图标样式 + val mDefaultBitmap = + AndroidBitmap(BitmapFactory.decodeResource(context.resources, R.mipmap.marker)); + + val markerSymbol = MarkerSymbol( + mDefaultBitmap, + MarkerSymbol.HotspotPlace.BOTTOM_CENTER + ); + //新增marker图层 + mDefaultMarkerLayer = ItemizedLayer( + mapView.vtmMap, + ArrayList(), + markerSymbol, + object : OnItemGestureListener { + override fun onItemSingleTapUp(index: Int, item: MarkerInterface?): Boolean { + return false + } + + override fun onItemLongPress(index: Int, item: MarkerInterface?): Boolean { + return false + } + + } + ) + addLayer(mDefaultMarkerLayer, NIMapView.LAYER_GROUPS.OPERATE); } - override fun onItemLongPress(index: Int, item: MarkerInterface): Boolean { - return false + //增加marker + fun addMarker( + geoPoint: GeoPoint, + title: String?, + description: String? = "" + ) { + var marker: MarkerItem? = null + for (e in mDefaultMarkerLayer.itemList) { + if (e is MarkerItem && e.title == title) { + marker = e + break + } + } + if (marker == null) { + var tempTitle = title; + if (tempTitle.isNullOrBlank()) { + tempTitle = StringUtil.createUUID(); + } + val marker = MarkerItem( + tempTitle, + description, + org.oscim.core.GeoPoint(geoPoint.latitude, geoPoint.longitude) + ) + mDefaultMarkerLayer.addItem(marker); + mMapView.vtmMap.updateMap(true) + } else { + marker.description = description + marker.geoPoint = org.oscim.core.GeoPoint(geoPoint.latitude, geoPoint.longitude) + mDefaultMarkerLayer.removeItem(marker) + mDefaultMarkerLayer.addItem(marker) + mMapView.vtmMap.updateMap(true) + } + } + + fun removeMarker(title: String) { + var marker: MarkerItem? = null + for (e in mDefaultMarkerLayer.itemList) { + if (e is MarkerItem && e.title == title) { + marker = e + break + } + } + if (marker != null) { + mDefaultMarkerLayer.removeItem(marker) + mMapView.vtmMap.updateMap(true) + } } } \ No newline at end of file diff --git a/collect-library/src/main/java/com/navinfo/collect/library/map/handler/ViewportHandler.kt b/collect-library/src/main/java/com/navinfo/collect/library/map/handler/ViewportHandler.kt index d2463cf2..dd3ef9d7 100644 --- a/collect-library/src/main/java/com/navinfo/collect/library/map/handler/ViewportHandler.kt +++ b/collect-library/src/main/java/com/navinfo/collect/library/map/handler/ViewportHandler.kt @@ -7,7 +7,6 @@ import org.oscim.core.GeoPoint import org.oscim.core.Point open class ViewportHandler(context: Context, mapView: NIMapView) : BaseHandler(context, mapView) { - /** * Set pivot horizontal / vertical relative to view center in [-1, 1]. * e.g. pivotY 0.5 is usually preferred for navigation, moving center to 25% of view height. @@ -73,7 +72,7 @@ open class ViewportHandler(context: Context, mapView: NIMapView) : BaseHandler(c px: Float, py: Float ): Map { - val geo = mMapView.vtmMap.viewport().fromScreenPoint(px,py) + val geo = mMapView.vtmMap.viewport().fromScreenPoint(px, py) return mapOf( "latitude" to geo.latitude, diff --git a/collect-library/src/main/java/com/navinfo/collect/library/map/source/NavinfoMultiMapFileTileSource.java b/collect-library/src/main/java/com/navinfo/collect/library/map/source/NavinfoMultiMapFileTileSource.java index aea634f0..e4b064fa 100644 --- a/collect-library/src/main/java/com/navinfo/collect/library/map/source/NavinfoMultiMapFileTileSource.java +++ b/collect-library/src/main/java/com/navinfo/collect/library/map/source/NavinfoMultiMapFileTileSource.java @@ -1,39 +1,38 @@ package com.navinfo.collect.library.map.source; +import android.util.Log; + +import com.navinfo.collect.library.system.Constant; + import org.oscim.core.BoundingBox; -import org.oscim.core.MapElement; -import org.oscim.core.Tag; import org.oscim.core.Tile; import org.oscim.tiling.ITileDataSource; import org.oscim.tiling.OverzoomTileDataSource; import org.oscim.tiling.source.UrlTileSource; -import org.oscim.tiling.source.geojson.GeojsonTileSource; import org.oscim.tiling.source.mapfile.IMapFileTileSource; import org.oscim.tiling.source.mapfile.MapDatabase; import org.oscim.tiling.source.mapfile.MapFileTileSource; -import org.oscim.utils.FastMath; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; -import java.util.LinkedHashMap; import java.util.List; import java.util.Map; /* - *com.navinfo.map.source + *com.nmp.map.source *zhjch *2021/9/17 *9:46 *说明() */ -public class NavinfoMultiMapFileTileSource extends GeojsonTileSource implements IMapFileTileSource { +public class NavinfoMultiMapFileTileSource extends UrlTileSource implements IMapFileTileSource { -// private static final String DEFAULT_URL = "http://cmp-gateway-sp9-port.ayiqdpfs.cloud.app.ncloud.navinfo.com/maponline/map/online"; - private static final String DEFAULT_URL = "http://epohvqjxts85k6u-port.ayiqdpfs.cloud.app.ncloud.navinfo.com/map/online/geoJson"; - private static final String DEFAULT_PATH = "x={X}&y={Y}&z={Z}"; + private static final String DEFAULT_URL = "http://c.tile.opencyclemap.org/cycle"; + + private static final String DEFAULT_PATH = "/{Z}}/{X}/{Y}.png"; private static final TileUrlFormatter mTileUrlFormatter = URL_FORMATTER; @@ -67,7 +66,6 @@ public class NavinfoMultiMapFileTileSource extends GeojsonTileSource implements public String getTileUrl(Tile tile) { StringBuilder sb = new StringBuilder(); sb.append(DEFAULT_URL).append("?").append(mTileUrlFormatter.formatTilePath(this, tile)); - System.out.println(sb.toString()); return sb.toString(); } @@ -125,66 +123,13 @@ public class NavinfoMultiMapFileTileSource extends GeojsonTileSource implements mapDatabase.restrictToZoomRange(zoomLevels[0], zoomLevels[1]); multiMapDatabase.add(mapDatabase); } catch (IOException e) { + Log.e("jingo", e.getMessage()); log.debug(e.getMessage()); } } // return new NavinfoMultiMapDatabase(this, new NavinfoVectorTileDecoder(locale), getHttpEngine()); return new OverzoomTileDataSource(multiMapDatabase, mOverZoom); } - private static Map mappings = new LinkedHashMap<>(); - - private static Tag addMapping(String key, String val) { - Tag tag = new Tag(key, val); - mappings.put(key + "=" + val, tag); - return tag; - } - @Override - public void decodeTags(MapElement mapElement, Map properties) { - boolean hasName = false; - String fallbackName = null; - - for (Map.Entry entry : properties.entrySet()) { - String key = entry.getKey(); - Object value = entry.getValue(); - String val = (value instanceof String) ? (String) value : String.valueOf(value); - - if (key.startsWith(Tag.KEY_NAME)) { - int len = key.length(); - if (len == 4) { - fallbackName = val; - continue; - } - if (len < 7) - continue; - if (locale.equals(key.substring(5))) { - hasName = true; - mapElement.tags.add(new Tag(Tag.KEY_NAME, val, false)); - } - continue; - } - - Tag tag = mappings.get(key + "=" + val); - if (tag == null) - tag = addMapping(key, val); - mapElement.tags.add(tag); - } - - if (!hasName && fallbackName != null) - mapElement.tags.add(new Tag(Tag.KEY_NAME, fallbackName, false)); - - // Calculate height of building parts - if (!properties.containsKey(Tag.KEY_HEIGHT)) { - if (properties.containsKey(Tag.KEY_VOLUME) && properties.containsKey(Tag.KEY_AREA)) { - Object volume = properties.get(Tag.KEY_VOLUME); - String volumeStr = (volume instanceof String) ? (String) volume : String.valueOf(volume); - Object area = properties.get(Tag.KEY_AREA); - String areaStr = (area instanceof String) ? (String) area : String.valueOf(area); - float height = Float.parseFloat(volumeStr) / Float.parseFloat(areaStr); - String heightStr = String.valueOf(FastMath.round2(height)); - mapElement.tags.add(new Tag(Tag.KEY_HEIGHT, heightStr, false)); - } - } - } @Override public OpenResult open() { @@ -194,8 +139,10 @@ public class NavinfoMultiMapFileTileSource extends GeojsonTileSource implements if (result != OpenResult.SUCCESS) openResult = result; } - if(openResult != OpenResult.SUCCESS) - return super.open(); + if(openResult != OpenResult.SUCCESS) { + + return super.open(); + } return openResult; } diff --git a/collect-library/src/main/java/com/navinfo/collect/library/system/Constant.java b/collect-library/src/main/java/com/navinfo/collect/library/system/Constant.java index d6156d20..6b355bdb 100644 --- a/collect-library/src/main/java/com/navinfo/collect/library/system/Constant.java +++ b/collect-library/src/main/java/com/navinfo/collect/library/system/Constant.java @@ -11,6 +11,9 @@ import java.util.Map; public class Constant { + //服务地址 + public static String URL_BASE = "http://cmp-gateway-sp9-port.ayiqdpfs.cloud.app.ncloud.navinfo.com/"; + public static String MAP_PATH = Environment.getExternalStorageDirectory() + "/map/"; public static void setVisibleTypeMap(Map visibleTypeMap) { diff --git a/collect-library/src/main/java/com/navinfo/collect/library/utils/GeometryToolsKt.kt b/collect-library/src/main/java/com/navinfo/collect/library/utils/GeometryToolsKt.kt index a0469a68..5fdddace 100644 --- a/collect-library/src/main/java/com/navinfo/collect/library/utils/GeometryToolsKt.kt +++ b/collect-library/src/main/java/com/navinfo/collect/library/utils/GeometryToolsKt.kt @@ -10,7 +10,7 @@ class GeometryToolsKt { /** * 根据给定的geometry计算其横跨的20级瓦片Y值 */ - fun getTileYByGeometry(wkt: String, tileYSet: MutableSet): Set? { + fun getTileYByGeometry(wkt: String, tileYSet: MutableSet){ val reader = WKTReader() val geometry = reader.read(wkt); @@ -20,6 +20,7 @@ class GeometryToolsKt { if (tileYSet == null) { tileYSet = RealmSet() } + tileYSet.clear() val envelope = geometry.envelope if (envelope != null) { val coordinates = envelope.coordinates @@ -48,13 +49,12 @@ class GeometryToolsKt { } } println("YGeometry-time:" + (System.currentTimeMillis() - startTime)) - return tileYSet } /** * 根据给定的geometry计算其横跨的20级瓦片X值 */ - fun getTileXByGeometry(wkt: String, tileXSet: MutableSet): Set? { + fun getTileXByGeometry(wkt: String, tileXSet: MutableSet) { val reader = WKTReader() val geometry = reader.read(wkt); @@ -63,6 +63,7 @@ class GeometryToolsKt { if (tileXSet == null) { tileXSet = RealmSet() } + tileXSet.clear() if (geometry != null) { val envelope = geometry.envelope if (envelope != null) { @@ -92,7 +93,6 @@ class GeometryToolsKt { } } println("XGeometry-time:" + (System.currentTimeMillis() - startTime)) - return tileXSet } fun getMasterPoint(wkt: String): String { diff --git a/settings.gradle b/settings.gradle index d591dffb..b39629d4 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,30 +1,32 @@ pluginManagement { repositories { - gradlePluginPortal() google() + gradlePluginPortal() mavenCentral() - maven { url 'https://jitpack.io' } maven { url "https://maven.aliyun.com/repository/google" } maven { url 'https://maven.aliyun.com/repository/gradle-plugin' } maven { url 'https://maven.aliyun.com/repository/public' } maven { url 'https://maven.aliyun.com/repository/jcenter' } - maven { url 'https://download.flutter.io' } - maven { url 'https://storage.googleapis.com/download.flutter.io' } + maven { url 'https://maven.aliyun.com/repository/central' } + maven { url 'https://jitpack.io' } } } dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { + google() mavenCentral() gradlePluginPortal() - maven { url 'https://jitpack.io' } + maven { url "https://maven.aliyun.com/repository/google" } maven { url 'https://maven.aliyun.com/repository/gradle-plugin' } maven { url 'https://maven.aliyun.com/repository/public' } maven { url 'https://maven.aliyun.com/repository/jcenter' } - maven { url 'https://download.flutter.io' } - maven { url 'https://storage.googleapis.com/download.flutter.io' } + maven { url 'https://maven.aliyun.com/repository/central' } + maven { url 'https://jitpack.io' } + + } } rootProject.name = "OMQualityInspection"