Update Compose

This commit is contained in:
AnranYus
2024-06-29 21:58:26 +08:00
parent 5d84fc4df2
commit e9c1f9d176
57 changed files with 91 additions and 1250 deletions
+7 -21
View File
@@ -10,11 +10,11 @@ android {
compileSdkVersion = 34 compileSdkVersion = 34
defaultConfig { defaultConfig {
applicationId "com.lsp.view" applicationId "moe.uni.moe"
minSdkVersion 29 minSdkVersion 29
targetSdkVersion 34 targetSdkVersion 34
versionCode 221022 versionCode 240629
versionName "1.2.7" versionName "2.0.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables { vectorDrawables {
@@ -27,12 +27,10 @@ android {
debuggable true debuggable true
minifyEnabled false minifyEnabled false
shrinkResources false shrinkResources false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
} }
release { release {
minifyEnabled false minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
} }
} }
compileOptions { compileOptions {
@@ -42,7 +40,7 @@ android {
kotlinOptions { kotlinOptions {
jvmTarget = '1.8' jvmTarget = '1.8'
} }
namespace 'com.lsp.view' namespace 'moe.uni.view'
buildFeatures { buildFeatures {
compose true compose true
} }
@@ -58,7 +56,6 @@ android {
dependencies { dependencies {
implementation("androidx.navigation:navigation-compose:2.7.7") implementation("androidx.navigation:navigation-compose:2.7.7")
implementation("com.squareup.okhttp3:logging-interceptor:4.9.1") implementation("com.squareup.okhttp3:logging-interceptor:4.9.1")
implementation 'androidx.preference:preference-ktx:1.2.1' implementation 'androidx.preference:preference-ktx:1.2.1'
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.8.2' implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.8.2'
@@ -73,29 +70,18 @@ dependencies {
androidTestImplementation 'androidx.compose.ui:ui-test-junit4' androidTestImplementation 'androidx.compose.ui:ui-test-junit4'
debugImplementation 'androidx.compose.ui:ui-tooling' debugImplementation 'androidx.compose.ui:ui-tooling'
debugImplementation 'androidx.compose.ui:ui-test-manifest' debugImplementation 'androidx.compose.ui:ui-test-manifest'
def room_version = "2.5.0"
implementation("io.coil-kt:coil-compose:2.6.0") implementation("io.coil-kt:coil-compose:2.6.0")
def room_version = "2.6.1"
implementation "androidx.room:room-runtime:$room_version" implementation "androidx.room:room-runtime:$room_version"
implementation "androidx.room:room-ktx:$room_version" implementation "androidx.room:room-ktx:$room_version"
annotationProcessor "androidx.room:room-compiler:$room_version" annotationProcessor "androidx.room:room-compiler:$room_version"
ksp "androidx.room:room-compiler:$room_version" ksp "androidx.room:room-compiler:$room_version"
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.6.1") implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.8.2")
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.6.2"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2"
implementation("com.google.android.material:material:1.10.0")
implementation 'jp.wasabeef:recyclerview-animators:4.0.2'
implementation 'com.github.chrisbanes:PhotoView:2.3.0'
implementation 'com.google.android.flexbox:flexbox:3.0.0'
implementation ('androidx.swiperefreshlayout:swiperefreshlayout:1.1.0')
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.github.bumptech.glide:glide:4.12.0' implementation "org.jetbrains.kotlin:kotlin-stdlib:1.9.0"
implementation 'androidx.navigation:navigation-fragment-ktx:2.7.5'
implementation "androidx.navigation:navigation-ui-ktx:2.7.5"
annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'
implementation "org.jetbrains.kotlin:kotlin-stdlib:1.9.22"
implementation 'androidx.core:core-ktx:1.13.1' implementation 'androidx.core:core-ktx:1.13.1'
implementation 'androidx.appcompat:appcompat:1.7.0' implementation 'androidx.appcompat:appcompat:1.7.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4' implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
+1 -5
View File
@@ -25,7 +25,7 @@
<provider <provider
android:name="androidx.core.content.FileProvider" android:name="androidx.core.content.FileProvider"
android:authorities="com.lsp.view.fileprovider" android:authorities="moe.uni.view.fileprovider"
android:exported="false" android:exported="false"
android:grantUriPermissions="true"> android:grantUriPermissions="true">
<meta-data <meta-data
@@ -38,11 +38,7 @@
android:name=".service.DownloadService" android:name=".service.DownloadService"
android:enabled="true" android:enabled="true"
android:exported="true" /> android:exported="true" />
<activity
android:name=".ui.activity.MainActivity"
android:exported="true">
</activity>
</application> </application>
</manifest> </manifest>
@@ -1,190 +0,0 @@
package com.lsp.view.ui
import android.content.Context
import android.os.Bundle
import androidx.lifecycle.AbstractSavedStateViewModelFactory
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.lsp.view.ui.fragment.adapter.PostAdapter
import com.lsp.view.repository.network.PostRepository
import androidx.savedstate.SavedStateRegistryOwner
import com.lsp.view.YandViewApplication
import com.lsp.view.bean.YandPost
import com.lsp.view.repository.datasource.CollectDatabase
import com.lsp.view.repository.datasource.CollectRepository
import com.lsp.view.repository.datasource.CollectRepositoryImpl
import com.lsp.view.repository.datasource.model.Collect
import com.lsp.view.repository.exception.NetworkErrorException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.async
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
class MainViewModel(private val repository: PostRepository, context: Context):ViewModel() {
private val _uiState = MutableStateFlow(UiState())
val uiState:StateFlow<UiState>
get() = _uiState.asStateFlow()
private var fetchJob: Job? = null
private val TAG = this::class.java.simpleName
private val _toastMessage = MutableLiveData<String>()
val toastMessage
get() = _toastMessage as LiveData<String>
private val _postList = MutableLiveData<ArrayList<YandPost>>()
val postList get() = _postList as LiveData<ArrayList<YandPost>>
val adapter by lazy {
fetchPostByRefresh()
PostAdapter(context)
}
private val _collectList:MutableLiveData<List<Collect>> = MutableLiveData()
val collectList:LiveData<List<Collect>>
get() = _collectList
private val collectRepository: CollectRepository by lazy {
CollectRepositoryImpl(CollectDatabase.getDatabase(context).collectDao())
}
@Volatile private var isProcessing = false
private val _tagsList = MutableLiveData<ArrayList<String>>()
val tagsList get() = _tagsList as LiveData<ArrayList<String>>
companion object {
fun provideFactory(
repository: PostRepository,
context: Context,
owner: SavedStateRegistryOwner,
defaultArgs: Bundle? = null,
): AbstractSavedStateViewModelFactory =
object : AbstractSavedStateViewModelFactory(owner, defaultArgs) {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(
key: String,
modelClass: Class<T>,
handle: SavedStateHandle
): T {
return MainViewModel(repository, context) as T
}
}
}
init {
//初始化写入
val configSp = context.getSharedPreferences("com.lsp.view_preferences", 0)
if (configSp.getString("source_name", null) == null) {
configSp.edit().putString("source_name", "yande.re").apply()
configSp.edit().putString("type", "0").apply()
}
_uiState.update {
it.apply {
it.nowSourceName.postValue(configSp.getString("source_name",null))
}
it.copy(isSafe = configSp.getBoolean("safe_mode",true))
}
}
private fun fetchNewPost() {
fetchJob?.cancel()
fetchJob = viewModelScope.launch(Dispatchers.IO) {
_uiState.value.isRefreshing.postValue(true)
try {
val newPosts = repository.fetchPostData(
_uiState.value.nowSearchText.value,
_uiState.value.nowPage
)
val currentPosts = _postList.value ?: arrayListOf()
currentPosts.addAll(0,newPosts)
_postList.postValue(currentPosts)
} catch (e: NetworkErrorException) {
postNewToast(e.message.toString())
}
_uiState.value.isRefreshing.postValue(false)
}
}
fun fetchPostByRefresh(){
_uiState.update {
it.copy(nowPage = 1)
}
fetchNewPost()
}
fun fetchPostBySearch(searchTarget:String){
_uiState.update {
it.apply {
it.nowSearchText.postValue(searchTarget)
}
}
fetchNewPost()
}
fun fetchMore(){
_uiState.value.nowPage++
fetchNewPost()
}
fun updateSafeMode(mode:Boolean){
_uiState.update {
it.copy(isSafe = mode)
}
fetchNewPost()
}
fun updateNowSource(source:String){
_uiState.value.nowSourceName.postValue(source)
fetchPostByRefresh()
}
fun getCollectList(){
viewModelScope.launch {
_collectList.postValue(collectRepository.getAllCollect())
}
}
fun addCollect(collect: Collect){
if (!isProcessing){
viewModelScope.launch {
collectRepository.addNewCollect(collect)
}
}
}
fun removeCollect(collect:Collect){
if (!isProcessing){
viewModelScope.launch {
collectRepository.removeCollect(collect)
}
}
}
suspend fun getCollectByPostId(postId:String) = viewModelScope.async {
val type = YandViewApplication.context?.getSharedPreferences("com.lsp.view_preferences",Context.MODE_PRIVATE)?.getString("source_name", "yande.re")
collectRepository.getCollectByPostId(postId,type!!)
}
fun postNewToast(content:String){
_toastMessage.postValue(content)
}
}
@@ -1,12 +0,0 @@
package com.lsp.view.ui
import androidx.lifecycle.MutableLiveData
data class UiState(
var showSearchBar:Boolean = false,
val isRefreshing:MutableLiveData<Boolean> = MutableLiveData(),
var nowSearchText:MutableLiveData<String> = MutableLiveData(""),
val nowSourceName: MutableLiveData<String> = MutableLiveData(),
val isSafe: Boolean = true, //安全模式
var nowPage:Int = 1,
)
@@ -1,60 +0,0 @@
package com.lsp.view.ui.activity
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.ui.setupWithNavController
import com.google.android.material.bottomnavigation.BottomNavigationView
import com.lsp.view.R
import com.lsp.view.ui.MainViewModel
import com.lsp.view.repository.network.PostRepository
class MainActivity : AppCompatActivity() {
val TAG = javaClass.simpleName
lateinit var viewModel: MainViewModel
lateinit var bottomNav:BottomNavigationView
val repository = PostRepository()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
viewModel = MainViewModel.provideFactory(repository, this,this).create(MainViewModel::class.java)
bottomNav = findViewById(R.id.bottom_navigation)
viewModel.toastMessage.observe(this){
Toast.makeText(this,it,Toast.LENGTH_SHORT).show()
}
val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
val navController = navHostFragment.navController
bottomNav.setupWithNavController(navController)
}
override fun onResume() {
super.onResume()
//加载源改变
val configSp = getSharedPreferences("com.lsp.view_preferences", 0)
val nowSourceName = configSp.getString("source_name", null)
if (nowSourceName != null && viewModel.uiState.value.nowSourceName.value != nowSourceName){
viewModel.updateNowSource(nowSourceName)
}
val nowMode = configSp.getBoolean("safe_mode", true)
if (viewModel.uiState.value.isSafe != nowMode){
viewModel.updateSafeMode(nowMode)
Log.e(TAG,nowMode.toString())
}
}
}
@@ -1,81 +0,0 @@
package com.lsp.view.ui.fragment
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.animation.AnimationUtils
import androidx.navigation.Navigation
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.StaggeredGridLayoutManager
import com.lsp.view.ui.fragment.adapter.PostAdapter
import com.lsp.view.R
import com.lsp.view.ui.activity.MainActivity
import com.lsp.view.bean.Post
/**
* A simple [Fragment] subclass.
* create an instance of this fragment.
*/
class CollectFragment : Fragment() {
private lateinit var activityContext: MainActivity
private val collectAdapter by lazy {
PostAdapter(activityContext)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
activityContext = requireActivity() as MainActivity
activityContext.viewModel.getCollectList()
activityContext.viewModel.collectList.observe(this){
collectAdapter.pushNewData(it)
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_collect, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
//刷新
val refresh =
view.findViewById<androidx.swiperefreshlayout.widget.SwipeRefreshLayout>(R.id.swipeRefreshLayout)
activityContext.viewModel.uiState.value.isRefreshing.observe(activityContext) {
refresh.isRefreshing = it
}
collectAdapter.setOnListItemClick(object : PostAdapter.OnListItemClick {
override fun setOnListItemClick(post: Post) {
ImageFragment.navigationToImageFragment(activityContext,post,view,R.id.action_collectFragment_to_imageFragment)
}
})
view.findViewById<RecyclerView>(R.id.recyclerview).apply {
val layoutManager = StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL)
this.layoutManager = layoutManager
this.adapter = collectAdapter
}
}
override fun onResume() {
super.onResume()
if (activityContext.bottomNav.visibility == View.GONE){
val slideOutAnimation = AnimationUtils.loadAnimation(context, R.anim.slide_on_bottom)
activityContext.bottomNav.startAnimation(slideOutAnimation)
activityContext.bottomNav.visibility = View.VISIBLE
}
}
}
@@ -1,218 +0,0 @@
package com.lsp.view.ui.fragment
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.content.pm.ResolveInfo
import android.graphics.Bitmap
import android.os.Build
import android.os.Bundle
import android.os.Environment
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.view.animation.AnimationUtils
import android.widget.ImageView
import androidx.core.content.FileProvider
import androidx.lifecycle.lifecycleScope
import androidx.navigation.Navigation
import com.bumptech.glide.Glide
import com.bumptech.glide.load.model.GlideUrl
import com.bumptech.glide.load.model.LazyHeaders
import com.google.android.material.bottomappbar.BottomAppBar
import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.lsp.view.R
import com.lsp.view.YandViewApplication
import com.lsp.view.ui.activity.MainActivity
import com.lsp.view.bean.Post
import com.lsp.view.repository.datasource.model.Collect
import com.lsp.view.repository.datasource.model.Collect.Companion.toCollect
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.io.File
class ImageFragment : Fragment() {
private val post by lazy {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
arguments?.getParcelable("post",Post::class.java)
}else{
arguments?.getParcelable("post")
}
}
@Volatile private var isCollect = false
private val activityContext : MainActivity by lazy {
requireActivity() as MainActivity
}
private lateinit var collectBtn:MenuItem
private lateinit var collect:Collect
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_image, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val bottomAppBar = view.findViewById<BottomAppBar>(R.id.bottomAppBar)
collectBtn = bottomAppBar.menu.findItem(R.id.collect_menu_btn)
collectBtn.setIcon(R.drawable.ic_bashline_unfavorite_24)
bottomAppBar.setOnMenuItemClickListener {
when(it.itemId){
R.id.share_menu_btn ->{
post?.sampleUrl?.let { it1 -> share(it1,activityContext) }
true
}
R.id.collect_menu_btn ->{
if (!isCollect){
collect = post!!.toCollect()
collectIt(collect)
}else{
unCollectIt(collect)
}
true
}
else -> {false}
}
}
lifecycleScope.launch {
post?.postId?.let {
val collectPost = activityContext.viewModel.getCollectByPostId(it)
val result = collectPost.await()
if (result != null){
collect = result
collectBtn.setIcon(R.drawable.ic_baseline_favorite_24)
isCollect = true
}
}
}
val downloadBtn = view.findViewById<FloatingActionButton>(R.id.download_btn)
downloadBtn.setOnClickListener {
lifecycleScope.launch {
if (post!=null){
download(post!!.fileUrl)
}
}
}
if (post!=null){
val imageContent = view.findViewById<ImageView>(R.id.image_content)
val glideUrl = GlideUrl(
post!!.sampleUrl,
LazyHeaders.Builder().addHeader("User-Agent", YandViewApplication.UA)
.build()
)
Glide.with(this).load(glideUrl).into(imageContent)
}
}
private fun collectIt(collect: Collect){
activityContext.viewModel.addCollect(collect)
collectBtn.setIcon(R.drawable.ic_baseline_favorite_24)
isCollect = true
}
private fun unCollectIt(collect:Collect){
activityContext.viewModel.removeCollect(collect)
collectBtn.setIcon(R.drawable.ic_bashline_unfavorite_24)
isCollect = false
}
private fun download(fileUrl: String){
}
private fun share(url:String,context: Context){
lifecycleScope.launch(Dispatchers.IO) {
val bitmap = Glide.with(context).asBitmap().load(url).submit().get()
val path = File("${context.cacheDir}/image")
val split = url.split("/")
val fileName = split[split.size - 1]
if (!path.exists())
path.mkdirs()
var file = File("${context.cacheDir}/image/$fileName")
val downloadFile =
File("${Environment.getExternalStorageDirectory()}/${Environment.DIRECTORY_PICTURES}/LspMake/$fileName")
if (downloadFile.exists()){
file = downloadFile
}else if (!file.exists()) {
file.outputStream().apply {
bitmap.compress(Bitmap.CompressFormat.PNG, 100, this)
}
}
val imageUri = FileProvider.getUriForFile(
context,
"com.lsp.view.fileprovider",
file
)
context.grantUriPermission(
"com.lsp.view",
imageUri,
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
)
val shareIntent = Intent(Intent.ACTION_SEND)
shareIntent.type = "image/*"
shareIntent.putExtra(Intent.EXTRA_STREAM, imageUri)
shareIntent.putExtra(Intent.EXTRA_TITLE,fileName)
val intent = Intent.createChooser(shareIntent, R.string.title_share.toString())
val resInfoList: List<ResolveInfo> = if (Build.VERSION.SDK_INT < 33) {
context.packageManager
.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY)
} else {
context.packageManager.queryIntentActivities(
intent,
PackageManager.ResolveInfoFlags.of(PackageManager.MATCH_DEFAULT_ONLY.toLong())
)
}
for (resolveInfo in resInfoList) {
val packageName = resolveInfo.activityInfo.packageName
context.grantUriPermission(
packageName,
imageUri,
Intent.FLAG_GRANT_WRITE_URI_PERMISSION or Intent.FLAG_GRANT_READ_URI_PERMISSION
)
}
context.startActivity(intent)
}
}
companion object{
fun navigationToImageFragment(context:MainActivity ,post:Post,view:View,fragmentId:Int){
val bundle = Bundle()
bundle.putParcelable("post",post)
val tags = post.tags?.split(",")?.toList()
context.viewModel.tagsList.value?.apply {
clear()
tags?.let { addAll(it) }
}
Navigation.findNavController(view).navigate(fragmentId,bundle)
val slideOutAnimation = AnimationUtils.loadAnimation(context,
R.anim.slide_out_bottom
)
context.bottomNav.startAnimation(slideOutAnimation)
context.bottomNav.visibility = View.GONE
}
}
}
@@ -1,108 +0,0 @@
package com.lsp.view.ui.fragment
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.animation.AnimationUtils
import android.view.inputmethod.EditorInfo
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.Navigation
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.StaggeredGridLayoutManager
import com.google.android.material.search.SearchBar
import com.google.android.material.search.SearchView
import com.lsp.view.ui.fragment.adapter.PostAdapter
import com.lsp.view.R
import com.lsp.view.ui.activity.MainActivity
import com.lsp.view.bean.Post
import com.lsp.view.ui.MainViewModel
class PostListFragment:Fragment() {
private val viewModel: MainViewModel by lazy {
ViewModelProvider(requireActivity(),
MainViewModel.provideFactory(activityContext.repository,requireContext(),this)).get(
MainViewModel::class.java)
}
private lateinit var activityContext: MainActivity
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_list, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
activityContext = requireActivity() as MainActivity
val searchBar = view.findViewById<SearchBar>(R.id.search_bar)
val searchView = view.findViewById<SearchView>(R.id.search_view)
searchView.editText.setOnEditorActionListener { v, actionId, event ->
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
viewModel.fetchPostBySearch(searchView.text.toString())
searchBar.setText(searchView.text)
searchView.hide()
}
return@setOnEditorActionListener false
}
viewModel.uiState.value.nowSearchText.observe(requireActivity()){
searchView.setText(it)
searchBar.setText(it)
}
//刷新
val refresh =
view.findViewById<androidx.swiperefreshlayout.widget.SwipeRefreshLayout>(R.id.swipeRefreshLayout)
viewModel.uiState.value.isRefreshing.observe(activityContext) {
refresh.isRefreshing = it
}
refresh.setOnRefreshListener {
viewModel.fetchPostByRefresh()
}
viewModel.postList.observe(activityContext){
viewModel.adapter.pushNewData(it)
}
viewModel.adapter.apply {
setLoadMoreListener(object : PostAdapter.OnScrollToBottom {
override fun event(position: Int) {
viewModel.fetchMore()
}
})
}
view.findViewById<RecyclerView>(R.id.recyclerview).apply {
val layoutManager = StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL)
this.layoutManager = layoutManager
this.adapter = viewModel.adapter
}
viewModel.adapter.setOnListItemClick(object : PostAdapter.OnListItemClick {
override fun setOnListItemClick(post: Post) {
ImageFragment.navigationToImageFragment(activityContext,post,view,R.id.action_postListFragment_to_imageFragment)
}
})
}
override fun onResume() {
super.onResume()
if (activityContext.bottomNav.visibility == View.GONE){
val slideOutAnimation = AnimationUtils.loadAnimation(context, R.anim.slide_on_bottom)
activityContext.bottomNav.startAnimation(slideOutAnimation)
activityContext.bottomNav.visibility = View.VISIBLE
}
}
}
@@ -1,12 +0,0 @@
package com.lsp.view.ui.fragment
import android.os.Bundle
import androidx.preference.PreferenceFragmentCompat
import com.lsp.view.R
class SettingsFragment : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.root_preferences, rootKey)
}
}
@@ -1,143 +0,0 @@
package com.lsp.view.ui.fragment.adapter
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.bumptech.glide.load.model.GlideUrl
import com.bumptech.glide.load.model.LazyHeaders
import com.lsp.view.R
import com.lsp.view.YandViewApplication
import com.lsp.view.bean.Post
class PostAdapter(val context: Context,private val postList: ArrayList<Post> = ArrayList()):
RecyclerView.Adapter<PostAdapter.ViewHolder>() {
val TAG = this::class.java.simpleName
private var inBottom:Boolean = false//当前列表是否在底部
inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val picImage: ImageView = view.findViewById(R.id.image)
}
fun pushNewData(list: List<Post>) {
if (inBottom) {
//在底部,则追加数据
val pos = postList.size
postList.addAll(list)
notifyItemRangeInserted(pos - 1, list.size)
} else {
//不在则刷新数据
val oldSize = postList.size
postList.clear()
notifyItemRangeRemoved(0, oldSize)
postList.addAll(list)
notifyItemRangeInserted(0, list.size)
}
inBottom = false//加入新数据后不在底部,若仍在底部说明无更多数据,也不必处理
}
private lateinit var mOnListItemClick: OnListItemClick
interface OnListItemClick {
fun setOnListItemClick(post:Post)
}
fun setOnListItemClick(mOnListItemClick: OnListItemClick) {
this.mOnListItemClick = mOnListItemClick
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(context).inflate(R.layout.img_item_layout, parent, false)
val viewHolder = ViewHolder(view)
viewHolder.picImage.setOnClickListener {
val position = viewHolder.adapterPosition
mOnListItemClick.setOnListItemClick(postList[position])
}
return viewHolder
}
private lateinit var mScrollToBottom: OnScrollToBottom
interface OnScrollToBottom {
fun event(position: Int)
}
fun setLoadMoreListener(mLoadMoreListener: OnScrollToBottom) {
this.mScrollToBottom = mLoadMoreListener
}
private fun preLoad(p:Int){
if (p%20==0){
//执行预加载
val last:Int = if (postList.size-p<20){
postList.size-1
}else{
p+19
}
for(index in p..last){
if (p>6) {
val source: String = postList[index].sampleUrl
val glideUrl = GlideUrl(
source,
LazyHeaders.Builder().addHeader("User-Agent", YandViewApplication.UA)
.build()
)
Glide.with(context).download(glideUrl).preload()
}
}
}
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
preLoad(position)
val post = postList[position]
val source: String = post.sampleUrl
val height = postList[position].sampleHeight
val width = postList[position].sampleWidth
//图片过长则缩减高度以保证显示完整
if (width > height){
holder.picImage.layoutParams.height = height / 2
}else {
holder.picImage.layoutParams.height = height
}
val glideUrl = GlideUrl(
source,
LazyHeaders.Builder().addHeader("User-Agent", YandViewApplication.UA)
.build()
)
Glide.with(context).load(glideUrl).into(holder.picImage)
if (position == postList.size - 1 && postList.size > 6 ) {
//到达底部
inBottom = true
if (this::mScrollToBottom.isInitialized) {
mScrollToBottom.event(position)
}
}
}
override fun getItemCount(): Int {
return postList.size
}
}
@@ -1,28 +0,0 @@
package com.lsp.view.ui.fragment.adapter
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.lsp.view.R
class TagsAdapter(private val tagList:List<String>):RecyclerView.Adapter<TagsAdapter.ViewHolder>() {
inner class ViewHolder(view:View):RecyclerView.ViewHolder(view) {
val tagItem = view.findViewById<TextView>(R.id.tag)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view =
LayoutInflater.from(parent.context).inflate(R.layout.tags_item_layout, parent, false)
val viewHolder = ViewHolder(view)
return viewHolder
}
override fun getItemCount() = tagList.size
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.tagItem.text = tagList[position]
}
}
@@ -1,9 +1,8 @@
package com.lsp.view package moe.uni.view
import android.app.Application import android.app.Application
import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.DefaultLifecycleObserver
import android.content.Context import android.content.Context
import com.google.android.material.color.DynamicColors
class YandViewApplication : Application(), DefaultLifecycleObserver { class YandViewApplication : Application(), DefaultLifecycleObserver {
@@ -11,8 +10,6 @@ class YandViewApplication : Application(), DefaultLifecycleObserver {
override fun onCreate() { override fun onCreate() {
super<Application>.onCreate() super<Application>.onCreate()
context = applicationContext context = applicationContext
DynamicColors.applyToActivitiesIfAvailable(this)
} }
companion object { companion object {
@@ -1,4 +1,4 @@
package com.lsp.view.bean package moe.uni.view.bean
import android.os.Parcel import android.os.Parcel
import android.os.Parcelable import android.os.Parcelable
@@ -1,4 +1,4 @@
package com.lsp.view.bean package moe.uni.view.bean
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
@@ -1,4 +1,4 @@
package com.lsp.view.common package moe.uni.view.common
class Config(val safeMode:Boolean,val source:String) { class Config(val safeMode:Boolean,val source:String) {
companion object{ companion object{
@@ -1,4 +1,4 @@
package com.lsp.view.common package moe.uni.view.common
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context import android.content.Context
@@ -13,7 +13,7 @@ import androidx.core.content.FileProvider
import androidx.core.graphics.drawable.toBitmap import androidx.core.graphics.drawable.toBitmap
import coil.ImageLoader import coil.ImageLoader
import coil.request.ImageRequest import coil.request.ImageRequest
import com.lsp.view.R import moe.uni.view.R
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@@ -95,7 +95,7 @@ suspend fun getImageInfo(url: String, context: Context): ImageInfo {
} }
val imageUri = FileProvider.getUriForFile( val imageUri = FileProvider.getUriForFile(
context, context,
"com.lsp.view.fileprovider", "moe.uni.view.fileprovider",
file file
) )
return ImageInfo(path = file.path, name = fileName, uri = imageUri) return ImageInfo(path = file.path, name = fileName, uri = imageUri)
@@ -1,4 +1,4 @@
package com.lsp.view.common package moe.uni.view.common
object PreKV { object PreKV {
const val SOURCE_NAME = "source_name" const val SOURCE_NAME = "source_name"
@@ -6,5 +6,5 @@ object PreKV {
} }
object Pre{ object Pre{
const val NAME = "com.lsp.view_preferences" const val NAME = "moe.uni.view_preferences"
} }
@@ -1,7 +1,7 @@
package com.lsp.view.common package moe.uni.view.common
import android.content.Context import android.content.Context
import com.lsp.view.YandViewApplication import moe.uni.view.YandViewApplication
object PreUtils { object PreUtils {
private val PRE = YandViewApplication.context!!.getSharedPreferences(Pre.NAME,Context.MODE_PRIVATE) private val PRE = YandViewApplication.context!!.getSharedPreferences(Pre.NAME,Context.MODE_PRIVATE)
@@ -1,11 +1,11 @@
package com.lsp.view.repository.datasource package moe.uni.view.repository.datasource
import android.content.Context import android.content.Context
import androidx.room.Database import androidx.room.Database
import androidx.room.Room import androidx.room.Room
import androidx.room.RoomDatabase import androidx.room.RoomDatabase
import com.lsp.view.repository.datasource.dao.CollectDao import moe.uni.view.repository.datasource.dao.CollectDao
import com.lsp.view.repository.datasource.model.Collect import moe.uni.view.repository.datasource.model.Collect
@Database(entities = [Collect::class], version = 1,exportSchema = false) @Database(entities = [Collect::class], version = 1,exportSchema = false)
abstract class CollectDatabase: RoomDatabase(){ abstract class CollectDatabase: RoomDatabase(){
@@ -1,6 +1,6 @@
package com.lsp.view.repository.datasource package moe.uni.view.repository.datasource
import com.lsp.view.repository.datasource.model.Collect import moe.uni.view.repository.datasource.model.Collect
interface CollectRepository { interface CollectRepository {
suspend fun addNewCollect(collect: Collect) suspend fun addNewCollect(collect: Collect)
@@ -1,7 +1,7 @@
package com.lsp.view.repository.datasource package moe.uni.view.repository.datasource
import com.lsp.view.repository.datasource.dao.CollectDao import moe.uni.view.repository.datasource.dao.CollectDao
import com.lsp.view.repository.datasource.model.Collect import moe.uni.view.repository.datasource.model.Collect
class CollectRepositoryImpl(private val collectDao:CollectDao):CollectRepository { class CollectRepositoryImpl(private val collectDao:CollectDao):CollectRepository {
override suspend fun addNewCollect(collect: Collect) { override suspend fun addNewCollect(collect: Collect) {
@@ -1,10 +1,10 @@
package com.lsp.view.repository.datasource.dao package moe.uni.view.repository.datasource.dao
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Insert import androidx.room.Insert
import androidx.room.OnConflictStrategy import androidx.room.OnConflictStrategy
import androidx.room.Query import androidx.room.Query
import com.lsp.view.repository.datasource.model.Collect import moe.uni.view.repository.datasource.model.Collect
@Dao @Dao
interface CollectDao { interface CollectDao {
@@ -1,11 +1,11 @@
package com.lsp.view.repository.datasource.model package moe.uni.view.repository.datasource.model
import android.content.Context import android.content.Context
import androidx.room.ColumnInfo import androidx.room.ColumnInfo
import androidx.room.Entity import androidx.room.Entity
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import com.lsp.view.YandViewApplication import moe.uni.view.YandViewApplication
import com.lsp.view.bean.Post import moe.uni.view.bean.Post
@Entity(tableName = "collect") @Entity(tableName = "collect")
data class Collect( data class Collect(
@@ -28,7 +28,7 @@ data class Collect(
sampleWeight:Int, sampleWeight:Int,
tags:String? tags:String?
): this(0, postId, sampleUrl, rating, fileUrl, sampleHeight, sampleWeight, ): this(0, postId, sampleUrl, rating, fileUrl, sampleHeight, sampleWeight,
YandViewApplication.context?.getSharedPreferences("com.lsp.view_preferences",Context.MODE_PRIVATE)?.getString("source_name", "yande.re")!!,tags) YandViewApplication.context?.getSharedPreferences("moe.uni.view_preferences",Context.MODE_PRIVATE)?.getString("source_name", "yande.re")!!,tags)
companion object{ companion object{
fun Post.toCollect():Collect = Collect(this.postId,this.sampleUrl,this.rating,this.fileUrl,this.sampleHeight,this.sampleWidth ,this.tags) fun Post.toCollect():Collect = Collect(this.postId,this.sampleUrl,this.rating,this.fileUrl,this.sampleHeight,this.sampleWidth ,this.tags)
@@ -1,4 +1,4 @@
package com.lsp.view.repository.exception package moe.uni.view.repository.exception
import android.util.Log import android.util.Log
@@ -1,6 +1,4 @@
package com.lsp.view.repository.exception package moe.uni.view.repository.exception
import okhttp3.ResponseBody
class NetworkErrorException(override val message: String?) : LoggerException(message) { class NetworkErrorException(override val message: String?) : LoggerException(message) {
} }
@@ -1,6 +1,4 @@
package com.lsp.view.repository.exception package moe.uni.view.repository.exception
import java.lang.Exception
class UnableConstructObjectException(override val message: String?):LoggerException(message) { class UnableConstructObjectException(override val message: String?):LoggerException(message) {
} }
@@ -1,14 +1,9 @@
package com.lsp.view.repository.network package moe.uni.view.repository.network
import com.lsp.view.R import moe.uni.view.repository.network.api.PostApi
import com.lsp.view.YandViewApplication import moe.uni.view.repository.network.api.PostApiImpl
import com.lsp.view.repository.exception.UnableConstructObjectException import moe.uni.view.bean.YandPost
import com.lsp.view.repository.network.api.PostApi import moe.uni.view.common.Config
import com.lsp.view.repository.network.api.PostApiImpl
import com.lsp.view.bean.YandPost
import com.lsp.view.common.Config
import com.lsp.view.common.Pre
import com.lsp.view.common.PreKV
class PostDataSource { class PostDataSource {
companion object{ companion object{
@@ -1,7 +1,7 @@
package com.lsp.view.repository.network package moe.uni.view.repository.network
import com.lsp.view.bean.YandPost import moe.uni.view.bean.YandPost
import com.lsp.view.common.Config import moe.uni.view.common.Config
class PostRepository { class PostRepository {
@@ -1,9 +1,9 @@
package com.lsp.view.repository.network.api package moe.uni.view.repository.network.api
import com.lsp.view.repository.network.Load import moe.uni.view.repository.network.Load
import com.lsp.view.bean.Post import moe.uni.view.bean.Post
import com.lsp.view.bean.YandPost import moe.uni.view.bean.YandPost
import com.lsp.view.repository.exception.NetworkErrorException import moe.uni.view.repository.exception.NetworkErrorException
import retrofit2.Call import retrofit2.Call
interface PostApi { interface PostApi {
@@ -1,9 +1,9 @@
package com.lsp.view.repository.network.api package moe.uni.view.repository.network.api
import com.lsp.view.repository.network.Load import moe.uni.view.repository.network.Load
import com.lsp.view.repository.network.retrofit.PostService import moe.uni.view.repository.network.retrofit.PostService
import com.lsp.view.repository.network.retrofit.ServiceCreator import moe.uni.view.repository.network.retrofit.ServiceCreator
import com.lsp.view.bean.YandPost import moe.uni.view.bean.YandPost
import retrofit2.Call import retrofit2.Call
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
@@ -1,6 +1,6 @@
package com.lsp.view.repository.network.retrofit package moe.uni.view.repository.network.retrofit
import com.lsp.view.bean.YandPost import moe.uni.view.bean.YandPost
import retrofit2.Call import retrofit2.Call
import retrofit2.http.GET import retrofit2.http.GET
import retrofit2.http.Query import retrofit2.http.Query
@@ -1,4 +1,4 @@
package com.lsp.view.repository.network.retrofit package moe.uni.view.repository.network.retrofit
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor import okhttp3.logging.HttpLoggingInterceptor
@@ -1,11 +1,11 @@
package com.lsp.view.service package moe.uni.view.service
import android.app.Service import android.app.Service
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.media.MediaScannerConnection import android.media.MediaScannerConnection
import android.os.* import android.os.*
import com.lsp.view.repository.network.retrofit.ServiceCreator import moe.uni.view.repository.network.retrofit.ServiceCreator
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Deferred import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@@ -1,4 +1,4 @@
package com.lsp.view.ui.compose package moe.uni.view.ui.compose
import android.content.ComponentName import android.content.ComponentName
import android.content.Intent import android.content.Intent
@@ -14,14 +14,14 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import com.lsp.view.ui.compose.theme.LspViewTheme import moe.uni.view.ui.compose.theme.LspViewTheme
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.compose.NavHost import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.rememberNavController
import com.lsp.view.service.DownloadService import moe.uni.view.service.DownloadService
import com.lsp.view.ui.compose.screen.DetailScreen import moe.uni.view.ui.compose.screen.DetailScreen
import com.lsp.view.ui.compose.screen.PostListScreen import moe.uni.view.ui.compose.screen.PostListScreen
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@@ -1,8 +1,8 @@
package com.lsp.view.ui.compose package moe.uni.view.ui.compose
import androidx.compose.runtime.MutableState import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import com.lsp.view.bean.Post import moe.uni.view.bean.Post
data class PostUiState( data class PostUiState(
var refresh:MutableState<Boolean> = mutableStateOf(true), var refresh:MutableState<Boolean> = mutableStateOf(true),
@@ -1,12 +1,12 @@
package com.lsp.view.ui.compose package moe.uni.view.ui.compose
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 com.lsp.view.bean.YandPost import moe.uni.view.bean.YandPost
import com.lsp.view.repository.exception.NetworkErrorException import moe.uni.view.repository.exception.NetworkErrorException
import com.lsp.view.repository.network.PostRepository import moe.uni.view.repository.network.PostRepository
import com.lsp.view.service.Result import moe.uni.view.service.Result
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
@@ -1,4 +1,4 @@
package com.lsp.view.ui.compose.screen package moe.uni.view.ui.compose.screen
import android.widget.Toast import android.widget.Toast
import androidx.compose.foundation.background import androidx.compose.foundation.background
@@ -8,6 +8,7 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.Share import androidx.compose.material.icons.filled.Share
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
@@ -29,10 +30,10 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.navigation.NavController import androidx.navigation.NavController
import coil.compose.AsyncImage import coil.compose.AsyncImage
import com.lsp.view.R import moe.uni.view.R
import com.lsp.view.common.setWallpaper import moe.uni.view.common.setWallpaper
import com.lsp.view.common.share import moe.uni.view.common.share
import com.lsp.view.ui.compose.PostViewModel import moe.uni.view.ui.compose.PostViewModel
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@@ -77,7 +78,7 @@ fun DetailScreen(navController: NavController, viewModel: PostViewModel) {
title = {}, title = {},
navigationIcon = { navigationIcon = {
IconButton(onClick = { navController.popBackStack() }) { IconButton(onClick = { navController.popBackStack() }) {
Icon(imageVector = Icons.Filled.ArrowBack, contentDescription = R.string.description_back.toString()) Icon(imageVector = Icons.AutoMirrored.Default.ArrowBack, contentDescription = R.string.description_back.toString())
} }
}, },
actions = { actions = {
@@ -1,4 +1,4 @@
package com.lsp.view.ui.compose.screen package moe.uni.view.ui.compose.screen
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.tween import androidx.compose.animation.core.tween
@@ -53,11 +53,11 @@ import androidx.compose.ui.unit.dp
import androidx.navigation.NavController import androidx.navigation.NavController
import coil.compose.SubcomposeAsyncImage import coil.compose.SubcomposeAsyncImage
import coil.compose.SubcomposeAsyncImageContent import coil.compose.SubcomposeAsyncImageContent
import com.lsp.view.bean.Post import moe.uni.view.bean.Post
import com.lsp.view.common.Config import moe.uni.view.common.Config
import com.lsp.view.ui.compose.NAV_ROUTE_DETAIL_SCREEN import moe.uni.view.ui.compose.NAV_ROUTE_DETAIL_SCREEN
import com.lsp.view.ui.compose.PostViewModel import moe.uni.view.ui.compose.PostViewModel
import com.lsp.view.ui.compose.widget.SearchBar import moe.uni.view.ui.compose.widget.SearchBar
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
@@ -1,4 +1,4 @@
package com.lsp.view.ui.compose.theme package moe.uni.view.ui.compose.theme
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
@@ -1,4 +1,4 @@
package com.lsp.view.ui.compose.theme package moe.uni.view.ui.compose.theme
import android.app.Activity import android.app.Activity
import android.os.Build import android.os.Build
@@ -1,4 +1,4 @@
package com.lsp.view.ui.compose.theme package moe.uni.view.ui.compose.theme
import androidx.compose.material3.Typography import androidx.compose.material3.Typography
import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.TextStyle
@@ -1,4 +1,4 @@
package com.lsp.view.ui.compose.widget package moe.uni.view.ui.compose.widget
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromYDelta="100%p" android:toYDelta="0%p"
android:duration="400"/>
</set>
@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromYDelta="0%p" android:toYDelta="100%p"
android:duration="400"/>
</set>
-31
View File
@@ -1,31 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
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=".ui.activity.MainActivity">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:defaultNavHost="true"
app:navGraph="@navigation/nav_graph" />
<com.google.android.material.bottomnavigation.BottomNavigationView
android:layout_gravity="bottom"
android:id="@+id/bottom_navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:menu="@menu/nav_menu" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.fragment.CollectFragment">
<include layout="@layout/post_list_layout"/>
</FrameLayout>
@@ -1,38 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".ui.fragment.ImageFragment">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.github.chrisbanes.photoview.PhotoView
android:id="@+id/image_content"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<com.google.android.material.bottomappbar.BottomAppBar
android:id="@+id/bottomAppBar"
style="@style/Widget.Material3.BottomAppBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
app:menu="@menu/image_bottom_menu" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/download_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_anchor="@id/bottomAppBar"
app:srcCompat="@drawable/baseline_file_download_24"
android:contentDescription="@string/description_download" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</FrameLayout>
-38
View File
@@ -1,38 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="@layout/post_list_layout"/>
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/app_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.search.SearchBar
android:id="@+id/search_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</com.google.android.material.appbar.AppBarLayout>
<com.google.android.material.search.SearchView
android:id="@+id/search_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_anchor="@id/search_bar">
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/search_tip"
android:visibility="gone"/>
</com.google.android.material.search.SearchView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</FrameLayout>
@@ -1,22 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
android:elevation="20dp"
android:layout_width="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
app:cardCornerRadius="4dp"
android:layout_margin="5dp"
style="?attr/materialCardViewElevatedStyle"
android:layout_height="wrap_content">
<FrameLayout
android:id="@+id/item_pic"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:id="@+id/image"/>
</FrameLayout>
</com.google.android.material.card.MaterialCardView>
@@ -1,17 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
android:id="@+id/swipeRefreshLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:ignore="MissingConstraints">
<androidx.recyclerview.widget.RecyclerView
app:layout_behavior="@string/appbar_scrolling_view_behavior"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/recyclerview"/>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
@@ -1,25 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical">
<com.google.android.material.appbar.AppBarLayout
android:fitsSystemWindows="true"
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize">
<androidx.appcompat.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:id="@+id/toolbar"
/>
</com.google.android.material.appbar.AppBarLayout>
<FrameLayout
android:id="@+id/settings"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
@@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:layout_margin="10dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/tag"/>
</com.google.android.material.card.MaterialCardView>
@@ -1,15 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:contentDescription="@string/description_share"
android:id="@+id/share_menu_btn"
android:title="@string/title_share"
android:icon="@drawable/ic_twotone_share_24"
app:showAsAction="ifRoom"/>
<item android:id="@+id/collect_menu_btn"
android:title="@string/title_collect"
android:icon="@drawable/ic_baseline_favorite_24"
app:showAsAction="ifRoom"/>
</menu>
-17
View File
@@ -1,17 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<group android:checkableBehavior="single">
<item
android:id="@+id/postListFragment"
android:icon="@drawable/ic_baseline_insert_photo_24"
android:title="@string/item_gallery"/>
<item
android:id="@+id/collectFragment"
android:icon="@drawable/ic_baseline_favorite_24"
android:title="@string/item_collect"/>
<item
android:id="@+id/settingsFragment"
android:icon="@drawable/baseline_settings_24"
android:title="@string/item_settings"/>
</group>
</menu>
-44
View File
@@ -1,44 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<navigation 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:id="@+id/nav_graph"
app:startDestination="@id/postListFragment">
<fragment
android:id="@+id/postListFragment"
android:name="com.lsp.view.ui.fragment.PostListFragment"
android:label="PostListFragment" >
<action
android:id="@+id/action_postListFragment_to_imageFragment"
app:destination="@id/imageFragment"
app:enterAnim="@anim/slide_on_bottom"
app:exitAnim="@anim/nav_default_exit_anim"
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
app:popExitAnim="@anim/slide_out_bottom" >
</action>
</fragment>
<fragment
android:id="@+id/imageFragment"
android:name="com.lsp.view.ui.fragment.ImageFragment"
android:label="fragment_pic"
tools:layout="@layout/fragment_image" >
</fragment>
<fragment
android:id="@+id/collectFragment"
android:name="com.lsp.view.ui.fragment.CollectFragment"
android:label="fragment_collect"
tools:layout="@layout/fragment_collect" >
<action
android:id="@+id/action_collectFragment_to_imageFragment"
app:destination="@id/imageFragment"
app:enterAnim="@anim/slide_on_bottom"
app:exitAnim="@anim/nav_default_exit_anim"
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
app:popExitAnim="@anim/slide_out_bottom"/>
</fragment>
<fragment
android:id="@+id/settingsFragment"
android:name="com.lsp.view.ui.fragment.SettingsFragment"
android:label="SettingsFragment" />
</navigation>
+2 -2
View File
@@ -6,9 +6,9 @@ buildscript {
mavenCentral() mavenCentral()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:8.3.1' classpath 'com.android.tools.build:gradle:8.3.2'
classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.10' classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.10'
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.7.5" classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.7.7"
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files // in the individual module build.gradle files
+1 -1
View File
@@ -1,2 +1,2 @@
include ':app' include ':app'
rootProject.name = "LspView" rootProject.name = "YandView"