使用Hilt框架

This commit is contained in:
squallzhjch
2023-01-04 15:08:35 +08:00
parent dc6d5424dc
commit 6852953866
63 changed files with 1183 additions and 193 deletions

View File

@@ -1,9 +1,13 @@
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id 'kotlin-android'
id 'kotlin-kapt'
id 'dagger.hilt.android.plugin'
// id 'com.google.dagger.hilt.android'
}
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
//apply plugin: 'realm-android'
android {
namespace 'com.navinfo.volvo'
@@ -21,7 +25,7 @@ android {
// 指定room.schemaLocation生成的文件路径
javaCompileOptions {
annotationProcessorOptions {
arguments = ["room.schemaLocation": "$projectDir/schemas".toString()]
arguments += ["room.schemaLocation": "$projectDir/schemas".toString()]
}
}
ndk {
@@ -41,45 +45,84 @@ android {
}
kotlinOptions {
jvmTarget = '1.8'
freeCompilerArgs += [
"-Xjvm-default=all",
]
}
buildFeatures {
viewBinding true
dataBinding true
}
}
dependencies {
implementation 'androidx.core:core-ktx:1.8.0'
implementation 'androidx.appcompat:appcompat:1.5.1'
implementation 'com.google.android.material:material:1.7.0'
implementation "androidx.compose.material3:material3:1.0.0-alpha04"
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.4.1'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.1'
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.1"
implementation "androidx.lifecycle:lifecycle-common-java8:2.4.1"
implementation 'androidx.navigation:navigation-fragment-ktx:2.4.1'
implementation 'androidx.navigation:navigation-ui-ktx:2.4.1'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.4'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.0'
//room
implementation 'com.tencent.wcdb:room:1.1-19' // 代替 room-runtime同时也不需要再引用 wcdb-android
api 'androidx.sqlite:sqlite:2.2.0'
implementation 'androidx.room:room-runtime:2.4.3'
implementation 'androidx.room:room-ktx:2.4.3'
annotationProcessor 'androidx.room:room-compiler:2.4.3'
annotationProcessor 'android.arch.persistence.room:compiler:1.1.1'
kapt 'android.arch.persistence.room:compiler:1.1.1'// compiler 需要用 room 的
kapt 'androidx.room:room-compiler:2.4.3'
kapt 'androidx.room:room-ktx:2.4.3'
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'
// 文件选择器 https://github.com/rosuH/AndroidFilePicker/blob/master/README_CN.md
implementation 'me.rosuh:AndroidFilePicker:0.8.2'
// 时间选择器 https://github.com/Gredicer/datetimepicker
implementation 'com.github.Gredicer:datetimepicker:V1.0.0'
implementation 'com.google.code.gson:gson:2.10'
implementation 'com.yanzhenjie.recyclerview:x:1.3.2'
// implementation 'androidx.appcompat:appcompat:1.5.1'
// // Koin
// implementation("io.insert-koin:koin-android:3.3.2")
// implementation("io.insert-koin:koin-core:3.3.2")
// Retrofit 网络请求相关
implementation("com.squareup.retrofit2:retrofit:2.9.0")
implementation("com.squareup.retrofit2:converter-gson:2.9.0")
implementation("com.squareup.retrofit2:converter-gson:2.9.0")
// const val chuck = "com.readystatesoftware.chuck:library:${Versions.chuck}"
// const val chuckNoOp = "com.readystatesoftware.chuck:library-no-op:${Versions.chuck}"
implementation("com.squareup.okhttp3:okhttp:4.9.0")
implementation("com.squareup.okhttp3:logging-interceptor:4.9.0")
implementation("com.google.code.gson:gson:2.8.6")
//hilt
implementation "com.google.dagger:hilt-android:2.41"
kapt "com.google.dagger:hilt-compiler:2.41"
// implementation "androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha03"
// androidTestImplementation "com.google.dagger:hilt-android-testing:2.41"
// kaptAndroidTest "com.google.dagger:hilt-android-compiler:2.41"
}
kapt {
correctErrorTypes true
}

View File

@@ -3,6 +3,7 @@
xmlns:tools="http://schemas.android.com/tools">
<application
android:name=".MyApplication"
android:allowBackup="true"
android:configChanges="locale"
android:dataExtractionRules="@xml/data_extraction_rules"
@@ -14,7 +15,7 @@
android:theme="@style/Theme.NavinfoVolvo"
tools:targetApi="31">
<activity
android:name="com.navinfo.volvo.MainActivity"
android:name="com.navinfo.volvo.ui.MainActivity"
android:exported="true"
android:label="@string/app_name">
<intent-filter>
@@ -28,5 +29,5 @@
android:value="" />
</activity>
</application>
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
</manifest>

View File

@@ -0,0 +1,14 @@
package com.navinfo.volvo
import java.lang.Boolean
class Constant {
companion object{
/**
* 服务器地址
*/
const val SERVER_ADDRESS = "http://ec2-52-81-73-5.cn-north-1.compute.amazonaws.com.cn:8088/"
val DEBUG = Boolean.parseBoolean("true")
}
}

View File

@@ -0,0 +1,11 @@
package com.navinfo.volvo
import android.app.Application
import dagger.hilt.android.HiltAndroidApp
@HiltAndroidApp
open class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
}
}

View File

