fix: 增加Message禁用功能

This commit is contained in:
xiaoyan 2023-01-10 17:41:42 +08:00
commit cf72615d96
45 changed files with 992 additions and 768 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'
@ -108,29 +108,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'
@ -155,6 +146,30 @@ dependencies {
// 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'
implementation 'com.github.majidarabi:AndroidFilePicker:0.2.1' implementation 'com.github.majidarabi:AndroidFilePicker:0.2.1'
// Preferences DataStore
implementation "androidx.datastore:datastore-preferences:1.0.0"
// Proto DataStore
implementation "androidx.datastore:datastore-core:1.0.0"
implementation "com.google.protobuf:protobuf-javalite:3.18.0"
}
//Proto DataStore
protobuf {
protoc {
artifact = "com.google.protobuf:protoc:3.14.0"
}
// Generates the java Protobuf-lite code for the Protobufs in this project. See
// https://github.com/google/protobuf-gradle-plugin#customizing-protobuf-compilation
// for more information.
generateProtoTasks {
all().each { task ->
task.builtins {
java {
option 'lite'
}
}
}
}
} }
kapt { 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

@ -9,6 +9,8 @@ class Constant {
*/ */
const val SERVER_ADDRESS = "http://ec2-52-81-73-5.cn-north-1.compute.amazonaws.com.cn:8088/" const val SERVER_ADDRESS = "http://ec2-52-81-73-5.cn-north-1.compute.amazonaws.com.cn:8088/"
val DEBUG = Boolean.parseBoolean("true") val DEBUG = Boolean.parseBoolean("true")
const val message_status_late = "预约,待发送"
} }
} }

View File

@ -1,8 +1,39 @@
package com.navinfo.volvo package com.navinfo.volvo
import android.app.Activity
import android.app.Application import android.app.Application
import android.content.pm.ActivityInfo
import android.os.Bundle
import dagger.hilt.android.HiltAndroidApp import dagger.hilt.android.HiltAndroidApp
@HiltAndroidApp @HiltAndroidApp
open class MyApplication : Application() { open class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacks {
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
}
override fun onActivityStarted(activity: Activity) {
}
override fun onActivityResumed(activity: Activity) {
}
override fun onActivityPaused(activity: Activity) {
}
override fun onActivityStopped(activity: Activity) {
}
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {
}
override fun onActivityDestroyed(activity: Activity) {
}
})
}
} }

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

