diff --git a/app/build.gradle b/app/build.gradle index 0210770..a020d87 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -2,9 +2,10 @@ plugins { id 'com.android.application' id 'org.jetbrains.kotlin.android' id 'kotlin-android' - id 'kotlin-parcelize' + id 'kotlin-parcelize' // 序列化 id 'kotlin-kapt' - id 'dagger.hilt.android.plugin' + id 'dagger.hilt.android.plugin' //hilt 依赖注入 + id "com.google.protobuf" version "0.8.17" //Proto DataStore 插件 // id 'com.google.dagger.hilt.android' } @@ -82,7 +83,7 @@ dependencies { //room 数据库相关 implementation 'com.tencent.wcdb:room:1.1-19' // 代替 room-runtime,同时也不需要再引用 wcdb-android - api 'androidx.sqlite:sqlite:2.2.0' + implementation 'androidx.sqlite:sqlite-ktx:2.2.0' implementation 'androidx.room:room-runtime:2.4.3' implementation 'androidx.room:room-ktx:2.4.3' annotationProcessor 'androidx.room:room-compiler:2.4.3' @@ -90,13 +91,12 @@ dependencies { kapt 'android.arch.persistence.room:compiler:1.1.1'// compiler 需要用 room 的 kapt 'androidx.room:room-compiler:2.4.3' kapt 'androidx.room:room-ktx:2.4.3' + //分页加载 implementation "androidx.room:room-paging:2.4.3" implementation "androidx.paging:paging-runtime-ktx:3.1.1" androidTestImplementation "android.arch.persistence.room:testing:1.1.1" -// implementation "android.arch.lifecycle:extensions:1.1.1" -// annotationProcessor "android.arch.lifecycle:compiler:1.1.1" implementation 'com.tencent.wcdb:wcdb-android:1.1-19' @@ -108,29 +108,20 @@ dependencies { //带侧滑的自定义列表 implementation 'com.yanzhenjie.recyclerview:x:1.3.2' -// implementation 'androidx.appcompat:appcompat:1.5.1' - -// // Koin -// implementation("io.insert-koin:koin-android:3.3.2") -// implementation("io.insert-koin:koin-core:3.3.2") + //下拉刷新,上拉加载 + implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0" // Retrofit 网络请求相关 implementation("com.squareup.retrofit2:retrofit:2.9.0") implementation("com.squareup.retrofit2:converter-gson:2.9.0") implementation("com.squareup.retrofit2:converter-gson:2.9.0") -// const val chuck = "com.readystatesoftware.chuck:library:${Versions.chuck}" -// const val chuckNoOp = "com.readystatesoftware.chuck:library-no-op:${Versions.chuck}" implementation("com.squareup.okhttp3:okhttp:4.9.0") implementation("com.squareup.okhttp3:logging-interceptor:4.9.0") implementation("com.google.code.gson:gson:2.8.6") - //hilt implementation "com.google.dagger:hilt-android:2.41" kapt "com.google.dagger:hilt-compiler:2.41" -// implementation "androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha03" -// androidTestImplementation "com.google.dagger:hilt-android-testing:2.41" -// kaptAndroidTest "com.google.dagger:hilt-android-compiler:2.41" // 显示错误提示 https://github.com/nhaarman/supertooltips implementation 'com.nhaarman.supertooltips:library:3.0.0' @@ -155,6 +146,30 @@ dependencies { // 图片查看器 https://github.com/XiaoGe-1996/ImageViewer implementation 'com.github.XiaoGe-1996:ImageViewer:v1.0.0' implementation 'com.github.majidarabi:AndroidFilePicker:0.2.1' + // 数据存储 Preferences DataStore 键值对 + implementation "androidx.datastore:datastore-preferences:1.0.0" + // 数据存储 Proto DataStore 对象 + implementation "androidx.datastore:datastore-core:1.0.0" + implementation "com.google.protobuf:protobuf-javalite:3.18.0" +} + +//Proto DataStore 数据存储插件 +protobuf { + protoc { + artifact = "com.google.protobuf:protoc:3.14.0" + } + // Generates the java Protobuf-lite code for the Protobufs in this project. See + // https://github.com/google/protobuf-gradle-plugin#customizing-protobuf-compilation + // for more information. + generateProtoTasks { + all().each { task -> + task.builtins { + java { + option 'lite' + } + } + } + } } kapt { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index d539e1e..8bfd0a9 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -24,14 +24,15 @@ android:dataExtractionRules="@xml/data_extraction_rules" android:fullBackupContent="@xml/backup_rules" android:icon="@mipmap/volvo_logo_small" - android:requestLegacyExternalStorage="true" android:label="@string/app_name" + android:requestLegacyExternalStorage="true" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.NavinfoVolvo" android:usesCleartextTraffic="true"> - + \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/volvo/Constant.kt b/app/src/main/java/com/navinfo/volvo/Constant.kt index 60c9260..0af2369 100644 --- a/app/src/main/java/com/navinfo/volvo/Constant.kt +++ b/app/src/main/java/com/navinfo/volvo/Constant.kt @@ -9,6 +9,8 @@ class Constant { */ const val SERVER_ADDRESS = "http://ec2-52-81-73-5.cn-north-1.compute.amazonaws.com.cn:8088/" val DEBUG = Boolean.parseBoolean("true") + + const val message_status_late = "预约,待发送" } } \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/volvo/MyApplication.kt b/app/src/main/java/com/navinfo/volvo/MyApplication.kt index d076fd5..d8aa2ba 100644 --- a/app/src/main/java/com/navinfo/volvo/MyApplication.kt +++ b/app/src/main/java/com/navinfo/volvo/MyApplication.kt @@ -1,8 +1,39 @@ package com.navinfo.volvo +import android.app.Activity import android.app.Application +import android.content.pm.ActivityInfo +import android.os.Bundle import dagger.hilt.android.HiltAndroidApp @HiltAndroidApp open class MyApplication : Application() { + override fun onCreate() { + super.onCreate() + + registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacks { + + override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) { + activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT + } + + override fun onActivityStarted(activity: Activity) { + } + + override fun onActivityResumed(activity: Activity) { + } + + override fun onActivityPaused(activity: Activity) { + } + + override fun onActivityStopped(activity: Activity) { + } + + override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) { + } + + override fun onActivityDestroyed(activity: Activity) { + } + }) + } } \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/volvo/database/MapLifeDataBase.java b/app/src/main/java/com/navinfo/volvo/database/MapLifeDataBase.java deleted file mode 100644 index f1ebae1..0000000 --- a/app/src/main/java/com/navinfo/volvo/database/MapLifeDataBase.java +++ /dev/null @@ -1,191 +0,0 @@ -//package com.navinfo.volvo.database; -// -// -//import android.content.Context; -// -//import androidx.annotation.NonNull; -//import androidx.room.Database; -//import androidx.room.Room; -//import androidx.room.RoomDatabase; -//import androidx.sqlite.db.SupportSQLiteDatabase; -//import androidx.sqlite.db.SupportSQLiteOpenHelper; -// -//import com.navinfo.volvo.database.dao.MessageDao; -//import com.navinfo.volvo.database.dao.UserDao; -//import com.navinfo.volvo.database.entity.Message; -//import com.navinfo.volvo.database.entity.Attachment; -//import com.navinfo.volvo.database.entity.User; -//import com.tencent.wcdb.database.SQLiteCipherSpec; -//import com.tencent.wcdb.database.SQLiteDatabase; -// -//import com.tencent.wcdb.room.db.WCDBOpenHelperFactory; -// -//import android.os.AsyncTask; -//import android.util.Log; -// -//import com.tencent.wcdb.repair.BackupKit; -//import com.tencent.wcdb.repair.RecoverKit; -//import com.tencent.wcdb.room.db.WCDBDatabase; -// -//@Database(entities = {Message.class, Attachment.class, User.class}, version = 1, exportSchema = false) -//public abstract class MapLifeDataBase extends RoomDatabase { -// // marking the instance as volatile to ensure atomic access to the variable -// /** -// * 数据库单例对象 -// */ -// private static volatile MapLifeDataBase INSTANCE; -// -// /** -// * 要素数据库类 -// */ -// public abstract MessageDao getMessageDao(); -// -// public abstract UserDao getUserDao(); -// -// /** -// * 数据库秘钥 -// */ -// private final static String DB_PASSWORD = "123456"; -// -// public static MapLifeDataBase getDatabase(final Context context, final String name) { -// if (INSTANCE == null) { -// synchronized (MapLifeDataBase.class) { -// if (INSTANCE == null) { -// // [WCDB] To use Room library with WCDB, pass a WCDBOpenHelper factory object -// // to the database builder with .openHelperFactory(...). In the factory object, -// // you can specify passphrase and cipher options to open or create encrypted -// // database, as well as optimization options like asynchronous checkpoint. -// SQLiteCipherSpec cipherSpec = new SQLiteCipherSpec() -// .setPageSize(1024) -// .setSQLCipherVersion(3); -// WCDBOpenHelperFactory factory = new WCDBOpenHelperFactory() -// .passphrase(DB_PASSWORD.getBytes()) // 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 -// -// INSTANCE = Room.databaseBuilder(context.getApplicationContext(), MapLifeDataBase.class, name) -// -// // [WCDB] Specify open helper to use WCDB database implementation instead -// // of the Android framework. -// .openHelperFactory((SupportSQLiteOpenHelper.Factory) factory) -// -// // Wipes and rebuilds instead of migrating if no Migration object. -// // Migration is not part of this codelab. -// .fallbackToDestructiveMigration().addCallback(sRoomDatabaseCallback).build(); -// } -// } -// } -// return INSTANCE; -// } -// -// /** -// * Override the onOpen method to populate the database. -// * For this sample, we clear the database every time it is created or opened. -// *

-// * If you want to populate the database only when the database is created for the 1st time, -// * override RoomDatabase.Callback()#onCreate -// */ -// private static Callback sRoomDatabaseCallback = new Callback() { -// -// @Override -// public void onOpen(@NonNull SupportSQLiteDatabase db) { -// super.onOpen(db); -// // If you want to keep the data through app restarts, -// // comment out the following line. -// new PopulateDbAsync(INSTANCE).execute(); -// } -// }; -// -// /** -// * Populate the database in the background. -// * If you want to start with more words, just add them. -// */ -// private static class PopulateDbAsync extends AsyncTask { -// -// private final MessageDao messageDao; -// -// PopulateDbAsync(MapLifeDataBase db) { -// messageDao = db.getMessageDao(); -// } -// -// @Override -// protected Void doInBackground(final Void... params) { -// // Start the app with a clean database every time. -// // Not needed if you only populate on creation. -// //mDao.deleteAll(); -// Log.e("qj", "doInBackground"); -// return null; -// } -// } -// -// /** -// * 数据恢复 -// */ -// protected boolean recoverData() { -// if (INSTANCE != null) { -// SQLiteDatabase sqlite = ((WCDBDatabase) INSTANCE.getOpenHelper().getWritableDatabase()).getInnerDatabase(); -// RecoverKit recover = new RecoverKit(sqlite, // 要恢复到的目标 DB -// sqlite.getPath() + "-backup", // 备份文件 -// DB_PASSWORD.getBytes() // 加密备份文件的密钥,非 DB 密钥 -// ); -// int result = recover.run(false); // fatal 参数传 false 表示遇到错误忽略并继续, -// // 若传 true 遇到错误则中止并返回 FAILED -// switch (result) { -// case RecoverKit.RESULT_OK: -// /* 成功 */ -// Log.e("qj", "sRoomDatabaseCallback==RecoverKit成功"); -// return true; -// case RecoverKit.RESULT_CANCELED: /* 取消操作 */ -// Log.e("qj", "sRoomDatabaseCallback==RecoverKit取消操作"); -// break; -// case RecoverKit.RESULT_FAILED: /* 失败 */ -// Log.e("qj", "sRoomDatabaseCallback==RecoverKit失败"); -// break; -// -// } -// -// recover.release(); -// } -// -// return false; -// } -// -// /** -// * 备份数据 -// */ -// protected boolean backup() { -// Log.e("qj", "sRoomDatabaseCallback===backup==start"); -// if (INSTANCE != null) { -// //备份文件 -// SQLiteDatabase sqlite = ((WCDBDatabase) INSTANCE.getOpenHelper().getWritableDatabase()).getInnerDatabase(); -// BackupKit backup = new BackupKit(sqlite, // 要备份的 DB -// sqlite.getPath() + "-backup", // 备份文件 -// "123456".getBytes(), // 加密备份文件的密钥,非 DB 密钥 -// 0, null); -// int result = backup.run(); -// switch (result) { -// case BackupKit.RESULT_OK: -// /* 成功 */ -// Log.e("qj", "sRoomDatabaseCallback==成功"); -// return true; -// case BackupKit.RESULT_CANCELED: -// /* 取消操作 */ -// Log.e("qj", "sRoomDatabaseCallback==取消操作"); -// break; -// case BackupKit.RESULT_FAILED: -// /* 失败 */ -// Log.e("qj", "sRoomDatabaseCallback==失败"); -// break; -// } -// -// backup.release(); -// } -// Log.e("qj", "sRoomDatabaseCallback===backup==end"); -// return false; -// } -// -// protected void release() { -// INSTANCE = null; -// } -//} diff --git a/app/src/main/java/com/navinfo/volvo/database/dao/GreetingMessageDao.kt b/app/src/main/java/com/navinfo/volvo/database/dao/GreetingMessageDao.kt index 3da19ab..26b4c98 100644 --- a/app/src/main/java/com/navinfo/volvo/database/dao/GreetingMessageDao.kt +++ b/app/src/main/java/com/navinfo/volvo/database/dao/GreetingMessageDao.kt @@ -1,21 +1,28 @@ package com.navinfo.volvo.database.dao +import android.util.Log import androidx.paging.PagingSource import androidx.room.* +import com.navinfo.volvo.Constant import com.navinfo.volvo.database.entity.GreetingMessage import kotlinx.coroutines.flow.Flow @Dao interface GreetingMessageDao { + @Query("DELETE from GreetingMessage WHERE id=:id") + suspend fun deleteById(id: Long) + @Insert - fun insert(message: GreetingMessage): Long + suspend fun insert(message: GreetingMessage): Long @Update(onConflict = OnConflictStrategy.REPLACE) - fun update(message: GreetingMessage) + suspend fun update(message: GreetingMessage) - - @Query("SELECT count(id) FROM GreetingMessage WHERE read = 0") + /** + * 未读消息统计 + */ + @Query("SELECT count(id) FROM GreetingMessage WHERE status = '${Constant.message_status_late}'") fun countUnreadByFlow(): Flow /** @@ -27,18 +34,29 @@ interface GreetingMessageDao { /** * 检查某条数据是否存在 */ - @Query("SELECT id From GreetingMessage WHERE id = :id LIMIT 1") - fun getMessageId(id: Long): Long + @Query("SELECT uuid From GreetingMessage WHERE id = :id LIMIT 1") + suspend fun getMessageId(id: Long): Long? + /** + * + */ @Transaction suspend fun insertOrUpdate(list: List) { for (message in list) { - val id = getMessageId(message.id) - if (id == 0L) { - insert(message) - }else{ + Log.e("jingo", "insertOrUpdate ${message.id}") + val uuid = getMessageId(message.id) + Log.e("jingo", "insertOrUpdate $uuid") + if (uuid == null || uuid == 0L) { + Log.e("jingo", "insertOrUpdate start ") + val l = insert(message) + Log.e("jingo", "insertOrUpdate $l ") + } else { + message.uuid = uuid update(message) } + Log.e("jingo", "insertOrUpdate end") } } + + } \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/volvo/di/module/NetworkUtilModule.kt b/app/src/main/java/com/navinfo/volvo/di/module/NetworkUtilModule.kt index 6e0212e..406016f 100644 --- a/app/src/main/java/com/navinfo/volvo/di/module/NetworkUtilModule.kt +++ b/app/src/main/java/com/navinfo/volvo/di/module/NetworkUtilModule.kt @@ -22,6 +22,7 @@ import javax.inject.Singleton @Module class NetworkUtilModule { + @Provides @Singleton fun provideContext(application: Application): Context { diff --git a/app/src/main/java/com/navinfo/volvo/di/module/UtilModule.kt b/app/src/main/java/com/navinfo/volvo/di/module/UtilModule.kt index 316e5c5..32af514 100644 --- a/app/src/main/java/com/navinfo/volvo/di/module/UtilModule.kt +++ b/app/src/main/java/com/navinfo/volvo/di/module/UtilModule.kt @@ -1,7 +1,13 @@ package com.navinfo.volvo.di.module import android.content.Context -import com.navinfo.volvo.util.SharedPreferenceHelper +import androidx.datastore.core.DataStore +import androidx.datastore.core.DataStoreFactory +import androidx.datastore.core.Serializer +import androidx.datastore.dataStoreFile +import androidx.datastore.preferences.core.Preferences +import androidx.datastore.preferences.preferencesDataStore +import com.navinfo.volvo.model.proto.LoginUser import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -14,7 +20,12 @@ class UtilModule { @Provides @Singleton - fun provideSharedPreferencesHelper(context: Context): SharedPreferenceHelper { - return SharedPreferenceHelper.getInstance(context) - } + fun provideLoginUserDataStore( + context: Context, + serializer: Serializer + ): DataStore = DataStoreFactory.create( + serializer = serializer, + produceFile = { context.dataStoreFile("login_user") } + ) + } \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/volvo/di/module/UtilRepositoryModule.kt b/app/src/main/java/com/navinfo/volvo/di/module/UtilRepositoryModule.kt new file mode 100644 index 0000000..ec5dd0e --- /dev/null +++ b/app/src/main/java/com/navinfo/volvo/di/module/UtilRepositoryModule.kt @@ -0,0 +1,22 @@ +package com.navinfo.volvo.di.module + +import androidx.datastore.core.Serializer +import com.navinfo.volvo.model.proto.LoginUser +import com.navinfo.volvo.model.proto.LoginUserSerializer +import com.navinfo.volvo.repository.preferences.PreferencesRepository +import com.navinfo.volvo.repository.preferences.PreferencesRepositoryImp +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@InstallIn(SingletonComponent::class) +@Module +abstract class UtilRepositoryModule { + @Binds + abstract fun providePreferencesRepository(preferencesRepositoryImp: PreferencesRepositoryImp): PreferencesRepository + + @Binds + abstract fun userLocalSerializer(impl: LoginUserSerializer): Serializer +} \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/volvo/di/module/ViewModelFactory.kt b/app/src/main/java/com/navinfo/volvo/di/module/ViewModelFactory.kt index 079be5e..93bd5ce 100644 --- a/app/src/main/java/com/navinfo/volvo/di/module/ViewModelFactory.kt +++ b/app/src/main/java/com/navinfo/volvo/di/module/ViewModelFactory.kt @@ -1,35 +1,35 @@ -package com.navinfo.volvo.di.module - -import androidx.lifecycle.ViewModel -import androidx.lifecycle.ViewModelProvider -import javax.inject.Inject -import javax.inject.Provider -import javax.inject.Singleton - - -/** - * Factory for all ViewModels. - * reference : https://github.com/googlesamples/android-architecture-components - */ -@Singleton -class ViewModelFactory @Inject constructor( - private val viewModelMap: Map, @JvmSuppressWildcards Provider> -) : ViewModelProvider.Factory { - - @Suppress("UNCHECKED_CAST") - override fun create(modelClass: Class): T { - var viewModel = viewModelMap[modelClass] - - if (viewModel == null) { - for (entry in viewModelMap) { - if (modelClass.isAssignableFrom(entry.key)) { - viewModel = entry.value - break - } - } - } - - if (viewModel == null) throw IllegalArgumentException("Unknown model class $modelClass") - return viewModel.get() as T - } -} +//package com.navinfo.volvo.di.module +// +//import androidx.lifecycle.ViewModel +//import androidx.lifecycle.ViewModelProvider +//import javax.inject.Inject +//import javax.inject.Provider +//import javax.inject.Singleton +// +// +///** +// * Factory for all ViewModels. +// * reference : https://github.com/googlesamples/android-architecture-components +// */ +//@Singleton +//class ViewModelFactory @Inject constructor( +// private val viewModelMap: Map, @JvmSuppressWildcards Provider> +//) : ViewModelProvider.Factory { +// +// @Suppress("UNCHECKED_CAST") +// override fun create(modelClass: Class): T { +// var viewModel = viewModelMap[modelClass] +// +// if (viewModel == null) { +// for (entry in viewModelMap) { +// if (modelClass.isAssignableFrom(entry.key)) { +// viewModel = entry.value +// break +// } +// } +// } +// +// if (viewModel == null) throw IllegalArgumentException("Unknown model class $modelClass") +// return viewModel.get() as T +// } +//} diff --git a/app/src/main/java/com/navinfo/volvo/di/module/ViewModelModule.kt b/app/src/main/java/com/navinfo/volvo/di/module/ViewModelModule.kt index ec45c18..611e8fa 100644 --- a/app/src/main/java/com/navinfo/volvo/di/module/ViewModelModule.kt +++ b/app/src/main/java/com/navinfo/volvo/di/module/ViewModelModule.kt @@ -1,44 +1,44 @@ -package com.navinfo.volvo.di.module - -import androidx.lifecycle.ViewModel -import androidx.lifecycle.ViewModelProvider -import com.navinfo.volvo.di.key.ViewModelKey -import com.navinfo.volvo.ui.MainActivityViewModel -import com.navinfo.volvo.ui.fragments.home.HomeViewModel -import com.navinfo.volvo.ui.fragments.login.LoginViewModel -import com.navinfo.volvo.ui.fragments.message.ObtainMessageViewModel -import dagger.Binds -import dagger.Module -import dagger.hilt.InstallIn -import dagger.hilt.components.SingletonComponent -import dagger.multibindings.IntoMap - -@InstallIn(SingletonComponent::class) -@Module -abstract class ViewModelModule { - - @Binds - abstract fun bindViewModelFactory(viewModelFactory: ViewModelFactory): ViewModelProvider.Factory - - @IntoMap - @Binds - @ViewModelKey(MainActivityViewModel::class) - abstract fun bindMainViewModel(viewModel: MainActivityViewModel): ViewModel - - @IntoMap - @Binds - @ViewModelKey(LoginViewModel::class) - abstract fun bindLoginFragmentViewModel(viewModel: LoginViewModel): ViewModel - - @IntoMap - @Binds - @ViewModelKey(HomeViewModel::class) - abstract fun bindMessageFragmentViewModel(viewModel: HomeViewModel): ViewModel - - @IntoMap - @Binds - @ViewModelKey(ObtainMessageViewModel::class) - abstract fun bindObtainMessageFragmentViewModel(viewModel: ObtainMessageViewModel): ViewModel - - -} \ No newline at end of file +//package com.navinfo.volvo.di.module +// +//import androidx.lifecycle.ViewModel +//import androidx.lifecycle.ViewModelProvider +//import com.navinfo.volvo.di.key.ViewModelKey +//import com.navinfo.volvo.ui.MainActivityViewModel +//import com.navinfo.volvo.ui.fragments.home.HomeViewModel +//import com.navinfo.volvo.ui.fragments.login.LoginViewModel +//import com.navinfo.volvo.ui.fragments.message.ObtainMessageViewModel +//import dagger.Binds +//import dagger.Module +//import dagger.hilt.InstallIn +//import dagger.hilt.components.SingletonComponent +//import dagger.multibindings.IntoMap +// +//@InstallIn(SingletonComponent::class) +//@Module +//abstract class ViewModelModule { +// +// @Binds +// abstract fun bindViewModelFactory(viewModelFactory: ViewModelFactory): ViewModelProvider.Factory +// +// @IntoMap +// @Binds +// @ViewModelKey(MainActivityViewModel::class) +// abstract fun bindMainViewModel(viewModel: MainActivityViewModel): ViewModel +// +// @IntoMap +// @Binds +// @ViewModelKey(LoginViewModel::class) +// abstract fun bindLoginFragmentViewModel(viewModel: LoginViewModel): ViewModel +// +// @IntoMap +// @Binds +// @ViewModelKey(HomeViewModel::class) +// abstract fun bindMessageFragmentViewModel(viewModel: HomeViewModel): ViewModel +// +// @IntoMap +// @Binds +// @ViewModelKey(ObtainMessageViewModel::class) +// abstract fun bindObtainMessageFragmentViewModel(viewModel: ObtainMessageViewModel): ViewModel +// +// +//} \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/volvo/model/LoginUser.kt b/app/src/main/java/com/navinfo/volvo/model/LoginUser.kt deleted file mode 100644 index 1c55d05..0000000 --- a/app/src/main/java/com/navinfo/volvo/model/LoginUser.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.navinfo.volvo.model - -/** - * 登录用户信息 - */ -data class LoginUser( - var name: String, - var password: String -) \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/volvo/model/messagelist/NetworkMessageListResponse.kt b/app/src/main/java/com/navinfo/volvo/model/network/NetworkMessageListResponse.kt similarity index 59% rename from app/src/main/java/com/navinfo/volvo/model/messagelist/NetworkMessageListResponse.kt rename to app/src/main/java/com/navinfo/volvo/model/network/NetworkMessageListResponse.kt index 94b7a40..2061065 100644 --- a/app/src/main/java/com/navinfo/volvo/model/messagelist/NetworkMessageListResponse.kt +++ b/app/src/main/java/com/navinfo/volvo/model/network/NetworkMessageListResponse.kt @@ -1,8 +1,8 @@ -package com.navinfo.volvo.model.messagelist +package com.navinfo.volvo.model.network import com.navinfo.volvo.database.entity.GreetingMessage data class NetworkMessageListResponse( val total: Int, - val rows: List + val rows: List? ) \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/volvo/model/messagelist/NetworkMessageListPost.kt b/app/src/main/java/com/navinfo/volvo/model/network/NetworkPosts.kt similarity index 85% rename from app/src/main/java/com/navinfo/volvo/model/messagelist/NetworkMessageListPost.kt rename to app/src/main/java/com/navinfo/volvo/model/network/NetworkPosts.kt index 91d0dd5..2d65102 100644 --- a/app/src/main/java/com/navinfo/volvo/model/messagelist/NetworkMessageListPost.kt +++ b/app/src/main/java/com/navinfo/volvo/model/network/NetworkPosts.kt @@ -1,4 +1,4 @@ -package com.navinfo.volvo.model.messagelist +package com.navinfo.volvo.model.network data class NetworkMessageListPost( val name: String,//问候名称,非必填项 @@ -12,4 +12,8 @@ data class NetworkMessageListPost( constructor(who: String, toWho: String) : this("", who, toWho, "", "", "10", "1") { } -} \ No newline at end of file +} + +data class NetworkDeleteMessagePost( + val id: Long +) \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/volvo/model/proto/LoginUserSerializer.kt b/app/src/main/java/com/navinfo/volvo/model/proto/LoginUserSerializer.kt new file mode 100644 index 0000000..5784ffc --- /dev/null +++ b/app/src/main/java/com/navinfo/volvo/model/proto/LoginUserSerializer.kt @@ -0,0 +1,22 @@ +package com.navinfo.volvo.model.proto + +import androidx.datastore.core.CorruptionException +import androidx.datastore.core.Serializer +import androidx.datastore.preferences.protobuf.InvalidProtocolBufferException +import java.io.InputStream +import java.io.OutputStream +import javax.inject.Inject + +class LoginUserSerializer @Inject constructor(): Serializer { + override val defaultValue: LoginUser = LoginUser.getDefaultInstance() + + override suspend fun readFrom(input: InputStream): LoginUser { + try { + return LoginUser.parseFrom(input) + } catch (exception: InvalidProtocolBufferException) { + throw CorruptionException("Cannot read proto.", exception) + } + } + + override suspend fun writeTo(t: LoginUser, output: OutputStream) = t.writeTo(output) +} \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/volvo/repository/database/DatabaseRepositoryImp.kt b/app/src/main/java/com/navinfo/volvo/repository/database/DatabaseRepositoryImp.kt index fcbba0d..7c0b535 100644 --- a/app/src/main/java/com/navinfo/volvo/repository/database/DatabaseRepositoryImp.kt +++ b/app/src/main/java/com/navinfo/volvo/repository/database/DatabaseRepositoryImp.kt @@ -11,12 +11,14 @@ import javax.inject.Inject class DatabaseRepositoryImp @Inject constructor( private val messageDao: GreetingMessageDao, - private val database: AppDatabase ) : DatabaseRepository { companion object { const val PAGE_SIZE = 20 } + /** + * 分页加载消息 + */ override fun getMessageByPaging(): Flow> { return Pager(PagingConfig(PAGE_SIZE)) { messageDao.findAllByDataSource() diff --git a/app/src/main/java/com/navinfo/volvo/repository/network/NetworkRepository.kt b/app/src/main/java/com/navinfo/volvo/repository/network/NetworkRepository.kt index b03fde5..461f0b0 100644 --- a/app/src/main/java/com/navinfo/volvo/repository/network/NetworkRepository.kt +++ b/app/src/main/java/com/navinfo/volvo/repository/network/NetworkRepository.kt @@ -1,13 +1,22 @@ package com.navinfo.volvo.repository.network import com.navinfo.volvo.http.DefaultResponse -import com.navinfo.volvo.model.messagelist.NetworkMessageListPost -import com.navinfo.volvo.model.messagelist.NetworkMessageListResponse +import com.navinfo.volvo.model.network.NetworkDeleteMessagePost +import com.navinfo.volvo.model.network.NetworkMessageListPost +import com.navinfo.volvo.model.network.NetworkMessageListResponse import com.navinfo.volvo.util.NetResult /** * 网络访问接口 */ -interface NetworkRepository { - suspend fun getCardList(message: NetworkMessageListPost): NetResult> +interface NetworkRepository { + /** + * 获取问候列表 + */ + suspend fun getMessageList(message: NetworkMessageListPost): NetResult> + + /** + *删除问候 + */ + suspend fun deleteMessage(message: NetworkDeleteMessagePost): NetResult> } \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/volvo/repository/network/NetworkRepositoryImp.kt b/app/src/main/java/com/navinfo/volvo/repository/network/NetworkRepositoryImp.kt index 5fd1e2f..d367065 100644 --- a/app/src/main/java/com/navinfo/volvo/repository/network/NetworkRepositoryImp.kt +++ b/app/src/main/java/com/navinfo/volvo/repository/network/NetworkRepositoryImp.kt @@ -1,11 +1,12 @@ package com.navinfo.volvo.repository.network +import com.google.gson.Gson import com.navinfo.volvo.database.dao.GreetingMessageDao import com.navinfo.volvo.di.scope.IoDispatcher import com.navinfo.volvo.http.DefaultResponse -import com.navinfo.volvo.model.messagelist.NetworkMessageListPost -import com.navinfo.volvo.model.messagelist.NetworkMessageListResponse -import com.navinfo.volvo.tools.GsonUtil +import com.navinfo.volvo.model.network.NetworkDeleteMessagePost +import com.navinfo.volvo.model.network.NetworkMessageListPost +import com.navinfo.volvo.model.network.NetworkMessageListResponse import com.navinfo.volvo.util.NetResult import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.withContext @@ -16,22 +17,47 @@ import javax.inject.Inject class NetworkRepositoryImp @Inject constructor( private val netWorkService: NetworkService, - private val messageDao: GreetingMessageDao, + private val gson: Gson, @IoDispatcher private val ioDispatcher: CoroutineDispatcher ) : NetworkRepository { - - override suspend fun getCardList(message: NetworkMessageListPost): NetResult> = + /** + * 获取问候列表 + */ + override suspend fun getMessageList(message: NetworkMessageListPost): NetResult> = withContext(ioDispatcher) { return@withContext try { - val stringBody = GsonUtil.getInstance().toJson(message) + val stringBody = gson.toJson(message) .toRequestBody("application/json;charset=utf-8".toMediaType()) - val result = netWorkService.queryCardListByApp(stringBody) + val result = netWorkService.queryMessageListByApp(stringBody) if (result.isSuccessful) { - val body = result.body() - if(body!!.data != null && body.data!!.rows != null){ - messageDao.insertOrUpdate(body.data!!.rows) + if (result.body()!!.code == 200) { + NetResult.Success(result.body()) + } else { + NetResult.Failure(result.body()!!.code, result.body()!!.msg) + } + } else { + NetResult.Success(null) + } + } catch (e: Exception) { + NetResult.Error(e) + } + } + + /** + * 删除问候 + */ + override suspend fun deleteMessage(message: NetworkDeleteMessagePost): NetResult> = + withContext(ioDispatcher) { + return@withContext try { + val stringBody = gson.toJson(message) + .toRequestBody("application/json;charset=utf-8".toMediaType()) + val result = netWorkService.deleteMessage(stringBody) + if (result.isSuccessful) { + if (result.body()!!.code == 200) { + NetResult.Success(result.body()) + } else { + NetResult.Failure(result.body()!!.code, result.body()!!.msg) } - NetResult.Success(body) } else { NetResult.Success(null) } diff --git a/app/src/main/java/com/navinfo/volvo/repository/network/NetworkService.kt b/app/src/main/java/com/navinfo/volvo/repository/network/NetworkService.kt index b211c41..f39d4b9 100644 --- a/app/src/main/java/com/navinfo/volvo/repository/network/NetworkService.kt +++ b/app/src/main/java/com/navinfo/volvo/repository/network/NetworkService.kt @@ -1,13 +1,23 @@ package com.navinfo.volvo.repository.network import com.navinfo.volvo.http.DefaultResponse -import com.navinfo.volvo.model.messagelist.NetworkMessageListResponse +import com.navinfo.volvo.model.network.NetworkMessageListResponse import okhttp3.RequestBody import retrofit2.Response import retrofit2.http.Body import retrofit2.http.POST interface NetworkService { + /** + * 获取问候列表 + */ @POST("/navi/cardDelivery/queryCardListByApp") - suspend fun queryCardListByApp(@Body body: RequestBody): Response> + suspend fun queryMessageListByApp(@Body body: RequestBody): Response> + + /** + * 删除问候 + */ + @POST("/navi/cardDelivery/deleteCardByApp") + suspend fun deleteMessage(@Body body: RequestBody): Response> + } \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/volvo/repository/preferences/PreferencesRepository.kt b/app/src/main/java/com/navinfo/volvo/repository/preferences/PreferencesRepository.kt new file mode 100644 index 0000000..d35e07e --- /dev/null +++ b/app/src/main/java/com/navinfo/volvo/repository/preferences/PreferencesRepository.kt @@ -0,0 +1,17 @@ +package com.navinfo.volvo.repository.preferences + +import com.navinfo.volvo.model.proto.LoginUser +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.Flow + +/** + * 数据库操作接口 + */ +interface PreferencesRepository { + suspend fun saveLoginUser(id: String, name: String, password: String) + fun loginUser(): Flow + suspend fun saveString(key: String, content: String) + suspend fun getString(key: String): Flow + suspend fun saveInt(key: String, content: Int) + suspend fun getInt(key: String): Flow +} \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/volvo/repository/preferences/PreferencesRepositoryImp.kt b/app/src/main/java/com/navinfo/volvo/repository/preferences/PreferencesRepositoryImp.kt new file mode 100644 index 0000000..1bdd0d9 --- /dev/null +++ b/app/src/main/java/com/navinfo/volvo/repository/preferences/PreferencesRepositoryImp.kt @@ -0,0 +1,61 @@ +package com.navinfo.volvo.repository.preferences + +import android.content.Context +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.core.Preferences +import androidx.datastore.preferences.core.edit +import androidx.datastore.preferences.core.intPreferencesKey +import androidx.datastore.preferences.core.stringPreferencesKey +import androidx.datastore.preferences.preferencesDataStore +import androidx.lifecycle.viewModelScope +import com.navinfo.volvo.model.proto.LoginUser +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.launch +import javax.inject.Inject + + +const val DataStore_NAME = "ShardPreferences" +val Context.datastore: DataStore by preferencesDataStore(name = DataStore_NAME) + +class PreferencesRepositoryImp @Inject constructor( + private val context: Context, + private val loginUser: DataStore +) : PreferencesRepository { + + companion object { + val NAME = stringPreferencesKey("NAME") + val PHONE_NUMBER = stringPreferencesKey("PHONE") + val address = stringPreferencesKey("ADDRESS") + } + + override suspend fun saveLoginUser(id: String, name: String, password: String) { + loginUser.updateData { preference -> + preference.toBuilder().setUsername(name).setPassword(password).build() + } + } + + override suspend fun saveString(key: String, content: String) { + context.datastore.edit { + it[stringPreferencesKey(key)] = content + } + } + + override suspend fun getString(key: String): Flow = context.datastore.data.map { + it[stringPreferencesKey(key)] + } + + override suspend fun saveInt(key: String, content: Int) { + context.datastore.edit { + it[intPreferencesKey(key)] = content + } + } + + override suspend fun getInt(key: String): Flow = context.datastore.data.map { + it[intPreferencesKey(key)] + } + + override fun loginUser(): Flow = loginUser.data + +} \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/volvo/ui/BaseActivity.kt b/app/src/main/java/com/navinfo/volvo/ui/BaseActivity.kt index 77c1577..8748a0c 100644 --- a/app/src/main/java/com/navinfo/volvo/ui/BaseActivity.kt +++ b/app/src/main/java/com/navinfo/volvo/ui/BaseActivity.kt @@ -6,6 +6,6 @@ import androidx.lifecycle.ViewModelProvider import javax.inject.Inject abstract class BaseActivity : AppCompatActivity() { - @Inject - lateinit var viewModelFactoryProvider: ViewModelProvider.Factory +// @Inject +// lateinit var viewModelFactoryProvider: ViewModelProvider.Factory } \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/volvo/ui/MainActivity.kt b/app/src/main/java/com/navinfo/volvo/ui/MainActivity.kt index e4fce06..191b16a 100644 --- a/app/src/main/java/com/navinfo/volvo/ui/MainActivity.kt +++ b/app/src/main/java/com/navinfo/volvo/ui/MainActivity.kt @@ -1,8 +1,8 @@ package com.navinfo.volvo.ui import android.content.DialogInterface -import android.content.Intent import android.os.Bundle +import android.view.Gravity import android.view.View import android.widget.Toast import androidx.activity.viewModels @@ -11,6 +11,9 @@ import androidx.navigation.findNavController import androidx.navigation.ui.AppBarConfiguration import androidx.navigation.ui.setupActionBarWithNavController import androidx.navigation.ui.setupWithNavController +import androidx.transition.Slide +import androidx.transition.Transition +import androidx.transition.TransitionManager import com.easytools.tools.FileUtils import com.elvishew.xlog.BuildConfig import com.elvishew.xlog.LogConfiguration @@ -30,16 +33,17 @@ import com.hjq.permissions.Permission import com.hjq.permissions.XXPermissions import com.navinfo.volvo.R import com.navinfo.volvo.databinding.ActivityMainBinding -import com.navinfo.volvo.ui.message.MessageActivity import com.navinfo.volvo.utils.SystemConstant import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.launch + @AndroidEntryPoint class MainActivity : BaseActivity() { private lateinit var binding: ActivityMainBinding - private val viewModel by viewModels { viewModelFactoryProvider } + private val viewModel by viewModels() + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -47,7 +51,6 @@ class MainActivity : BaseActivity() { binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) - XXPermissions.with(this) // 申请单个权限 .permission(Permission.WRITE_EXTERNAL_STORAGE) @@ -98,28 +101,29 @@ class MainActivity : BaseActivity() { lifecycleScope.launch { viewModel.getUnreadCount().collect { - runOnUiThread { - if (it == 0L) { - navView.removeBadge(R.id.navigation_home) - } else { - var badge = navView.getOrCreateBadge(R.id.navigation_home); - badge.number = it.toInt() - } + if (it == 0L) { + navView.removeBadge(R.id.navigation_home) + } else { + var badge = navView.getOrCreateBadge(R.id.navigation_home); + badge.number = it.toInt() } } } navController.addOnDestinationChangedListener { controller, destination, arguments -> - if (destination.id == R.id.navigation_home - || destination.id == R.id.navigation_dashboard - || destination.id == R.id.navigation_notifications - ) { + if (destination.id == R.id.navigation_home || destination.id == R.id.navigation_dashboard || destination.id == R.id.navigation_notifications) { runOnUiThread { + val transition: Transition = Slide(Gravity.BOTTOM) + transition.duration = 300; + TransitionManager.beginDelayedTransition(binding.root, transition); navView.visibility = View.VISIBLE newMessageView.visibility = View.VISIBLE } } else { runOnUiThread { + val transition: Transition = Slide(Gravity.BOTTOM) + transition.duration = 300; + TransitionManager.beginDelayedTransition(binding.root, transition); navView.visibility = View.GONE newMessageView.visibility = View.GONE } @@ -132,6 +136,7 @@ class MainActivity : BaseActivity() { } } + override fun onSupportNavigateUp() = findNavController(R.id.nav_host_fragment_activity_main).navigateUp() @@ -151,13 +156,10 @@ class MainActivity : BaseActivity() { } fun xLogInit(logFolder: String) { - val config = LogConfiguration.Builder() - .logLevel( - if (BuildConfig.DEBUG) - LogLevel.ALL // 指定日志级别,低于该级别的日志将不会被打印,默认为 LogLevel.ALL - else LogLevel.NONE - ) - .tag("Volvo") // 指定 TAG,默认为 "X-LOG" + val config = LogConfiguration.Builder().logLevel( + if (BuildConfig.DEBUG) LogLevel.ALL // 指定日志级别,低于该级别的日志将不会被打印,默认为 LogLevel.ALL + else LogLevel.NONE + ).tag("Volvo") // 指定 TAG,默认为 "X-LOG" .enableThreadInfo() // 允许打印线程信息,默认禁止 .enableStackTrace(2) // 允许打印深度为 2 的调用栈信息,默认禁止 .enableBorder() // 允许打印日志边框,默认禁止 @@ -165,8 +167,7 @@ class MainActivity : BaseActivity() { BlacklistTagsFilterInterceptor( // 添加黑名单 TAG 过滤器 "blacklist1", "blacklist2", "blacklist3" ) - ) - .build() + ).build() val androidPrinter: Printer = AndroidPrinter(true) // 通过 android.util.Log 打印日志的打印器 @@ -181,8 +182,7 @@ class MainActivity : BaseActivity() { XLog.init( // 初始化 XLog config, // 指定日志配置,如果不指定,会默认使用 new LogConfiguration.Builder().build() androidPrinter, // 添加任意多的打印器。如果没有添加任何打印器,会默认使用 AndroidPrinter(Android)/ConsolePrinter(java) - consolePrinter, - filePrinter + consolePrinter, filePrinter ) } @@ -190,14 +190,11 @@ class MainActivity : BaseActivity() { fun showRationaleForSDCard(permissions: MutableList) { // showRationaleDialog(R.string.permission_camera_rationale, request) // Toast.makeText(context, "当前操作需要您授权相机权限!", Toast.LENGTH_SHORT).show() - MaterialAlertDialogBuilder(this) - .setTitle("提示") - .setMessage("当前操作需要您授权读写SD卡权限!") + MaterialAlertDialogBuilder(this).setTitle("提示").setMessage("当前操作需要您授权读写SD卡权限!") .setPositiveButton("确定", DialogInterface.OnClickListener { dialogInterface, i -> dialogInterface.dismiss() XXPermissions.startPermissionActivity(this@MainActivity, permissions) - }) - .show() + }).show() } // @OnPermissionDenied(Manifest.permission.MANAGE_EXTERNAL_STORAGE) diff --git a/app/src/main/java/com/navinfo/volvo/ui/MainActivityViewModel.kt b/app/src/main/java/com/navinfo/volvo/ui/MainActivityViewModel.kt index b0ffafe..cec763a 100644 --- a/app/src/main/java/com/navinfo/volvo/ui/MainActivityViewModel.kt +++ b/app/src/main/java/com/navinfo/volvo/ui/MainActivityViewModel.kt @@ -4,9 +4,12 @@ import androidx.lifecycle.ViewModel import androidx.paging.PagingData import com.navinfo.volvo.database.dao.GreetingMessageDao import com.navinfo.volvo.database.entity.GreetingMessage +import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.Flow import javax.inject.Inject +import javax.inject.Singleton +@HiltViewModel class MainActivityViewModel @Inject constructor( private val messageDao: GreetingMessageDao, ) : ViewModel() { diff --git a/app/src/main/java/com/navinfo/volvo/ui/fragments/BaseFragment.kt b/app/src/main/java/com/navinfo/volvo/ui/fragments/BaseFragment.kt index b112973..93cd6e2 100644 --- a/app/src/main/java/com/navinfo/volvo/ui/fragments/BaseFragment.kt +++ b/app/src/main/java/com/navinfo/volvo/ui/fragments/BaseFragment.kt @@ -5,6 +5,6 @@ import androidx.lifecycle.ViewModelProvider import javax.inject.Inject abstract class BaseFragment : Fragment() { - @Inject - lateinit var viewModelFactoryProvider: ViewModelProvider.Factory +// @Inject +// lateinit var viewModelFactoryProvider: ViewModelProvider.Factory } \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/volvo/ui/fragments/home/HomeAdapter.kt b/app/src/main/java/com/navinfo/volvo/ui/fragments/home/HomeAdapter.kt index 398faf6..06c4d19 100644 --- a/app/src/main/java/com/navinfo/volvo/ui/fragments/home/HomeAdapter.kt +++ b/app/src/main/java/com/navinfo/volvo/ui/fragments/home/HomeAdapter.kt @@ -19,23 +19,11 @@ class HomeAdapter(fragment: Fragment) : PagingDataAdapter(DiffCallback()) { val fragment = fragment -// var itemList = ArrayList() -// -// fun addItem(message: GreetingMessage) { -// itemList.add(message) -// notifyItemInserted(itemList.size - 1) -// } -// -// fun setItems(messageList: List) { -// itemList.clear() -// itemList.addAll(messageList) -// notifyDataSetChanged() -// } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder { val mDataBinding: AdapterHomeBinding = DataBindingUtil.inflate( - LayoutInflater.from(fragment.context), + LayoutInflater.from(parent.context), R.layout.adapter_home, parent, false @@ -47,9 +35,11 @@ class HomeAdapter(fragment: Fragment) : holder.onBind(position) } -// override fun getItemCount(): Int { -// return itemList.size -// } + + fun getItemData(position: Int): GreetingMessage { + return getItem(position)!! + } + inner class MyViewHolder(private val mDataBinding: AdapterHomeBinding) : RecyclerView.ViewHolder(mDataBinding.root) { @@ -63,12 +53,11 @@ class HomeAdapter(fragment: Fragment) : .error(R.mipmap.volvo_logo_small) .into(mDataBinding.messageHeadIcon) } - } class DiffCallback : DiffUtil.ItemCallback() { override fun areItemsTheSame(oldItem: GreetingMessage, newItem: GreetingMessage): Boolean { - return oldItem.uuid == newItem.uuid + return oldItem.uuid == newItem.uuid && oldItem.status == newItem.status } override fun areContentsTheSame( diff --git a/app/src/main/java/com/navinfo/volvo/ui/fragments/home/HomeFragment.kt b/app/src/main/java/com/navinfo/volvo/ui/fragments/home/HomeFragment.kt index d78a6e7..a23ee65 100644 --- a/app/src/main/java/com/navinfo/volvo/ui/fragments/home/HomeFragment.kt +++ b/app/src/main/java/com/navinfo/volvo/ui/fragments/home/HomeFragment.kt @@ -1,96 +1,172 @@ package com.navinfo.volvo.ui.fragments.home import android.os.Bundle +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.databinding.DataBindingUtil +import android.widget.Toast import androidx.fragment.app.viewModels +import androidx.lifecycle.Observer import androidx.lifecycle.lifecycleScope +import androidx.paging.LoadState import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.LinearLayoutManager +import com.easytools.tools.ThreadPoolUtils.runOnUiThread import com.navinfo.volvo.R import com.navinfo.volvo.databinding.FragmentHomeBinding +import com.navinfo.volvo.databinding.HomeAdapterNotingBinding +import com.navinfo.volvo.databinding.LoadStateViewBinding import com.navinfo.volvo.tools.DisplayUtil import com.navinfo.volvo.ui.fragments.BaseFragment import com.yanzhenjie.recyclerview.* import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import java.io.IOException @AndroidEntryPoint class HomeFragment : BaseFragment(), OnItemClickListener, OnItemMenuClickListener { private var _binding: FragmentHomeBinding? = null + // This property is only valid between onCreateView and // onDestroyView. private val binding get() = _binding!! - private val viewModel by viewModels { viewModelFactoryProvider } - - private lateinit var messageAdapter: HomeAdapter + private val viewModel by viewModels() + private val messageAdapter by lazy { HomeAdapter(this) } + private var headBinding: HomeAdapterNotingBinding? = null override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { _binding = FragmentHomeBinding.inflate(inflater, container, false) val root: View = binding.root + + headBinding = HomeAdapterNotingBinding.inflate(inflater, container, false) initView() return root } private fun initView() { -// mDataBinding.homeViewModel = viewModel - messageAdapter = HomeAdapter(this) - val recyclerview: SwipeRecyclerView = binding.homeRecyclerview - recyclerview.adapter = null //先设置null,否则会报错 + + //创建菜单选项 //注意:使用滑动菜单不能开启滑动删除,否则只有滑动删除没有滑动菜单 - var mSwipeMenuCreator = - SwipeMenuCreator { _, rightMenu, position -> - //添加菜单自动添加至尾部 - var deleteItem = SwipeMenuItem(context) - deleteItem.height = DisplayUtil.dip2px(context!!, 60f) - deleteItem.width = DisplayUtil.dip2px(context!!, 80f) - deleteItem.background = context!!.getDrawable(R.color.red) - deleteItem.text = context!!.getString(R.string.delete) - rightMenu.addMenuItem(deleteItem) + var mSwipeMenuCreator = SwipeMenuCreator { _, rightMenu, _ -> + //添加菜单自动添加至尾部 + var deleteItem = SwipeMenuItem(context) + deleteItem.height = DisplayUtil.dip2px(requireContext(), 60f) + deleteItem.width = DisplayUtil.dip2px(requireContext(), 80f) + deleteItem.background = requireContext().getDrawable(R.color.red) + deleteItem.text = requireContext().getString(R.string.delete) + rightMenu.addMenuItem(deleteItem) + + //分享 + var shareItem = SwipeMenuItem(context) + shareItem.height = DisplayUtil.dip2px(requireContext(), 60f) + shareItem.width = DisplayUtil.dip2px(requireContext(), 80f) + shareItem.background = requireContext().getDrawable(R.color.gray) + shareItem.text = requireContext().getString(R.string.share) + shareItem.setTextColor(requireContext().getColor(R.color.white)) + rightMenu.addMenuItem(shareItem) + } + //侧滑按钮 + binding.homeRecyclerview.setOnItemMenuClickListener { menuBridge, position -> + menuBridge.closeMenu() +// val direction: Int = menuBridge.getDirection() // 左侧还是右侧菜单。 + + when (menuBridge.position) {// 菜单在RecyclerView的Item中的Position。 + 0 -> {//删除按钮 + viewModel.deleteMessage(messageAdapter.getItemData(position).id) + } + 1 -> {//分享按钮 + } - //分享 - var shareItem = SwipeMenuItem(context) - shareItem.height = DisplayUtil.dip2px(context!!, 60f) - shareItem.width = DisplayUtil.dip2px(context!!, 80f) - shareItem.background = context!!.getDrawable(R.color.gray) - shareItem.text = context!!.getString(R.string.share) - shareItem.setTextColor(R.color.white) - rightMenu.addMenuItem(shareItem) } - val layoutManager = LinearLayoutManager(context) + } - recyclerview.layoutManager = layoutManager - recyclerview.addItemDecoration(DividerItemDecoration(context, layoutManager.orientation)) - recyclerview.setSwipeMenuCreator(mSwipeMenuCreator) - recyclerview.setOnItemClickListener(this) -// recyclerview.useDefaultLoadMore() -// recyclerview.setLoadMoreListener { -// -// } + val layoutManager = LinearLayoutManager(context) + binding.homeRecyclerview.layoutManager = layoutManager + //自动增加分割线 + binding.homeRecyclerview.addItemDecoration( + DividerItemDecoration( + context, layoutManager.orientation + ) + ) + //增加侧滑按钮 + binding.homeRecyclerview.setSwipeMenuCreator(mSwipeMenuCreator) + //单项点击 + binding.homeRecyclerview.setOnItemClickListener(this) + //使用下拉加载 +// binding.homeRecyclerview.useDefaultLoadMore() // 使用默认的加载更多的View。 + + binding.homeRecyclerview.setLoadMoreListener { + Log.e("jingo", "下拉加载开始") + + } // 加载更多的监听。 + + //开始下拉刷新 + binding.homeSwipeRefreshLayout.setOnRefreshListener { + Log.e("jingo", "开始刷新") + viewModel.getNetMessageList() + } + + //列表自动分页 lifecycleScope.launch { - viewModel.messageList.collectLatest { + viewModel.messageList.collect { messageAdapter.submitData(it) } } -// messageAdapter.withLoadStateFooter( -// footer = RecLoadStateAdapter { messageAdapter.retry() } -// ) + binding.homeRecyclerview.adapter = messageAdapter + + //初始状态添加监听 + messageAdapter.addLoadStateListener { + when (it.refresh) { + is LoadState.NotLoading -> { + if (messageAdapter.itemCount == 0) + binding.homeRecyclerview.addHeaderView(headBinding!!.root) + else{ + binding.homeRecyclerview.removeHeaderView(headBinding!!.root) + } + Log.d("jingo", "is NotLoading") + } + is LoadState.Loading -> { + Log.d("jingo", "is Loading") + } + is LoadState.Error -> { + Log.d("jingo", "is Error:") + when ((it.refresh as LoadState.Error).error) { + is IOException -> { + Log.d("jingo", "IOException") + } + else -> { + Log.d("jingo", "others exception") + } + } + } + } + } + loadMoreFinish() + //监听数据请求是否结束 + viewModel.isLoading.observe(viewLifecycleOwner, Observer { + if (!it) loadMoreFinish() + }) -// messageAdapter.withLoadStateHeader() - recyclerview.adapter = messageAdapter } + private fun loadMoreFinish() { + binding.homeSwipeRefreshLayout.isRefreshing = false + + // 第一次加载数据:一定要掉用这个方法。 + // 第一个参数:表示此次数据是否为空,假如你请求到的list为空(== null || list.size == 0),那么这里就要true。 + // 第二个参数:表示是否还有更多数据,根据服务器返回给你的page等信息判断是否还有更多,这样可以提供性能,如果不能判断则传true。 + binding.homeRecyclerview.loadMoreFinish(false, false) + } + + override fun onStart() { super.onStart() viewModel.getNetMessageList() @@ -100,6 +176,7 @@ class HomeFragment : BaseFragment(), OnItemClickListener, OnItemMenuClickListene override fun onDestroyView() { super.onDestroyView() _binding = null + headBinding = null } override fun onItemClick(view: View?, adapterPosition: Int) { diff --git a/app/src/main/java/com/navinfo/volvo/ui/fragments/home/HomeViewModel.kt b/app/src/main/java/com/navinfo/volvo/ui/fragments/home/HomeViewModel.kt index ac68d39..8b4a0fe 100644 --- a/app/src/main/java/com/navinfo/volvo/ui/fragments/home/HomeViewModel.kt +++ b/app/src/main/java/com/navinfo/volvo/ui/fragments/home/HomeViewModel.kt @@ -1,49 +1,100 @@ package com.navinfo.volvo.ui.fragments.home +import android.app.Application +import android.widget.Toast import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import androidx.paging.PagingData import com.navinfo.volvo.database.dao.GreetingMessageDao import com.navinfo.volvo.database.entity.GreetingMessage -import com.navinfo.volvo.model.messagelist.NetworkMessageListPost +import com.navinfo.volvo.model.network.NetworkDeleteMessagePost +import com.navinfo.volvo.model.network.NetworkMessageListPost import com.navinfo.volvo.repository.database.DatabaseRepository import com.navinfo.volvo.repository.network.NetworkRepository +import com.navinfo.volvo.repository.preferences.PreferencesRepository import com.navinfo.volvo.util.NetResult import com.navinfo.volvo.util.asLiveData +import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.launch import javax.inject.Inject +@HiltViewModel class HomeViewModel @Inject constructor( + private val application: Application, private val netRepository: NetworkRepository, - private val dataRepository: DatabaseRepository + private val dataRepository: DatabaseRepository, + private val messageDao: GreetingMessageDao, + private val shard: PreferencesRepository ) : ViewModel() { private val _isLoading = MutableLiveData() val isLoading = _isLoading.asLiveData() + // private val _messageList = MutableLiveData>() // val messageList = _messageList.asLiveData() - val messageList: Flow> get() = dataRepository.getMessageByPaging() + lateinit var userName: String + + init { + viewModelScope.launch { + shard.loginUser().collect { + userName = it!!.username + } + } + } fun getNetMessageList() { + if (_isLoading.value == true) + return _isLoading.postValue(true) viewModelScope.launch { - val messagePost = NetworkMessageListPost(who = "", toWho = "") - when (val result = netRepository.getCardList(messagePost)) { + val messagePost = NetworkMessageListPost(who = userName, toWho = "") + when (val result = netRepository.getMessageList(messagePost)) { is NetResult.Success -> { _isLoading.value = false -// if (result.data != null) { -// val list = (result.data.data as NetworkMessageListResponse).rows -// _messageList.value = list -// } + if ((result.data!!.data != null) && (result.data.data!!.rows != null)) { + messageDao.insertOrUpdate(result.data.data!!.rows!!) + } } + is NetResult.Failure -> { + _isLoading.value = false + Toast.makeText(application, "${result.code}:${result.msg}", Toast.LENGTH_SHORT) + .show() + } + is NetResult.Error -> { + _isLoading.value = false + } + is NetResult.Loading -> { + _isLoading.postValue(true) + } + } + } + } + fun deleteMessage(id: Long) { + viewModelScope.launch { + val post = NetworkDeleteMessagePost(id) + netRepository.deleteMessage(post) + messageDao.deleteById(id) + when (val result = netRepository.deleteMessage(post)) { + is NetResult.Success -> { + _isLoading.value = false + } + is NetResult.Failure -> { + _isLoading.value = false + Toast.makeText( + application, + "服务返回信息:${result.code}:${result.msg}", + Toast.LENGTH_SHORT + ) + .show() + } is NetResult.Error -> { _isLoading.value = false } diff --git a/app/src/main/java/com/navinfo/volvo/ui/fragments/home/LoadStateAdapter.kt b/app/src/main/java/com/navinfo/volvo/ui/fragments/home/LoadStateAdapter.kt new file mode 100644 index 0000000..d3aa8e3 --- /dev/null +++ b/app/src/main/java/com/navinfo/volvo/ui/fragments/home/LoadStateAdapter.kt @@ -0,0 +1,18 @@ +package com.navinfo.volvo.ui.fragments.home + +import android.view.ViewGroup +import androidx.paging.LoadState +import androidx.paging.LoadStateAdapter + +class LoadStateAdapter(private val retry: () -> Unit) : + LoadStateAdapter() { + + override fun onBindViewHolder(holder: LoadStateViewHolder, loadState: LoadState) { + holder.bindState(loadState) + } + + override fun onCreateViewHolder(parent: ViewGroup, loadState: LoadState): LoadStateViewHolder { + return LoadStateViewHolder(parent, retry) + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/volvo/ui/fragments/home/LoadStateViewHolder.kt b/app/src/main/java/com/navinfo/volvo/ui/fragments/home/LoadStateViewHolder.kt new file mode 100644 index 0000000..4a2c190 --- /dev/null +++ b/app/src/main/java/com/navinfo/volvo/ui/fragments/home/LoadStateViewHolder.kt @@ -0,0 +1,39 @@ +package com.navinfo.volvo.ui.fragments.home + +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.paging.LoadState +import androidx.recyclerview.widget.RecyclerView +import com.navinfo.volvo.R +import com.navinfo.volvo.databinding.LoadStateViewBinding + +class LoadStateViewHolder(parent: ViewGroup, var retry: () -> Unit) : RecyclerView.ViewHolder( + LayoutInflater.from(parent.context) + .inflate(R.layout.load_state_view, parent, false) +) { + + var itemLoadStateBindingUtil: LoadStateViewBinding = LoadStateViewBinding.bind(itemView) + + fun bindState(loadState: LoadState) { + when (loadState) { + is LoadState.Error -> { + itemLoadStateBindingUtil.loadStateLayout.visibility = View.VISIBLE +// itemLoadStateBindingUtil.btnRetry.setOnClickListener { +// retry() +// } + Log.d("jingo", "error了吧") + } + is LoadState.Loading -> { + itemLoadStateBindingUtil.loadStateLayout.visibility = View.VISIBLE + Log.d("jingo", "该显示了") + } + else -> { + Log.d("jingo", "--其他的错误") + } + } + + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/volvo/ui/fragments/home/RecLoadStateAdapter.kt b/app/src/main/java/com/navinfo/volvo/ui/fragments/home/RecLoadStateAdapter.kt deleted file mode 100644 index 034840f..0000000 --- a/app/src/main/java/com/navinfo/volvo/ui/fragments/home/RecLoadStateAdapter.kt +++ /dev/null @@ -1,51 +0,0 @@ -//package com.navinfo.volvo.ui.fragments.home -// -//import android.view.LayoutInflater -//import android.view.View -//import android.view.ViewGroup -//import androidx.core.view.isVisible -//import androidx.paging.LoadState -//import androidx.paging.LoadStateAdapter -//import androidx.recyclerview.widget.RecyclerView -//import com.example.picsapp.R -//import com.example.picsapp.databinding.LoadStateViewBinding -// -//class RecLoadStateAdapter( -// private val retry: () -> Unit -//) : LoadStateAdapter() { -// -// override fun onCreateViewHolder(parent: ViewGroup, loadState: LoadState): LoadStateViewHolder { -// -// val binding = LoadStateViewBinding -// .inflate(LayoutInflater.from(parent.context), parent, false) -// -// return LoadStateViewHolder(binding) -// } -// -// override fun onBindViewHolder(holder: LoadStateViewHolder, loadState: LoadState) { -// holder.onBind(loadState) -// -// } -// -// -// -// inner class LoadStateViewHolder(private val binding: LoadStateViewBinding) : RecyclerView.ViewHolder(binding.root){ -// fun onBind(loadState: LoadState) { -// val progress = binding.loadStateProgress -// val btnRetry = binding.loadStateRetry -// val txtErrorMessage = binding.loadStateErrorMessage -// -// btnRetry.isVisible = loadState !is LoadState.Loading -// txtErrorMessage.isVisible = loadState !is LoadState.Loading -// progress.isVisible = loadState is LoadState.Loading -// -// if (loadState is LoadState.Error){ -// txtErrorMessage.text = loadState.error.localizedMessage -// } -// -// btnRetry.setOnClickListener { -// retry.invoke() -// } -// } -// } -//} \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/volvo/ui/fragments/login/LoginFragment.kt b/app/src/main/java/com/navinfo/volvo/ui/fragments/login/LoginFragment.kt index e921ce5..4844f7a 100644 --- a/app/src/main/java/com/navinfo/volvo/ui/fragments/login/LoginFragment.kt +++ b/app/src/main/java/com/navinfo/volvo/ui/fragments/login/LoginFragment.kt @@ -4,13 +4,18 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.Toast import androidx.databinding.DataBindingUtil import androidx.fragment.app.viewModels +import androidx.lifecycle.Observer +import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController import com.navinfo.volvo.R import com.navinfo.volvo.databinding.FragmentLoginBinding import com.navinfo.volvo.ui.fragments.BaseFragment import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.cancel +import kotlinx.coroutines.launch @AndroidEntryPoint @@ -20,7 +25,7 @@ class LoginFragment : BaseFragment() { private lateinit var viewBinding: FragmentLoginBinding - private val viewModel by viewModels { viewModelFactoryProvider } + private val viewModel by viewModels() override fun onCreateView( inflater: LayoutInflater, @@ -34,11 +39,30 @@ class LoginFragment : BaseFragment() { } private fun initView() { + + lifecycleScope.launch { + viewModel.user.collect { + if (it != null){ + viewBinding.loginUser = it + } + } + } viewBinding.loginFragmentRegisterButton.setOnClickListener { } viewBinding.loginFragmentLoginButton.setOnClickListener { -// viewModel.login(viewBinding.loginFragmentUserLayout) + if (viewBinding.loginUsername.text!!.isEmpty()) { + Toast.makeText(context, "请输入用户名", Toast.LENGTH_SHORT).show() + return@setOnClickListener + } + if (viewBinding.loginPassword.text!!.isEmpty()) { + Toast.makeText(context, "请输入密码", Toast.LENGTH_SHORT).show() + return@setOnClickListener + } + viewModel.onClickLogin( + viewBinding.loginUsername.text.toString(), + viewBinding.loginPassword.text.toString() + ) findNavController().navigate(R.id.action_login_to_home) } } diff --git a/app/src/main/java/com/navinfo/volvo/ui/fragments/login/LoginViewModel.kt b/app/src/main/java/com/navinfo/volvo/ui/fragments/login/LoginViewModel.kt index 8f47282..a868bb6 100644 --- a/app/src/main/java/com/navinfo/volvo/ui/fragments/login/LoginViewModel.kt +++ b/app/src/main/java/com/navinfo/volvo/ui/fragments/login/LoginViewModel.kt @@ -1,23 +1,29 @@ package com.navinfo.volvo.ui.fragments.login -import android.view.View -import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel -import com.navinfo.volvo.database.AppDatabase -import com.navinfo.volvo.database.entity.User -import com.navinfo.volvo.util.SharedPreferenceHelper +import androidx.lifecycle.viewModelScope +import com.navinfo.volvo.repository.preferences.PreferencesRepository +import com.navinfo.volvo.util.asLiveData +//import com.navinfo.volvo.repository.preferences.PreferencesRepository +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.cancel +import kotlinx.coroutines.launch import javax.inject.Inject -class LoginViewModel @Inject constructor(private val sharedPreferenceHelper: SharedPreferenceHelper) : ViewModel() { +@HiltViewModel +class LoginViewModel @Inject constructor(private val repository: PreferencesRepository) : + ViewModel() { -// val user: LiveData = _user - - fun liveDataOnclick(view: View) { + val user = repository.loginUser() + fun onClickLogin(name: String, password: String) { + viewModelScope.launch { + repository.saveLoginUser(id = "", name = name, password = password) + } } - fun userRegister(username: String, password: String) { + fun onClickLoginRegister(username: String, password: String) { } } \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/volvo/ui/fragments/message/ObtainMessageFragment.kt b/app/src/main/java/com/navinfo/volvo/ui/fragments/message/ObtainMessageFragment.kt index 1fe7bf3..ab43736 100644 --- a/app/src/main/java/com/navinfo/volvo/ui/fragments/message/ObtainMessageFragment.kt +++ b/app/src/main/java/com/navinfo/volvo/ui/fragments/message/ObtainMessageFragment.kt @@ -7,7 +7,8 @@ import android.text.TextUtils import android.view.LayoutInflater import android.view.MotionEvent import android.view.View -import android.view.View.* +import android.view.View.GONE +import android.view.View.VISIBLE import android.view.ViewGroup import android.widget.AdapterView import android.widget.AdapterView.OnItemSelectedListener @@ -17,15 +18,11 @@ import android.widget.Toast import androidx.core.content.ContextCompat import androidx.core.widget.addTextChangedListener import androidx.fragment.app.Fragment +import androidx.fragment.app.viewModels import androidx.lifecycle.Observer -import androidx.lifecycle.ViewModelProvider import androidx.navigation.Navigation import androidx.navigation.fragment.findNavController -import com.easytools.tools.DateUtils -import com.easytools.tools.DisplayUtils -import com.easytools.tools.FileIOUtils -import com.easytools.tools.ResourceUtils -import com.easytools.tools.ToastUtils +import com.easytools.tools.* import com.elvishew.xlog.XLog import com.github.file_picker.FileType import com.github.file_picker.ListDirection @@ -52,6 +49,7 @@ import com.navinfo.volvo.util.PhotoLoader import com.navinfo.volvo.utils.EasyMediaFile import com.navinfo.volvo.utils.SystemConstant import com.nhaarman.supertooltips.ToolTip +import dagger.hilt.android.AndroidEntryPoint import indi.liyi.viewer.Utils import indi.liyi.viewer.ViewData import top.zibin.luban.Luban @@ -62,12 +60,10 @@ import java.util.* import kotlin.streams.toList -//@RuntimePermissions -class ObtainMessageFragment: Fragment() { +@AndroidEntryPoint +class ObtainMessageFragment : Fragment() { private var _binding: FragmentObtainMessageBinding? = null - private val obtainMessageViewModel by lazy { - ViewModelProvider(requireActivity()).get(ObtainMessageViewModel::class.java) - } + private val obtainMessageViewModel by viewModels() private val photoHelper by lazy { EasyMediaFile().setCrop(true) } @@ -91,18 +87,17 @@ class ObtainMessageFragment: Fragment() { _binding = FragmentObtainMessageBinding.inflate(inflater, container, false) val root: View = binding.root - val greetingMessage = GreetingMessage() - obtainMessageViewModel.setCurrentMessage(greetingMessage) + obtainMessageViewModel.setCurrentMessage(GreetingMessage(who = obtainMessageViewModel.username)) - obtainMessageViewModel?.getMessageLiveData()?.observe( + obtainMessageViewModel.getMessageLiveData().observe( viewLifecycleOwner, Observer { // 初始化界面显示内容 - if(it.name?.isNotEmpty() == true) + if (it.name?.isNotEmpty() == true) binding.tvMessageTitle?.setText(it.name) if (it.sendDate?.isNotEmpty() == true) { // 获取当前发送时间,如果早于当前时间,则显示现在 val sendDate = DateUtils.str2Date(it.sendDate, dateSendFormat) - if (sendDate<=Date()) { + if (sendDate <= Date()) { binding.btnSendTime.text = "现在" } else { binding.btnSendTime.text = it.sendDate @@ -112,39 +107,47 @@ class ObtainMessageFragment: Fragment() { } var hasPhoto = false var hasAudio = false - if (it.imageUrl!=null&&it.imageUrl?.isNotEmpty() == true) { + if (it.imageUrl != null && it.imageUrl?.isNotEmpty() == true) { hasPhoto = true -// Glide.with(this@ObtainMessageFragment) -// .asBitmap().fitCenter() -// .load(it.imageUrl) -// .diskCacheStrategy(DiskCacheStrategy.ALL) -// .into(binding.imgMessageAttachment) + // Glide.with(this@ObtainMessageFragment) + // .asBitmap().fitCenter() + // .load(it.imageUrl) + // .diskCacheStrategy(DiskCacheStrategy.ALL) + // .into(binding.imgMessageAttachment) // 如果当前attachment文件是本地文件,开始尝试网络上传 val str = it.imageUrl?.replace("\\", "/") - binding.tvPhotoName.getPaint().setFlags(Paint.UNDERLINE_TEXT_FLAG ) + binding.tvPhotoName.getPaint().setFlags(Paint.UNDERLINE_TEXT_FLAG) if (!str!!.startsWith("http")) { - obtainMessageViewModel.uploadAttachment(File(it.imageUrl), AttachmentType.PIC) + obtainMessageViewModel.uploadAttachment( + File(it.imageUrl), + AttachmentType.PIC + ) binding.tvPhotoName.text = str.substringAfterLast("/", "picture.jpg") } else { if (str.contains("?")) { - binding.tvPhotoName.text = str.substring(str.lastIndexOf("/")+1, str.indexOf("?")) + binding.tvPhotoName.text = + str.substring(str.lastIndexOf("/") + 1, str.indexOf("?")) } else { binding.tvPhotoName.text = str.substringAfterLast("/") } } } - if (it.mediaUrl!=null&&it.mediaUrl?.isNotEmpty() == true) { + if (it.mediaUrl != null && it.mediaUrl?.isNotEmpty() == true) { hasAudio = true - binding.tvAudioName.getPaint().setFlags(Paint.UNDERLINE_TEXT_FLAG ) + binding.tvAudioName.getPaint().setFlags(Paint.UNDERLINE_TEXT_FLAG) // 如果当前attachment文件是本地文件,开始尝试网络上传 val str = it.mediaUrl?.replace("\\", "/") if (!str!!.startsWith("http")) { - obtainMessageViewModel.uploadAttachment(File(it.mediaUrl),AttachmentType.AUDIO) + obtainMessageViewModel.uploadAttachment( + File(it.mediaUrl), + AttachmentType.AUDIO + ) binding.tvAudioName.text = str.substringAfterLast("/", "audio.m4a") } else { if (str.contains("?")) { - binding.tvAudioName.text = str.substring(str.lastIndexOf("/")+1, str.indexOf("?")) + binding.tvAudioName.text = + str.substring(str.lastIndexOf("/") + 1, str.indexOf("?")) } else { binding.tvAudioName.text = str.substringAfterLast("/") } @@ -152,7 +155,7 @@ class ObtainMessageFragment: Fragment() { } binding.layerPhotoResult.visibility = if (hasPhoto) VISIBLE else GONE binding.layerGetPhoto.visibility = if (hasPhoto) GONE else VISIBLE -// binding.imgMessageAttachment.visibility = if (hasPhoto) VISIBLE else GONE + // binding.imgMessageAttachment.visibility = if (hasPhoto) VISIBLE else GONE binding.layerAudioResult.visibility = if (hasAudio) VISIBLE else GONE binding.layerGetAudio.visibility = if (hasAudio) GONE else VISIBLE @@ -188,7 +191,7 @@ class ObtainMessageFragment: Fragment() { obtainMessageViewModel.getMessageLiveData().value?.name = it.toString() }) - binding.edtSendFrom.addTextChangedListener (afterTextChanged = { + binding.edtSendFrom.addTextChangedListener(afterTextChanged = { obtainMessageViewModel.getMessageLiveData().value?.who = it.toString() }) @@ -201,8 +204,11 @@ class ObtainMessageFragment: Fragment() { } val sendToArray = mutableListOf(VolvoModel("XC60", "智雅", "LYVXFEFEXNL754427")) binding.edtSendTo.adapter = ArrayAdapter(requireContext(), - android.R.layout.simple_dropdown_item_1line, android.R.id.text1, sendToArray.stream().map { it -> "${it.version} ${it.model} ${it.num}" }.toList()) - binding.edtSendTo.onItemSelectedListener = object: OnItemSelectedListener { + android.R.layout.simple_dropdown_item_1line, + android.R.id.text1, + sendToArray.stream().map { it -> "${it.version} ${it.model} ${it.num}" }.toList() + ) + binding.edtSendTo.onItemSelectedListener = object : OnItemSelectedListener { override fun onItemSelected(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) { obtainMessageViewModel.getMessageLiveData().value?.toWho = sendToArray[p2].num } @@ -219,9 +225,19 @@ class ObtainMessageFragment: Fragment() { override fun onClickListener(selectTime: String) { val sendDate = DateUtils.str2Date(selectTime, dateShowFormat) if (sendDate <= Date()) { - obtainMessageViewModel.updateMessageSendTime(DateUtils.date2Str(Date(), dateSendFormat)) + obtainMessageViewModel.updateMessageSendTime( + DateUtils.date2Str( + Date(), + dateSendFormat + ) + ) } else { - obtainMessageViewModel.updateMessageSendTime(DateUtils.date2Str(sendDate, dateSendFormat)) + obtainMessageViewModel.updateMessageSendTime( + DateUtils.date2Str( + sendDate, + dateSendFormat + ) + ) } } } @@ -238,7 +254,8 @@ class ObtainMessageFragment: Fragment() { override fun onGranted(permissions: MutableList, all: Boolean) { if (!all) { - Toast.makeText(activity, "获取部分权限成功,但部分权限未正常授予", Toast.LENGTH_SHORT).show() + Toast.makeText(activity, "获取部分权限成功,但部分权限未正常授予", Toast.LENGTH_SHORT) + .show() return } // 开始启动拍照界面 @@ -280,13 +297,23 @@ class ObtainMessageFragment: Fragment() { // Do something here with selected files val audioFile = files.get(0).file if (!audioFile.parentFile.parentFile.absolutePath.equals(SystemConstant.SoundFolder)) { - val copyResult = FileIOUtils.writeFileFromIS(File(SystemConstant.SoundFolder, audioFile.name), FileInputStream(audioFile)) - XLog.e("拷贝结果:"+copyResult) + val copyResult = FileIOUtils.writeFileFromIS( + File( + SystemConstant.SoundFolder, + audioFile.name + ), FileInputStream(audioFile) + ) + XLog.e("拷贝结果:" + copyResult) if (!copyResult) { ToastUtils.showToast("无法访问该文件,请重新选择其他文件") return } - obtainMessageViewModel.updateMessageAudio(File(SystemConstant.SoundFolder, audioFile.name).absolutePath) + obtainMessageViewModel.updateMessageAudio( + File( + SystemConstant.SoundFolder, + audioFile.name + ).absolutePath + ) } else { obtainMessageViewModel.updateMessageAudio(audioFile.absolutePath) } @@ -299,7 +326,7 @@ class ObtainMessageFragment: Fragment() { ToastUtils.showToast("只能选择.m4a文件") return } - if (media.file.length()>2*1000*1000) { + if (media.file.length() > 2 * 1000 * 1000) { ToastUtils.showToast("文件不能超过2M!") return } @@ -330,23 +357,25 @@ class ObtainMessageFragment: Fragment() { .request(object : OnPermissionCallback { override fun onGranted(permissions: MutableList, all: Boolean) { if (!all) { - Toast.makeText(activity, "获取部分权限成功,但部分权限未正常授予", Toast.LENGTH_SHORT).show() + Toast.makeText(activity, "获取部分权限成功,但部分权限未正常授予", Toast.LENGTH_SHORT) + .show() return } - when(motionEvent.action) { - MotionEvent.ACTION_DOWN-> { + when (motionEvent.action) { + MotionEvent.ACTION_DOWN -> { // 申请权限 recorderLifecycleObserver.initAndStartRecorder() startRecordTime = System.currentTimeMillis() false } MotionEvent.ACTION_UP -> { - if (System.currentTimeMillis() - startRecordTime<2000) { + if (System.currentTimeMillis() - startRecordTime < 2000) { ToastUtils.showToast("录音时间太短!") recorderLifecycleObserver.stopAndReleaseRecorder() return } - val recorderAudioPath = recorderLifecycleObserver.stopAndReleaseRecorder() + val recorderAudioPath = + recorderLifecycleObserver.stopAndReleaseRecorder() if (File(recorderAudioPath).exists()) { obtainMessageViewModel.updateMessageAudio(recorderAudioPath) } @@ -376,7 +405,7 @@ class ObtainMessageFragment: Fragment() { photoHelper.setCallback { if (it.exists()) { val fileName = it.name.lowercase() - if (fileName.endsWith(".jpg")||fileName.endsWith(".jpeg")||fileName.endsWith(".png")) { + if (fileName.endsWith(".jpg") || fileName.endsWith(".jpeg") || fileName.endsWith(".png")) { // 获取选中的图片,自动压缩图片质量 // 压缩图片文件 Luban.with(context) @@ -394,19 +423,35 @@ class ObtainMessageFragment: Fragment() { override fun onSuccess(file: File?) { XLog.d("压缩图片成功:${file?.absolutePath}") - // 删除源文件 - if (!it.absolutePath.equals(file?.absolutePath)) { - it?.delete() - } + // 删除源文件 + if (!it.absolutePath.equals(file?.absolutePath)) { + it?.delete() + } // 如果当前文件不在camera缓存文件夹下,则移动该文件 if (!file!!.parentFile.absolutePath.equals(SystemConstant.CameraFolder)) { - val copyResult = FileIOUtils.writeFileFromIS(File(SystemConstant.CameraFolder, fileName), FileInputStream(file)) - XLog.e("拷贝结果:"+copyResult) - // 跳转回原Fragment,展示拍摄的照片 - ViewModelProvider(requireActivity()).get(ObtainMessageViewModel::class.java).updateMessagePic(File(SystemConstant.CameraFolder, fileName).absolutePath) + try { + val copyResult = FileIOUtils.writeFileFromIS( + File( + SystemConstant.CameraFolder, + fileName + ), FileInputStream(file) + ) + XLog.e("拷贝结果:$copyResult") + // 跳转回原Fragment,展示拍摄的照片 + obtainMessageViewModel + .updateMessagePic( + File( + SystemConstant.CameraFolder, + fileName + ).absolutePath + ) + } catch (e: Exception) { + XLog.e("崩溃:${e.message}") + } + } else { // 跳转回原Fragment,展示拍摄的照片 - ViewModelProvider(requireActivity()).get(ObtainMessageViewModel::class.java).updateMessagePic(file!!.absolutePath) + obtainMessageViewModel.updateMessagePic(file!!.absolutePath) } } @@ -414,12 +459,23 @@ class ObtainMessageFragment: Fragment() { XLog.d("压缩图片失败:${e.message}") } }).launch() - } else if (fileName.endsWith(".mp3")||fileName.endsWith(".wav")||fileName.endsWith(".amr")||fileName.endsWith(".m4a")) { + } else if (fileName.endsWith(".mp3") || fileName.endsWith(".wav") || fileName.endsWith( + ".amr" + ) || fileName.endsWith(".m4a") + ) { ToastUtils.showToast(it.absolutePath) if (!it.parentFile.parentFile.absolutePath.equals(SystemConstant.SoundFolder)) { - val copyResult = FileIOUtils.writeFileFromIS(File(SystemConstant.SoundFolder, fileName), FileInputStream(it)) - XLog.e("拷贝结果:"+copyResult) - obtainMessageViewModel.updateMessageAudio(File(SystemConstant.SoundFolder, fileName).absolutePath) + val copyResult = FileIOUtils.writeFileFromIS( + File(SystemConstant.SoundFolder, fileName), + FileInputStream(it) + ) + XLog.e("拷贝结果:" + copyResult) + obtainMessageViewModel.updateMessageAudio( + File( + SystemConstant.SoundFolder, + fileName + ).absolutePath + ) } else { obtainMessageViewModel.updateMessageAudio(it.absolutePath) } @@ -432,12 +488,14 @@ class ObtainMessageFragment: Fragment() { } binding.tvAudioName.setOnClickListener { - binding.llAudioPlay.visibility = if (binding.llAudioPlay.visibility == VISIBLE) GONE else VISIBLE + binding.llAudioPlay.visibility = + if (binding.llAudioPlay.visibility == VISIBLE) GONE else VISIBLE // 判断当前播放的文件是否在缓存文件夹内,如果不在首先下载该文件 val fileUrl = obtainMessageViewModel.getMessageLiveData().value!!.mediaUrl!! - val localFile = obtainMessageViewModel.getLocalFileFromNetUrl(fileUrl, AttachmentType.AUDIO) + val localFile = + obtainMessageViewModel.getLocalFileFromNetUrl(fileUrl, AttachmentType.AUDIO) if (!localFile.exists()) { - obtainMessageViewModel.downLoadFile(fileUrl, localFile, object: DownloadCallback { + obtainMessageViewModel.downLoadFile(fileUrl, localFile, object : DownloadCallback { override fun progress(progress: Int) { } @@ -455,7 +513,7 @@ class ObtainMessageFragment: Fragment() { } binding.btnObtainMessageBack.setOnClickListener { - Navigation.findNavController(it).popBackStack() + findNavController().popBackStack() } binding.btnObtainMessageConfirm.setOnClickListener { @@ -476,7 +534,7 @@ class ObtainMessageFragment: Fragment() { toolTipRelativeLayout.showToolTipForView(toolTip, binding.tiLayoutTitle) checkResult = false } else { - if (messageData?.name!!.length>10) { + if (messageData?.name!!.length > 10) { val toolTipRelativeLayout = binding.ttTitle val toolTip = ToolTip() @@ -514,7 +572,7 @@ class ObtainMessageFragment: Fragment() { checkResult = false } - if (messageData?.who?.isEmpty()==true) { + if (messageData?.who?.isEmpty() == true) { val toolTipRelativeLayout = binding.ttSendFrom val toolTip = ToolTip() @@ -526,7 +584,7 @@ class ObtainMessageFragment: Fragment() { toolTipRelativeLayout.showToolTipForView(toolTip, binding.edtSendFrom) checkResult = false } - if (messageData?.toWho?.isEmpty()==true) { + if (messageData?.toWho?.isEmpty() == true) { val toolTipRelativeLayout = binding.ttSendTo val toolTip = ToolTip() @@ -547,22 +605,30 @@ class ObtainMessageFragment: Fragment() { localAttachmentList.add(imageAttachment) } if (messageData?.mediaUrl?.startsWith("http") == false) { - val audioAttachment = Attachment("", messageData.mediaUrl!!, AttachmentType.AUDIO) + val audioAttachment = + Attachment("", messageData.mediaUrl!!, AttachmentType.AUDIO) localAttachmentList.add(audioAttachment) } if (localAttachmentList.isNotEmpty()) { MaterialAlertDialogBuilder(requireContext()) .setTitle("提示") .setMessage("当前照片及音频内容需首先上传,是否尝试上传?") - .setPositiveButton("确定", DialogInterface.OnClickListener { dialogInterface, i -> - dialogInterface.dismiss() - for (attachment in localAttachmentList) { - obtainMessageViewModel.uploadAttachment(File(attachment.pathUrl), attachment.attachmentType) - } - }) - .setNegativeButton("取消", DialogInterface.OnClickListener { - dialogInterface, i -> dialogInterface.dismiss() - }) + .setPositiveButton( + "确定", + DialogInterface.OnClickListener { dialogInterface, i -> + dialogInterface.dismiss() + for (attachment in localAttachmentList) { + obtainMessageViewModel.uploadAttachment( + File(attachment.pathUrl), + attachment.attachmentType + ) + } + }) + .setNegativeButton( + "取消", + DialogInterface.OnClickListener { dialogInterface, i -> + dialogInterface.dismiss() + }) .show() return@setOnClickListener } @@ -571,7 +637,7 @@ class ObtainMessageFragment: Fragment() { val sendDate = DateUtils.str2Date(messageData?.sendDate, dateSendFormat) val cal = Calendar.getInstance() cal.time = Date() - cal.set(Calendar.MINUTE, cal.get(Calendar.MINUTE)+1) + cal.set(Calendar.MINUTE, cal.get(Calendar.MINUTE) + 1) if (sendDate.time < cal.time.time) { // 发送时间设置小于当前时间1分钟后,Toast提示用户并自动设置发送时间 messageData?.sendDate = DateUtils.date2Str(cal.time, dateSendFormat) ToastUtils.showToast("自动调整发送时间为1分钟后发送") @@ -581,7 +647,7 @@ class ObtainMessageFragment: Fragment() { } // 开始网络提交数据 - if (obtainMessageViewModel.getMessageLiveData().value?.id==0L) { // 如果网络id为空,则调用更新操作 + if (obtainMessageViewModel.getMessageLiveData().value?.id == 0L) { // 如果网络id为空,则调用更新操作 obtainMessageViewModel.insertCardByApp(confirmCallback) } else { obtainMessageViewModel.updateCardByApp(confirmCallback) @@ -593,7 +659,8 @@ class ObtainMessageFragment: Fragment() { val viewData = ViewData() viewData.imageSrc = obtainMessageViewModel.getMessageLiveData().value!!.imageUrl viewData.targetX = Utils.dp2px(context, 10F).toFloat() - viewData.targetWidth = DisplayUtils.getScreenWidthPixels(activity) - Utils.dp2px(context, 20F) + viewData.targetWidth = + DisplayUtils.getScreenWidthPixels(activity) - Utils.dp2px(context, 20F) viewData.targetHeight = Utils.dp2px(context, 200F) val viewDataList = listOf(viewData) binding.imageViewer.overlayStatusBar(true) // ImageViewer 是否会占据 StatusBar 的空间 @@ -603,11 +670,13 @@ class ObtainMessageFragment: Fragment() { .watch(0) // 开启浏览 } + binding.edtSendFrom.setText(obtainMessageViewModel.username) + } - val confirmCallback = object: ObtainMessageViewModel.MyConfirmCallback { + val confirmCallback = object : ObtainMessageViewModel.MyConfirmCallback { override fun onSucess() { - findNavController().navigate(R.id.navigation_home) + findNavController().popBackStack() } } @@ -648,6 +717,7 @@ class ObtainMessageFragment: Fragment() { }) .show() } + fun onRecorderDenied() { ToastUtils.showToast("当前操作需要您授权录音权限!") } diff --git a/app/src/main/java/com/navinfo/volvo/ui/fragments/message/ObtainMessageViewModel.kt b/app/src/main/java/com/navinfo/volvo/ui/fragments/message/ObtainMessageViewModel.kt index 3ef2925..db7cde8 100644 --- a/app/src/main/java/com/navinfo/volvo/ui/fragments/message/ObtainMessageViewModel.kt +++ b/app/src/main/java/com/navinfo/volvo/ui/fragments/message/ObtainMessageViewModel.kt @@ -1,36 +1,47 @@ package com.navinfo.volvo.ui.fragments.message -import android.security.ConfirmationCallback -import androidx.lifecycle.* +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope import com.easytools.tools.FileIOUtils import com.easytools.tools.FileUtils import com.easytools.tools.ToastUtils import com.elvishew.xlog.XLog -import com.navinfo.volvo.database.entity.Attachment import com.navinfo.volvo.database.entity.AttachmentType import com.navinfo.volvo.database.entity.GreetingMessage import com.navinfo.volvo.http.DownloadCallback import com.navinfo.volvo.http.DownloadManager import com.navinfo.volvo.http.DownloadState import com.navinfo.volvo.http.NavinfoVolvoCall +import com.navinfo.volvo.repository.preferences.PreferencesRepository import com.navinfo.volvo.utils.SystemConstant -import kotlinx.coroutines.flow.Flow +import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.launch -import okhttp3.MediaType import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.MultipartBody import okhttp3.RequestBody import java.io.File import java.io.FileInputStream -import java.util.* import javax.inject.Inject - -class ObtainMessageViewModel @Inject constructor(): ViewModel() { +@HiltViewModel +class ObtainMessageViewModel @Inject constructor( + private val pre: PreferencesRepository, +) : ViewModel() { private val msgLiveData: MutableLiveData by lazy { MutableLiveData() } + var username: String = "" + + init { + viewModelScope.launch { + pre.loginUser().collect { + username = it!!.username + } + } + } + fun setCurrentMessage(msg: GreetingMessage) { msgLiveData.postValue(msg) } @@ -131,7 +142,11 @@ class ObtainMessageViewModel @Inject constructor(): ViewModel() { try { val requestFile: RequestBody = RequestBody.create("multipart/form-data".toMediaTypeOrNull(), attachmentFile) - val body = MultipartBody.Part.createFormData("picture", attachmentFile.getName(), requestFile) + val body = MultipartBody.Part.createFormData( + "picture", + attachmentFile.getName(), + requestFile + ) val result = NavinfoVolvoCall.getApi().uploadAttachment(body) XLog.d(result.code) if (result.code == 200) { // 请求成功 @@ -144,17 +159,19 @@ class ObtainMessageViewModel @Inject constructor(): ViewModel() { if (destFile.exists()) { FileUtils.deleteFile(destFile) } - val copyResult = FileIOUtils.writeFileFromIS(destFile, FileInputStream(attachmentFile)) - XLog.e("拷贝结果:"+copyResult) + val copyResult = + FileIOUtils.writeFileFromIS(destFile, FileInputStream(attachmentFile)) + XLog.e("拷贝结果:" + copyResult) } else { val destFile = File(SystemConstant.SoundFolder, newFileName) if (destFile.exists()) { FileUtils.deleteFile(destFile) } - val copyResult = FileIOUtils.writeFileFromIS(destFile, FileInputStream(attachmentFile)) - XLog.e("拷贝结果:"+copyResult) + val copyResult = + FileIOUtils.writeFileFromIS(destFile, FileInputStream(attachmentFile)) + XLog.e("拷贝结果:" + copyResult) } - if (fileKey!=null) { + if (fileKey != null) { downloadAttachment(fileKey, attachmentType) } } else { @@ -179,7 +196,7 @@ class ObtainMessageViewModel @Inject constructor(): ViewModel() { if (result.code == 200) { // 请求成功 // 获取上传后的结果 val imageUrl = result.data - if (imageUrl!=null) { + if (imageUrl != null) { XLog.d("downloadAttachment-imageUrl:${imageUrl}") // 获取到图片的网络地址 if (attachmentType == AttachmentType.PIC) { @@ -198,7 +215,7 @@ class ObtainMessageViewModel @Inject constructor(): ViewModel() { } } - fun downLoadFile(url: String, destFile: File, downloadCallback: DownloadCallback){ + fun downLoadFile(url: String, destFile: File, downloadCallback: DownloadCallback) { viewModelScope.launch { DownloadManager.download( url, @@ -236,7 +253,8 @@ class ObtainMessageViewModel @Inject constructor(): ViewModel() { "sendDate" to message?.sendDate, "version" to message?.version ) - val result = NavinfoVolvoCall.getApi().insertCardByApp(insertData as Map) + val result = + NavinfoVolvoCall.getApi().insertCardByApp(insertData as Map) XLog.d("insertCardByApp:${result.code}") if (result.code == 200) { // 请求成功 // 获取上传后的结果 @@ -269,7 +287,8 @@ class ObtainMessageViewModel @Inject constructor(): ViewModel() { "sendDate" to message?.sendDate, "version" to message?.version ) - val result = NavinfoVolvoCall.getApi().updateCardByApp(updateData as Map) + val result = + NavinfoVolvoCall.getApi().updateCardByApp(updateData as Map) XLog.d("updateCardByApp:${result.code}") if (result.code == 200) { // 请求成功 // 数据更新成功 @@ -289,14 +308,14 @@ class ObtainMessageViewModel @Inject constructor(): ViewModel() { /** * 根据网络地址获取本地的缓存文件路径 * */ - fun getLocalFileFromNetUrl(url: String, attachmentType: AttachmentType):File { + fun getLocalFileFromNetUrl(url: String, attachmentType: AttachmentType): File { if (url.startsWith("http")) { - val folder = when(attachmentType) { - AttachmentType.PIC-> SystemConstant.CameraFolder + val folder = when (attachmentType) { + AttachmentType.PIC -> SystemConstant.CameraFolder else -> SystemConstant.SoundFolder } var name = if (url.contains("?")) { - url.substring(url.lastIndexOf("/")+1, url.indexOf("?")) + url.substring(url.lastIndexOf("/") + 1, url.indexOf("?")) } else { url.substringAfterLast("/") } diff --git a/app/src/main/java/com/navinfo/volvo/util/NetResult.kt b/app/src/main/java/com/navinfo/volvo/util/NetResult.kt index 21c532c..8f5aca2 100644 --- a/app/src/main/java/com/navinfo/volvo/util/NetResult.kt +++ b/app/src/main/java/com/navinfo/volvo/util/NetResult.kt @@ -11,14 +11,16 @@ package com.navinfo.volvo.util sealed class NetResult { data class Success(val data: T?) : NetResult() + data class Failure(val code: Int, val msg: String) : NetResult() data class Error(val exception: Exception) : NetResult() object Loading : NetResult() override fun toString(): String { return when (this) { - is Success<*> -> "Success[data=$data]" - is Error -> "Error[exception=$exception]" - is Loading -> "Loading" + is Success<*> -> "网络访问成功,返回正确结果Success[data=$data]" + is Failure -> "网络访问成功,返回错误结果Failure[$msg]" + is Error -> "网络访问出错 Error[exception=$exception]" + is Loading -> "网络访问中 Loading" } } } diff --git a/app/src/main/java/com/navinfo/volvo/util/SharedPreferenceHelper.kt b/app/src/main/java/com/navinfo/volvo/util/SharedPreferenceHelper.kt deleted file mode 100644 index f4e5fdc..0000000 --- a/app/src/main/java/com/navinfo/volvo/util/SharedPreferenceHelper.kt +++ /dev/null @@ -1,131 +0,0 @@ -package com.navinfo.volvo.util - -import android.content.Context -import android.content.SharedPreferences -import android.preference.PreferenceManager -import androidx.core.content.edit -import com.google.gson.Gson - - -class SharedPreferenceHelper { - - companion object { - - private const val WEATHER_PREF_TIME = "Weather pref time" - private const val WEATHER_FORECAST_PREF_TIME = "Forecast pref time" - private const val CITY_ID = "City ID" - private var prefs: SharedPreferences? = null - private const val LOCATION = "LOCATION" - - @Volatile - private var instance: SharedPreferenceHelper? = null - - /** - * This checks if there is an existing instance of the [SharedPreferences] in the - * specified [context] and creates one if there isn't or else, it returns the - * already existing instance. This function ensures that the [SharedPreferences] is - * accessed at any instance by a single thread. - */ - fun getInstance(context: Context): SharedPreferenceHelper { - synchronized(this) { - val _instance = instance - if (_instance == null) { - prefs = PreferenceManager.getDefaultSharedPreferences(context) - instance = _instance - } - return SharedPreferenceHelper() - } - } - } - - /** - * This function saves the initial time [System.nanoTime] at which the weather information - * at the user's location is accessed. - * @param time the value of [System.nanoTime] when the weather information is received. - */ - fun saveTimeOfInitialWeatherFetch(time: Long) { - prefs?.edit(commit = true) { - putLong(WEATHER_PREF_TIME, time) - } - } - - /** - * This function returns the saved value of [System.nanoTime] when the weather information - * at the user's location was accessed. - * @see saveTimeOfInitialWeatherFetch - */ - fun getTimeOfInitialWeatherFetch() = prefs?.getLong(WEATHER_PREF_TIME, 0L) - - /** - * This function saves the initial time [System.nanoTime] at which the weather forecast - * at the user's location is accessed. - * @param time the value of [System.nanoTime] when the weather forecast is received. - */ - fun saveTimeOfInitialWeatherForecastFetch(time: Long) { - prefs?.edit(commit = true) { - putLong(WEATHER_FORECAST_PREF_TIME, time) - } - } - - /** - * This function returns the saved value of [System.nanoTime] when the weather forecast - * at the user's location was accessed. - * @see saveTimeOfInitialWeatherForecastFetch - */ - fun getTimeOfInitialWeatherForecastFetch() = prefs?.getLong(WEATHER_FORECAST_PREF_TIME, 0L) - - /** - * This function saves the [cityId] of the location whose weather information has been - * received. - * @param cityId the id of the location whose weather has been received - */ - fun saveCityId(cityId: Int) { - prefs?.edit(commit = true) { - putInt(CITY_ID, cityId) - } - } - - /** - * This function returns the id of the location whose weather information has been received. - * @see saveCityId - */ - fun getCityId() = prefs?.getInt(CITY_ID, 0) - - /** - * This function gets the value of the cache duration the user set in the - * Settings Fragment. - */ - fun getUserSetCacheDuration() = prefs?.getString("cache_key", "0") - - /** - * This function gets the value of the app theme the user set in the - * Settings Fragment. - */ - fun getSelectedThemePref() = prefs?.getString("theme_key", "") - - /** - * This function gets the value of the temperature unit the user set in the - * Settings Fragment. - */ - fun getSelectedTemperatureUnit() = prefs?.getString("unit_key", "") - -// /** -// * This function saves a [LocationModel] -// */ -// fun saveLocation(location: LocationModel) { -// prefs?.edit(commit = true) { -// val gson = Gson() -// val json = gson.toJson(location) -// putString(LOCATION, json) -// } -// } - -// /** -// * This function gets the value of the saved [LocationModel] -// */ -// fun getLocation(): LocationModel { -// val gson = Gson() -// val json = prefs?.getString(LOCATION, null) -// return gson.fromJson(json, LocationModel::class.java) -// } -} diff --git a/app/src/main/proto/LoginUser.proto b/app/src/main/proto/LoginUser.proto new file mode 100644 index 0000000..15d5d45 --- /dev/null +++ b/app/src/main/proto/LoginUser.proto @@ -0,0 +1,9 @@ +syntax = "proto3"; + +option java_package = "com.navinfo.volvo.model.proto"; +option java_multiple_files = true; + +message LoginUser { + string username = 1; + string password = 2; +} \ No newline at end of file diff --git a/app/src/main/res/layout/adapter_home.xml b/app/src/main/res/layout/adapter_home.xml index 7a24c31..98fc513 100644 --- a/app/src/main/res/layout/adapter_home.xml +++ b/app/src/main/res/layout/adapter_home.xml @@ -1,12 +1,14 @@ - + + + @@ -17,15 +19,17 @@ android:layout_height="wrap_content"> - + app:layout_constraintTop_toTopOf="parent" + app:roundPercent="0.4" /> @@ -23,8 +23,8 @@ android:hint="请输入查询内容" /> - + app:layout_constraintTop_toBottomOf="@id/home_search"> + + + + diff --git a/app/src/main/res/layout/fragment_login.xml b/app/src/main/res/layout/fragment_login.xml index 6829329..f468b7a 100644 --- a/app/src/main/res/layout/fragment_login.xml +++ b/app/src/main/res/layout/fragment_login.xml @@ -6,7 +6,7 @@ + type="com.navinfo.volvo.model.proto.LoginUser" /> - - @@ -54,6 +55,7 @@ app:layout_constraintTop_toBottomOf="@id/login_fragment_user_layout"> + tools:context="com.navinfo.volvo.ui.fragments.message.ObtainMessageFragment"> + + - \ No newline at end of file diff --git a/app/src/main/res/layout/home_adapter_noting.xml b/app/src/main/res/layout/home_adapter_noting.xml new file mode 100644 index 0000000..56ba3de --- /dev/null +++ b/app/src/main/res/layout/home_adapter_noting.xml @@ -0,0 +1,12 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/load_state_view.xml b/app/src/main/res/layout/load_state_view.xml new file mode 100644 index 0000000..7450e8b --- /dev/null +++ b/app/src/main/res/layout/load_state_view.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/navigation/mobile_navigation.xml b/app/src/main/res/navigation/mobile_navigation.xml index 83fe244..c59218b 100644 --- a/app/src/main/res/navigation/mobile_navigation.xml +++ b/app/src/main/res/navigation/mobile_navigation.xml @@ -13,12 +13,9 @@ +