@@ -0,0 +1,20 @@
package com.navinfo.volvo.database
import androidx.room.Database
import androidx.room.RoomDatabase
import com.navinfo.volvo.database.dao.MessageDao
import com.navinfo.volvo.database.dao.UserDao
import com.navinfo.volvo.model.Attachment
import com.navinfo.volvo.model.Message
import com.navinfo.volvo.model.User
@Database(
entities = [Message::class, Attachment::class, User::class],
version = 1,
exportSchema = false
)
abstract class AppDatabase : RoomDatabase() {
abstract fun getMessageDao(): MessageDao
abstract fun getUserDao(): UserDao
}

View File

@@ -1,4 +1,4 @@
package com.navinfo.volvo.db.dao;
package com.navinfo.volvo.database;
import android.content.Context;
@@ -10,10 +10,11 @@ import androidx.room.RoomDatabase;
import androidx.sqlite.db.SupportSQLiteDatabase;
import androidx.sqlite.db.SupportSQLiteOpenHelper;
import com.navinfo.volvo.db.dao.entity.Message;
import com.navinfo.volvo.db.dao.entity.Attachment;
import com.navinfo.volvo.db.dao.entity.Message;
import com.navinfo.volvo.db.dao.entity.User;
import com.navinfo.volvo.database.dao.MessageDao;
import com.navinfo.volvo.database.dao.UserDao;
import com.navinfo.volvo.model.Message;
import com.navinfo.volvo.model.Attachment;
import com.navinfo.volvo.model.User;
import com.tencent.wcdb.database.SQLiteCipherSpec;
import com.tencent.wcdb.database.SQLiteDatabase;
@@ -39,6 +40,8 @@ public abstract class MapLifeDataBase extends RoomDatabase {
*/
public abstract MessageDao getMessageDao();
public abstract UserDao getUserDao();
/**
* 数据库秘钥
*/

View File

@@ -1,10 +1,10 @@
package com.navinfo.volvo.db.dao
package com.navinfo.volvo.database.dao
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import com.navinfo.volvo.db.dao.entity.Message
import com.navinfo.volvo.model.Message
@Dao
interface MessageDao {

View File

@@ -0,0 +1,12 @@
package com.navinfo.volvo.database.dao
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import com.navinfo.volvo.model.User
@Dao
interface UserDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertUser(vararg user: User)
}

View File

@@ -1,13 +0,0 @@
package com.navinfo.volvo.db.dao.entity
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "User")
data class User(
@PrimaryKey()
val id:String,
var name:String,
var nickname:String,
)

View File

@@ -0,0 +1,16 @@
package com.navinfo.volvo.di.key
import androidx.lifecycle.ViewModel
import dagger.MapKey
import kotlin.reflect.KClass
@MustBeDocumented
@MapKey
@Retention(AnnotationRetention.RUNTIME)
@Target(
AnnotationTarget.FUNCTION,
AnnotationTarget.PROPERTY_GETTER,
AnnotationTarget.PROPERTY_SETTER
)
internal annotation class ViewModelKey(val value: KClass<out ViewModel>)

View File

@@ -0,0 +1,57 @@
package com.navinfo.volvo.di.module
import android.content.Context
import androidx.room.Room
import com.navinfo.volvo.database.AppDatabase
import com.navinfo.volvo.database.dao.MessageDao
import com.navinfo.volvo.database.dao.UserDao
import com.tencent.wcdb.database.SQLiteCipherSpec
import com.tencent.wcdb.room.db.WCDBOpenHelperFactory
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@InstallIn(SingletonComponent::class)
@Module
class DatabaseModule {
@Singleton
@Provides
fun provideDatabase(context: Context): AppDatabase {
val DB_PASSWORD = "123456";
val cipherSpec = SQLiteCipherSpec()
.setPageSize(1024)
.setSQLCipherVersion(3)
val factory = WCDBOpenHelperFactory()
.passphrase(DB_PASSWORD.toByteArray()) // passphrase to the database, remove this line for plain-text
.cipherSpec(cipherSpec) // cipher to use, remove for default settings
.writeAheadLoggingEnabled(true) // enable WAL mode, remove if not needed
.asyncCheckpointEnabled(true); // enable asynchronous checkpoint, remove if not needed
return Room.databaseBuilder(context, AppDatabase::class.java, "NavinfoVolvoDb")
// [WCDB] Specify open helper to use WCDB database implementation instead
// of the Android framework.
.openHelperFactory(factory)
// Wipes and rebuilds instead of migrating if no Migration object.
// Migration is not part of this codelab.
// .fallbackToDestructiveMigration().addCallback(sRoomDatabaseCallback)
.build();
}
@Singleton
@Provides
fun provideMessageDao(database: AppDatabase): MessageDao {
return database.getMessageDao()
}
@Singleton
@Provides
fun provideUserDao(database: AppDatabase): UserDao {
return database.getUserDao()
}
}

View File

@@ -0,0 +1,29 @@
package com.navinfo.volvo.di.module
import com.navinfo.volvo.di.scope.DefaultDispatcher
import com.navinfo.volvo.di.scope.IoDispatcher
import com.navinfo.volvo.di.scope.MainDispatcher
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
@InstallIn(SingletonComponent::class)
@Module
object DispatcherModule {
@Provides
@DefaultDispatcher
fun provideDefaultDispatcher(): CoroutineDispatcher = Dispatchers.Default
@Provides
@IoDispatcher
fun providesIODispatcher(): CoroutineDispatcher = Dispatchers.IO
@Provides
@MainDispatcher
fun providesMainDispatcher(): CoroutineDispatcher = Dispatchers.Main
}

View File

@@ -0,0 +1,15 @@
package com.navinfo.volvo.di.module
import com.navinfo.volvo.repository.NetworkDataSource
import com.navinfo.volvo.repository.NetworkDataSourceImp
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
@InstallIn(SingletonComponent::class)
@Module
abstract class NetworkDataModule {
@Binds
abstract fun bindNetworkData(networkDataSourceImp: NetworkDataSourceImp): NetworkDataSource
}

