增加sharde数据存储功能,记录登录用户

优化页面跳转动画
增加message删除流程
This commit is contained in:
squallzhjch 2023-01-09 10:58:19 +08:00
parent 64972c3c88
commit 1e52eac92c
38 changed files with 681 additions and 639 deletions

View File

@ -2,9 +2,10 @@ plugins {
id 'com.android.application' id 'com.android.application'
id 'org.jetbrains.kotlin.android' id 'org.jetbrains.kotlin.android'
id 'kotlin-android' id 'kotlin-android'
id 'kotlin-parcelize' id 'kotlin-parcelize' //
id 'kotlin-kapt' 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' // id 'com.google.dagger.hilt.android'
} }
@ -82,7 +83,7 @@ dependencies {
//room //room
implementation 'com.tencent.wcdb:room:1.1-19' // room-runtime wcdb-android 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-runtime:2.4.3'
implementation 'androidx.room:room-ktx:2.4.3' implementation 'androidx.room:room-ktx:2.4.3'
annotationProcessor 'androidx.room:room-compiler: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 'android.arch.persistence.room:compiler:1.1.1'// compiler room
kapt 'androidx.room:room-compiler:2.4.3' kapt 'androidx.room:room-compiler:2.4.3'
kapt 'androidx.room:room-ktx:2.4.3' kapt 'androidx.room:room-ktx:2.4.3'
// //
implementation "androidx.room:room-paging:2.4.3" implementation "androidx.room:room-paging:2.4.3"
implementation "androidx.paging:paging-runtime-ktx:3.1.1" implementation "androidx.paging:paging-runtime-ktx:3.1.1"
androidTestImplementation "android.arch.persistence.room:testing:1.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' implementation 'com.tencent.wcdb:wcdb-android:1.1-19'
@ -107,29 +107,20 @@ dependencies {
// //
implementation 'com.yanzhenjie.recyclerview:x:1.3.2' implementation 'com.yanzhenjie.recyclerview:x:1.3.2'
// implementation 'androidx.appcompat:appcompat:1.5.1' //
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
// // Koin
// implementation("io.insert-koin:koin-android:3.3.2")
// implementation("io.insert-koin:koin-core:3.3.2")
// Retrofit // Retrofit
implementation("com.squareup.retrofit2:retrofit:2.9.0") 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")
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:okhttp:4.9.0")
implementation("com.squareup.okhttp3:logging-interceptor:4.9.0") implementation("com.squareup.okhttp3:logging-interceptor:4.9.0")
implementation("com.google.code.gson:gson:2.8.6") implementation("com.google.code.gson:gson:2.8.6")
//hilt //hilt
implementation "com.google.dagger:hilt-android:2.41" implementation "com.google.dagger:hilt-android:2.41"
kapt "com.google.dagger:hilt-compiler: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 // https://github.com/nhaarman/supertooltips
implementation 'com.nhaarman.supertooltips:library:3.0.0' implementation 'com.nhaarman.supertooltips:library:3.0.0'
@ -153,6 +144,31 @@ dependencies {
implementation 'com.github.JagarYousef:ChatVoicePlayer:1.1.0' implementation 'com.github.JagarYousef:ChatVoicePlayer:1.1.0'
// https://github.com/XiaoGe-1996/ImageViewer // https://github.com/XiaoGe-1996/ImageViewer
implementation 'com.github.XiaoGe-1996:ImageViewer:v1.0.0' 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 { kapt {

View File

@ -24,14 +24,15 @@
android:dataExtractionRules="@xml/data_extraction_rules" android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules" android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/volvo_logo_small" android:icon="@mipmap/volvo_logo_small"
android:requestLegacyExternalStorage="true"
android:label="@string/app_name" android:label="@string/app_name"
android:requestLegacyExternalStorage="true"
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/Theme.NavinfoVolvo" android:theme="@style/Theme.NavinfoVolvo"
android:usesCleartextTraffic="true"> android:usesCleartextTraffic="true">
<activity <activity
android:name=".ui.message.MessageActivity" android:name=".ui.message.MessageActivity"
android:configChanges="orientation"
android:exported="false" android:exported="false"
android:label="@string/title_activity_second" android:label="@string/title_activity_second"
android:screenOrientation="portrait" android:screenOrientation="portrait"
@ -54,6 +55,8 @@
<meta-data <meta-data
android:name="android.app.lib_name" android:name="android.app.lib_name"
android:value="" /> android:value="" />
<meta-data android:name="ScopedStorage" android:value="true" /> <meta-data
android:name="ScopedStorage"
android:value="true" />
</application> </application>
</manifest> </manifest>

View File

@ -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.
// * <p>
// * 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<Void, Void, Void> {
//
// 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;
// }
//}

View File

@ -8,13 +8,18 @@ import kotlinx.coroutines.flow.Flow
@Dao @Dao
interface GreetingMessageDao { interface GreetingMessageDao {
@Query("DELETE from GreetingMessage WHERE id=:id")
suspend fun deleteById(id: Long)
@Insert @Insert
fun insert(message: GreetingMessage): Long suspend fun insert(message: GreetingMessage): Long
@Update(onConflict = OnConflictStrategy.REPLACE) @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 read = 0")
fun countUnreadByFlow(): Flow<Long> fun countUnreadByFlow(): Flow<Long>
@ -28,17 +33,22 @@ interface GreetingMessageDao {
* 检查某条数据是否存在 * 检查某条数据是否存在
*/ */
@Query("SELECT id From GreetingMessage WHERE id = :id LIMIT 1") @Query("SELECT id From GreetingMessage WHERE id = :id LIMIT 1")
fun getMessageId(id: Long): Long suspend fun getMessageId(id: Long): Long
/**
*
*/
@Transaction @Transaction
suspend fun insertOrUpdate(list: List<GreetingMessage>) { suspend fun insertOrUpdate(list: List<GreetingMessage>) {
for (message in list) { for (message in list) {
val id = getMessageId(message.id) val id = getMessageId(message.id)
if (id == 0L) { if (id == 0L) {
insert(message) insert(message)
}else{ } else {
update(message) update(message)
} }
} }
} }
} }

View File

@ -22,6 +22,7 @@ import javax.inject.Singleton
@Module @Module
class NetworkUtilModule { class NetworkUtilModule {
@Provides @Provides
@Singleton @Singleton
fun provideContext(application: Application): Context { fun provideContext(application: Application): Context {

View File

@ -1,7 +1,13 @@
package com.navinfo.volvo.di.module package com.navinfo.volvo.di.module
import android.content.Context 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.Module
import dagger.Provides import dagger.Provides
import dagger.hilt.InstallIn import dagger.hilt.InstallIn
@ -14,7 +20,12 @@ class UtilModule {
@Provides @Provides
@Singleton @Singleton
fun provideSharedPreferencesHelper(context: Context): SharedPreferenceHelper { fun provideLoginUserDataStore(
return SharedPreferenceHelper.getInstance(context) context: Context,
} serializer: Serializer<LoginUser>
): DataStore<LoginUser> = DataStoreFactory.create(
serializer = serializer,
produceFile = { context.dataStoreFile("login_user") }
)
} }

View File

@ -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<LoginUser>
}

View File

@ -1,35 +1,35 @@
package com.navinfo.volvo.di.module //package com.navinfo.volvo.di.module
//
import androidx.lifecycle.ViewModel //import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider //import androidx.lifecycle.ViewModelProvider
import javax.inject.Inject //import javax.inject.Inject
import javax.inject.Provider //import javax.inject.Provider
import javax.inject.Singleton //import javax.inject.Singleton
//
//
/** ///**
* Factory for all ViewModels. // * Factory for all ViewModels.
* reference : https://github.com/googlesamples/android-architecture-components // * reference : https://github.com/googlesamples/android-architecture-components
*/ // */
@Singleton //@Singleton
class ViewModelFactory @Inject constructor( //class ViewModelFactory @Inject constructor(
private val viewModelMap: Map<Class<out ViewModel>, @JvmSuppressWildcards Provider<ViewModel>> // private val viewModelMap: Map<Class<out ViewModel>, @JvmSuppressWildcards Provider<ViewModel>>
) : ViewModelProvider.Factory { //) : ViewModelProvider.Factory {
//
@Suppress("UNCHECKED_CAST") // @Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T { // override fun <T : ViewModel> create(modelClass: Class<T>): T {
var viewModel = viewModelMap[modelClass] // var viewModel = viewModelMap[modelClass]
//
if (viewModel == null) { // if (viewModel == null) {
for (entry in viewModelMap) { // for (entry in viewModelMap) {
if (modelClass.isAssignableFrom(entry.key)) { // if (modelClass.isAssignableFrom(entry.key)) {
viewModel = entry.value // viewModel = entry.value
break // break
} // }
} // }
} // }
//
if (viewModel == null) throw IllegalArgumentException("Unknown model class $modelClass") // if (viewModel == null) throw IllegalArgumentException("Unknown model class $modelClass")
return viewModel.get() as T // return viewModel.get() as T
} // }
} //}

View File

@ -1,44 +1,44 @@
package com.navinfo.volvo.di.module //package com.navinfo.volvo.di.module
//
import androidx.lifecycle.ViewModel //import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider //import androidx.lifecycle.ViewModelProvider
import com.navinfo.volvo.di.key.ViewModelKey //import com.navinfo.volvo.di.key.ViewModelKey
import com.navinfo.volvo.ui.MainActivityViewModel //import com.navinfo.volvo.ui.MainActivityViewModel
import com.navinfo.volvo.ui.fragments.home.HomeViewModel //import com.navinfo.volvo.ui.fragments.home.HomeViewModel
import com.navinfo.volvo.ui.fragments.login.LoginViewModel //import com.navinfo.volvo.ui.fragments.login.LoginViewModel
import com.navinfo.volvo.ui.fragments.message.ObtainMessageViewModel //import com.navinfo.volvo.ui.fragments.message.ObtainMessageViewModel
import dagger.Binds //import dagger.Binds
import dagger.Module //import dagger.Module
import dagger.hilt.InstallIn //import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent //import dagger.hilt.components.SingletonComponent
import dagger.multibindings.IntoMap //import dagger.multibindings.IntoMap
//
@InstallIn(SingletonComponent::class) //@InstallIn(SingletonComponent::class)
@Module //@Module
abstract class ViewModelModule { //abstract class ViewModelModule {
//
@Binds // @Binds
abstract fun bindViewModelFactory(viewModelFactory: ViewModelFactory): ViewModelProvider.Factory // abstract fun bindViewModelFactory(viewModelFactory: ViewModelFactory): ViewModelProvider.Factory
//
@IntoMap // @IntoMap
@Binds // @Binds
@ViewModelKey(MainActivityViewModel::class) // @ViewModelKey(MainActivityViewModel::class)
abstract fun bindMainViewModel(viewModel: MainActivityViewModel): ViewModel // abstract fun bindMainViewModel(viewModel: MainActivityViewModel): ViewModel
//
@IntoMap // @IntoMap
@Binds // @Binds
@ViewModelKey(LoginViewModel::class) // @ViewModelKey(LoginViewModel::class)
abstract fun bindLoginFragmentViewModel(viewModel: LoginViewModel): ViewModel // abstract fun bindLoginFragmentViewModel(viewModel: LoginViewModel): ViewModel
//
@IntoMap // @IntoMap
@Binds // @Binds
@ViewModelKey(HomeViewModel::class) // @ViewModelKey(HomeViewModel::class)
abstract fun bindMessageFragmentViewModel(viewModel: HomeViewModel): ViewModel // abstract fun bindMessageFragmentViewModel(viewModel: HomeViewModel): ViewModel
//
@IntoMap // @IntoMap
@Binds // @Binds
@ViewModelKey(ObtainMessageViewModel::class) // @ViewModelKey(ObtainMessageViewModel::class)
abstract fun bindObtainMessageFragmentViewModel(viewModel: ObtainMessageViewModel): ViewModel // abstract fun bindObtainMessageFragmentViewModel(viewModel: ObtainMessageViewModel): ViewModel
//
//
} //}

View File

@ -1,9 +0,0 @@
package com.navinfo.volvo.model
/**
* 登录用户信息
*/
data class LoginUser(
var name: String,
var password: String
)

View File

@ -1,8 +1,8 @@
package com.navinfo.volvo.model.messagelist package com.navinfo.volvo.model.network
import com.navinfo.volvo.database.entity.GreetingMessage import com.navinfo.volvo.database.entity.GreetingMessage
data class NetworkMessageListResponse( data class NetworkMessageListResponse(
val total: Int, val total: Int,
val rows: List<GreetingMessage> val rows: List<GreetingMessage>?
) )

View File

@ -1,4 +1,4 @@
package com.navinfo.volvo.model.messagelist package com.navinfo.volvo.model.network
data class NetworkMessageListPost( data class NetworkMessageListPost(
val name: String,//问候名称,非必填项 val name: String,//问候名称,非必填项
@ -12,4 +12,8 @@ data class NetworkMessageListPost(
constructor(who: String, toWho: String) : this("", who, toWho, "", "", "10", "1") { constructor(who: String, toWho: String) : this("", who, toWho, "", "", "10", "1") {
} }
} }
data class NetworkDeleteMessagePost(
val id: Long
)

View File

@ -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<LoginUser> {
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)
}

View File

@ -11,12 +11,14 @@ import javax.inject.Inject
class DatabaseRepositoryImp @Inject constructor( class DatabaseRepositoryImp @Inject constructor(
private val messageDao: GreetingMessageDao, private val messageDao: GreetingMessageDao,
private val database: AppDatabase
) : DatabaseRepository { ) : DatabaseRepository {
companion object { companion object {
const val PAGE_SIZE = 20 const val PAGE_SIZE = 20
} }
/**
* 分页加载消息
*/
override fun getMessageByPaging(): Flow<PagingData<GreetingMessage>> { override fun getMessageByPaging(): Flow<PagingData<GreetingMessage>> {
return Pager(PagingConfig(PAGE_SIZE)) { return Pager(PagingConfig(PAGE_SIZE)) {
messageDao.findAllByDataSource() messageDao.findAllByDataSource()

View File

@ -1,13 +1,22 @@
package com.navinfo.volvo.repository.network package com.navinfo.volvo.repository.network
import com.navinfo.volvo.http.DefaultResponse import com.navinfo.volvo.http.DefaultResponse
import com.navinfo.volvo.model.messagelist.NetworkMessageListPost import com.navinfo.volvo.model.network.NetworkDeleteMessagePost
import com.navinfo.volvo.model.messagelist.NetworkMessageListResponse import com.navinfo.volvo.model.network.NetworkMessageListPost
import com.navinfo.volvo.model.network.NetworkMessageListResponse
import com.navinfo.volvo.util.NetResult import com.navinfo.volvo.util.NetResult
/** /**
* 网络访问接口 * 网络访问接口
*/ */
interface NetworkRepository { interface NetworkRepository {
suspend fun getCardList(message: NetworkMessageListPost): NetResult<DefaultResponse<NetworkMessageListResponse>> /**
* 获取问候列表
*/
suspend fun getMessageList(message: NetworkMessageListPost): NetResult<DefaultResponse<NetworkMessageListResponse>>
/**
*删除问候
*/
suspend fun deleteMessage(message: NetworkDeleteMessagePost): NetResult<DefaultResponse<*>>
} }

View File

@ -1,11 +1,12 @@
package com.navinfo.volvo.repository.network package com.navinfo.volvo.repository.network
import com.google.gson.Gson
import com.navinfo.volvo.database.dao.GreetingMessageDao import com.navinfo.volvo.database.dao.GreetingMessageDao
import com.navinfo.volvo.di.scope.IoDispatcher import com.navinfo.volvo.di.scope.IoDispatcher
import com.navinfo.volvo.http.DefaultResponse import com.navinfo.volvo.http.DefaultResponse
import com.navinfo.volvo.model.messagelist.NetworkMessageListPost import com.navinfo.volvo.model.network.NetworkDeleteMessagePost
import com.navinfo.volvo.model.messagelist.NetworkMessageListResponse import com.navinfo.volvo.model.network.NetworkMessageListPost
import com.navinfo.volvo.tools.GsonUtil import com.navinfo.volvo.model.network.NetworkMessageListResponse
import com.navinfo.volvo.util.NetResult import com.navinfo.volvo.util.NetResult
import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@ -16,22 +17,47 @@ import javax.inject.Inject
class NetworkRepositoryImp @Inject constructor( class NetworkRepositoryImp @Inject constructor(
private val netWorkService: NetworkService, private val netWorkService: NetworkService,
private val messageDao: GreetingMessageDao, private val gson: Gson,
@IoDispatcher private val ioDispatcher: CoroutineDispatcher @IoDispatcher private val ioDispatcher: CoroutineDispatcher
) : NetworkRepository { ) : NetworkRepository {
/**
override suspend fun getCardList(message: NetworkMessageListPost): NetResult<DefaultResponse<NetworkMessageListResponse>> = * 获取问候列表
*/
override suspend fun getMessageList(message: NetworkMessageListPost): NetResult<DefaultResponse<NetworkMessageListResponse>> =
withContext(ioDispatcher) { withContext(ioDispatcher) {
return@withContext try { return@withContext try {
val stringBody = GsonUtil.getInstance().toJson(message) val stringBody = gson.toJson(message)
.toRequestBody("application/json;charset=utf-8".toMediaType()) .toRequestBody("application/json;charset=utf-8".toMediaType())
val result = netWorkService.queryCardListByApp(stringBody) val result = netWorkService.queryMessageListByApp(stringBody)
if (result.isSuccessful) { if (result.isSuccessful) {
val body = result.body() if (result.body()!!.code == 200) {
if(body!!.data != null && body.data!!.rows != null){ NetResult.Success(result.body())
messageDao.insertOrUpdate(body.data!!.rows) } 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<DefaultResponse<*>> =
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 { } else {
NetResult.Success(null) NetResult.Success(null)
} }

View File

@ -1,13 +1,23 @@
package com.navinfo.volvo.repository.network package com.navinfo.volvo.repository.network
import com.navinfo.volvo.http.DefaultResponse import com.navinfo.volvo.http.DefaultResponse
import com.navinfo.volvo.model.messagelist.NetworkMessageListResponse import com.navinfo.volvo.model.network.NetworkMessageListResponse
import okhttp3.RequestBody import okhttp3.RequestBody
import retrofit2.Response import retrofit2.Response
import retrofit2.http.Body import retrofit2.http.Body
import retrofit2.http.POST import retrofit2.http.POST
interface NetworkService { interface NetworkService {
/**
* 获取问候列表
*/
@POST("/navi/cardDelivery/queryCardListByApp") @POST("/navi/cardDelivery/queryCardListByApp")
suspend fun queryCardListByApp(@Body body: RequestBody): Response<DefaultResponse<NetworkMessageListResponse>> suspend fun queryMessageListByApp(@Body body: RequestBody): Response<DefaultResponse<NetworkMessageListResponse>>
/**
* 删除问候
*/
@POST("/navi/cardDelivery/deleteCardByApp")
suspend fun deleteMessage(@Body body: RequestBody): Response<DefaultResponse<*>>
} }

View File

@ -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<LoginUser?>
suspend fun saveString(key: String, content: String)
suspend fun getString(key: String): Flow<String?>
suspend fun saveInt(key: String, content: Int)
suspend fun getInt(key: String): Flow<Int?>
}

View File

@ -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<Preferences> by preferencesDataStore(name = DataStore_NAME)
class PreferencesRepositoryImp @Inject constructor(
private val context: Context,
private val loginUser: DataStore<LoginUser>
) : 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<String?> = 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<Int?> = context.datastore.data.map {
it[intPreferencesKey(key)]
}
override fun loginUser(): Flow<LoginUser?> = loginUser.data
}

View File

@ -6,6 +6,6 @@ import androidx.lifecycle.ViewModelProvider
import javax.inject.Inject import javax.inject.Inject
abstract class BaseActivity : AppCompatActivity() { abstract class BaseActivity : AppCompatActivity() {
@Inject // @Inject
lateinit var viewModelFactoryProvider: ViewModelProvider.Factory // lateinit var viewModelFactoryProvider: ViewModelProvider.Factory
} }

View File

@ -1,8 +1,8 @@
package com.navinfo.volvo.ui package com.navinfo.volvo.ui
import android.content.DialogInterface import android.content.DialogInterface
import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.view.Gravity
import android.view.View import android.view.View
import android.widget.Toast import android.widget.Toast
import androidx.activity.viewModels import androidx.activity.viewModels
@ -11,6 +11,9 @@ import androidx.navigation.findNavController
import androidx.navigation.ui.AppBarConfiguration import androidx.navigation.ui.AppBarConfiguration
import androidx.navigation.ui.setupActionBarWithNavController import androidx.navigation.ui.setupActionBarWithNavController
import androidx.navigation.ui.setupWithNavController import androidx.navigation.ui.setupWithNavController
import androidx.transition.Slide
import androidx.transition.Transition
import androidx.transition.TransitionManager
import com.easytools.tools.FileUtils import com.easytools.tools.FileUtils
import com.elvishew.xlog.BuildConfig import com.elvishew.xlog.BuildConfig
import com.elvishew.xlog.LogConfiguration import com.elvishew.xlog.LogConfiguration
@ -30,16 +33,17 @@ import com.hjq.permissions.Permission
import com.hjq.permissions.XXPermissions import com.hjq.permissions.XXPermissions
import com.navinfo.volvo.R import com.navinfo.volvo.R
import com.navinfo.volvo.databinding.ActivityMainBinding import com.navinfo.volvo.databinding.ActivityMainBinding
import com.navinfo.volvo.ui.message.MessageActivity
import com.navinfo.volvo.utils.SystemConstant import com.navinfo.volvo.utils.SystemConstant
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@AndroidEntryPoint @AndroidEntryPoint
class MainActivity : BaseActivity() { class MainActivity : BaseActivity() {
private lateinit var binding: ActivityMainBinding private lateinit var binding: ActivityMainBinding
private val viewModel by viewModels<MainActivityViewModel> { viewModelFactoryProvider } private val viewModel by viewModels<MainActivityViewModel>()
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -47,7 +51,6 @@ class MainActivity : BaseActivity() {
binding = ActivityMainBinding.inflate(layoutInflater) binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
XXPermissions.with(this) XXPermissions.with(this)
// 申请单个权限 // 申请单个权限
.permission(Permission.WRITE_EXTERNAL_STORAGE) .permission(Permission.WRITE_EXTERNAL_STORAGE)
@ -98,28 +101,29 @@ class MainActivity : BaseActivity() {
lifecycleScope.launch { lifecycleScope.launch {
viewModel.getUnreadCount().collect { viewModel.getUnreadCount().collect {
runOnUiThread { if (it == 0L) {
if (it == 0L) { navView.removeBadge(R.id.navigation_home)
navView.removeBadge(R.id.navigation_home) } else {
} else { var badge = navView.getOrCreateBadge(R.id.navigation_home);
var badge = navView.getOrCreateBadge(R.id.navigation_home); badge.number = it.toInt()
badge.number = it.toInt()
}
} }
} }
} }
navController.addOnDestinationChangedListener { controller, destination, arguments -> navController.addOnDestinationChangedListener { controller, destination, arguments ->
if (destination.id == R.id.navigation_home if (destination.id == R.id.navigation_home || destination.id == R.id.navigation_dashboard || destination.id == R.id.navigation_notifications) {
|| destination.id == R.id.navigation_dashboard
|| destination.id == R.id.navigation_notifications
) {
runOnUiThread { runOnUiThread {
val transition: Transition = Slide(Gravity.BOTTOM)
transition.duration = 300;
TransitionManager.beginDelayedTransition(binding.root, transition);
navView.visibility = View.VISIBLE navView.visibility = View.VISIBLE
newMessageView.visibility = View.VISIBLE newMessageView.visibility = View.VISIBLE
} }
} else { } else {
runOnUiThread { runOnUiThread {
val transition: Transition = Slide(Gravity.BOTTOM)
transition.duration = 300;
TransitionManager.beginDelayedTransition(binding.root, transition);
navView.visibility = View.GONE navView.visibility = View.GONE
newMessageView.visibility = View.GONE newMessageView.visibility = View.GONE
} }
@ -132,6 +136,7 @@ class MainActivity : BaseActivity() {
} }
} }
override fun onSupportNavigateUp() = override fun onSupportNavigateUp() =
findNavController(R.id.nav_host_fragment_activity_main).navigateUp() findNavController(R.id.nav_host_fragment_activity_main).navigateUp()
@ -151,13 +156,10 @@ class MainActivity : BaseActivity() {
} }
fun xLogInit(logFolder: String) { fun xLogInit(logFolder: String) {
val config = LogConfiguration.Builder() val config = LogConfiguration.Builder().logLevel(
.logLevel( if (BuildConfig.DEBUG) LogLevel.ALL // 指定日志级别,低于该级别的日志将不会被打印,默认为 LogLevel.ALL
if (BuildConfig.DEBUG) else LogLevel.NONE
LogLevel.ALL // 指定日志级别,低于该级别的日志将不会被打印,默认为 LogLevel.ALL ).tag("Volvo") // 指定 TAG默认为 "X-LOG"
else LogLevel.NONE
)
.tag("Volvo") // 指定 TAG默认为 "X-LOG"
.enableThreadInfo() // 允许打印线程信息,默认禁止 .enableThreadInfo() // 允许打印线程信息,默认禁止
.enableStackTrace(2) // 允许打印深度为 2 的调用栈信息,默认禁止 .enableStackTrace(2) // 允许打印深度为 2 的调用栈信息,默认禁止
.enableBorder() // 允许打印日志边框,默认禁止 .enableBorder() // 允许打印日志边框,默认禁止
@ -165,8 +167,7 @@ class MainActivity : BaseActivity() {
BlacklistTagsFilterInterceptor( // 添加黑名单 TAG 过滤器 BlacklistTagsFilterInterceptor( // 添加黑名单 TAG 过滤器
"blacklist1", "blacklist2", "blacklist3" "blacklist1", "blacklist2", "blacklist3"
) )
) ).build()
.build()
val androidPrinter: Printer = AndroidPrinter(true) // 通过 android.util.Log 打印日志的打印器 val androidPrinter: Printer = AndroidPrinter(true) // 通过 android.util.Log 打印日志的打印器
@ -181,8 +182,7 @@ class MainActivity : BaseActivity() {
XLog.init( // 初始化 XLog XLog.init( // 初始化 XLog
config, // 指定日志配置,如果不指定,会默认使用 new LogConfiguration.Builder().build() config, // 指定日志配置,如果不指定,会默认使用 new LogConfiguration.Builder().build()
androidPrinter, // 添加任意多的打印器。如果没有添加任何打印器,会默认使用 AndroidPrinter(Android)/ConsolePrinter(java) androidPrinter, // 添加任意多的打印器。如果没有添加任何打印器,会默认使用 AndroidPrinter(Android)/ConsolePrinter(java)
consolePrinter, consolePrinter, filePrinter
filePrinter
) )
} }
@ -190,14 +190,11 @@ class MainActivity : BaseActivity() {
fun showRationaleForSDCard(permissions: MutableList<String>) { fun showRationaleForSDCard(permissions: MutableList<String>) {
// showRationaleDialog(R.string.permission_camera_rationale, request) // showRationaleDialog(R.string.permission_camera_rationale, request)
// Toast.makeText(context, "当前操作需要您授权相机权限!", Toast.LENGTH_SHORT).show() // Toast.makeText(context, "当前操作需要您授权相机权限!", Toast.LENGTH_SHORT).show()
MaterialAlertDialogBuilder(this) MaterialAlertDialogBuilder(this).setTitle("提示").setMessage("当前操作需要您授权读写SD卡权限")
.setTitle("提示")
.setMessage("当前操作需要您授权读写SD卡权限")
.setPositiveButton("确定", DialogInterface.OnClickListener { dialogInterface, i -> .setPositiveButton("确定", DialogInterface.OnClickListener { dialogInterface, i ->
dialogInterface.dismiss() dialogInterface.dismiss()
XXPermissions.startPermissionActivity(this@MainActivity, permissions) XXPermissions.startPermissionActivity(this@MainActivity, permissions)
}) }).show()
.show()
} }
// @OnPermissionDenied(Manifest.permission.MANAGE_EXTERNAL_STORAGE) // @OnPermissionDenied(Manifest.permission.MANAGE_EXTERNAL_STORAGE)

View File

@ -4,9 +4,12 @@ import androidx.lifecycle.ViewModel
import androidx.paging.PagingData import androidx.paging.PagingData
import com.navinfo.volvo.database.dao.GreetingMessageDao import com.navinfo.volvo.database.dao.GreetingMessageDao
import com.navinfo.volvo.database.entity.GreetingMessage import com.navinfo.volvo.database.entity.GreetingMessage
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton
@HiltViewModel
class MainActivityViewModel @Inject constructor( class MainActivityViewModel @Inject constructor(
private val messageDao: GreetingMessageDao, private val messageDao: GreetingMessageDao,
) : ViewModel() { ) : ViewModel() {

View File

@ -5,6 +5,6 @@ import androidx.lifecycle.ViewModelProvider
import javax.inject.Inject import javax.inject.Inject
abstract class BaseFragment : Fragment() { abstract class BaseFragment : Fragment() {
@Inject // @Inject
lateinit var viewModelFactoryProvider: ViewModelProvider.Factory // lateinit var viewModelFactoryProvider: ViewModelProvider.Factory
} }

View File

@ -19,23 +19,11 @@ class HomeAdapter(fragment: Fragment) :
PagingDataAdapter<GreetingMessage, HomeAdapter.MyViewHolder>(DiffCallback()) { PagingDataAdapter<GreetingMessage, HomeAdapter.MyViewHolder>(DiffCallback()) {
val fragment = fragment val fragment = fragment
// var itemList = ArrayList<GreetingMessage>()
//
// fun addItem(message: GreetingMessage) {
// itemList.add(message)
// notifyItemInserted(itemList.size - 1)
// }
//
// fun setItems(messageList: List<GreetingMessage>) {
// itemList.clear()
// itemList.addAll(messageList)
// notifyDataSetChanged()
// }
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val mDataBinding: AdapterHomeBinding = val mDataBinding: AdapterHomeBinding =
DataBindingUtil.inflate( DataBindingUtil.inflate(
LayoutInflater.from(fragment.context), LayoutInflater.from(parent.context),
R.layout.adapter_home, R.layout.adapter_home,
parent, parent,
false false
@ -47,9 +35,10 @@ class HomeAdapter(fragment: Fragment) :
holder.onBind(position) holder.onBind(position)
} }
// override fun getItemCount(): Int { fun getItemData(position: Int): GreetingMessage {
// return itemList.size return getItem(position)!!
// } }
inner class MyViewHolder(private val mDataBinding: AdapterHomeBinding) : inner class MyViewHolder(private val mDataBinding: AdapterHomeBinding) :
RecyclerView.ViewHolder(mDataBinding.root) { RecyclerView.ViewHolder(mDataBinding.root) {
@ -63,7 +52,6 @@ class HomeAdapter(fragment: Fragment) :
.error(R.mipmap.volvo_logo_small) .error(R.mipmap.volvo_logo_small)
.into(mDataBinding.messageHeadIcon) .into(mDataBinding.messageHeadIcon)
} }
} }
class DiffCallback : DiffUtil.ItemCallback<GreetingMessage>() { class DiffCallback : DiffUtil.ItemCallback<GreetingMessage>() {

View File

@ -1,12 +1,15 @@
package com.navinfo.volvo.ui.fragments.home package com.navinfo.volvo.ui.fragments.home
import android.os.Bundle import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.databinding.DataBindingUtil import android.widget.Toast
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import androidx.lifecycle.Observer
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.paging.LoadState
import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.navinfo.volvo.R import com.navinfo.volvo.R
@ -15,25 +18,24 @@ import com.navinfo.volvo.tools.DisplayUtil
import com.navinfo.volvo.ui.fragments.BaseFragment import com.navinfo.volvo.ui.fragments.BaseFragment
import com.yanzhenjie.recyclerview.* import com.yanzhenjie.recyclerview.*
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.io.IOException
@AndroidEntryPoint @AndroidEntryPoint
class HomeFragment : BaseFragment(), OnItemClickListener, OnItemMenuClickListener { class HomeFragment : BaseFragment(), OnItemClickListener, OnItemMenuClickListener {
private var _binding: FragmentHomeBinding? = null private var _binding: FragmentHomeBinding? = null
// This property is only valid between onCreateView and // This property is only valid between onCreateView and
// onDestroyView. // onDestroyView.
private val binding get() = _binding!! private val binding get() = _binding!!
private val viewModel by viewModels<HomeViewModel> { viewModelFactoryProvider } private val viewModel by viewModels<HomeViewModel>()
private lateinit var messageAdapter: HomeAdapter private val messageAdapter by lazy { HomeAdapter(this) }
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
container: ViewGroup?,
savedInstanceState: Bundle?
): View { ): View {
_binding = FragmentHomeBinding.inflate(inflater, container, false) _binding = FragmentHomeBinding.inflate(inflater, container, false)
val root: View = binding.root val root: View = binding.root
@ -42,55 +44,117 @@ class HomeFragment : BaseFragment(), OnItemClickListener, OnItemMenuClickListene
} }
private fun initView() { private fun initView() {
// mDataBinding.homeViewModel = viewModel
messageAdapter = HomeAdapter(this)
val recyclerview: SwipeRecyclerView = binding.homeRecyclerview
recyclerview.adapter = null //先设置null否则会报错
//创建菜单选项 //创建菜单选项
//注意:使用滑动菜单不能开启滑动删除,否则只有滑动删除没有滑动菜单 //注意:使用滑动菜单不能开启滑动删除,否则只有滑动删除没有滑动菜单
var mSwipeMenuCreator = var mSwipeMenuCreator = SwipeMenuCreator { _, rightMenu, _ ->
SwipeMenuCreator { _, rightMenu, position -> //添加菜单自动添加至尾部
//添加菜单自动添加至尾部 var deleteItem = SwipeMenuItem(context)
var deleteItem = SwipeMenuItem(context) deleteItem.height = DisplayUtil.dip2px(context!!, 60f)
deleteItem.height = DisplayUtil.dip2px(context!!, 60f) deleteItem.width = DisplayUtil.dip2px(context!!, 80f)
deleteItem.width = DisplayUtil.dip2px(context!!, 80f) deleteItem.background = context!!.getDrawable(R.color.red)
deleteItem.background = context!!.getDrawable(R.color.red) deleteItem.text = context!!.getString(R.string.delete)
deleteItem.text = context!!.getString(R.string.delete) rightMenu.addMenuItem(deleteItem)
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 val layoutManager = LinearLayoutManager(context)
recyclerview.addItemDecoration(DividerItemDecoration(context, layoutManager.orientation)) binding.homeRecyclerview.layoutManager = layoutManager
recyclerview.setSwipeMenuCreator(mSwipeMenuCreator) //自动增加分割线
recyclerview.setOnItemClickListener(this) binding.homeRecyclerview.addItemDecoration(
// recyclerview.useDefaultLoadMore() DividerItemDecoration(
// recyclerview.setLoadMoreListener { 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 { lifecycleScope.launch {
viewModel.messageList.collectLatest { viewModel.messageList.collect {
messageAdapter.submitData(it) messageAdapter.submitData(it)
} }
} }
// messageAdapter.withLoadStateFooter( binding.homeRecyclerview.adapter = messageAdapter
// footer = RecLoadStateAdapter { messageAdapter.retry() }
// ) //初始状态添加监听
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() { override fun onStart() {
super.onStart() super.onStart()
viewModel.getNetMessageList() viewModel.getNetMessageList()

View File

@ -1,23 +1,30 @@
package com.navinfo.volvo.ui.fragments.home package com.navinfo.volvo.ui.fragments.home
import android.app.Application
import android.widget.Toast
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import androidx.paging.PagingData import androidx.paging.PagingData
import com.navinfo.volvo.database.dao.GreetingMessageDao import com.navinfo.volvo.database.dao.GreetingMessageDao
import com.navinfo.volvo.database.entity.GreetingMessage 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.database.DatabaseRepository
import com.navinfo.volvo.repository.network.NetworkRepository import com.navinfo.volvo.repository.network.NetworkRepository
import com.navinfo.volvo.util.NetResult import com.navinfo.volvo.util.NetResult
import com.navinfo.volvo.util.asLiveData import com.navinfo.volvo.util.asLiveData
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import javax.inject.Inject import javax.inject.Inject
@HiltViewModel
class HomeViewModel @Inject constructor( class HomeViewModel @Inject constructor(
private val application: Application,
private val netRepository: NetworkRepository, private val netRepository: NetworkRepository,
private val dataRepository: DatabaseRepository private val dataRepository: DatabaseRepository,
private val messageDao: GreetingMessageDao,
) : ViewModel() { ) : ViewModel() {
private val _isLoading = MutableLiveData<Boolean>() private val _isLoading = MutableLiveData<Boolean>()
@ -32,18 +39,46 @@ class HomeViewModel @Inject constructor(
fun getNetMessageList() { fun getNetMessageList() {
if (_isLoading.value == true)
return
_isLoading.postValue(true) _isLoading.postValue(true)
viewModelScope.launch { viewModelScope.launch {
val messagePost = NetworkMessageListPost(who = "", toWho = "") val messagePost = NetworkMessageListPost(who = "", toWho = "")
when (val result = netRepository.getCardList(messagePost)) { when (val result = netRepository.getMessageList(messagePost)) {
is NetResult.Success -> { is NetResult.Success -> {
_isLoading.value = false _isLoading.value = false
// if (result.data != null) { if ((result.data!!.data != null) && (result.data.data!!.rows != null)) {
// val list = (result.data.data as NetworkMessageListResponse).rows messageDao.insertOrUpdate(result.data.data!!.rows!!)
// _messageList.value = list }
// }
} }
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 -> { is NetResult.Error -> {
_isLoading.value = false _isLoading.value = false
} }

View File

@ -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<LoadStateViewHolder>() {
override fun onBindViewHolder(holder: LoadStateViewHolder, loadState: LoadState) {
holder.bindState(loadState)
}
override fun onCreateViewHolder(parent: ViewGroup, loadState: LoadState): LoadStateViewHolder {
return LoadStateViewHolder(parent, retry)
}
}

View File

@ -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", "--其他的错误")
}
}
}
}

View File

@ -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<RecLoadStateAdapter.LoadStateViewHolder>() {
//
// 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()
// }
// }
// }
//}

View File

@ -4,13 +4,18 @@ import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.Toast
import androidx.databinding.DataBindingUtil import androidx.databinding.DataBindingUtil
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import androidx.lifecycle.Observer
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import com.navinfo.volvo.R import com.navinfo.volvo.R
import com.navinfo.volvo.databinding.FragmentLoginBinding import com.navinfo.volvo.databinding.FragmentLoginBinding
import com.navinfo.volvo.ui.fragments.BaseFragment import com.navinfo.volvo.ui.fragments.BaseFragment
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
@AndroidEntryPoint @AndroidEntryPoint
@ -20,7 +25,7 @@ class LoginFragment : BaseFragment() {
private lateinit var viewBinding: FragmentLoginBinding private lateinit var viewBinding: FragmentLoginBinding
private val viewModel by viewModels<LoginViewModel> { viewModelFactoryProvider } private val viewModel by viewModels<LoginViewModel>()
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
@ -34,11 +39,31 @@ class LoginFragment : BaseFragment() {
} }
private fun initView() { private fun initView() {
lifecycleScope.launch {
viewModel.user.collect {
if (it != null){
viewBinding.loginUser = it
}
cancel()
}
}
viewBinding.loginFragmentRegisterButton.setOnClickListener { viewBinding.loginFragmentRegisterButton.setOnClickListener {
} }
viewBinding.loginFragmentLoginButton.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) findNavController().navigate(R.id.action_login_to_home)
} }
} }

View File

@ -1,23 +1,29 @@
package com.navinfo.volvo.ui.fragments.login package com.navinfo.volvo.ui.fragments.login
import android.view.View
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import com.navinfo.volvo.database.AppDatabase import androidx.lifecycle.viewModelScope
import com.navinfo.volvo.database.entity.User import com.navinfo.volvo.repository.preferences.PreferencesRepository
import com.navinfo.volvo.util.SharedPreferenceHelper 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 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> = _user val user = repository.loginUser()
fun liveDataOnclick(view: View) {
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) {
} }
} }

View File

@ -11,14 +11,16 @@ package com.navinfo.volvo.util
sealed class NetResult<out R> { sealed class NetResult<out R> {
data class Success<out T>(val data: T?) : NetResult<T>() data class Success<out T>(val data: T?) : NetResult<T>()
data class Failure(val code: Int, val msg: String) : NetResult<Nothing>()
data class Error(val exception: Exception) : NetResult<Nothing>() data class Error(val exception: Exception) : NetResult<Nothing>()
object Loading : NetResult<Nothing>() object Loading : NetResult<Nothing>()
override fun toString(): String { override fun toString(): String {
return when (this) { return when (this) {
is Success<*> -> "Success[data=$data]" is Success<*> -> "网络访问成功返回正确结果Success[data=$data]"
is Error -> "Error[exception=$exception]" is Failure -> "网络访问成功返回错误结果Failure[$msg]"
is Loading -> "Loading" is Error -> "网络访问出错 Error[exception=$exception]"
is Loading -> "网络访问中 Loading"
} }
} }
} }

View File

@ -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)
// }
}

View File

@ -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;
}

View File

@ -11,8 +11,8 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginLeft="20dp" android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:layout_marginTop="5dp" android:layout_marginTop="5dp"
android:layout_marginRight="20dp"
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"> app:layout_constraintTop_toTopOf="parent">
@ -23,8 +23,8 @@
android:hint="请输入查询内容" /> android:hint="请输入查询内容" />
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
<com.yanzhenjie.recyclerview.SwipeRecyclerView <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/home_recyclerview" android:id="@+id/home_SwipeRefreshLayout"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="0dp" android:layout_height="0dp"
android:layout_marginTop="10dp" android:layout_marginTop="10dp"
@ -33,7 +33,14 @@
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/home_search" /> app:layout_constraintTop_toBottomOf="@id/home_search">
<com.yanzhenjie.recyclerview.SwipeRecyclerView
android:id="@+id/home_recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -6,7 +6,7 @@
<data> <data>
<variable <variable
name="loginUser" name="loginUser"
type="com.navinfo.volvo.model.LoginUser" /> type="com.navinfo.volvo.model.proto.LoginUser" />
</data> </data>
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
@ -14,7 +14,6 @@
android:layout_height="match_parent" android:layout_height="match_parent"
tools:context="com.navinfo.volvo.ui.fragments.login.LoginFragment"> tools:context="com.navinfo.volvo.ui.fragments.login.LoginFragment">
<androidx.constraintlayout.utils.widget.ImageFilterView <androidx.constraintlayout.utils.widget.ImageFilterView
android:id="@+id/login_fragment_logo" android:id="@+id/login_fragment_logo"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -40,9 +39,10 @@
app:layout_constraintVertical_bias="0.4"> app:layout_constraintVertical_bias="0.4">
<com.google.android.material.textfield.TextInputEditText <com.google.android.material.textfield.TextInputEditText
android:id="@+id/login_username"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@{loginUser.name}" android:text="@{loginUser.username}"
android:hint="请输入用户名" /> android:hint="请输入用户名" />
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
@ -54,6 +54,7 @@
app:layout_constraintTop_toBottomOf="@id/login_fragment_user_layout"> app:layout_constraintTop_toBottomOf="@id/login_fragment_user_layout">
<com.google.android.material.textfield.TextInputEditText <com.google.android.material.textfield.TextInputEditText
android:id="@+id/login_password"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:inputType="textPassword" android:inputType="textPassword"

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/load_state_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp">
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View File

@ -13,12 +13,9 @@
<action <action
android:id="@+id/action_login_to_home" android:id="@+id/action_login_to_home"
app:destination="@id/navigation_home" app:destination="@id/navigation_home"
app:enterAnim="@anim/from_left"
app:exitAnim="@anim/to_right"
app:popEnterAnim="@anim/from_right"
app:popExitAnim="@anim/to_left"
app:popUpTo="@id/navigation_login" app:popUpTo="@id/navigation_login"
app:popUpToInclusive="true" /> app:popUpToInclusive="true" />
</fragment> </fragment>
<fragment <fragment
android:id="@+id/navigation_home" android:id="@+id/navigation_home"