@ -1,21 +1,28 @@
package com.navinfo.volvo.database.dao package com.navinfo.volvo.database.dao
import android.util.Log
import androidx.paging.PagingSource import androidx.paging.PagingSource
import androidx.room.* import androidx.room.*
import com.navinfo.volvo.Constant
import com.navinfo.volvo.database.entity.GreetingMessage import com.navinfo.volvo.database.entity.GreetingMessage
import kotlinx.coroutines.flow.Flow 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 status = '${Constant.message_status_late}'")
fun countUnreadByFlow(): Flow<Long> fun countUnreadByFlow(): Flow<Long>
/** /**
@ -27,18 +34,29 @@ interface GreetingMessageDao {
/** /**
* 检查某条数据是否存在 * 检查某条数据是否存在
*/ */
@Query("SELECT id From GreetingMessage WHERE id = :id LIMIT 1") @Query("SELECT uuid 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) Log.e("jingo", "insertOrUpdate ${message.id}")
if (id == 0L) { val uuid = getMessageId(message.id)
insert(message) Log.e("jingo", "insertOrUpdate $uuid")
}else{ if (uuid == null || uuid == 0L) {
Log.e("jingo", "insertOrUpdate start ")
val l = insert(message)
Log.e("jingo", "insertOrUpdate $l ")
} else {
message.uuid = uuid
update(message) update(message)
} }
Log.e("jingo", "insertOrUpdate end")
} }
} }
} }

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,//问候名称,非必填项
@ -13,3 +13,7 @@ data class NetworkMessageListPost(
} }
} }
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,17 @@
package com.navinfo.volvo.repository.preferences
import com.navinfo.volvo.model.proto.LoginUser
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
/**
* 数据库操作接口
*/
interface PreferencesRepository {
suspend fun saveLoginUser(id: String, name: String, password: String)
fun loginUser(): Flow<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,61 @@
package com.navinfo.volvo.repository.preferences
import android.content.Context
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.intPreferencesKey
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import androidx.lifecycle.viewModelScope
import com.navinfo.volvo.model.proto.LoginUser
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import javax.inject.Inject
const val DataStore_NAME = "ShardPreferences"
val Context.datastore: DataStore<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,7 +101,6 @@ 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 {
@ -107,19 +109,21 @@ class MainActivity : BaseActivity() {
} }
} }
} }
}
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)
LogLevel.ALL // 指定日志级别,低于该级别的日志将不会被打印,默认为 LogLevel.ALL
else LogLevel.NONE else LogLevel.NONE
) ).tag("Volvo") // 指定 TAG默认为 "X-LOG"
.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,11 @@ class HomeAdapter(fragment: Fragment) :
holder.onBind(position) 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) : inner class MyViewHolder(private val mDataBinding: AdapterHomeBinding) :
RecyclerView.ViewHolder(mDataBinding.root) { RecyclerView.ViewHolder(mDataBinding.root) {
@ -63,12 +53,11 @@ 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>() {
override fun areItemsTheSame(oldItem: GreetingMessage, newItem: GreetingMessage): Boolean { override fun areItemsTheSame(oldItem: GreetingMessage, newItem: GreetingMessage): Boolean {
return oldItem.uuid == newItem.uuid return oldItem.uuid == newItem.uuid && oldItem.status == newItem.status
} }
override fun areContentsTheSame( override fun areContentsTheSame(

View File

@ -1,96 +1,172 @@
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.easytools.tools.ThreadPoolUtils.runOnUiThread
import com.navinfo.volvo.R import com.navinfo.volvo.R
import com.navinfo.volvo.databinding.FragmentHomeBinding import com.navinfo.volvo.databinding.FragmentHomeBinding
import com.navinfo.volvo.databinding.HomeAdapterNotingBinding
import com.navinfo.volvo.databinding.LoadStateViewBinding
import com.navinfo.volvo.tools.DisplayUtil import com.navinfo.volvo.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 kotlinx.coroutines.withContext
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) }
private var headBinding: HomeAdapterNotingBinding? = null
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
headBinding = HomeAdapterNotingBinding.inflate(inflater, container, false)
initView() initView()
return root return root
} }
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(requireContext(), 60f)
deleteItem.width = DisplayUtil.dip2px(context!!, 80f) deleteItem.width = DisplayUtil.dip2px(requireContext(), 80f)
deleteItem.background = context!!.getDrawable(R.color.red) deleteItem.background = requireContext().getDrawable(R.color.red)
deleteItem.text = context!!.getString(R.string.delete) deleteItem.text = requireContext().getString(R.string.delete)
rightMenu.addMenuItem(deleteItem) rightMenu.addMenuItem(deleteItem)
//分享 //分享
var shareItem = SwipeMenuItem(context) var shareItem = SwipeMenuItem(context)
shareItem.height = DisplayUtil.dip2px(context!!, 60f) shareItem.height = DisplayUtil.dip2px(requireContext(), 60f)
shareItem.width = DisplayUtil.dip2px(context!!, 80f) shareItem.width = DisplayUtil.dip2px(requireContext(), 80f)
shareItem.background = context!!.getDrawable(R.color.gray) shareItem.background = requireContext().getDrawable(R.color.gray)
shareItem.text = context!!.getString(R.string.share) shareItem.text = requireContext().getString(R.string.share)
shareItem.setTextColor(R.color.white) shareItem.setTextColor(requireContext().getColor(R.color.white))
rightMenu.addMenuItem(shareItem) rightMenu.addMenuItem(shareItem)
} }
val layoutManager = LinearLayoutManager(context) //侧滑按钮
binding.homeRecyclerview.setOnItemMenuClickListener { menuBridge, position ->
menuBridge.closeMenu()
// val direction: Int = menuBridge.getDirection() // 左侧还是右侧菜单。
recyclerview.layoutManager = layoutManager when (menuBridge.position) {// 菜单在RecyclerView的Item中的Position。
recyclerview.addItemDecoration(DividerItemDecoration(context, layoutManager.orientation)) 0 -> {//删除按钮
recyclerview.setSwipeMenuCreator(mSwipeMenuCreator) viewModel.deleteMessage(messageAdapter.getItemData(position).id)
recyclerview.setOnItemClickListener(this) }
// recyclerview.useDefaultLoadMore() 1 -> {//分享按钮
// 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 { 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 -> {
if (messageAdapter.itemCount == 0)
binding.homeRecyclerview.addHeaderView(headBinding!!.root)
else{
binding.homeRecyclerview.removeHeaderView(headBinding!!.root)
}
Log.d("jingo", "is NotLoading")
}
is LoadState.Loading -> {
Log.d("jingo", "is Loading")
}
is LoadState.Error -> {
Log.d("jingo", "is Error:")
when ((it.refresh as LoadState.Error).error) {
is IOException -> {
Log.d("jingo", "IOException")
}
else -> {
Log.d("jingo", "others exception")
}
}
}
}
}
loadMoreFinish()
//监听数据请求是否结束
viewModel.isLoading.observe(viewLifecycleOwner, Observer {
if (!it) loadMoreFinish()
})
// messageAdapter.withLoadStateHeader()
recyclerview.adapter = messageAdapter
} }
private fun loadMoreFinish() {
binding.homeSwipeRefreshLayout.isRefreshing = false
// 第一次加载数据:一定要掉用这个方法。
// 第一个参数表示此次数据是否为空假如你请求到的list为空(== null || list.size == 0)那么这里就要true。
// 第二个参数表示是否还有更多数据根据服务器返回给你的page等信息判断是否还有更多这样可以提供性能如果不能判断则传true。
binding.homeRecyclerview.loadMoreFinish(false, false)
}
override fun onStart() { override fun onStart() {
super.onStart() super.onStart()
viewModel.getNetMessageList() viewModel.getNetMessageList()
@ -100,6 +176,7 @@ class HomeFragment : BaseFragment(), OnItemClickListener, OnItemMenuClickListene
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() super.onDestroyView()
_binding = null _binding = null
headBinding = null
} }
override fun onItemClick(view: View?, adapterPosition: Int) { override fun onItemClick(view: View?, adapterPosition: Int) {

View File

@ -1,49 +1,100 @@
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.repository.preferences.PreferencesRepository
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,
private val shard: PreferencesRepository
) : ViewModel() { ) : ViewModel() {
private val _isLoading = MutableLiveData<Boolean>() private val _isLoading = MutableLiveData<Boolean>()
val isLoading = _isLoading.asLiveData() val isLoading = _isLoading.asLiveData()
// private val _messageList = MutableLiveData<List<GreetingMessage>>() // private val _messageList = MutableLiveData<List<GreetingMessage>>()
// val messageList = _messageList.asLiveData() // val messageList = _messageList.asLiveData()
val messageList: Flow<PagingData<GreetingMessage>> val messageList: Flow<PagingData<GreetingMessage>>
get() = dataRepository.getMessageByPaging() get() = dataRepository.getMessageByPaging()
lateinit var userName: String
fun getNetMessageList() { init {
_isLoading.postValue(true)
viewModelScope.launch { viewModelScope.launch {
val messagePost = NetworkMessageListPost(who = "", toWho = "") shard.loginUser().collect {
when (val result = netRepository.getCardList(messagePost)) { userName = it!!.username
is NetResult.Success -> { }
_isLoading.value = false }
// if (result.data != null) {
// val list = (result.data.data as NetworkMessageListResponse).rows
// _messageList.value = list
// }
} }
fun getNetMessageList() {
if (_isLoading.value == true)
return
_isLoading.postValue(true)
viewModelScope.launch {
val messagePost = NetworkMessageListPost(who = userName, toWho = "")
when (val result = netRepository.getMessageList(messagePost)) {
is NetResult.Success -> {
_isLoading.value = false
if ((result.data!!.data != null) && (result.data.data!!.rows != null)) {
messageDao.insertOrUpdate(result.data.data!!.rows!!)
}
}
is NetResult.Failure -> {
_isLoading.value = false
Toast.makeText(application, "${result.code}:${result.msg}", Toast.LENGTH_SHORT)
.show()
}
is NetResult.Error -> {
_isLoading.value = false
}
is NetResult.Loading -> {
_isLoading.postValue(true)
}
}
}
}
fun deleteMessage(id: Long) {
viewModelScope.launch {
val post = NetworkDeleteMessagePost(id)
netRepository.deleteMessage(post)
messageDao.deleteById(id)
when (val result = netRepository.deleteMessage(post)) {
is NetResult.Success -> {
_isLoading.value = false
}
is NetResult.Failure -> {
_isLoading.value = false
Toast.makeText(
application,
"服务返回信息:${result.code}:${result.msg}",
Toast.LENGTH_SHORT
)
.show()
}
is NetResult.Error -> { 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,30 @@ class LoginFragment : BaseFragment() {
} }
private fun initView() { private fun initView() {
lifecycleScope.launch {
viewModel.user.collect {
if (it != null){
viewBinding.loginUser = it
}
}
}
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

@ -7,7 +7,8 @@ import android.text.TextUtils
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.MotionEvent import android.view.MotionEvent
import android.view.View import android.view.View
import android.view.View.* import android.view.View.GONE
import android.view.View.VISIBLE
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.AdapterView import android.widget.AdapterView
import android.widget.AdapterView.OnItemSelectedListener import android.widget.AdapterView.OnItemSelectedListener
@ -17,15 +18,11 @@ import android.widget.Toast
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.widget.addTextChangedListener import androidx.core.widget.addTextChangedListener
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.Navigation import androidx.navigation.Navigation
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import com.easytools.tools.DateUtils import com.easytools.tools.*
import com.easytools.tools.DisplayUtils
import com.easytools.tools.FileIOUtils
import com.easytools.tools.ResourceUtils
import com.easytools.tools.ToastUtils
import com.elvishew.xlog.XLog import com.elvishew.xlog.XLog
import com.github.file_picker.FileType import com.github.file_picker.FileType
import com.github.file_picker.ListDirection import com.github.file_picker.ListDirection
@ -52,6 +49,7 @@ import com.navinfo.volvo.util.PhotoLoader
import com.navinfo.volvo.utils.EasyMediaFile import com.navinfo.volvo.utils.EasyMediaFile
import com.navinfo.volvo.utils.SystemConstant import com.navinfo.volvo.utils.SystemConstant
import com.nhaarman.supertooltips.ToolTip import com.nhaarman.supertooltips.ToolTip
import dagger.hilt.android.AndroidEntryPoint
import indi.liyi.viewer.Utils import indi.liyi.viewer.Utils
import indi.liyi.viewer.ViewData import indi.liyi.viewer.ViewData
import top.zibin.luban.Luban import top.zibin.luban.Luban
@ -62,12 +60,10 @@ import java.util.*
import kotlin.streams.toList import kotlin.streams.toList
//@RuntimePermissions @AndroidEntryPoint
class ObtainMessageFragment: Fragment() { class ObtainMessageFragment : Fragment() {
private var _binding: FragmentObtainMessageBinding? = null private var _binding: FragmentObtainMessageBinding? = null
private val obtainMessageViewModel by lazy { private val obtainMessageViewModel by viewModels<ObtainMessageViewModel>()
ViewModelProvider(requireActivity()).get(ObtainMessageViewModel::class.java)
}
private val photoHelper by lazy { private val photoHelper by lazy {
EasyMediaFile().setCrop(true) EasyMediaFile().setCrop(true)
} }
@ -91,18 +87,17 @@ class ObtainMessageFragment: Fragment() {
_binding = FragmentObtainMessageBinding.inflate(inflater, container, false) _binding = FragmentObtainMessageBinding.inflate(inflater, container, false)
val root: View = binding.root val root: View = binding.root
val greetingMessage = GreetingMessage() obtainMessageViewModel.setCurrentMessage(GreetingMessage(who = obtainMessageViewModel.username))
obtainMessageViewModel.setCurrentMessage(greetingMessage)
obtainMessageViewModel?.getMessageLiveData()?.observe( obtainMessageViewModel.getMessageLiveData().observe(
viewLifecycleOwner, Observer { viewLifecycleOwner, Observer {
// 初始化界面显示内容 // 初始化界面显示内容
if(it.name?.isNotEmpty() == true) if (it.name?.isNotEmpty() == true)
binding.tvMessageTitle?.setText(it.name) binding.tvMessageTitle?.setText(it.name)
if (it.sendDate?.isNotEmpty() == true) { if (it.sendDate?.isNotEmpty() == true) {
// 获取当前发送时间,如果早于当前时间,则显示现在 // 获取当前发送时间,如果早于当前时间,则显示现在
val sendDate = DateUtils.str2Date(it.sendDate, dateSendFormat) val sendDate = DateUtils.str2Date(it.sendDate, dateSendFormat)
if (sendDate<=Date()) { if (sendDate <= Date()) {
binding.btnSendTime.text = "现在" binding.btnSendTime.text = "现在"
} else { } else {
binding.btnSendTime.text = it.sendDate binding.btnSendTime.text = it.sendDate
@ -112,39 +107,47 @@ class ObtainMessageFragment: Fragment() {
} }
var hasPhoto = false var hasPhoto = false
var hasAudio = false var hasAudio = false
if (it.imageUrl!=null&&it.imageUrl?.isNotEmpty() == true) { if (it.imageUrl != null && it.imageUrl?.isNotEmpty() == true) {
hasPhoto = true hasPhoto = true
// Glide.with(this@ObtainMessageFragment) // Glide.with(this@ObtainMessageFragment)
// .asBitmap().fitCenter() // .asBitmap().fitCenter()
// .load(it.imageUrl) // .load(it.imageUrl)
// .diskCacheStrategy(DiskCacheStrategy.ALL) // .diskCacheStrategy(DiskCacheStrategy.ALL)
// .into(binding.imgMessageAttachment) // .into(binding.imgMessageAttachment)
// 如果当前attachment文件是本地文件开始尝试网络上传 // 如果当前attachment文件是本地文件开始尝试网络上传
val str = it.imageUrl?.replace("\\", "/") val str = it.imageUrl?.replace("\\", "/")
binding.tvPhotoName.getPaint().setFlags(Paint.UNDERLINE_TEXT_FLAG ) binding.tvPhotoName.getPaint().setFlags(Paint.UNDERLINE_TEXT_FLAG)
if (!str!!.startsWith("http")) { if (!str!!.startsWith("http")) {
obtainMessageViewModel.uploadAttachment(File(it.imageUrl), AttachmentType.PIC) obtainMessageViewModel.uploadAttachment(
File(it.imageUrl),
AttachmentType.PIC
)
binding.tvPhotoName.text = str.substringAfterLast("/", "picture.jpg") binding.tvPhotoName.text = str.substringAfterLast("/", "picture.jpg")
} else { } else {
if (str.contains("?")) { if (str.contains("?")) {
binding.tvPhotoName.text = str.substring(str.lastIndexOf("/")+1, str.indexOf("?")) binding.tvPhotoName.text =
str.substring(str.lastIndexOf("/") + 1, str.indexOf("?"))
} else { } else {
binding.tvPhotoName.text = str.substringAfterLast("/") binding.tvPhotoName.text = str.substringAfterLast("/")
} }
} }
} }
if (it.mediaUrl!=null&&it.mediaUrl?.isNotEmpty() == true) { if (it.mediaUrl != null && it.mediaUrl?.isNotEmpty() == true) {
hasAudio = true hasAudio = true
binding.tvAudioName.getPaint().setFlags(Paint.UNDERLINE_TEXT_FLAG ) binding.tvAudioName.getPaint().setFlags(Paint.UNDERLINE_TEXT_FLAG)
// 如果当前attachment文件是本地文件开始尝试网络上传 // 如果当前attachment文件是本地文件开始尝试网络上传
val str = it.mediaUrl?.replace("\\", "/") val str = it.mediaUrl?.replace("\\", "/")
if (!str!!.startsWith("http")) { if (!str!!.startsWith("http")) {
obtainMessageViewModel.uploadAttachment(File(it.mediaUrl),AttachmentType.AUDIO) obtainMessageViewModel.uploadAttachment(
File(it.mediaUrl),
AttachmentType.AUDIO
)
binding.tvAudioName.text = str.substringAfterLast("/", "audio.m4a") binding.tvAudioName.text = str.substringAfterLast("/", "audio.m4a")
} else { } else {
if (str.contains("?")) { if (str.contains("?")) {
binding.tvAudioName.text = str.substring(str.lastIndexOf("/")+1, str.indexOf("?")) binding.tvAudioName.text =
str.substring(str.lastIndexOf("/") + 1, str.indexOf("?"))
} else { } else {
binding.tvAudioName.text = str.substringAfterLast("/") binding.tvAudioName.text = str.substringAfterLast("/")
} }
@ -152,7 +155,7 @@ class ObtainMessageFragment: Fragment() {
} }
binding.layerPhotoResult.visibility = if (hasPhoto) VISIBLE else GONE binding.layerPhotoResult.visibility = if (hasPhoto) VISIBLE else GONE
binding.layerGetPhoto.visibility = if (hasPhoto) GONE else VISIBLE binding.layerGetPhoto.visibility = if (hasPhoto) GONE else VISIBLE
// binding.imgMessageAttachment.visibility = if (hasPhoto) VISIBLE else GONE // binding.imgMessageAttachment.visibility = if (hasPhoto) VISIBLE else GONE
binding.layerAudioResult.visibility = if (hasAudio) VISIBLE else GONE binding.layerAudioResult.visibility = if (hasAudio) VISIBLE else GONE
binding.layerGetAudio.visibility = if (hasAudio) GONE else VISIBLE binding.layerGetAudio.visibility = if (hasAudio) GONE else VISIBLE
@ -188,7 +191,7 @@ class ObtainMessageFragment: Fragment() {
obtainMessageViewModel.getMessageLiveData().value?.name = it.toString() obtainMessageViewModel.getMessageLiveData().value?.name = it.toString()
}) })
binding.edtSendFrom.addTextChangedListener (afterTextChanged = { binding.edtSendFrom.addTextChangedListener(afterTextChanged = {
obtainMessageViewModel.getMessageLiveData().value?.who = it.toString() obtainMessageViewModel.getMessageLiveData().value?.who = it.toString()
}) })
@ -201,8 +204,11 @@ class ObtainMessageFragment: Fragment() {
} }
val sendToArray = mutableListOf<VolvoModel>(VolvoModel("XC60", "智雅", "LYVXFEFEXNL754427")) val sendToArray = mutableListOf<VolvoModel>(VolvoModel("XC60", "智雅", "LYVXFEFEXNL754427"))
binding.edtSendTo.adapter = ArrayAdapter<String>(requireContext(), binding.edtSendTo.adapter = ArrayAdapter<String>(requireContext(),
android.R.layout.simple_dropdown_item_1line, android.R.id.text1, sendToArray.stream().map { it -> "${it.version} ${it.model} ${it.num}" }.toList()) android.R.layout.simple_dropdown_item_1line,
binding.edtSendTo.onItemSelectedListener = object: OnItemSelectedListener { android.R.id.text1,
sendToArray.stream().map { it -> "${it.version} ${it.model} ${it.num}" }.toList()
)
binding.edtSendTo.onItemSelectedListener = object : OnItemSelectedListener {
override fun onItemSelected(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) { override fun onItemSelected(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) {
obtainMessageViewModel.getMessageLiveData().value?.toWho = sendToArray[p2].num obtainMessageViewModel.getMessageLiveData().value?.toWho = sendToArray[p2].num
} }
@ -219,9 +225,19 @@ class ObtainMessageFragment: Fragment() {
override fun onClickListener(selectTime: String) { override fun onClickListener(selectTime: String) {
val sendDate = DateUtils.str2Date(selectTime, dateShowFormat) val sendDate = DateUtils.str2Date(selectTime, dateShowFormat)
if (sendDate <= Date()) { if (sendDate <= Date()) {
obtainMessageViewModel.updateMessageSendTime(DateUtils.date2Str(Date(), dateSendFormat)) obtainMessageViewModel.updateMessageSendTime(
DateUtils.date2Str(
Date(),
dateSendFormat
)
)
} else { } else {
obtainMessageViewModel.updateMessageSendTime(DateUtils.date2Str(sendDate, dateSendFormat)) obtainMessageViewModel.updateMessageSendTime(
DateUtils.date2Str(
sendDate,
dateSendFormat
)
)
} }
} }
} }
@ -238,7 +254,8 @@ class ObtainMessageFragment: Fragment() {
override fun onGranted(permissions: MutableList<String>, all: Boolean) { override fun onGranted(permissions: MutableList<String>, all: Boolean) {
if (!all) { if (!all) {
Toast.makeText(activity, "获取部分权限成功,但部分权限未正常授予", Toast.LENGTH_SHORT).show() Toast.makeText(activity, "获取部分权限成功,但部分权限未正常授予", Toast.LENGTH_SHORT)
.show()
return return
} }
// 开始启动拍照界面 // 开始启动拍照界面
@ -280,13 +297,23 @@ class ObtainMessageFragment: Fragment() {
// Do something here with selected files // Do something here with selected files
val audioFile = files.get(0).file val audioFile = files.get(0).file
if (!audioFile.parentFile.parentFile.absolutePath.equals(SystemConstant.SoundFolder)) { if (!audioFile.parentFile.parentFile.absolutePath.equals(SystemConstant.SoundFolder)) {
val copyResult = FileIOUtils.writeFileFromIS(File(SystemConstant.SoundFolder, audioFile.name), FileInputStream(audioFile)) val copyResult = FileIOUtils.writeFileFromIS(
XLog.e("拷贝结果:"+copyResult) File(
SystemConstant.SoundFolder,
audioFile.name
), FileInputStream(audioFile)
)
XLog.e("拷贝结果:" + copyResult)
if (!copyResult) { if (!copyResult) {
ToastUtils.showToast("无法访问该文件,请重新选择其他文件") ToastUtils.showToast("无法访问该文件,请重新选择其他文件")
return return
} }
obtainMessageViewModel.updateMessageAudio(File(SystemConstant.SoundFolder, audioFile.name).absolutePath) obtainMessageViewModel.updateMessageAudio(
File(
SystemConstant.SoundFolder,
audioFile.name
).absolutePath
)
} else { } else {
obtainMessageViewModel.updateMessageAudio(audioFile.absolutePath) obtainMessageViewModel.updateMessageAudio(audioFile.absolutePath)
} }
@ -299,7 +326,7 @@ class ObtainMessageFragment: Fragment() {
ToastUtils.showToast("只能选择.m4a文件") ToastUtils.showToast("只能选择.m4a文件")
return return
} }
if (media.file.length()>2*1000*1000) { if (media.file.length() > 2 * 1000 * 1000) {
ToastUtils.showToast("文件不能超过2M") ToastUtils.showToast("文件不能超过2M")
return return
} }
@ -330,23 +357,25 @@ class ObtainMessageFragment: Fragment() {
.request(object : OnPermissionCallback { .request(object : OnPermissionCallback {
override fun onGranted(permissions: MutableList<String>, all: Boolean) { override fun onGranted(permissions: MutableList<String>, all: Boolean) {
if (!all) { if (!all) {
Toast.makeText(activity, "获取部分权限成功,但部分权限未正常授予", Toast.LENGTH_SHORT).show() Toast.makeText(activity, "获取部分权限成功,但部分权限未正常授予", Toast.LENGTH_SHORT)
.show()
return return
} }
when(motionEvent.action) { when (motionEvent.action) {
MotionEvent.ACTION_DOWN-> { MotionEvent.ACTION_DOWN -> {
// 申请权限 // 申请权限
recorderLifecycleObserver.initAndStartRecorder() recorderLifecycleObserver.initAndStartRecorder()
startRecordTime = System.currentTimeMillis() startRecordTime = System.currentTimeMillis()
false false
} }
MotionEvent.ACTION_UP -> { MotionEvent.ACTION_UP -> {
if (System.currentTimeMillis() - startRecordTime<2000) { if (System.currentTimeMillis() - startRecordTime < 2000) {
ToastUtils.showToast("录音时间太短!") ToastUtils.showToast("录音时间太短!")
recorderLifecycleObserver.stopAndReleaseRecorder() recorderLifecycleObserver.stopAndReleaseRecorder()
return return
} }
val recorderAudioPath = recorderLifecycleObserver.stopAndReleaseRecorder() val recorderAudioPath =
recorderLifecycleObserver.stopAndReleaseRecorder()
if (File(recorderAudioPath).exists()) { if (File(recorderAudioPath).exists()) {
obtainMessageViewModel.updateMessageAudio(recorderAudioPath) obtainMessageViewModel.updateMessageAudio(recorderAudioPath)
} }
@ -376,7 +405,7 @@ class ObtainMessageFragment: Fragment() {
photoHelper.setCallback { photoHelper.setCallback {
if (it.exists()) { if (it.exists()) {
val fileName = it.name.lowercase() val fileName = it.name.lowercase()
if (fileName.endsWith(".jpg")||fileName.endsWith(".jpeg")||fileName.endsWith(".png")) { if (fileName.endsWith(".jpg") || fileName.endsWith(".jpeg") || fileName.endsWith(".png")) {
// 获取选中的图片,自动压缩图片质量 // 获取选中的图片,自动压缩图片质量
// 压缩图片文件 // 压缩图片文件
Luban.with(context) Luban.with(context)
@ -400,13 +429,29 @@ class ObtainMessageFragment: Fragment() {
} }
// 如果当前文件不在camera缓存文件夹下则移动该文件 // 如果当前文件不在camera缓存文件夹下则移动该文件
if (!file!!.parentFile.absolutePath.equals(SystemConstant.CameraFolder)) { if (!file!!.parentFile.absolutePath.equals(SystemConstant.CameraFolder)) {
val copyResult = FileIOUtils.writeFileFromIS(File(SystemConstant.CameraFolder, fileName), FileInputStream(file)) try {
XLog.e("拷贝结果:"+copyResult) val copyResult = FileIOUtils.writeFileFromIS(
File(
SystemConstant.CameraFolder,
fileName
), FileInputStream(file)
)
XLog.e("拷贝结果:$copyResult")
// 跳转回原Fragment展示拍摄的照片 // 跳转回原Fragment展示拍摄的照片
ViewModelProvider(requireActivity()).get(ObtainMessageViewModel::class.java).updateMessagePic(File(SystemConstant.CameraFolder, fileName).absolutePath) obtainMessageViewModel
.updateMessagePic(
File(
SystemConstant.CameraFolder,
fileName
).absolutePath
)
} catch (e: Exception) {
XLog.e("崩溃:${e.message}")
}
} else { } else {
// 跳转回原Fragment展示拍摄的照片 // 跳转回原Fragment展示拍摄的照片
ViewModelProvider(requireActivity()).get(ObtainMessageViewModel::class.java).updateMessagePic(file!!.absolutePath) obtainMessageViewModel.updateMessagePic(file!!.absolutePath)
} }
} }
@ -414,12 +459,23 @@ class ObtainMessageFragment: Fragment() {
XLog.d("压缩图片失败:${e.message}") XLog.d("压缩图片失败:${e.message}")
} }
}).launch() }).launch()
} else if (fileName.endsWith(".mp3")||fileName.endsWith(".wav")||fileName.endsWith(".amr")||fileName.endsWith(".m4a")) { } else if (fileName.endsWith(".mp3") || fileName.endsWith(".wav") || fileName.endsWith(
".amr"
) || fileName.endsWith(".m4a")
) {
ToastUtils.showToast(it.absolutePath) ToastUtils.showToast(it.absolutePath)
if (!it.parentFile.parentFile.absolutePath.equals(SystemConstant.SoundFolder)) { if (!it.parentFile.parentFile.absolutePath.equals(SystemConstant.SoundFolder)) {
val copyResult = FileIOUtils.writeFileFromIS(File(SystemConstant.SoundFolder, fileName), FileInputStream(it)) val copyResult = FileIOUtils.writeFileFromIS(
XLog.e("拷贝结果:"+copyResult) File(SystemConstant.SoundFolder, fileName),
obtainMessageViewModel.updateMessageAudio(File(SystemConstant.SoundFolder, fileName).absolutePath) FileInputStream(it)
)
XLog.e("拷贝结果:" + copyResult)
obtainMessageViewModel.updateMessageAudio(
File(
SystemConstant.SoundFolder,
fileName
).absolutePath
)
} else { } else {
obtainMessageViewModel.updateMessageAudio(it.absolutePath) obtainMessageViewModel.updateMessageAudio(it.absolutePath)
} }
@ -432,12 +488,14 @@ class ObtainMessageFragment: Fragment() {
} }
binding.tvAudioName.setOnClickListener { binding.tvAudioName.setOnClickListener {
binding.llAudioPlay.visibility = if (binding.llAudioPlay.visibility == VISIBLE) GONE else VISIBLE binding.llAudioPlay.visibility =
if (binding.llAudioPlay.visibility == VISIBLE) GONE else VISIBLE
// 判断当前播放的文件是否在缓存文件夹内,如果不在首先下载该文件 // 判断当前播放的文件是否在缓存文件夹内,如果不在首先下载该文件
val fileUrl = obtainMessageViewModel.getMessageLiveData().value!!.mediaUrl!! val fileUrl = obtainMessageViewModel.getMessageLiveData().value!!.mediaUrl!!
val localFile = obtainMessageViewModel.getLocalFileFromNetUrl(fileUrl, AttachmentType.AUDIO) val localFile =
obtainMessageViewModel.getLocalFileFromNetUrl(fileUrl, AttachmentType.AUDIO)
if (!localFile.exists()) { if (!localFile.exists()) {
obtainMessageViewModel.downLoadFile(fileUrl, localFile, object: DownloadCallback { obtainMessageViewModel.downLoadFile(fileUrl, localFile, object : DownloadCallback {
override fun progress(progress: Int) { override fun progress(progress: Int) {
} }
@ -455,7 +513,7 @@ class ObtainMessageFragment: Fragment() {
} }
binding.btnObtainMessageBack.setOnClickListener { binding.btnObtainMessageBack.setOnClickListener {
Navigation.findNavController(it).popBackStack() findNavController().popBackStack()
} }
binding.btnObtainMessageConfirm.setOnClickListener { binding.btnObtainMessageConfirm.setOnClickListener {
@ -476,7 +534,7 @@ class ObtainMessageFragment: Fragment() {
toolTipRelativeLayout.showToolTipForView(toolTip, binding.tiLayoutTitle) toolTipRelativeLayout.showToolTipForView(toolTip, binding.tiLayoutTitle)
checkResult = false checkResult = false
} else { } else {
if (messageData?.name!!.length>10) { if (messageData?.name!!.length > 10) {
val toolTipRelativeLayout = val toolTipRelativeLayout =
binding.ttTitle binding.ttTitle
val toolTip = ToolTip() val toolTip = ToolTip()
@ -514,7 +572,7 @@ class ObtainMessageFragment: Fragment() {
checkResult = false checkResult = false
} }
if (messageData?.who?.isEmpty()==true) { if (messageData?.who?.isEmpty() == true) {
val toolTipRelativeLayout = val toolTipRelativeLayout =
binding.ttSendFrom binding.ttSendFrom
val toolTip = ToolTip() val toolTip = ToolTip()
@ -526,7 +584,7 @@ class ObtainMessageFragment: Fragment() {
toolTipRelativeLayout.showToolTipForView(toolTip, binding.edtSendFrom) toolTipRelativeLayout.showToolTipForView(toolTip, binding.edtSendFrom)
checkResult = false checkResult = false
} }
if (messageData?.toWho?.isEmpty()==true) { if (messageData?.toWho?.isEmpty() == true) {
val toolTipRelativeLayout = val toolTipRelativeLayout =
binding.ttSendTo binding.ttSendTo
val toolTip = ToolTip() val toolTip = ToolTip()
@ -547,21 +605,29 @@ class ObtainMessageFragment: Fragment() {
localAttachmentList.add(imageAttachment) localAttachmentList.add(imageAttachment)
} }
if (messageData?.mediaUrl?.startsWith("http") == false) { if (messageData?.mediaUrl?.startsWith("http") == false) {
val audioAttachment = Attachment("", messageData.mediaUrl!!, AttachmentType.AUDIO) val audioAttachment =
Attachment("", messageData.mediaUrl!!, AttachmentType.AUDIO)
localAttachmentList.add(audioAttachment) localAttachmentList.add(audioAttachment)
} }
if (localAttachmentList.isNotEmpty()) { if (localAttachmentList.isNotEmpty()) {
MaterialAlertDialogBuilder(requireContext()) MaterialAlertDialogBuilder(requireContext())
.setTitle("提示") .setTitle("提示")
.setMessage("当前照片及音频内容需首先上传,是否尝试上传?") .setMessage("当前照片及音频内容需首先上传,是否尝试上传?")
.setPositiveButton("确定", DialogInterface.OnClickListener { dialogInterface, i -> .setPositiveButton(
"确定",
DialogInterface.OnClickListener { dialogInterface, i ->
dialogInterface.dismiss() dialogInterface.dismiss()
for (attachment in localAttachmentList) { for (attachment in localAttachmentList) {
obtainMessageViewModel.uploadAttachment(File(attachment.pathUrl), attachment.attachmentType) obtainMessageViewModel.uploadAttachment(
File(attachment.pathUrl),
attachment.attachmentType
)
} }
}) })
.setNegativeButton("取消", DialogInterface.OnClickListener { .setNegativeButton(
dialogInterface, i -> dialogInterface.dismiss() "取消",
DialogInterface.OnClickListener { dialogInterface, i ->
dialogInterface.dismiss()
}) })
.show() .show()
return@setOnClickListener return@setOnClickListener
@ -571,7 +637,7 @@ class ObtainMessageFragment: Fragment() {
val sendDate = DateUtils.str2Date(messageData?.sendDate, dateSendFormat) val sendDate = DateUtils.str2Date(messageData?.sendDate, dateSendFormat)
val cal = Calendar.getInstance() val cal = Calendar.getInstance()
cal.time = Date() cal.time = Date()
cal.set(Calendar.MINUTE, cal.get(Calendar.MINUTE)+1) cal.set(Calendar.MINUTE, cal.get(Calendar.MINUTE) + 1)
if (sendDate.time < cal.time.time) { // 发送时间设置小于当前时间1分钟后Toast提示用户并自动设置发送时间 if (sendDate.time < cal.time.time) { // 发送时间设置小于当前时间1分钟后Toast提示用户并自动设置发送时间
messageData?.sendDate = DateUtils.date2Str(cal.time, dateSendFormat) messageData?.sendDate = DateUtils.date2Str(cal.time, dateSendFormat)
ToastUtils.showToast("自动调整发送时间为1分钟后发送") ToastUtils.showToast("自动调整发送时间为1分钟后发送")
@ -581,7 +647,7 @@ class ObtainMessageFragment: Fragment() {
} }
// 开始网络提交数据 // 开始网络提交数据
if (obtainMessageViewModel.getMessageLiveData().value?.id==0L) { // 如果网络id为空则调用更新操作 if (obtainMessageViewModel.getMessageLiveData().value?.id == 0L) { // 如果网络id为空则调用更新操作
obtainMessageViewModel.insertCardByApp(confirmCallback) obtainMessageViewModel.insertCardByApp(confirmCallback)
} else { } else {
obtainMessageViewModel.updateCardByApp(confirmCallback) obtainMessageViewModel.updateCardByApp(confirmCallback)
@ -593,7 +659,8 @@ class ObtainMessageFragment: Fragment() {
val viewData = ViewData() val viewData = ViewData()
viewData.imageSrc = obtainMessageViewModel.getMessageLiveData().value!!.imageUrl viewData.imageSrc = obtainMessageViewModel.getMessageLiveData().value!!.imageUrl
viewData.targetX = Utils.dp2px(context, 10F).toFloat() viewData.targetX = Utils.dp2px(context, 10F).toFloat()
viewData.targetWidth = DisplayUtils.getScreenWidthPixels(activity) - Utils.dp2px(context, 20F) viewData.targetWidth =
DisplayUtils.getScreenWidthPixels(activity) - Utils.dp2px(context, 20F)
viewData.targetHeight = Utils.dp2px(context, 200F) viewData.targetHeight = Utils.dp2px(context, 200F)
val viewDataList = listOf(viewData) val viewDataList = listOf(viewData)
binding.imageViewer.overlayStatusBar(true) // ImageViewer 是否会占据 StatusBar 的空间 binding.imageViewer.overlayStatusBar(true) // ImageViewer 是否会占据 StatusBar 的空间
@ -603,11 +670,13 @@ class ObtainMessageFragment: Fragment() {
.watch(0) // 开启浏览 .watch(0) // 开启浏览
} }
binding.edtSendFrom.setText(obtainMessageViewModel.username)
} }
val confirmCallback = object: ObtainMessageViewModel.MyConfirmCallback { val confirmCallback = object : ObtainMessageViewModel.MyConfirmCallback {
override fun onSucess() { override fun onSucess() {
findNavController().navigate(R.id.navigation_home) findNavController().popBackStack()
} }
} }
@ -648,6 +717,7 @@ class ObtainMessageFragment: Fragment() {
}) })
.show() .show()
} }
fun onRecorderDenied() { fun onRecorderDenied() {
ToastUtils.showToast("当前操作需要您授权录音权限!") ToastUtils.showToast("当前操作需要您授权录音权限!")
} }

View File

@ -1,36 +1,47 @@
package com.navinfo.volvo.ui.fragments.message package com.navinfo.volvo.ui.fragments.message
import android.security.ConfirmationCallback import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.* import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.easytools.tools.FileIOUtils import com.easytools.tools.FileIOUtils
import com.easytools.tools.FileUtils import com.easytools.tools.FileUtils
import com.easytools.tools.ToastUtils import com.easytools.tools.ToastUtils
import com.elvishew.xlog.XLog import com.elvishew.xlog.XLog
import com.navinfo.volvo.database.entity.Attachment
import com.navinfo.volvo.database.entity.AttachmentType import com.navinfo.volvo.database.entity.AttachmentType
import com.navinfo.volvo.database.entity.GreetingMessage import com.navinfo.volvo.database.entity.GreetingMessage
import com.navinfo.volvo.http.DownloadCallback import com.navinfo.volvo.http.DownloadCallback
import com.navinfo.volvo.http.DownloadManager import com.navinfo.volvo.http.DownloadManager
import com.navinfo.volvo.http.DownloadState import com.navinfo.volvo.http.DownloadState
import com.navinfo.volvo.http.NavinfoVolvoCall import com.navinfo.volvo.http.NavinfoVolvoCall
import com.navinfo.volvo.repository.preferences.PreferencesRepository
import com.navinfo.volvo.utils.SystemConstant import com.navinfo.volvo.utils.SystemConstant
import kotlinx.coroutines.flow.Flow import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import okhttp3.MediaType
import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.MultipartBody import okhttp3.MultipartBody
import okhttp3.RequestBody import okhttp3.RequestBody
import java.io.File import java.io.File
import java.io.FileInputStream import java.io.FileInputStream
import java.util.*
import javax.inject.Inject import javax.inject.Inject
@HiltViewModel
class ObtainMessageViewModel @Inject constructor(): ViewModel() { class ObtainMessageViewModel @Inject constructor(
private val pre: PreferencesRepository,
) : ViewModel() {
private val msgLiveData: MutableLiveData<GreetingMessage> by lazy { private val msgLiveData: MutableLiveData<GreetingMessage> by lazy {
MutableLiveData<GreetingMessage>() MutableLiveData<GreetingMessage>()
} }
var username: String = ""
init {
viewModelScope.launch {
pre.loginUser().collect {
username = it!!.username
}
}
}
fun setCurrentMessage(msg: GreetingMessage) { fun setCurrentMessage(msg: GreetingMessage) {
msgLiveData.postValue(msg) msgLiveData.postValue(msg)
} }
@ -131,7 +142,11 @@ class ObtainMessageViewModel @Inject constructor(): ViewModel() {
try { try {
val requestFile: RequestBody = val requestFile: RequestBody =
RequestBody.create("multipart/form-data".toMediaTypeOrNull(), attachmentFile) RequestBody.create("multipart/form-data".toMediaTypeOrNull(), attachmentFile)
val body = MultipartBody.Part.createFormData("picture", attachmentFile.getName(), requestFile) val body = MultipartBody.Part.createFormData(
"picture",
attachmentFile.getName(),
requestFile
)
val result = NavinfoVolvoCall.getApi().uploadAttachment(body) val result = NavinfoVolvoCall.getApi().uploadAttachment(body)
XLog.d(result.code) XLog.d(result.code)
if (result.code == 200) { // 请求成功 if (result.code == 200) { // 请求成功
@ -144,17 +159,19 @@ class ObtainMessageViewModel @Inject constructor(): ViewModel() {
if (destFile.exists()) { if (destFile.exists()) {
FileUtils.deleteFile(destFile) FileUtils.deleteFile(destFile)
} }
val copyResult = FileIOUtils.writeFileFromIS(destFile, FileInputStream(attachmentFile)) val copyResult =
XLog.e("拷贝结果:"+copyResult) FileIOUtils.writeFileFromIS(destFile, FileInputStream(attachmentFile))
XLog.e("拷贝结果:" + copyResult)
} else { } else {
val destFile = File(SystemConstant.SoundFolder, newFileName) val destFile = File(SystemConstant.SoundFolder, newFileName)
if (destFile.exists()) { if (destFile.exists()) {
FileUtils.deleteFile(destFile) FileUtils.deleteFile(destFile)
} }
val copyResult = FileIOUtils.writeFileFromIS(destFile, FileInputStream(attachmentFile)) val copyResult =
XLog.e("拷贝结果:"+copyResult) FileIOUtils.writeFileFromIS(destFile, FileInputStream(attachmentFile))
XLog.e("拷贝结果:" + copyResult)
} }
if (fileKey!=null) { if (fileKey != null) {
downloadAttachment(fileKey, attachmentType) downloadAttachment(fileKey, attachmentType)
} }
} else { } else {
@ -179,7 +196,7 @@ class ObtainMessageViewModel @Inject constructor(): ViewModel() {
if (result.code == 200) { // 请求成功 if (result.code == 200) { // 请求成功
// 获取上传后的结果 // 获取上传后的结果
val imageUrl = result.data val imageUrl = result.data
if (imageUrl!=null) { if (imageUrl != null) {
XLog.d("downloadAttachment-imageUrl:${imageUrl}") XLog.d("downloadAttachment-imageUrl:${imageUrl}")
// 获取到图片的网络地址 // 获取到图片的网络地址
if (attachmentType == AttachmentType.PIC) { if (attachmentType == AttachmentType.PIC) {
@ -198,7 +215,7 @@ class ObtainMessageViewModel @Inject constructor(): ViewModel() {
} }
} }
fun downLoadFile(url: String, destFile: File, downloadCallback: DownloadCallback){ fun downLoadFile(url: String, destFile: File, downloadCallback: DownloadCallback) {
viewModelScope.launch { viewModelScope.launch {
DownloadManager.download( DownloadManager.download(
url, url,
@ -236,7 +253,8 @@ class ObtainMessageViewModel @Inject constructor(): ViewModel() {
"sendDate" to message?.sendDate, "sendDate" to message?.sendDate,
"version" to message?.version "version" to message?.version
) )
val result = NavinfoVolvoCall.getApi().insertCardByApp(insertData as Map<String, String>) val result =
NavinfoVolvoCall.getApi().insertCardByApp(insertData as Map<String, String>)
XLog.d("insertCardByApp:${result.code}") XLog.d("insertCardByApp:${result.code}")
if (result.code == 200) { // 请求成功 if (result.code == 200) { // 请求成功
// 获取上传后的结果 // 获取上传后的结果
@ -269,7 +287,8 @@ class ObtainMessageViewModel @Inject constructor(): ViewModel() {
"sendDate" to message?.sendDate, "sendDate" to message?.sendDate,
"version" to message?.version "version" to message?.version
) )
val result = NavinfoVolvoCall.getApi().updateCardByApp(updateData as Map<String, String>) val result =
NavinfoVolvoCall.getApi().updateCardByApp(updateData as Map<String, String>)
XLog.d("updateCardByApp:${result.code}") XLog.d("updateCardByApp:${result.code}")
if (result.code == 200) { // 请求成功 if (result.code == 200) { // 请求成功
// 数据更新成功 // 数据更新成功
@ -289,14 +308,14 @@ class ObtainMessageViewModel @Inject constructor(): ViewModel() {
/** /**
* 根据网络地址获取本地的缓存文件路径 * 根据网络地址获取本地的缓存文件路径
* */ * */
fun getLocalFileFromNetUrl(url: String, attachmentType: AttachmentType):File { fun getLocalFileFromNetUrl(url: String, attachmentType: AttachmentType): File {
if (url.startsWith("http")) { if (url.startsWith("http")) {
val folder = when(attachmentType) { val folder = when (attachmentType) {
AttachmentType.PIC-> SystemConstant.CameraFolder AttachmentType.PIC -> SystemConstant.CameraFolder
else -> SystemConstant.SoundFolder else -> SystemConstant.SoundFolder
} }
var name = if (url.contains("?")) { var name = if (url.contains("?")) {
url.substring(url.lastIndexOf("/")+1, url.indexOf("?")) url.substring(url.lastIndexOf("/") + 1, url.indexOf("?"))
} else { } else {
url.substringAfterLast("/") url.substringAfterLast("/")
} }

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

@ -1,12 +1,14 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<layout xmlns:tools="http://schemas.android.com/tools" <layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:tools="http://schemas.android.com/tools">
<data> <data>
<import type="android.view.View" /> <import type="android.view.View" />
<import type="com.navinfo.volvo.Constant" />
<variable <variable
name="greetingMessage" name="greetingMessage"
type="com.navinfo.volvo.database.entity.GreetingMessage" /> type="com.navinfo.volvo.database.entity.GreetingMessage" />
@ -17,15 +19,17 @@
android:layout_height="wrap_content"> android:layout_height="wrap_content">
<ImageView <androidx.constraintlayout.utils.widget.ImageFilterView
android:id="@+id/message_head_icon" android:id="@+id/message_head_icon"
android:layout_width="60dp" android:layout_width="60dp"
android:layout_height="60dp" android:layout_height="60dp"
android:layout_marginLeft="8dp" android:layout_marginLeft="8dp"
android:src="@mipmap/volvo_logo_small" android:scaleType="fitXY"
android:src="@mipmap/ic_launcher"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent"
app:roundPercent="0.4" />
<TextView <TextView
android:id="@+id/message_badge" android:id="@+id/message_badge"
@ -34,6 +38,7 @@
android:background="@drawable/shape_circular" android:background="@drawable/shape_circular"
android:gravity="center" android:gravity="center"
android:textColor="#000000" android:textColor="#000000"
android:visibility="@{Constant.message_status_late.equals(greetingMessage.status)?View.VISIBLE:View.INVISIBLE}"
app:layout_constraintCircle="@id/message_head_icon" app:layout_constraintCircle="@id/message_head_icon"
app:layout_constraintCircleAngle="45" app:layout_constraintCircleAngle="45"
app:layout_constraintCircleRadius="40dp" app:layout_constraintCircleRadius="40dp"

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,11 +14,11 @@
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">
<ImageView
<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="100dp"
android:layout_height="wrap_content" android:layout_height="100dp"
android:scaleType="fitXY"
android:src="@mipmap/volvo_logo_small" android:src="@mipmap/volvo_logo_small"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
@ -40,9 +40,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 +55,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

@ -6,7 +6,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:padding="@dimen/activity_default_padding" android:padding="@dimen/activity_default_padding"
tools:context=".ui.message.ObtainMessageFragment"> tools:context="com.navinfo.volvo.ui.fragments.message.ObtainMessageFragment">
<ScrollView <ScrollView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
@ -300,6 +300,7 @@
android:text="我是谁:"></TextView> android:text="我是谁:"></TextView>
<androidx.appcompat.widget.AppCompatEditText <androidx.appcompat.widget.AppCompatEditText
android:enabled="false"
android:id="@+id/edt_send_from" android:id="@+id/edt_send_from"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -333,6 +334,13 @@
android:id="@+id/edt_send_to" android:id="@+id/edt_send_to"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"></androidx.appcompat.widget.AppCompatSpinner> android:layout_height="wrap_content"></androidx.appcompat.widget.AppCompatSpinner>
<indi.liyi.viewer.ImageViewer
android:id="@+id/imageViewer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/transparent"
app:ivr_dragMode="agile" />
</LinearLayout> </LinearLayout>
<com.nhaarman.supertooltips.ToolTipRelativeLayout <com.nhaarman.supertooltips.ToolTipRelativeLayout
@ -404,10 +412,4 @@
android:text="确认提交"></com.google.android.material.button.MaterialButton> android:text="确认提交"></com.google.android.material.button.MaterialButton>
</LinearLayout> </LinearLayout>
<indi.liyi.viewer.ImageViewer
android:id="@+id/imageViewer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/transparent"
app:ivr_dragMode="agile" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="暂无数据,可下拉刷新"
android:textSize="16sp" />
</androidx.constraintlayout.widget.ConstraintLayout>

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"