View File

@@ -0,0 +1,104 @@
package com.navinfo.volvo.di.module
import android.app.Application
import android.content.Context
import com.google.gson.Gson
import com.navinfo.volvo.Constant
import com.navinfo.volvo.repository.service.NetworkService
import com.navinfo.volvo.tools.GsonUtil
import dagger.Lazy
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import java.util.concurrent.TimeUnit
import javax.inject.Singleton
@InstallIn(SingletonComponent::class)
@Module
class NetworkUtilModule {
@Provides
@Singleton
fun provideContext(application: Application): Context {
return application.applicationContext
}
@Provides
@Singleton
fun provideOkHttpClient(interceptor: HttpLoggingInterceptor): OkHttpClient {
return OkHttpClient.Builder().addInterceptor(interceptor).build()
}
@Provides
@Singleton
fun provideLoggingInterceptor(): HttpLoggingInterceptor {
return HttpLoggingInterceptor().apply {
level = if (Constant.DEBUG) {
HttpLoggingInterceptor.Level.BODY
} else {
HttpLoggingInterceptor.Level.NONE
}
}
}
@Provides
@Singleton
fun provideRetrofit(
client: Lazy<OkHttpClient>,
converterFactory: GsonConverterFactory,
context: Context
): Retrofit {
val retrofitBuilder = Retrofit.Builder()
.baseUrl(Constant.SERVER_ADDRESS)
.client(client.get())
.addConverterFactory(converterFactory)
// val okHttpClientBuilder = OkHttpClient.Builder()
// .addInterceptor { chain ->
//
// val original = chain.request()
// val originalHttpUrl = original.url
//
// val url = originalHttpUrl.newBuilder()
// .addQueryParameter("appid", BuildConfig.API_KEY)
// .build()
//
// Timber.d("Started making network call")
//
// val requestBuilder = original.newBuilder()
// .url(url)
//
// val request = requestBuilder.build()
// return@addInterceptor chain.proceed(request)
// }
// .readTimeout(60, TimeUnit.SECONDS)
// if (Constant.DEBUG) {
// okHttpClientBuilder.addInterceptor(ChuckInterceptor(context))
// }
// return retrofitBuilder.client(okHttpClientBuilder.build()).build()
return retrofitBuilder.build()
}
@Provides
@Singleton
fun provideGson(): Gson = GsonUtil.getInstance()
@Provides
@Singleton
fun provideGsonConverterFactory(gson: Gson): GsonConverterFactory {
return GsonConverterFactory.create(gson)
}
@Provides
@Singleton
fun provideNetworkService(retrofit: Retrofit): NetworkService {
return retrofit.create(NetworkService::class.java)
}
}

View File

@@ -0,0 +1,20 @@
package com.navinfo.volvo.di.module
import android.content.Context
import com.navinfo.volvo.util.SharedPreferenceHelper
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@InstallIn(SingletonComponent::class)
@Module
class UtilModule {
@Provides
@Singleton
fun provideSharedPreferencesHelper(context: Context): SharedPreferenceHelper {
return SharedPreferenceHelper.getInstance(context)
}
}

View File

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

View File

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

View File

@@ -0,0 +1,15 @@
package com.navinfo.volvo.di.scope
import javax.inject.Qualifier
@Retention(AnnotationRetention.BINARY)
@Qualifier
annotation class DefaultDispatcher
@Retention(AnnotationRetention.BINARY)
@Qualifier
annotation class IoDispatcher
@Retention(AnnotationRetention.BINARY)
@Qualifier
annotation class MainDispatcher

View File

@@ -1,4 +1,4 @@
package com.navinfo.volvo.db.dao.entity
package com.navinfo.volvo.model
import androidx.room.Entity
import androidx.room.PrimaryKey

View File

@@ -1,4 +1,4 @@
package com.navinfo.volvo.db.dao.entity
package com.navinfo.volvo.model
import androidx.room.Entity
import androidx.room.PrimaryKey

View File

@@ -0,0 +1,52 @@
package com.navinfo.volvo.model
import androidx.room.Entity
import androidx.room.PrimaryKey
import androidx.room.TypeConverter
import androidx.room.TypeConverters
import com.google.gson.reflect.TypeToken
import com.navinfo.volvo.tools.GsonUtil
import org.jetbrains.annotations.NotNull
import java.time.LocalDateTime
import java.time.LocalTime
import javax.inject.Inject
@Entity(tableName = "User")
@TypeConverters()
data class User(
@PrimaryKey()
@NotNull
val id: String,
var name: String,
var nickname: String,
){
@Inject constructor():this("${System.currentTimeMillis()}","df","sdfds")
}
class UserConverters() {
@TypeConverter
fun stringToUser(value: String): User {
val type = object : TypeToken<User>() {
}.type
return GsonUtil.getInstance().fromJson(value, type)
}
@TypeConverter
fun userToString(user: User): String {
return GsonUtil.getInstance().toJson(user)
}
@TypeConverter
fun userListToString(list: List<User>): String {
return GsonUtil.getInstance().toJson(list)
}
@TypeConverter
fun stringToUserList(value: String): List<User> {
val type = object : TypeToken<List<User>>() {
}.type
return GsonUtil.getInstance().fromJson(value, type)
}
}

View File

