refactor: To material3

This commit is contained in:
AnranYus
2023-11-06 23:04:03 +08:00
parent e3336b0458
commit b702e95f40
73 changed files with 1065 additions and 1487 deletions
+15 -2
View File
@@ -1,6 +1,9 @@
plugins { plugins {
id 'com.android.application' id 'com.android.application'
id 'kotlin-android' id 'kotlin-android'
id 'androidx.navigation.safeargs.kotlin'
id "com.google.devtools.ksp"
} }
android { android {
@@ -33,9 +36,17 @@ android {
} }
dependencies { dependencies {
def room_version = "2.5.0"
implementation "androidx.room:room-runtime:$room_version"
implementation "androidx.room:room-ktx:$room_version"
annotationProcessor "androidx.room:room-compiler:$room_version"
ksp "androidx.room:room-compiler:$room_version"
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("com.google.android.material:material:1.10.0")
implementation 'jp.wasabeef:recyclerview-animators:4.0.2' implementation 'jp.wasabeef:recyclerview-animators:4.0.2'
implementation "androidx.drawerlayout:drawerlayout:1.1.1"
implementation 'com.github.chrisbanes:PhotoView:2.3.0' implementation 'com.github.chrisbanes:PhotoView:2.3.0'
implementation 'com.google.android.flexbox:flexbox:3.0.0' implementation 'com.google.android.flexbox:flexbox:3.0.0'
implementation ('androidx.swiperefreshlayout:swiperefreshlayout:1.1.0') implementation ('androidx.swiperefreshlayout:swiperefreshlayout:1.1.0')
@@ -43,8 +54,10 @@ dependencies {
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 'com.github.bumptech.glide:glide:4.12.0'
implementation 'androidx.preference:preference-ktx:1.2.0' implementation 'androidx.preference:preference-ktx:1.2.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' annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib:1.9.0"
implementation 'androidx.core:core-ktx:1.9.0' implementation 'androidx.core:core-ktx:1.9.0'
implementation 'androidx.appcompat:appcompat:1.4.1' implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'androidx.constraintlayout:constraintlayout:2.1.3' implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
+2 -10
View File
@@ -23,12 +23,8 @@
android:resource="@xml/file_paths"> android:resource="@xml/file_paths">
</meta-data> </meta-data>
</provider> </provider>
<activity <activity
android:name=".activity.favtag.FavTagActivity" android:name=".activity.SettingsActivity"
android:exported="false" />
<activity
android:name=".activity.setting.SettingsActivity"
android:exported="false" android:exported="false"
android:label="@string/title_activity_settings" /> android:label="@string/title_activity_settings" />
@@ -36,12 +32,8 @@
android:name=".service.DownloadService" android:name=".service.DownloadService"
android:enabled="true" android:enabled="true"
android:exported="true" /> android:exported="true" />
<activity <activity
android:name=".activity.pic.PicActivity" android:name=".activity.MainActivity"
android:configChanges="uiMode"/>
<activity
android:name=".activity.main.MainActivity"
android:exported="true"> android:exported="true">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
@@ -9,11 +9,13 @@ import android.os.IBinder
import com.lsp.view.service.DownloadService.DownloadBinder import com.lsp.view.service.DownloadService.DownloadBinder
import com.google.android.material.color.DynamicColors import com.google.android.material.color.DynamicColors
import android.content.Intent import android.content.Intent
import android.util.Log
import com.lsp.view.service.DownloadService import com.lsp.view.service.DownloadService
import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import com.lsp.view.repository.PostRepository
class YandViewApplication : Application(), DefaultLifecycleObserver { class YandViewApplication : Application(), DefaultLifecycleObserver {
lateinit var downloadBinder:DownloadBinder
private val connection: ServiceConnection = object : ServiceConnection { private val connection: ServiceConnection = object : ServiceConnection {
override fun onServiceConnected(componentName: ComponentName, iBinder: IBinder) { override fun onServiceConnected(componentName: ComponentName, iBinder: IBinder) {
downloadBinder = iBinder as DownloadBinder downloadBinder = iBinder as DownloadBinder
@@ -21,14 +23,14 @@ class YandViewApplication : Application(), DefaultLifecycleObserver {
override fun onServiceDisconnected(componentName: ComponentName) {} override fun onServiceDisconnected(componentName: ComponentName) {}
} }
val repository = PostRepository()
override fun onCreate() { override fun onCreate() {
super<Application>.onCreate() super<Application>.onCreate()
context = applicationContext context = applicationContext
DynamicColors.applyToActivitiesIfAvailable(this)
val serviceIntent = Intent(this, DownloadService::class.java) val serviceIntent = Intent(this, DownloadService::class.java)
bindService(serviceIntent, connection, BIND_AUTO_CREATE) bindService(serviceIntent, connection, BIND_AUTO_CREATE)
DynamicColors.applyToActivitiesIfAvailable(this)
} }
override fun onDestroy(owner: LifecycleOwner) { override fun onDestroy(owner: LifecycleOwner) {
@@ -39,7 +41,7 @@ class YandViewApplication : Application(), DefaultLifecycleObserver {
companion object { companion object {
var context: Context? = null var context: Context? = null
private set private set
var downloadBinder: DownloadBinder? = null const val UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36"
private set
} }
} }
@@ -0,0 +1,66 @@
package com.lsp.view.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.model.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()
}
//接收来自PicActivity的快捷搜索Tag
val stringExtra = intent.getStringExtra("searchTag")
if (stringExtra!=null){
viewModel.uiState.value.nowSearchText.value = stringExtra
}
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,4 +1,4 @@
package com.lsp.view.activity.setting package com.lsp.view.activity
import android.os.Bundle import android.os.Bundle
import android.view.MenuItem import android.view.MenuItem
@@ -1,78 +0,0 @@
package com.lsp.view.activity.favtag
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.MenuItem
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar
import androidx.recyclerview.widget.RecyclerView
import com.google.android.flexbox.*
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import com.lsp.view.R
import com.lsp.view.activity.main.MainActivity
import com.lsp.view.repository.bean.Tags
class FavTagActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_fav_tag)
//Toolbar相关
val toolbar = findViewById<Toolbar>(R.id.toolbar)
setSupportActionBar(toolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.setHomeButtonEnabled(true)
val recyclerViewTag = findViewById<RecyclerView>(R.id.tag_recy)
recyclerViewTag.layoutManager = layoutManager()
val tagsArraySp = getSharedPreferences("tags_sp", Context.MODE_PRIVATE)
val array = tagsArraySp.getString("array", null)
var favTagList: ArrayList<Tags> = ArrayList()
if (array != null) {
//有收藏内容
val tagListType = object : TypeToken<ArrayList<Tags>>() {}.type
favTagList = Gson().fromJson(array, tagListType)
}
val favTagAdapter = FavTagAdapter(favTagList, this)
favTagAdapter.setOnItemClickListener(object : FavTagAdapter.OnItemClickListener {
override fun onItemClick(view: View, position: Int) {
val intent = Intent(this@FavTagActivity, MainActivity::class.java)
intent.putExtra("searchTag", favTagList[position].tag)
startActivity(intent)
}
})
recyclerViewTag.adapter = favTagAdapter
}
private fun layoutManager(): FlexboxLayoutManager {
val manager = object : FlexboxLayoutManager(this, FlexDirection.ROW, FlexWrap.WRAP) {
override fun canScrollVertically(): Boolean {
return false
}
}
manager.flexWrap = FlexWrap.WRAP
manager.flexDirection = FlexDirection.ROW
manager.alignItems = AlignItems.CENTER
manager.justifyContent = JustifyContent.FLEX_START
return manager
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
android.R.id.home -> {
finish()
}
}
return super.onOptionsItemSelected(item)
}
}
@@ -1,72 +0,0 @@
package com.lsp.view.activity.favtag
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import com.lsp.view.R
import com.lsp.view.repository.bean.Tags
class FavTagAdapter(val tagList: ArrayList<Tags>, val context: Context) :
RecyclerView.Adapter<FavTagAdapter.ViewHolder>() {
inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val fav_tag = view.findViewById<TextView>(R.id.fav_tag)
}
private lateinit var mOnItemClickListener: OnItemClickListener
interface OnItemClickListener {
fun onItemClick(view: View, position: Int)
}
fun setOnItemClickListener(mOnItemClickListener: OnItemClickListener) {
this.mOnItemClickListener = mOnItemClickListener
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view =
LayoutInflater.from(parent.context).inflate(R.layout.fav_tag_item_layout, parent, false)
val viewHolder = ViewHolder(view)
viewHolder.fav_tag.setOnLongClickListener {
val position = viewHolder.adapterPosition
removeData(position)
true
}
return viewHolder
}
fun removeData(position: Int) {
tagList.removeAt(position)
notifyItemRangeInserted(0, tagList.size)
val tagsArraySp = context.getSharedPreferences("tags_sp", Context.MODE_PRIVATE)
val tagsArrayJson = tagsArraySp.getString("array", null)
val tagListType = object : TypeToken<ArrayList<Tags>>() {}.type
val tagArray: ArrayList<Tags> = Gson().fromJson(tagsArrayJson, tagListType)
tagArray.removeAt(position)
tagsArraySp.edit().putString("array", Gson().toJson(tagArray)).apply()
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val tag = tagList[position].tag
holder.fav_tag.text = tag
holder.fav_tag.setOnClickListener {
mOnItemClickListener.onItemClick(it, position)
}
}
override fun getItemCount(): Int {
return tagList.size
}
}
@@ -1,130 +0,0 @@
package com.lsp.view.activity.main
import android.os.Bundle
import android.util.Log
import android.view.inputmethod.EditorInfo
import androidx.appcompat.app.AppCompatActivity
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.google.android.material.snackbar.Snackbar
import com.lsp.view.R
import com.lsp.view.YandViewApplication
import com.lsp.view.model.MainViewModel
class MainActivity : AppCompatActivity() {
private var shortAnnotationDuration: Int = 0
private var username: String? = ""
val TAG = javaClass.simpleName
private lateinit var viewModel: MainViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
viewModel = MainViewModel.provideFactory((application as YandViewApplication).repository, this,this).create(MainViewModel::class.java)
val adapter by lazy {
viewModel.fetchPostByRefresh()
PostAdapter(this)
}
//刷新
val refresh =
findViewById<androidx.swiperefreshlayout.widget.SwipeRefreshLayout>(R.id.swipeRefreshLayout)
viewModel.uiState.value.isRefreshing.observe(this) {
refresh.isRefreshing = it
}
viewModel.errorMessage.observe(this){
Snackbar.make(refresh,it,Snackbar.LENGTH_SHORT).show()
}
viewModel.postList.observe(this){
if (adapter.isAppendData){
adapter.appendDate(it)
}else{
adapter.pushNewData(it)
}
}
shortAnnotationDuration = resources.getInteger(android.R.integer.config_shortAnimTime)
val searchBar = findViewById<SearchBar>(R.id.search_bar)
val searchView = 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
}
//接收来自PicActivity的快捷搜索Tag
val stringExtra = intent.getStringExtra("searchTag")
if (stringExtra!=null){
viewModel.uiState.value.nowSearchText.value = stringExtra
}
viewModel.uiState.value.nowSearchText.observe(this){
searchView.setText(it)
searchBar.setText(it)
}
refresh.setOnRefreshListener {
viewModel.fetchPostByRefresh()
}
adapter.apply {
setLoadMoreListener(object :PostAdapter.OnScrollToBottom{
override fun event(position: Int) {
adapter.isAppendData = true//设置数据状态为追加数据
viewModel.fetchMore()
}
})
}
findViewById<RecyclerView>(R.id.recyclerview).apply {
val layoutManager = StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL)
this.layoutManager = layoutManager
this.adapter = adapter
}
supportActionBar?.let {
it.setDisplayHomeAsUpEnabled(true)
it.setHomeAsUpIndicator(R.drawable.ic_baseline_menu_24)
}
//侧边栏
val sp = getSharedPreferences("username", 0)
username = sp.getString("username", null)
}
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,62 +0,0 @@
package com.lsp.view.activity.pic
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.snackbar.Snackbar
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import com.lsp.view.R
import com.lsp.view.repository.bean.ID
import com.lsp.view.repository.bean.Tags
class IdAdapter(val idList: List<ID>,val context: Context) : RecyclerView.Adapter<IdAdapter.ViewHolder>() {
inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val tagText = view.findViewById<TextView>(R.id.tag)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view =
LayoutInflater.from(parent.context).inflate(R.layout.tag_item_layout, parent, false)
val viewHolder = ViewHolder(view)
viewHolder.tagText.setOnLongClickListener {
val position = viewHolder.adapterPosition
if (position == 0) {
//标题
return@setOnLongClickListener false
}
var tagsArray: ArrayList<Tags> = ArrayList()
val tagsArraySp = context.getSharedPreferences("tags_sp", Context.MODE_PRIVATE)
val tagsArrayJson = tagsArraySp.getString("array", null)
if (tagsArrayJson != null) {
val tagListType = object : TypeToken<ArrayList<Tags>>() {}.type
tagsArray = Gson().fromJson(tagsArrayJson, tagListType)
}
for (tag: Tags in tagsArray) {
if (tag.tag == viewHolder.tagText.text.toString()) {
Snackbar.make(viewHolder.tagText, "收藏过了哦", Snackbar.LENGTH_SHORT).show()
return@setOnLongClickListener true
}
}
tagsArray.add(Tags(viewHolder.tagText.text.toString()))
tagsArraySp.edit().putString("array", Gson().toJson(tagsArray)).apply()
Snackbar.make(viewHolder.tagText, "收藏了新标签", Snackbar.LENGTH_SHORT).show()
return@setOnLongClickListener true
}
return viewHolder
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val tag = idList[position]
holder.tagText.text = tag.id
}
override fun getItemCount(): Int {
return idList.size
}
}
@@ -1,297 +0,0 @@
package com.lsp.view.activity.pic
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.content.Context
import android.content.Intent
import android.graphics.drawable.Drawable
import android.os.*
import android.view.KeyEvent
import android.view.View
import android.widget.ImageView
import android.widget.LinearLayout
import androidx.activity.addCallback
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar
import com.bumptech.glide.Glide
import com.bumptech.glide.load.DataSource
import com.bumptech.glide.load.engine.GlideException
import com.bumptech.glide.load.model.GlideUrl
import com.bumptech.glide.load.model.LazyHeaders
import com.bumptech.glide.request.RequestListener
import com.bumptech.glide.request.target.Target
import com.github.chrisbanes.photoview.PhotoView
import com.google.android.flexbox.*
import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.google.android.material.snackbar.Snackbar
import com.lsp.view.R
import com.lsp.view.activity.main.MainActivity
import com.lsp.view.repository.bean.ID
import com.lsp.view.repository.bean.Size
import com.lsp.view.repository.bean.Tags
import com.lsp.view.util.Util
import kotlin.properties.Delegates
class PicActivity : AppCompatActivity() {
private val idList = ArrayList<ID>()
private val sizeList = ArrayList<Size>()
private lateinit var image: ImageView
private lateinit var photoView: PhotoView
private var shortAnnotationDuration by Delegates.notNull<Int>()
private var md5 : String?= null
companion object{
fun actionStartActivity(context: Context,id:String,sample_url:String,file_url:String,tags:String,
file_ext:String,file_size:String,md5:String,sample_height:Int,sample_width:Int){
val intent=Intent(context,PicActivity::class.java)
intent.putExtra("id", id)
intent.putExtra("sample_url", sample_url)
intent.putExtra("file_url", file_url)
intent.putExtra("tags", tags)
intent.putExtra("file_ext", file_ext)
intent.putExtra("file_size", file_size)
intent.putExtra("md5", md5)
intent.putExtra("sample_height",sample_height)
intent.putExtra("sample_width",sample_width)
context.startActivity(intent)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_pic)
val toolbar = findViewById<Toolbar>(R.id.toolbar)
setSupportActionBar(toolbar)
supportActionBar?.setDisplayShowTitleEnabled(false)
image = findViewById(R.id.titleImage)
val sample_height = intent.getIntExtra("sample_height",-1)
val sample_width = intent.getIntExtra("sample_width",-1)
if (sample_width > sample_height){
image.scaleType = ImageView.ScaleType.CENTER_INSIDE
}
shortAnnotationDuration = resources.getInteger(android.R.integer.config_shortAnimTime)
val intent = intent
val tags = intent.getStringExtra("tags")
if (tags != null) {
loadTags(tags, "tags")
}
val file_size = intent.getStringExtra("file_size")
if (file_size != null) {
loadTags(file_size, "size")
}
md5 = intent.getStringExtra("md5")
val id = intent.getStringExtra("id")
if (id != null) {
loadTags(id, "id")
}
val sample_url = intent.getStringExtra("sample_url")
sample_url?.let {
loadPic(it)
}
val file_url = intent.getStringExtra("file_url")
val file_ext = intent.getStringExtra("file_ext")
val download =
findViewById<FloatingActionButton>(
R.id.download
)
photoView = findViewById(R.id.photoView)
image.setOnClickListener {
photoView.apply {
alpha = 0f
visibility = View.VISIBLE
animate()
.alpha(1f)
.setDuration(shortAnnotationDuration.toLong())
.setListener(null)
}
Glide.with(this).load(sample_url).into(photoView)
}
photoView.setOnClickListener {
photoView.animate()
.alpha(0f)
.setDuration(shortAnnotationDuration.toLong())
.setListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
photoView.visibility = View.GONE
}
})
}
val share = findViewById<FloatingActionButton>(R.id.share)
share.setOnClickListener {
if (sample_url != null) {
Util.share(sample_url,this)
}
}
download.setOnClickListener {
Util.download(file_url, file_ext, md5)
}
val f_btn = findViewById<FloatingActionButton>(R.id.float_btn)
val ctrl = findViewById<LinearLayout>(R.id.ctrl)
f_btn.setOnClickListener {
if (ctrl.visibility == View.VISIBLE)
ctrl.visibility = View.GONE
else
ctrl.visibility = View.VISIBLE
}
val back = findViewById<ImageView>(R.id.back)
back.setOnClickListener {
finish()
}
}
override fun dispatchKeyEvent(event: KeyEvent?): Boolean {
onBackPressedDispatcher.addCallback{
if (photoView.visibility != View.GONE) {
photoView.animate()
.alpha(0f)
.setDuration(shortAnnotationDuration.toLong())
.setListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
photoView.visibility = View.GONE
}
})
}else{
finish()
}
}
return super.dispatchKeyEvent(event)
}
private fun loadPic(url: String) {
val glideUrl = GlideUrl(
url,
LazyHeaders.Builder().addHeader(
"User-Agent",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.93 Safari/537.36"
).build()
)
Glide.with(this).load(glideUrl).listener(object : RequestListener<Drawable?> {
override fun onLoadFailed(
e: GlideException?,
model: Any?,
target: Target<Drawable?>?,
isFirstResource: Boolean
): Boolean {
e?.printStackTrace()
val back = findViewById<ImageView>(R.id.back)
Snackbar.make(back, R.string.toast_load_fail, Snackbar.LENGTH_LONG).setAction(R.string.button_check) {
AlertDialog.Builder(this@PicActivity).apply {
setTitle("Log")
if (e != null) {
setMessage(e.stackTraceToString())
}
setNegativeButton(R.string.button_ok, null)
create()
show()
}
}.show()
return false
}
override fun onResourceReady(
resource: Drawable?,
model: Any?,
target: Target<Drawable?>?,
dataSource: DataSource?,
isFirstResource: Boolean
): Boolean {
return false
}
}).into(image)
}
private fun layoutManager(): FlexboxLayoutManager {
val manager = object : FlexboxLayoutManager(this, FlexDirection.ROW, FlexWrap.WRAP) {
override fun canScrollVertically(): Boolean {
return false
}
}
manager.alignItems = AlignItems.CENTER
manager.justifyContent = JustifyContent.FLEX_START
return manager
}
//设置tags列表
//这写的很烂 得改
private fun loadTags(tags: String, type: String) {
val tagList = ArrayList<Tags>()
when (type) {
"tags" -> {
tagList.add(Tags("Tag"))
val list = tags.split(" ")
val tagRecyclerView =
findViewById<androidx.recyclerview.widget.RecyclerView>(R.id.tagRecyclerView)
tagRecyclerView.layoutManager = layoutManager()
for (tag in list) run {
tagList.add(Tags(tag))
}
val adapter = TagAdapter(tagList, this)
adapter.setOnItemClickListener(object : TagAdapter.OnItemClickListener {
override fun onItemClick(view: View, position: Int) {
val intent = Intent(this@PicActivity, MainActivity::class.java)
intent.putExtra("searchTag", tagList[position].tag)
startActivity(intent)
}
})
tagRecyclerView.adapter = adapter
}
"id" -> {
idList.add(ID("ID"))
idList.add(ID(tags))
val idRecyclerView =
findViewById<androidx.recyclerview.widget.RecyclerView>(R.id.idRecyclerView)
idRecyclerView.layoutManager = layoutManager()
val adapter = IdAdapter(idList,this)
idRecyclerView.adapter = adapter
}
"size" -> {
sizeList.add(Size("Size"))
sizeList.add(Size(tags))
val sizeRecyclerView =
findViewById<androidx.recyclerview.widget.RecyclerView>(R.id.sizeRecyclerView)
sizeRecyclerView.layoutManager = layoutManager()
val adapter = SizeAdapter(sizeList)
sizeRecyclerView.adapter = adapter
}
}
}
}
@@ -1,40 +0,0 @@
package com.lsp.view.activity.pic
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
import com.lsp.view.repository.bean.Size
import java.math.RoundingMode
import java.text.DecimalFormat
class SizeAdapter(val tagList: List<Size>) : RecyclerView.Adapter<SizeAdapter.ViewHolder>() {
inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val tagText = view.findViewById<TextView>(R.id.tag)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view =
LayoutInflater.from(parent.context).inflate(R.layout.tag_item_layout, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val tag = tagList[position]
if (position == 0) {
holder.tagText.text = tag.file_size
} else {
val sizeKb = tag.file_size.toFloat() / 1024
val format = DecimalFormat("0.##")
format.roundingMode = RoundingMode.FLOOR
val size = format.format(sizeKb)
holder.tagText.text = "$size KB"
}
}
override fun getItemCount(): Int {
return tagList.size
}
}
@@ -1,76 +0,0 @@
package com.lsp.view.activity.pic
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.snackbar.Snackbar
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import com.lsp.view.R
import com.lsp.view.repository.bean.Tags
class TagAdapter(val tagList: List<Tags>, val context: Context) :
RecyclerView.Adapter<TagAdapter.ViewHolder>() {
inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val tagText = view.findViewById<TextView>(R.id.tag)
}
private lateinit var mOnItemClickListener: OnItemClickListener
interface OnItemClickListener {
fun onItemClick(view: View, position: Int)
}
fun setOnItemClickListener(mOnItemClickListener: OnItemClickListener) {
this.mOnItemClickListener = mOnItemClickListener
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view =
LayoutInflater.from(parent.context).inflate(R.layout.tag_item_layout, parent, false)
val viewHolder = ViewHolder(view)
viewHolder.tagText.setOnLongClickListener {
val position = viewHolder.adapterPosition
if (position == 0) {
//标题
return@setOnLongClickListener false
}
var tagsArray: ArrayList<Tags> = ArrayList()
val tagsArraySp = context.getSharedPreferences("tags_sp", Context.MODE_PRIVATE)
val tagsArrayJson = tagsArraySp.getString("array", null)
if (tagsArrayJson != null) {
val tagListType = object : TypeToken<ArrayList<Tags>>() {}.type
tagsArray = Gson().fromJson(tagsArrayJson, tagListType)
}
for (tag: Tags in tagsArray) {
if (tag.tag == viewHolder.tagText.text.toString()) {
Snackbar.make(viewHolder.tagText, R.string.toast_tag_exist, Snackbar.LENGTH_SHORT).show()
return@setOnLongClickListener true
}
}
tagsArray.add(Tags(viewHolder.tagText.text.toString()))
tagsArraySp.edit().putString("array", Gson().toJson(tagsArray)).apply()
Snackbar.make(viewHolder.tagText, R.string.toast_tag_add_fav, Snackbar.LENGTH_SHORT).show()
return@setOnLongClickListener true
}
return viewHolder
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val tag = tagList[position]
holder.tagText.text = tag.tag
holder.tagText.setOnClickListener {
mOnItemClickListener.onItemClick(it, position)
}
}
override fun getItemCount(): Int {
return tagList.size
}
}
@@ -0,0 +1,49 @@
package com.lsp.view.bean
import android.os.Parcel
import android.os.Parcelable
open class Post(
open val postId: String,
open val rating: String,
open val sampleUrl:String,
open val fileUrl:String,
open val sampleHeight: Int,
open val sampleWidth: Int,
): Parcelable {
constructor(parcel: Parcel) : this(
parcel.readString()!!,
parcel.readString()!!,
parcel.readString()!!,
parcel.readString()!!,
parcel.readInt(),
parcel.readInt()
)
override fun describeContents(): Int {
return 0
}
override fun writeToParcel(dest: Parcel, flags: Int) {
dest.writeString(postId)
dest.writeString(rating)
dest.writeString(sampleUrl)
dest.writeString(fileUrl)
dest.writeInt(sampleHeight)
dest.writeInt(sampleWidth)
}
companion object {
@JvmField
val CREATOR = object:Parcelable.Creator<Post> {
override fun createFromParcel(parcel: Parcel): Post {
return Post(parcel)
}
override fun newArray(size: Int): Array<Post?> {
return arrayOfNulls(size)
}
}
}
}
@@ -0,0 +1,14 @@
package com.lsp.view.bean
import com.google.gson.annotations.SerializedName
class YandPost(
@SerializedName("file_url") override val fileUrl: String,
@SerializedName("sample_url") override val sampleUrl: String,
@SerializedName("sample_height") override val sampleHeight : Int,
@SerializedName("sample_width") override val sampleWidth: Int,
@SerializedName("file_ext") val fileExt: String,
@SerializedName("file_size") val fileSize: String,
@SerializedName("id") override val postId: String,
rating: String
):Post(postId, rating, sampleUrl,fileUrl,sampleHeight,sampleWidth)
@@ -0,0 +1,88 @@
package com.lsp.view.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.fragment.adapter.PostAdapter
import com.lsp.view.R
import com.lsp.view.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) {
val bundle = Bundle()
bundle.putParcelable("post",post)
Navigation.findNavController(view).navigate(R.id.action_collectFragment_to_imageFragment,bundle)
val slideOutAnimation = AnimationUtils.loadAnimation(context,
R.anim.slide_out_bottom
)
activityContext.bottomNav.startAnimation(slideOutAnimation)
activityContext.bottomNav.visibility = View.GONE
}
})
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
}
}
}
@@ -0,0 +1,206 @@
package com.lsp.view.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.widget.ImageView
import androidx.core.content.FileProvider
import androidx.lifecycle.lifecycleScope
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.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){
lifecycleScope.launch(Dispatchers.IO) {
activityContext.viewModel.postNewToast("Start download")
val result = (activityContext.application as YandViewApplication).downloadBinder.downloadImage(fileUrl)
if (result.isSuccess){
activityContext.viewModel.postNewToast("Download successfully")
}else{
val exception = result.exceptionOrNull()
activityContext.viewModel.postNewToast(exception?.message.toString())
}
}
}
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)
}
}
}
@@ -0,0 +1,113 @@
package com.lsp.view.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.fragment.adapter.PostAdapter
import com.lsp.view.R
import com.lsp.view.activity.MainActivity
import com.lsp.view.bean.Post
import com.lsp.view.model.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) {
val bundle = Bundle()
bundle.putParcelable("post",post)
Navigation.findNavController(view).navigate(R.id.action_postListFragment_to_imageFragment,bundle)
val slideOutAnimation = AnimationUtils.loadAnimation(context,
R.anim.slide_out_bottom
)
activityContext.bottomNav.startAnimation(slideOutAnimation)
activityContext.bottomNav.visibility = View.GONE
}
})
}
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,4 +1,4 @@
package com.lsp.view.activity.main package com.lsp.view.fragment.adapter
import android.content.Context import android.content.Context
import android.view.LayoutInflater import android.view.LayoutInflater
@@ -10,34 +10,51 @@ import com.bumptech.glide.Glide
import com.bumptech.glide.load.model.GlideUrl import com.bumptech.glide.load.model.GlideUrl
import com.bumptech.glide.load.model.LazyHeaders import com.bumptech.glide.load.model.LazyHeaders
import com.lsp.view.R import com.lsp.view.R
import com.lsp.view.activity.pic.PicActivity import com.lsp.view.YandViewApplication
import com.lsp.view.repository.bean.YandPost import com.lsp.view.bean.Post
class PostAdapter(val context: Context,private val postList: ArrayList<YandPost> = ArrayList()): class PostAdapter(val context: Context,private val postList: ArrayList<Post> = ArrayList()):
RecyclerView.Adapter<PostAdapter.ViewHolder>() { RecyclerView.Adapter<PostAdapter.ViewHolder>() {
val TAG = this::class.java.simpleName val TAG = this::class.java.simpleName
private val UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36" private var inBottom:Boolean = false//当前列表是否在底部
var isAppendData = false//数据追加标志 @Volatile private var isProcess:Boolean = false
inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) { inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val picImage: ImageView = view.findViewById(R.id.picImgae) val picImage: ImageView = view.findViewById(R.id.image)
} }
fun pushNewData(list: List<YandPost>) { fun pushNewData(list: List<Post>) {
val oldSize = postList.size if (!isProcess) {
postList.clear() isProcess = true
notifyItemRangeRemoved(0,oldSize) if (inBottom) {
postList.addAll(list) //在底部,则追加数据
notifyItemRangeInserted(0, list.size) 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)
}
}
isProcess = false
inBottom = false//加入新数据后不在底部,若仍在底部说明无更多数据,也不必处理
} }
fun appendDate(list: List<YandPost>){
val pos = postList.size private lateinit var mOnListItemClick: OnListItemClick
postList.addAll(list) interface OnListItemClick {
notifyItemRangeInserted(pos-1, list.size) fun setOnListItemClick(post:Post)
isAppendData = false }
fun setOnListItemClick(mOnListItemClick: OnListItemClick) {
this.mOnListItemClick = mOnListItemClick
} }
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
@@ -46,27 +63,7 @@ class PostAdapter(val context: Context,private val postList: ArrayList<YandPost>
viewHolder.picImage.setOnClickListener { viewHolder.picImage.setOnClickListener {
val position = viewHolder.adapterPosition val position = viewHolder.adapterPosition
// Log.w("position", position.toString()) mOnListItemClick.setOnListItemClick(postList[position])
// Log.w("url", postList.value?.get(position)?.sample_url)
// Log.w("rating", postList[position].rating)
var file_ext = postList[position].file_ext
if (file_ext == null){
val strarr = postList[position].sample_url.split(".")
file_ext = strarr[strarr.lastIndex]
}
PicActivity.actionStartActivity(
context,
postList[position].id,
postList[position].sample_url,
postList[position].file_url,
postList[position].tags,
file_ext,
postList[position].file_size,
postList[position].md5,
postList[position].sample_height,
postList[position].sample_width)
} }
@@ -93,10 +90,10 @@ class PostAdapter(val context: Context,private val postList: ArrayList<YandPost>
} }
for(index in p..last){ for(index in p..last){
if (p>6) { if (p>6) {
val source: String = postList[index].sample_url val source: String = postList[index].sampleUrl
val glideUrl = GlideUrl( val glideUrl = GlideUrl(
source, source,
LazyHeaders.Builder().addHeader("User-Agent", UA) LazyHeaders.Builder().addHeader("User-Agent", YandViewApplication.UA)
.build() .build()
) )
Glide.with(context).download(glideUrl).preload() Glide.with(context).download(glideUrl).preload()
@@ -112,10 +109,10 @@ class PostAdapter(val context: Context,private val postList: ArrayList<YandPost>
val post = postList[position] val post = postList[position]
val source: String = post.sample_url val source: String = post.sampleUrl
val height = postList[position].sample_height val height = postList[position].sampleHeight
val width = postList[position].sample_width val width = postList[position].sampleWidth
//图片过长则缩减高度以保证显示完整 //图片过长则缩减高度以保证显示完整
if (width > height){ if (width > height){
@@ -127,22 +124,24 @@ class PostAdapter(val context: Context,private val postList: ArrayList<YandPost>
val glideUrl = GlideUrl( val glideUrl = GlideUrl(
source, source,
LazyHeaders.Builder().addHeader("User-Agent", UA) LazyHeaders.Builder().addHeader("User-Agent", YandViewApplication.UA)
.build() .build()
) )
Glide.with(context).load(glideUrl).into(holder.picImage) Glide.with(context).load(glideUrl).into(holder.picImage)
if (position == postList.size - 1 && postList.size > 6) { if (position == postList.size - 1 && postList.size > 6 ) {
//到达底部 //到达底部
mScrollToBottom.event(position) inBottom = true
if (this::mScrollToBottom.isInitialized) {
mScrollToBottom.event(position)
}
} }
} }
override fun getItemCount(): Int { override fun getItemCount(): Int {
val size = postList.size return postList.size
return size
} }
@@ -8,33 +8,51 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.lsp.view.activity.main.PostAdapter import com.lsp.view.fragment.adapter.PostAdapter
import com.lsp.view.repository.PostRepository import com.lsp.view.repository.network.PostRepository
import androidx.savedstate.SavedStateRegistryOwner import androidx.savedstate.SavedStateRegistryOwner
import com.lsp.view.repository.bean.Post import com.lsp.view.YandViewApplication
import com.lsp.view.repository.bean.YandPost 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 com.lsp.view.repository.exception.NetworkErrorException
import com.lsp.view.ui.UiState
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.async
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
class MainViewModel(private val repository: PostRepository,context: Context):ViewModel() { class MainViewModel(private val repository: PostRepository, context: Context):ViewModel() {
private val _uiState = MutableStateFlow(UiState()) private val _uiState = MutableStateFlow(UiState())
val uiState:StateFlow<UiState> val uiState:StateFlow<UiState>
get() = _uiState.asStateFlow() get() = _uiState.asStateFlow()
private var fetchJob: Job? = null private var fetchJob: Job? = null
private val TAG = this::class.java.simpleName private val TAG = this::class.java.simpleName
private val _errorMessage = MutableLiveData<String>() private val _toastMessage = MutableLiveData<String>()
val errorMessage val toastMessage
get() = _errorMessage as LiveData<String> get() = _toastMessage as LiveData<String>
private val _postList = MutableLiveData<ArrayList<YandPost>>() private val _postList = MutableLiveData<ArrayList<YandPost>>()
val postList get() = _postList as LiveData<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
companion object { companion object {
fun provideFactory( fun provideFactory(
@@ -76,7 +94,7 @@ class MainViewModel(private val repository: PostRepository,context: Context):Vie
} }
private fun fetchNewPost(append:Boolean = false){ private fun fetchNewPost(){
fetchJob?.cancel() fetchJob?.cancel()
fetchJob = viewModelScope.launch(Dispatchers.IO) { fetchJob = viewModelScope.launch(Dispatchers.IO) {
_uiState.value.isRefreshing.postValue(true) _uiState.value.isRefreshing.postValue(true)
@@ -88,7 +106,7 @@ class MainViewModel(private val repository: PostRepository,context: Context):Vie
)) ))
}catch (e:NetworkErrorException){ }catch (e:NetworkErrorException){
_errorMessage.postValue(e.message.toString()) postNewToast(e.message.toString())
} }
_uiState.value.isRefreshing.postValue(false) _uiState.value.isRefreshing.postValue(false)
@@ -118,7 +136,7 @@ class MainViewModel(private val repository: PostRepository,context: Context):Vie
fun fetchMore(){ fun fetchMore(){
_uiState.value.nowPage++ _uiState.value.nowPage++
fetchNewPost(true) fetchNewPost()
} }
fun updateSafeMode(mode:Boolean){ fun updateSafeMode(mode:Boolean){
@@ -132,4 +150,40 @@ class MainViewModel(private val repository: PostRepository,context: Context):Vie
_uiState.value.nowSourceName.postValue(source) _uiState.value.nowSourceName.postValue(source)
fetchPostByRefresh() 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,3 +0,0 @@
package com.lsp.view.repository.bean
class Author(val author: String)
@@ -1,3 +0,0 @@
package com.lsp.view.repository.bean
class ID(val id: String)
@@ -1,7 +0,0 @@
package com.lsp.view.repository.bean
open class Post(
val id: String,
val rating: String,
val author: String,
)
@@ -1,3 +0,0 @@
package com.lsp.view.repository.bean
class Size(val file_size: String)
@@ -1,3 +0,0 @@
package com.lsp.view.repository.bean
class Tags(val tag: String)
@@ -1,16 +0,0 @@
package com.lsp.view.repository.bean
class YandPost(
val preview_url: String,
val file_url: String,
val sample_url: String,
val tags: String,
val file_ext: String,
val file_size: String,
val sample_height : Int,
val sample_width: Int,
id: String,
rating: String,
author: String,
val md5 : String
):Post(id, rating, author)
@@ -0,0 +1,24 @@
package com.lsp.view.repository.datasource
import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import com.lsp.view.repository.datasource.dao.CollectDao
import com.lsp.view.repository.datasource.model.Collect
@Database(entities = [Collect::class], version = 1,exportSchema = false)
abstract class CollectDatabase: RoomDatabase(){
abstract fun collectDao():CollectDao
companion object{
@Volatile
private var Instance:CollectDatabase? = null
fun getDatabase(context: Context):CollectDatabase{
return Instance?: synchronized(this) {
Room.databaseBuilder(context,CollectDatabase::class.java,"collect_database").build().also { Instance = it }
}
}
}
}
@@ -0,0 +1,12 @@
package com.lsp.view.repository.datasource
import com.lsp.view.repository.datasource.model.Collect
interface CollectRepository {
suspend fun addNewCollect(collect: Collect)
suspend fun getAllCollect():List<Collect>
suspend fun getCollectByPostId(postId:String,type:String):Collect?
suspend fun removeCollect(collect: Collect)
}
@@ -0,0 +1,23 @@
package com.lsp.view.repository.datasource
import com.lsp.view.repository.datasource.dao.CollectDao
import com.lsp.view.repository.datasource.model.Collect
class CollectRepositoryImpl(private val collectDao:CollectDao):CollectRepository {
override suspend fun addNewCollect(collect: Collect) {
collectDao.insert(collect)
}
override suspend fun getAllCollect(): List<Collect> {
return collectDao.getAllCollect()
}
override suspend fun getCollectByPostId(postId: String,type:String): Collect? {
return collectDao.getCollectByPostId(postId,type)
}
override suspend fun removeCollect(collect: Collect) {
collectDao.delete(collect.postId,collect.type)
}
}
@@ -0,0 +1,19 @@
package com.lsp.view.repository.datasource.dao
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import com.lsp.view.repository.datasource.model.Collect
@Dao
interface CollectDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(collect:Collect)
@Query("select * from collect")
suspend fun getAllCollect():List<Collect>
@Query("delete from collect where post_id = :postId and type = :type")
suspend fun delete(postId: String,type: String)
@Query("select * from collect where post_id = :postId and type = :type")
suspend fun getCollectByPostId(postId:String,type:String):Collect?
}
@@ -0,0 +1,35 @@
package com.lsp.view.repository.datasource.model
import android.content.Context
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import com.lsp.view.YandViewApplication
import com.lsp.view.bean.Post
@Entity(tableName = "collect")
data class Collect(
@PrimaryKey(autoGenerate = true) var id:Int = 0,
@ColumnInfo(name = "post_id")override val postId: String,
@ColumnInfo(name = "sample_url")override val sampleUrl:String,
@ColumnInfo(name = "rating")override val rating:String,
@ColumnInfo(name = "file_url")override val fileUrl:String,
@ColumnInfo(name = "sampleHeight") override val sampleHeight:Int,
@ColumnInfo(name = "sampleWeight") override val sampleWidth:Int,
@ColumnInfo(name = "type")val type:String
):Post(postId,rating,sampleUrl,fileUrl,sampleHeight,sampleWidth){
constructor(
postId: String,
sampleUrl:String,
rating:String,
fileUrl:String,
sampleHeight:Int,
sampleWeight:Int,
): this(0, postId, sampleUrl, rating, fileUrl, sampleHeight, sampleWeight,
YandViewApplication.context?.getSharedPreferences("com.lsp.view_preferences",Context.MODE_PRIVATE)?.getString("source_name", "yande.re")!!)
companion object{
fun Post.toCollect():Collect = Collect(this.postId,this.sampleUrl,this.rating,this.fileUrl,this.sampleHeight,this.sampleWidth )
}
}
@@ -1,11 +1,11 @@
package com.lsp.view.repository package com.lsp.view.repository.network
import com.lsp.view.R import com.lsp.view.R
import com.lsp.view.YandViewApplication import com.lsp.view.YandViewApplication
import com.lsp.view.repository.exception.UnableConstructObjectException import com.lsp.view.repository.exception.UnableConstructObjectException
import com.lsp.view.repository.api.PostApi import com.lsp.view.repository.network.api.PostApi
import com.lsp.view.repository.api.PostApiImpl import com.lsp.view.repository.network.api.PostApiImpl
import com.lsp.view.repository.bean.YandPost import com.lsp.view.bean.YandPost
class PostDataSource { class PostDataSource {
private val api: PostApi = PostApiImpl() private val api: PostApi = PostApiImpl()
@@ -25,7 +25,7 @@ data class Load(
lateinit var source:String lateinit var source:String
companion object{ companion object{
fun Builder(tags: String?, page: Int, safe:Boolean):Load{ fun Builder(tags: String?, page: Int, safe:Boolean): Load {
val sourceNameArray = YandViewApplication.context!!.resources.getStringArray(R.array.pic_source) val sourceNameArray = YandViewApplication.context!!.resources.getStringArray(R.array.pic_source)
val sourceUrlArray = YandViewApplication.context!!.resources.getStringArray(R.array.url_source) val sourceUrlArray = YandViewApplication.context!!.resources.getStringArray(R.array.url_source)
val configSp = YandViewApplication.context?.getSharedPreferences("com.lsp.view_preferences", 0) val configSp = YandViewApplication.context?.getSharedPreferences("com.lsp.view_preferences", 0)
@@ -1,6 +1,6 @@
package com.lsp.view.repository package com.lsp.view.repository.network
import com.lsp.view.repository.bean.YandPost import com.lsp.view.bean.YandPost
class PostRepository { class PostRepository {
@@ -1,8 +1,8 @@
package com.lsp.view.repository.api package com.lsp.view.repository.network.api
import com.lsp.view.repository.Load import com.lsp.view.repository.network.Load
import com.lsp.view.repository.bean.Post import com.lsp.view.bean.Post
import com.lsp.view.repository.bean.YandPost import com.lsp.view.bean.YandPost
import com.lsp.view.repository.exception.NetworkErrorException import com.lsp.view.repository.exception.NetworkErrorException
import retrofit2.Call import retrofit2.Call
@@ -35,5 +35,5 @@ interface PostApi {
} }
} }
suspend fun fetchNewPost(load:Load):ArrayList<YandPost> suspend fun fetchNewPost(load: Load):ArrayList<YandPost>
} }
@@ -1,9 +1,9 @@
package com.lsp.view.repository.api package com.lsp.view.repository.network.api
import com.lsp.view.repository.Load import com.lsp.view.repository.network.Load
import com.lsp.view.retrofit.PostService import com.lsp.view.repository.network.retrofit.PostService
import com.lsp.view.retrofit.ServiceCreator import com.lsp.view.repository.network.retrofit.ServiceCreator
import com.lsp.view.repository.bean.YandPost import com.lsp.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.retrofit package com.lsp.view.repository.network.retrofit
import com.lsp.view.repository.bean.YandPost import com.lsp.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.retrofit package com.lsp.view.repository.network.retrofit
import retrofit2.Retrofit import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory import retrofit2.converter.gson.GsonConverterFactory
@@ -1,146 +1,75 @@
package com.lsp.view.service package com.lsp.view.service
import android.accounts.NetworkErrorException
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.util.Util
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import java.io.File import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream import java.io.FileOutputStream
import java.security.MessageDigest import java.io.Serializable
import kotlin.concurrent.thread
class DownloadService : Service() { class DownloadService : Service() {
private val mBinder = DownloadBinder(this) private val mBinder = DownloadBinder(this)
class DownloadBinder(val context: Context) : Binder() { class DownloadBinder(val context: Context) : Binder() {
fun callBack(handler: Handler,status:Int){ fun downloadImage(fileUrl: String):Result<Serializable> {
val msg = Message.obtain()
msg.obj = status
handler.sendMessage(msg)
} val fileDir =
fun downloadPic(file_url: String, end: String,handler: Handler,md5 : String?) { File("${Environment.getExternalStorageDirectory()}/${Environment.DIRECTORY_PICTURES}/LspMake/")
if (!fileDir.exists()) {
thread { fileDir.mkdirs()
val FileD =
File("${Environment.getExternalStorageDirectory()}/${Environment.DIRECTORY_PICTURES}/LspMake/")
if (FileD.exists()) {
val file =
File("${Environment.getExternalStorageDirectory()}/${Environment.DIRECTORY_PICTURES}/LspMake/$md5.$end")
if (file.exists()){
callBack(handler,Util.FILE_EXISTS)
return@thread
}
val fos = FileOutputStream(file)
try {
val client = OkHttpClient()
val request = Request.Builder()
.url(file_url)
.build()
val response = client.newCall(request).execute()
val responseData = response.body()?.bytes()
if (response.code() == 200) {
fos.write(responseData)
//通知媒体更新
MediaScannerConnection.scanFile(
context, arrayOf(file.path),
null, null
)
if (md5 == getFileMD5(file.path)) {
callBack(handler, Util.DOWNLOAD_SUCCESS)
} else {
file.delete()
callBack(handler,Util.MD5_COMPARE_ERROR)
}
return@thread
} else {
file.delete()
callBack(handler, Util.NETWORK_ERROR)
return@thread
}
} catch (e: Exception) {
e.printStackTrace()
callBack(handler, Util.DOWNLOAD_ERROR)
return@thread
} finally {
fos.close()
}
} else {
FileD.mkdirs()
downloadPic(file_url, end, handler, md5)
}
callBack(handler,Util.DOWNLOAD_ERROR)
} }
} val split = fileUrl.split("/")
val fileName = split[split.size - 1]
/** val file =
* 获取文件MD5 File("${Environment.getExternalStorageDirectory()}/${Environment.DIRECTORY_PICTURES}/LspMake/$fileName")
*/ if (file.exists()) {
fun getFileMD5(path: String?): String? { return Result.failure<Exception>(Exception("File exists"))
if (path.isNullOrEmpty()) {
return null
} }
var digest: MessageDigest? = null val fos = FileOutputStream(file)
var fileIS: FileInputStream? = null
val buffer = ByteArray(1024)
var len = 0
try { try {
digest = MessageDigest.getInstance("MD5") val client = OkHttpClient()
val oldF = File(path) val request = Request.Builder()
fileIS = FileInputStream(oldF) .url(fileUrl)
while (fileIS.read(buffer).also { len = it } != -1) { .build()
digest.update(buffer, 0, len) val response = client.newCall(request).execute()
val responseData = response.body()?.bytes()
return if (response.code() == 200) {
fos.write(responseData)
//通知媒体更新
MediaScannerConnection.scanFile(
context, arrayOf(file.path),
null, null
)
Result.success("File download successfully")
} else {
file.delete()
Result.failure<Exception>(NetworkErrorException("Connection timed out"))
} }
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() Result.failure<Exception>(e)
return null } finally {
}finally { fos.close()
fileIS?.close()
}
return bytesToHexString(digest?.digest())
}
fun bytesToHexString(src: ByteArray?): String? {
val result = StringBuilder("")
if (src?.isEmpty()==true) {
return null
} }
src?.forEach { return Result.failure(Exception("Unknown error"))
var i = it.toInt()
//这里需要对b与0xff做位与运算,
//若b为负数,强制转换将高位位扩展,导致错误,
//故需要高位清零
val hexStr = Integer.toHexString(i and 0xff)
//若转换后的十六进制数字只有一位,
//则在前补"0"
if (hexStr.length == 1) {
result.append(0)
}
result.append(hexStr)
}
return result.toString()
} }
} }
override fun onBind(intent: Intent): IBinder { override fun onBind(intent: Intent): IBinder {
return mBinder return mBinder
} }
}
}
@@ -1,4 +1,4 @@
package com.lsp.view.model package com.lsp.view.ui
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
@@ -9,8 +9,4 @@ data class UiState(
val nowSourceName: MutableLiveData<String> = MutableLiveData(), val nowSourceName: MutableLiveData<String> = MutableLiveData(),
val isSafe: Boolean = true, //安全模式 val isSafe: Boolean = true, //安全模式
var nowPage:Int = 1, var nowPage:Int = 1,
){ )
fun switchShowSearchBar(){
showSearchBar = !showSearchBar
}
}
-114
View File
@@ -1,114 +0,0 @@
package com.lsp.view.util
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.Handler
import android.os.Looper
import android.os.Message
import android.widget.Toast
import androidx.core.content.FileProvider
import com.bumptech.glide.Glide
import com.lsp.view.R
import com.lsp.view.YandViewApplication
import java.io.File
import kotlin.concurrent.thread
object Util {
const val NETWORK_ERROR: Int = 0
const val DOWNLOAD_SUCCESS = 1
const val DOWNLOAD_ERROR = 2
const val MD5_COMPARE_ERROR = 3
const val FILE_EXISTS = 4
fun share(url:String,context: Context){
thread {
val bitmap = Glide.with(context).asBitmap().load(url).submit().get()
val path = File("${context.cacheDir}/image")
if (!path.exists())
path.mkdirs()
val file = File("${context.cacheDir}/image/cache.png")
if (file.exists()){
file.delete()
}
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)
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)
}
}
fun download(file_url: String?, file_ext: String?,md5: String?){
Toast.makeText(YandViewApplication.context, R.string.toast_download_start, Toast.LENGTH_SHORT).show()
if (file_url != null) {
if (file_ext != null) {
YandViewApplication.downloadBinder?.downloadPic(file_url, file_ext,handler,md5)
}
}
}
private val handler = object : Handler(Looper.myLooper()!!){
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
when(msg.obj){
DOWNLOAD_SUCCESS ->{
Toast.makeText(YandViewApplication.context, R.string.toast_download_success, Toast.LENGTH_SHORT).show()
}
DOWNLOAD_ERROR -> {
Toast.makeText(YandViewApplication.context, R.string.toast_download_fail, Toast.LENGTH_SHORT).show()
}
MD5_COMPARE_ERROR -> {
Toast.makeText(YandViewApplication.context, R.string.toast_compar_md5_fail, Toast.LENGTH_SHORT).show()
}
FILE_EXISTS -> {
Toast.makeText(YandViewApplication.context, R.string.toast_file_exist, Toast.LENGTH_SHORT).show()
}
}
}
}
}
@@ -0,0 +1,5 @@
<?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>
@@ -0,0 +1,5 @@
<?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>
@@ -1,5 +1,5 @@
<vector android:height="24dp" android:tint="?attr/colorPrimary" <vector android:height="24dp" android:tint="#000000"
android:viewportHeight="24" android:viewportWidth="24" android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/> <path android:fillColor="@android:color/white" android:pathData="M19,9h-4V3H9v6H5l7,7 7,-7zM5,18v2h14v-2H5z"/>
</vector> </vector>
-6
View File
@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#fff"/>
<corners android:radius="5dp"/>
</shape>
@@ -1,5 +0,0 @@
<vector android:autoMirrored="true" android:height="24dp"
android:tint="?attr/colorPrimary" 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="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z"/>
</vector>
@@ -1,10 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
</vector>
@@ -1,4 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF" <vector android:height="24dp"
android:tint="?attr/colorControlNormal"
android:viewportHeight="24" android:viewportWidth="24" android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M12,21.35l-1.45,-1.32C5.4,15.36 2,12.28 2,8.5 2,5.42 4.42,3 7.5,3c1.74,0 3.41,0.81 4.5,2.09C13.09,3.81 14.76,3 16.5,3 19.58,3 22,5.42 22,8.5c0,3.78 -3.4,6.86 -8.55,11.54L12,21.35z"/> <path android:fillColor="@android:color/white" android:pathData="M12,21.35l-1.45,-1.32C5.4,15.36 2,12.28 2,8.5 2,5.42 4.42,3 7.5,3c1.74,0 3.41,0.81 4.5,2.09C13.09,3.81 14.76,3 16.5,3 19.58,3 22,5.42 22,8.5c0,3.78 -3.4,6.86 -8.55,11.54L12,21.35z"/>
@@ -1,5 +0,0 @@
<vector android:height="24dp" android:tint="#FFFFFF"
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="M17.63,5.84C17.27,5.33 16.67,5 16,5L5,5.01C3.9,5.01 3,5.9 3,7v10c0,1.1 0.9,1.99 2,1.99L16,19c0.67,0 1.27,-0.33 1.63,-0.84L22,12l-4.37,-6.16z"/>
</vector>
@@ -1,4 +1,4 @@
<vector android:height="24dp" android:tint="?attr/colorPrimary" <vector android:height="24dp" android:tint="#000000"
android:viewportHeight="24" android:viewportWidth="24" android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M3,18h18v-2L3,16v2zM3,13h18v-2L3,11v2zM3,6v2h18L21,6L3,6z"/> <path android:fillColor="@android:color/white" android:pathData="M3,18h18v-2L3,16v2zM3,13h18v-2L3,11v2zM3,6v2h18L21,6L3,6z"/>
@@ -1,10 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z"/>
</vector>
@@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="?attr/colorControlNormal"
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="M16.5,3c-1.74,0 -3.41,0.81 -4.5,2.09C10.91,3.81 9.24,3 7.5,3 4.42,3 2,5.42 2,8.5c0,3.78 3.4,6.86 8.55,11.54L12,21.35l1.45,-1.32C18.6,15.36 22,12.28 22,8.5 22,5.42 19.58,3 16.5,3zM12.1,18.55l-0.1,0.1 -0.1,-0.1C7.14,14.24 4,11.39 4,8.5 4,6.5 5.5,5 7.5,5c1.54,0 3.04,0.99 3.57,2.36h1.87C13.46,5.99 14.96,5 16.5,5c2,0 3.5,1.5 3.5,3.5 0,2.89 -3.14,5.74 -7.9,10.05z"/>
</vector>
-13
View File
@@ -1,13 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="?attr/colorAccent"/>
<corners android:radius="5dp"/>
<padding android:left="10dp"
android:right="10dp"
android:top="10dp"
android:bottom="10dp"/>
</shape>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

-13
View File
@@ -1,13 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="?attr/colorPrimary"/>
<corners android:radius="5dp"/>
<padding android:left="10dp"
android:right="10dp"
android:top="10dp"
android:bottom="10dp"/>
</shape>
@@ -1,31 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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=".activity.favtag.FavTagActivity"
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"
app:title="@string/title_activity_favouriteTag"
/>
</com.google.android.material.appbar.AppBarLayout>
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/tag_recy"/>
</LinearLayout>
+16 -30
View File
@@ -5,41 +5,27 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
tools:context=".activity.main.MainActivity"> tools:context=".activity.MainActivity">
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout <androidx.fragment.app.FragmentContainerView
app:layout_behavior="@string/appbar_scrolling_view_behavior" android:id="@+id/nav_host_fragment"
android:id="@+id/swipeRefreshLayout" android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<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>
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/app_bar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="match_parent"
<com.google.android.material.search.SearchBar app:layout_constraintLeft_toLeftOf="parent"
android:id="@+id/search_bar" app:layout_constraintRight_toRightOf="parent"
android:layout_width="match_parent" app:layout_constraintTop_toTopOf="parent"
android:layout_height="wrap_content" /> app:layout_constraintBottom_toBottomOf="parent"
</com.google.android.material.appbar.AppBarLayout> app:defaultNavHost="true"
app:navGraph="@navigation/nav_graph" />
<com.google.android.material.search.SearchView
android:id="@+id/search_view" <com.google.android.material.bottomnavigation.BottomNavigationView
android:layout_gravity="bottom"
android:id="@+id/bottom_navigation"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:layout_anchor="@id/search_bar"> app:menu="@menu/nav_menu" />
<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> </androidx.coordinatorlayout.widget.CoordinatorLayout>
-165
View File
@@ -1,165 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:android="http://schemas.android.com/apk/res/android" >
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:fitsSystemWindows="true"
android:layout_height="match_parent"
tools:context=".activity.pic.PicActivity">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:fitsSystemWindows="true"
android:layout_height="wrap_content"
android:background="?android:colorBackground"
android:id="@+id/appbar">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:id="@+id/ctl"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<androidx.appcompat.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:id="@+id/toolbar"/>
<ImageView
app:layout_collapseMode="parallax"
android:fitsSystemWindows="true"
android:id="@+id/titleImage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="centerCrop"
android:contentDescription="@string/appbar_scrolling_view_behavior"/>
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<com.google.android.material.card.MaterialCardView
style="?attr/materialCardViewStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="15dp"
android:layout_marginLeft="15dp"
android:layout_marginRight="15dp"
android:layout_marginTop="5dp"
app:cardCornerRadius="10dp">
<LinearLayout
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<androidx.recyclerview.widget.RecyclerView
android:layout_marginStart="5dp"
android:layout_marginBottom="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/idRecyclerView"/>
<androidx.recyclerview.widget.RecyclerView
android:layout_marginStart="5dp"
android:layout_marginBottom="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/tagRecyclerView"/>
<androidx.recyclerview.widget.RecyclerView
android:layout_marginStart="5dp"
android:layout_marginBottom="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/authorRecyclerView"/>
<androidx.recyclerview.widget.RecyclerView
android:layout_marginStart="5dp"
android:layout_marginBottom="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/sizeRecyclerView"/>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
<LinearLayout
android:id="@+id/float_line"
android:layout_width="wrap_content"
android:orientation="vertical"
android:gravity="center_vertical"
android:layout_margin="15dp"
android:layout_gravity="end|bottom"
android:layout_height="wrap_content">
<LinearLayout
android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/ctrl"
android:orientation="vertical">
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:layout_width="wrap_content"
android:elevation="5dp"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:src="@drawable/ic_twotone_arrow_downward_24"
android:id="@+id/download"/>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:elevation="5dp"
android:layout_margin="10dp"
android:src="@drawable/ic_twotone_share_24"
android:id="@+id/share"/>
</LinearLayout>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:layout_width="80dp"
android:layout_height="80dp"
app:fabCustomSize="80dp"
android:layout_margin="10dp"
android:src="@drawable/ic_baseline_add_24"
android:elevation="5dp"
android:id="@+id/float_btn"/>
</LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<com.github.chrisbanes.photoview.PhotoView
android:background="#000"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"
android:id="@+id/photoView"/>
<ImageView
android:layout_margin="30dp"
android:layout_width="30dp"
android:layout_height="30dp"
android:id="@+id/back"
android:src="@drawable/ic_baseline_arrow_back_24"
android:layout_gravity="top|start"
/>
</FrameLayout>
@@ -1,14 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView
style="?attr/materialCardViewFilledStyle"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="wrap_content"
android:layout_width="wrap_content">
<TextView
android:id="@+id/fav_tag"
android:layout_margin="5dp"
android:padding="5dp"
android:textSize="20sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</com.google.android.material.card.MaterialCardView>
@@ -0,0 +1,10 @@
<?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=".fragment.CollectFragment">
<include layout="@layout/post_list_layout"/>
</FrameLayout>
@@ -0,0 +1,39 @@
<?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=".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
@@ -0,0 +1,38 @@
<?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 -1
View File
@@ -15,7 +15,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:scaleType="centerCrop" android:scaleType="centerCrop"
android:id="@+id/picImgae"/> android:id="@+id/image"/>
</FrameLayout> </FrameLayout>
-8
View File
@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/nav_header"
android:layout_width="match_parent"
android:layout_height="30dp">
</FrameLayout>
@@ -0,0 +1,17 @@
<?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,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/text1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="marquee"
android:singleLine="true"
android:textAlignment="inherit"
android:textSize="20sp"/>
@@ -1,19 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView
android:id="@+id/card"
android:layout_margin="5dp"
style="?attr/materialCardViewFilledStyle"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="wrap_content"
android:layout_width="wrap_content">
<TextView
android:id="@+id/tag"
android:padding="5dp"
android:textSize="20sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</com.google.android.material.card.MaterialCardView>
@@ -0,0 +1,14 @@
<?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>
+7 -7
View File
@@ -2,16 +2,16 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"> <menu xmlns:android="http://schemas.android.com/apk/res/android">
<group android:checkableBehavior="single"> <group android:checkableBehavior="single">
<item <item
android:id="@+id/photo" android:id="@+id/postListFragment"
android:icon="@drawable/ic_baseline_insert_photo_24" android:icon="@drawable/ic_baseline_insert_photo_24"
android:title="@string/item_gallery"/> android:title="@string/item_gallery"/>
<item <item
android:id="@+id/taglist" android:id="@+id/collectFragment"
android:icon="@drawable/ic_baseline_label_24" android:icon="@drawable/ic_baseline_favorite_24"
android:title="@string/item_collect"/> android:title="@string/item_collect"/>
<item <!-- <item-->
android:id="@+id/setting" <!-- android:id="@+id/setting"-->
android:icon="@drawable/setting_icon" <!-- android:icon="@drawable/setting_icon"-->
android:title="@string/item_settings"/> <!-- android:title="@string/item_settings"/>-->
</group> </group>
</menu> </menu>
-10
View File
@@ -1,10 +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:id="@+id/search_nav"
android:icon="@drawable/ic_baseline_search_24"
app:showAsAction="always"
android:title="@string/item_search" />
</menu>
+43
View File
@@ -0,0 +1,43 @@
<?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.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>
<action
android:id="@+id/action_postListFragment_to_collectFragment"
app:destination="@id/collectFragment" />
</fragment>
<fragment
android:id="@+id/imageFragment"
android:name="com.lsp.view.fragment.ImageFragment"
android:label="fragment_pic"
tools:layout="@layout/fragment_image" >
</fragment>
<fragment
android:id="@+id/collectFragment"
android:name="com.lsp.view.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>
</navigation>
+1 -2
View File
@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<string name="title_activity_settings">设置</string> <string name="title_activity_settings">设置</string>
<string name="title_activity_favouriteTag">收藏标签</string>
<string name="item_gallery">画廊</string> <string name="item_gallery">画廊</string>
<string name="item_collect">收藏</string> <string name="item_collect">收藏</string>
<string name="item_settings">设置</string> <string name="item_settings">设置</string>
@@ -24,8 +23,8 @@
<string name="toast_tag_exist">收藏过了哦</string> <string name="toast_tag_exist">收藏过了哦</string>
<string name="toast_download_success">下载成功</string> <string name="toast_download_success">下载成功</string>
<string name="toast_download_fail">下载失败</string> <string name="toast_download_fail">下载失败</string>
<string name="toast_compar_md5_fail">MD5对比失败</string>
<string name="toast_file_exist">文件已经存在</string> <string name="toast_file_exist">文件已经存在</string>
<string name="toast_download_start">开始下载</string> <string name="toast_download_start">开始下载</string>
<string name="title_share">分享</string> <string name="title_share">分享</string>
<string name="title_collect">收藏</string>
</resources> </resources>
+1 -2
View File
@@ -2,7 +2,6 @@
<string name="app_name" translatable="false">YandView</string> <string name="app_name" translatable="false">YandView</string>
<string name="title_activity_settings">SettingsActivity</string> <string name="title_activity_settings">SettingsActivity</string>
<string name="title_activity_settings_backup" translatable="false">SettingsActivity_backup</string> <string name="title_activity_settings_backup" translatable="false">SettingsActivity_backup</string>
<string name="title_activity_favouriteTag">FavouriteTag</string>
<string name="item_gallery">Gallery</string> <string name="item_gallery">Gallery</string>
<string name="item_collect">Collect</string> <string name="item_collect">Collect</string>
<string name="item_settings">Settings</string> <string name="item_settings">Settings</string>
@@ -25,8 +24,8 @@
<string name="toast_tag_exist">This tag exist</string> <string name="toast_tag_exist">This tag exist</string>
<string name="toast_download_success">Successfully download</string> <string name="toast_download_success">Successfully download</string>
<string name="toast_download_fail">Download failed</string> <string name="toast_download_fail">Download failed</string>
<string name="toast_compar_md5_fail">MD5 comparison failed</string>
<string name="toast_file_exist">File exists</string> <string name="toast_file_exist">File exists</string>
<string name="toast_download_start">Start download</string> <string name="toast_download_start">Start download</string>
<string name="title_share">Share</string> <string name="title_share">Share</string>
<string name="title_collect">Collect</string>
</resources> </resources>
+5
View File
@@ -8,12 +8,17 @@ buildscript {
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:8.1.1' classpath 'com.android.tools.build:gradle:8.1.1'
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"
// 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
} }
} }
plugins {
id 'com.google.devtools.ksp' version '1.9.20-1.0.14' apply false
}
allprojects { allprojects {
repositories { repositories {
maven { url "https://www.jitpack.io" } maven { url "https://www.jitpack.io" }