diff --git a/app/build.gradle b/app/build.gradle index 981e1fe..ede1fcd 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' @@ -107,29 +107,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' @@ -153,6 +144,31 @@ dependencies { implementation 'com.github.JagarYousef:ChatVoicePlayer:1.1.0' // 图片查看器 https://github.com/XiaoGe-1996/ImageViewer implementation 'com.github.XiaoGe-1996:ImageViewer:v1.0.0' + + // 数据存储 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/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..228cb39 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 @@ -8,13 +8,18 @@ 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") fun countUnreadByFlow(): Flow @@ -28,17 +33,22 @@ interface GreetingMessageDao { * 检查某条数据是否存在 */ @Query("SELECT id From GreetingMessage WHERE id = :id LIMIT 1") - fun getMessageId(id: Long): Long + 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{ + } else { update(message) } } } + + } \ 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..202cfd8 --- /dev/null +++ b/app/src/main/java/com/navinfo/volvo/repository/preferences/PreferencesRepository.kt @@ -0,0 +1,16 @@ +package com.navinfo.volvo.repository.preferences + +import com.navinfo.volvo.model.proto.LoginUser +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..c7268a8 --- /dev/null +++ b/app/src/main/java/com/navinfo/volvo/repository/preferences/PreferencesRepositoryImp.kt @@ -0,0 +1,58 @@ +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 com.navinfo.volvo.model.proto.LoginUser +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map +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..844c329 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,10 @@ 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,7 +52,6 @@ class HomeAdapter(fragment: Fragment) : .error(R.mipmap.volvo_logo_small) .into(mDataBinding.messageHeadIcon) } - } class DiffCallback : DiffUtil.ItemCallback() { 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..860dbb1 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,12 +1,15 @@ 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.navinfo.volvo.R @@ -15,25 +18,24 @@ 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 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 val viewModel by viewModels() - private lateinit var messageAdapter: HomeAdapter + private val messageAdapter by lazy { HomeAdapter(this) } 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 @@ -42,55 +44,117 @@ class HomeFragment : BaseFragment(), OnItemClickListener, OnItemMenuClickListene } 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(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 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) + } + //侧滑按钮 + 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 -> { + 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() 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..4a70292 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,23 +1,30 @@ 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.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, ) : ViewModel() { private val _isLoading = MutableLiveData() @@ -32,18 +39,46 @@ class HomeViewModel @Inject constructor( fun getNetMessageList() { + if (_isLoading.value == true) + return _isLoading.postValue(true) viewModelScope.launch { val messagePost = NetworkMessageListPost(who = "", toWho = "") - when (val result = netRepository.getCardList(messagePost)) { + 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) + 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..030bea2 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,31 @@ class LoginFragment : BaseFragment() { } private fun initView() { + + lifecycleScope.launch { + viewModel.user.collect { + if (it != null){ + viewBinding.loginUser = it + } + cancel() + } + } 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/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/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml index 9660c83..d4965ee 100644 --- a/app/src/main/res/layout/fragment_home.xml +++ b/app/src/main/res/layout/fragment_home.xml @@ -11,8 +11,8 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginLeft="20dp" - android:layout_marginRight="20dp" android:layout_marginTop="5dp" + android:layout_marginRight="20dp" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent"> @@ -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..581dde0 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 +54,7 @@ app:layout_constraintTop_toBottomOf="@id/login_fragment_user_layout"> + + + + + + + + + + + + \ 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 @@ +