@@ -0,0 +1,15 @@
package com.navinfo.volvo.model.network
data class NetworkPostMessage(
val name: String,//问候名称,非必填项
val who: String, //我是谁
val toWho: String, //发送给谁
val startTime: String, //2023-01-02 15:49:50", //创建开始时间 非必填项 暂不支持按时间查询
val endTime: String,//" 2023-01-03 15:52:50", //创建结束时间 非必填项 暂不支持按时间查询
val pageSize: String, //查询数量
val pageNum: String, //分页查询
) {
constructor(who: String, toWho: String) : this("", who, toWho, "", "", "10", "1") {
}
}

View File

@@ -0,0 +1,9 @@
package com.navinfo.volvo.repository
import com.navinfo.volvo.model.Message
import com.navinfo.volvo.model.network.NetworkPostMessage
import com.navinfo.volvo.util.NetResult
interface NetworkDataSource {
suspend fun getCardList(message: NetworkPostMessage): NetResult<List<Message>>
}

View File

@@ -0,0 +1,40 @@
package com.navinfo.volvo.repository
import com.navinfo.volvo.di.scope.IoDispatcher
import com.navinfo.volvo.model.Message
import com.navinfo.volvo.model.network.NetworkPostMessage
import com.navinfo.volvo.repository.service.NetworkService
import com.navinfo.volvo.tools.GsonUtil
import com.navinfo.volvo.util.NetResult
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext
import okhttp3.FormBody
import okhttp3.MediaType
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.RequestBody.Companion.toRequestBody
import javax.inject.Inject
class NetworkDataSourceImp @Inject constructor(
private val netWorkService: NetworkService,
@IoDispatcher private val ioDispatcher: CoroutineDispatcher
) : NetworkDataSource {
override suspend fun getCardList(message: NetworkPostMessage): NetResult<List<Message>> =
withContext(ioDispatcher) {
return@withContext try {
val stringBody = GsonUtil.getInstance().toJson(message).toRequestBody("application/json;charset=utf-8".toMediaType())
val result = netWorkService.queryCardListByApp(stringBody)
if (result.isSuccessful) {
val list = result.body()
NetResult.Success(list)
} else {
NetResult.Success(null)
}
} catch (e: Exception) {
NetResult.Error(e)
}
}
}

View File

@@ -0,0 +1,12 @@
package com.navinfo.volvo.repository.service
import com.navinfo.volvo.model.Message
import okhttp3.RequestBody
import retrofit2.Response
import retrofit2.http.Body
import retrofit2.http.POST
interface NetworkService {
@POST("/navi/cardDelivery/queryCardListByApp")
suspend fun queryCardListByApp(@Body body: RequestBody): Response<List<Message>>
}

View File

@@ -0,0 +1,10 @@
package com.navinfo.volvo.ui
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import javax.inject.Inject
abstract class BaseFragment : Fragment() {
@Inject
lateinit var viewModelFactoryProvider: ViewModelProvider.Factory
}

View File

@@ -1,17 +1,18 @@
package com.navinfo.volvo
package com.navinfo.volvo.ui
import android.os.Bundle
import android.view.View
import com.google.android.material.bottomnavigation.BottomNavigationView
import androidx.appcompat.app.AppCompatActivity
import androidx.navigation.Navigation
import androidx.navigation.findNavController
import androidx.navigation.ui.AppBarConfiguration
import androidx.navigation.ui.setupActionBarWithNavController
import androidx.navigation.ui.setupWithNavController
import com.google.android.material.bottomnavigation.BottomNavigationView
import com.navinfo.volvo.R
import com.navinfo.volvo.databinding.ActivityMainBinding
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
@@ -21,23 +22,39 @@ class MainActivity : AppCompatActivity() {
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
setupNavigation()
}
private fun setupNavigation() {
val navView: BottomNavigationView = binding.navView
val newMessageView = binding.newMessageFab
val navController = findNavController(R.id.nav_host_fragment_activity_main)
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
val appBarConfiguration = AppBarConfiguration(
setOf(
R.id.navigation_home, R.id.navigation_dashboard, R.id.navigation_notifications, R.id.navigation_obtain_message
R.id.navigation_message, R.id.navigation_dashboard, R.id.navigation_notifications,
)
)
setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController)
findViewById<View>(R.id.fab_new_message).apply {
this.setOnClickListener {
navController.navigate(R.id.fab_new_message)
navController.addOnDestinationChangedListener { controller, destination, arguments ->
if (destination.id == R.id.navigation_message
|| destination.id == R.id.navigation_dashboard
|| destination.id == R.id.navigation_notifications
|| destination.id == R.id.navigation_obtain_message
) {
runOnUiThread {
navView.visibility = View.VISIBLE
newMessageView.visibility = View.VISIBLE
}
} else {
runOnUiThread {
navView.visibility = View.GONE
newMessageView.visibility = View.GONE
}
}
}
}
override fun onSupportNavigateUp() =
findNavController(R.id.nav_host_fragment_activity_main).navigateUp()
}

View File

@@ -7,7 +7,7 @@ import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.navinfo.volvo.R
import com.navinfo.volvo.db.dao.entity.Message
import com.navinfo.volvo.model.Message
class MessageAdapter : RecyclerView.Adapter<MessageAdapter.MyViewHolder>() {

View File

@@ -1,4 +1,4 @@
package com.navinfo.volvo.ui.dashboard
package com.navinfo.volvo.ui.fragments.dashboard
import android.os.Bundle
import android.view.LayoutInflater
@@ -23,7 +23,7 @@ class DashboardFragment : Fragment() {
savedInstanceState: Bundle?
): View {
val dashboardViewModel =
ViewModelProvider(this).get(DashboardViewModel::class.java)
ViewModelProvider(this)[DashboardViewModel::class.java]
_binding = FragmentDashboardBinding.inflate(inflater, container, false)
val root: View = binding.root

View File

@@ -1,4 +1,4 @@
package com.navinfo.volvo.ui.dashboard
package com.navinfo.volvo.ui.fragments.dashboard
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData

View File

@@ -1,11 +1,10 @@
package com.navinfo.volvo.ui.home
package com.navinfo.volvo.ui.fragments.home
import android.os.Bundle
import android.view.Display
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.DividerItemDecoration
@@ -13,12 +12,14 @@ import androidx.recyclerview.widget.LinearLayoutManager
import com.navinfo.volvo.R
import com.navinfo.volvo.databinding.FragmentHomeBinding
import com.navinfo.volvo.tools.DisplayUtil
import com.navinfo.volvo.ui.BaseFragment
import com.navinfo.volvo.ui.adapter.MessageAdapter
import com.navinfo.volvo.ui.message.ObtainMessageViewModel
import com.navinfo.volvo.ui.fragments.message.ObtainMessageViewModel
import com.yanzhenjie.recyclerview.*
import com.yanzhenjie.recyclerview.SwipeRecyclerView.LoadMoreListener
import dagger.hilt.android.AndroidEntryPoint
class HomeFragment : Fragment(), OnItemClickListener, OnItemMenuClickListener {
@AndroidEntryPoint
class MessageFragment : BaseFragment(), OnItemClickListener, OnItemMenuClickListener {
private var _binding: FragmentHomeBinding? = null
@@ -26,28 +27,26 @@ class HomeFragment : Fragment(), OnItemClickListener, OnItemMenuClickListener {
// onDestroyView.
private val binding get() = _binding!!
private val viewModel by viewModels<MessageViewModel> { viewModelFactoryProvider }
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val homeViewModel =
ViewModelProvider(this).get(HomeViewModel::class.java)
val obtainMessageViewModel = ViewModelProvider(requireActivity()).get(ObtainMessageViewModel::class.java)
// val homeViewModel =
// ViewModelProvider(this)[MessageViewModel::class.java]
// val obtainMessageViewModel =
// ViewModelProvider(requireActivity()).get(ObtainMessageViewModel::class.java)
_binding = FragmentHomeBinding.inflate(inflater, container, false)
val root: View = binding.root
initView()
return root
}
// val textView: TextView = binding.tvNewMessage
// textView.setOnClickListener {
// val message = Message(1, "新建标题", "", "", "", 0, "1", "2", mutableListOf())
// obtainMessageViewModel.setCurrentMessage(message)
// // 跳转到新建Message的Fragment
// Navigation.findNavController(it).navigate(R.id.home_2_obtain_message)
// }
// homeViewModel.text.observe(viewLifecycleOwner) {
//
// }
private fun initView() {
val recyclerview: SwipeRecyclerView = binding.homeMessageRecyclerview
recyclerview.adapter = null //先设置null否则会报错
//创建菜单选项
@@ -70,8 +69,6 @@ class HomeFragment : Fragment(), OnItemClickListener, OnItemMenuClickListener {
shareItem.text = context!!.getString(R.string.share)
shareItem.setTextColor(R.color.white)
rightMenu.addMenuItem(shareItem)
}
val layoutManager = LinearLayoutManager(context)
val adapter = MessageAdapter()
@@ -84,12 +81,21 @@ class HomeFragment : Fragment(), OnItemClickListener, OnItemMenuClickListener {
}
recyclerview.adapter = adapter
homeViewModel.getMessageList().observe(viewLifecycleOwner, Observer { contacts ->
adapter.setItem(contacts)
})
return root
// homeViewModel.getMessageList().observe(viewLifecycleOwner, Observer { contacts ->
// adapter.setItem(contacts)
// })
}
override fun onStart() {
super.onStart()
getMessageList()
}
private fun getMessageList() {
viewModel.getMessageList()
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null

View File

@@ -0,0 +1,47 @@
package com.navinfo.volvo.ui.fragments.home
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.navinfo.volvo.model.Message
import com.navinfo.volvo.model.network.NetworkPostMessage
import com.navinfo.volvo.repository.NetworkDataSource
import com.navinfo.volvo.util.NetResult
import com.navinfo.volvo.util.asLiveData
import kotlinx.coroutines.launch
import javax.inject.Inject
class MessageViewModel @Inject constructor(
private val repository: NetworkDataSource
) : ViewModel() {
private val _isLoading = MutableLiveData<Boolean>()
val isLoading = _isLoading.asLiveData()
private val _messageList = MutableLiveData<List<Message>>()
val messageList = _messageList.asLiveData()
fun getMessageList() {
_isLoading.postValue(true)
viewModelScope.launch {
val messagePost = NetworkPostMessage(who = "北京测试", toWho = "volvo测试")
when (val result = repository.getCardList(messagePost)) {
is NetResult.Success -> {
_isLoading.value = false
if (result.data != null) {
val list = result.data
_messageList.value = list
}
}
is NetResult.Error -> {
_isLoading.value = false
}
is NetResult.Loading -> {
_isLoading.postValue(true)
}
}
}
}
}

View File

@@ -0,0 +1,51 @@
package com.navinfo.volvo.ui.fragments.login
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.navigation.Navigation
import androidx.navigation.findNavController
import androidx.navigation.fragment.findNavController
import com.navinfo.volvo.R
import com.navinfo.volvo.databinding.FragmentLoginBinding
import com.navinfo.volvo.ui.BaseFragment
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class LoginFragment : BaseFragment() {
// private var loginViewModel:LoginViewModel by viewModel(get())
private var viewBinding: FragmentLoginBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = viewBinding!!
private val viewModel by viewModels<LoginViewModel> { viewModelFactoryProvider }
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
viewBinding = FragmentLoginBinding.inflate(inflater, container, false)
val root: View = binding.root
binding.loginFragmentRegisterButton.setOnClickListener {
}
binding.loginFragmentLoginButton.setOnClickListener {
findNavController().navigate(R.id.action_login_to_home)
}
return root
}
override fun onDestroyView() {
viewBinding = null
super.onDestroyView()
}
}

View File

@@ -0,0 +1,25 @@
package com.navinfo.volvo.ui.fragments.login
import android.view.View
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.navinfo.volvo.database.AppDatabase
import com.navinfo.volvo.model.User
import javax.inject.Inject
class LoginViewModel @Inject constructor(private val dataBase: AppDatabase) : ViewModel() {
private val _user = MutableLiveData<User>().apply {
}
val user: LiveData<User> = _user
fun liveDataOnclick(view: View) {
}
fun userRegister(username: String, password: String) {
}
}

View File

@@ -1,4 +1,4 @@
package com.navinfo.volvo.ui.message
package com.navinfo.volvo.ui.fragments.message
import android.os.Bundle
import android.view.LayoutInflater
@@ -29,13 +29,13 @@ class ObtainMessageFragment: Fragment() {
_binding = FragmentObtainMessageBinding.inflate(inflater, container, false)
val root: View = binding.root
obtainMessageViewModel?.getMessageLiveData()?.observe(
obtainMessageViewModel.getMessageLiveData()?.observe(
viewLifecycleOwner, Observer {
// 初始化界面显示内容
if(it.title!=null)
binding.tvMessageTitle?.setText(it.title)
binding.tvMessageTitle.setText(it.title)
if (it.sendDate!=null) {
binding.btnSendTime.setText(it.sendDate)
binding.btnSendTime.text = it.sendDate
}
}
)
@@ -43,7 +43,7 @@ class ObtainMessageFragment: Fragment() {
return root
}
fun initView() {
private fun initView() {
// 设置问候信息提示的红色星号
binding.tiLayoutTitle.markRequiredInRed()
// 设置点击按钮选择发送时间
@@ -59,6 +59,9 @@ class ObtainMessageFragment: Fragment() {
}
// 点击按钮选择拍照
binding.edtSendTo.setOnClickListener {
}
}
override fun onDestroyView() {

View File

@@ -1,11 +1,9 @@
package com.navinfo.volvo.ui.message
package com.navinfo.volvo.ui.fragments.message
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.liveData
import com.navinfo.volvo.db.dao.entity.Message
import com.navinfo.volvo.db.dao.entity.AttachmentType
import com.navinfo.volvo.model.Message
import com.navinfo.volvo.model.AttachmentType
class ObtainMessageViewModel: ViewModel() {
private val msgLiveData: MutableLiveData<Message> by lazy {

View File

@@ -1,4 +1,4 @@
package com.navinfo.volvo.ui.notifications
package com.navinfo.volvo.ui.fragments.notifications
import android.os.Bundle
import android.view.LayoutInflater

View File

@@ -1,4 +1,4 @@
package com.navinfo.volvo.ui.notifications
package com.navinfo.volvo.ui.fragments.notifications
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData

View File

@@ -1,34 +0,0 @@
package com.navinfo.volvo.ui.home
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.navinfo.volvo.db.dao.entity.Message
class HomeViewModel : ViewModel() {
private val messageList: LiveData<MutableList<Message>> =
MutableLiveData<MutableList<Message>>().apply {
value = mutableListOf<Message>()
value!!.add(Message())
value!!.add(Message())
value!!.add(Message())
value!!.add(Message())
value!!.add(Message())
value!!.add(Message())
value!!.add(Message())
value!!.add(Message())
value!!.add(Message())
value!!.add(Message())
value!!.add(Message())
value!!.add(Message())
value!!.add(Message())
value!!.add(Message())
value!!.add(Message())
value!!.add(Message())
}
fun getMessageList(): LiveData<MutableList<Message>> {
return messageList
}
}

View File

@@ -0,0 +1,28 @@
package com.navinfo.volvo.util
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Observer
/**
* This functions helps in transforming a [MutableLiveData] of type [T]
* to a [LiveData] of type [T]
*/
fun <T> MutableLiveData<T>.asLiveData() = this as LiveData<T>
/**
* This function helps to observe a [LiveData] once
*/
fun <T> LiveData<T>.observeOnce(lifecycleOwner: LifecycleOwner, observer: Observer<T>) {
observe(
lifecycleOwner,
object : Observer<T> {
override fun onChanged(t: T?) {
observer.onChanged(t)
removeObserver(this)
}
}
)
}

View File

@@ -0,0 +1,24 @@
package com.navinfo.volvo.util
/**
* Created by Mayokun Adeniyi on 23/05/2020.
*/
/**
* A generic class that holds a value with its loading status.
* @param <T>
*/
sealed class NetResult<out R> {
data class Success<out T>(val data: T?) : NetResult<T>()
data class Error(val exception: Exception) : NetResult<Nothing>()
object Loading : NetResult<Nothing>()
override fun toString(): String {
return when (this) {
is Success<*> -> "Success[data=$data]"
is Error -> "Error[exception=$exception]"
is Loading -> "Loading"
}
}
}

View File

@@ -0,0 +1,131 @@
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,7 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="800">
<translate
android:fromXDelta="-100%"
android:toXDelta="0%" />
</set>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<set
android:duration="800"
xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromXDelta="100%"
android:toXDelta="0%"
/>
</set>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<set
android:duration="800"
xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromXDelta="0%"
android:toXDelta="-100%"
/>
</set>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<set
android:duration="800"
xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromXDelta="0%"
android:toXDelta="100%"
/>
</set>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:left="10dp" >
<shape android:shape="rectangle">
<solid android:color="#F6F6F6" />
<size android:height="1dp" />
</shape>
</item>
</layer-list>

View File

@@ -0,0 +1 @@
<?xml version="1.0" encoding="utf-8"?>

View File

@@ -0,0 +1,5 @@
<vector android:autoMirrored="true" android:height="24dp"
android:tint="#000000" android:viewportHeight="24"
android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M10,6L8.59,7.41 13.17,12l-4.58,4.59L10,18l6,-6z"/>
</vector>

View File

@@ -0,0 +1,5 @@
<vector android:height="8dp" android:tint="#FF0202"
android:viewportHeight="24" android:viewportWidth="24"
android:width="8dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M12,17.27L18.18,21l-1.64,-7.03L22,9.24l-7.19,-0.61L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21z"/>
</vector>

View File

@@ -6,6 +6,7 @@
android:layout_height="match_parent">
<com.google.android.material.bottomnavigation.BottomNavigationView
android:visibility="gone"
android:id="@+id/nav_view"
android:layout_width="0dp"
android:layout_height="wrap_content"
@@ -31,14 +32,14 @@
app:navGraph="@navigation/mobile_navigation" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab_new_message"
android:visibility="gone"
android:id="@+id/new_message_fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:src="@drawable/ic_add_24dp"
app:elevation="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:elevation="8dp"
/>
app:layout_constraintRight_toRightOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -1,10 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.navinfo.volvo.ui.dashboard.DashboardFragment">
tools:context="com.navinfo.volvo.ui.fragments.dashboard.DashboardFragment">
<TextView
android:id="@+id/text_dashboard"

View File

@@ -6,5 +6,5 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.home.HomeFragment">
tools:context=".ui.fragments.home.MessageFragment">
</com.yanzhenjie.recyclerview.SwipeRecyclerView>

View File

@@ -0,0 +1,78 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.navinfo.volvo.ui.fragments.login.LoginFragment">
<androidx.constraintlayout.utils.widget.ImageFilterView
android:id="@+id/login_fragment_logo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/ic_launcher"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.2"
app:roundPercent="0.2" />
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/login_fragment_user_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:scrollbarAlwaysDrawHorizontalTrack="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.4">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="用户名" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
app:layout_constraintTop_toBottomOf="@id/login_fragment_user_layout">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="密码" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
android:id="@+id/login_fragment_register_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:text="注册"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_chainStyle="spread"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@id/login_fragment_login_button"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.7" />
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
android:id="@+id/login_fragment_login_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:text="登录"
app:layout_constraintBaseline_toBaselineOf="@id/login_fragment_register_button"
app:layout_constraintLeft_toRightOf="@id/login_fragment_register_button"
app:layout_constraintRight_toRightOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -4,7 +4,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.notifications.NotificationsFragment">
tools:context=".ui.fragments.notifications.NotificationsFragment">
<TextView
android:id="@+id/text_notifications"

View File

@@ -1,19 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="@dimen/activity_default_padding"
tools:context=".ui.message.ObtainMessageFragment">
tools:context=".ui.fragments.message.ObtainMessageFragment">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:overScrollMode="never"
android:scrollbars="none">
android:scrollbars="none"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
@@ -24,10 +25,10 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/default_widget_padding"
android:hint="问候信息"
app:counterEnabled="true"
app:counterMaxLength="10"
app:errorEnabled="true"
android:hint="问候信息"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
@@ -82,22 +83,24 @@
android:text="上传图片:"></TextView>
<com.google.android.material.button.MaterialButton
style="@style/Widget.Material3.Button.ElevatedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/Widget.Material3.Button.ElevatedButton"
app:icon="@drawable/ic_baseline_camera_24"
android:padding="@dimen/default_widget_padding"
android:text="点击拍照"
android:padding="@dimen/default_widget_padding"></com.google.android.material.button.MaterialButton>
app:icon="@drawable/ic_baseline_camera_24"></com.google.android.material.button.MaterialButton>
<Space
android:layout_width="@dimen/default_widget_padding"
android:layout_height="wrap_content"></Space>
<com.google.android.material.button.MaterialButton
style="@style/Widget.Material3.Button.ElevatedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/Widget.Material3.Button.ElevatedButton"
app:icon="@drawable/ic_baseline_image_search_24"
android:padding="@dimen/default_widget_padding"
android:text="相册选择"
android:padding="@dimen/default_widget_padding"></com.google.android.material.button.MaterialButton>
app:icon="@drawable/ic_baseline_image_search_24"></com.google.android.material.button.MaterialButton>
</LinearLayout>
<LinearLayout
@@ -119,22 +122,24 @@
android:text="上传音频:"></TextView>
<com.google.android.material.button.MaterialButton
style="@style/Widget.Material3.Button.ElevatedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/Widget.Material3.Button.ElevatedButton"
app:icon="@drawable/ic_baseline_fiber_manual_record_24"
android:padding="@dimen/default_widget_padding"
android:text="长按录音"
android:padding="@dimen/default_widget_padding"></com.google.android.material.button.MaterialButton>
app:icon="@drawable/ic_baseline_fiber_manual_record_24"></com.google.android.material.button.MaterialButton>
<Space
android:layout_width="@dimen/default_widget_padding"
android:layout_height="wrap_content"></Space>
<com.google.android.material.button.MaterialButton
style="@style/Widget.Material3.Button.ElevatedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/Widget.Material3.Button.ElevatedButton"
app:icon="@drawable/ic_baseline_audio_file_24"
android:padding="@dimen/default_widget_padding"
android:text="音频选择"
android:padding="@dimen/default_widget_padding"></com.google.android.material.button.MaterialButton>
app:icon="@drawable/ic_baseline_audio_file_24"></com.google.android.material.button.MaterialButton>
</LinearLayout>
</LinearLayout>
@@ -157,8 +162,12 @@
android:id="@+id/layer_send_info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:animateLayoutChanges="true"
android:background="@drawable/shape_radius5_white"
android:divider="@drawable/shape_divider_linear"
android:orientation="vertical"
android:padding="@dimen/default_widget_padding"
android:showDividers="middle"
app:layout_constraintTop_toBottomOf="@id/div_send_info">
<LinearLayout
@@ -187,31 +196,17 @@
tools:ignore="TouchTargetSizeCheck,SpeakableTextPresentCheck"></androidx.appcompat.widget.AppCompatEditText>
</LinearLayout>
<LinearLayout
style="@style/default_line"
<TextView
android:id="@+id/edt_send_to"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
android:drawableStart="@drawable/ic_baseline_star_8"
android:drawableEnd="@drawable/ic_baseline_navigate_next_24"
android:paddingTop="8dp"
android:paddingBottom="8dp"
android:text="发给谁:" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="*"
android:textColor="@color/red"></TextView>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="发给谁:"></TextView>
<androidx.appcompat.widget.AppCompatEditText
android:id="@+id/edt_send_to"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/selector_bg_4_round_corner"
tools:ignore="SpeakableTextPresentCheck,TouchTargetSizeCheck"></androidx.appcompat.widget.AppCompatEditText>
</LinearLayout>
<LinearLayout
style="@style/default_line"
@@ -233,18 +228,19 @@
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_send_time"
style="@style/Widget.Material3.Button.ElevatedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/Widget.Material3.Button.ElevatedButton"
app:icon="@drawable/ic_baseline_access_time_24"
android:padding="@dimen/default_widget_padding"
android:text="现在"
android:padding="@dimen/default_widget_padding"></com.google.android.material.button.MaterialButton>
app:icon="@drawable/ic_baseline_access_time_24"></com.google.android.material.button.MaterialButton>
</LinearLayout>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"

View File

@@ -2,7 +2,7 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/navigation_home"
android:id="@+id/navigation_message"
android:icon="@drawable/ic_home_black_24dp"
android:title="@string/title_home" />
<item

View File

@@ -3,40 +3,50 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/mobile_navigation"
app:startDestination="@+id/navigation_home">
app:startDestination="@+id/navigation_login">
<fragment
android:id="@+id/navigation_home"
android:name="com.navinfo.volvo.ui.home.HomeFragment"
android:label="@string/title_home"
tools:layout="@layout/fragment_home" >
<action android:id="@+id/home_2_obtain_message"
app:destination="@id/navigation_obtain_message">
</action>
android:id="@+id/navigation_login"
android:name="com.navinfo.volvo.ui.fragments.login.LoginFragment"
android:label="login"
tools:layout="@layout/fragment_login">
<action
android:id="@+id/action_login_to_home"
app:destination="@id/navigation_message"
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:popUpToInclusive="true" />
</fragment>
<fragment
android:id="@+id/navigation_message"
android:name="com.navinfo.volvo.ui.fragments.home.MessageFragment"
android:label="@string/title_home"
tools:layout="@layout/fragment_home"></fragment>
<fragment
android:id="@+id/navigation_dashboard"
android:name="com.navinfo.volvo.ui.dashboard.DashboardFragment"
android:name="com.navinfo.volvo.ui.fragments.dashboard.DashboardFragment"
android:label="@string/title_dashboard"
tools:layout="@layout/fragment_dashboard" />
<fragment
android:id="@+id/fab_new_message"
android:name="com.navinfo.volvo.ui.dashboard.DashboardFragment"
android:name="com.navinfo.volvo.ui.fragments.dashboard.DashboardFragment"
android:label="@string/title_dashboard"
tools:layout="@layout/fragment_dashboard" />
<fragment
android:id="@+id/navigation_notifications"
android:name="com.navinfo.volvo.ui.notifications.NotificationsFragment"
android:name="com.navinfo.volvo.ui.fragments.notifications.NotificationsFragment"
android:label="@string/title_notifications"
tools:layout="@layout/fragment_notifications" />
<fragment
android:id="@+id/navigation_obtain_message"
android:name="com.navinfo.volvo.ui.message.ObtainMessageFragment"
android:name="com.navinfo.volvo.ui.fragments.message.ObtainMessageFragment"
android:label="问候编辑"
tools:layout="@layout/fragment_obtain_message" />
</navigation>

View File

@@ -1,6 +1,6 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Theme.NavinfoVolvo" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<style name="Theme.NavinfoVolvo" parent="Theme.Material3.DynamicColors.DayNight">
<!-- Primary brand color. -->
<item name="colorPrimary">@color/purple_200</item>
<item name="colorPrimaryVariant">@color/purple_700</item>

View File

@@ -1,6 +1,6 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Theme.NavinfoVolvo" parent="Theme.Material3.DayNight.NoActionBar">
<style name="Theme.NavinfoVolvo" parent="Theme.Material3.DynamicColors.Light">
<!-- Primary brand color. -->
<item name="colorPrimary">@color/purple_500</item>
<item name="colorPrimaryVariant">@color/purple_700</item>