21 Commits

Author SHA1 Message Date
i 0f0f257f11 Update LICENSE
Android CI / build (push) Failing after 13m14s
2026-01-29 14:16:57 +00:00
AnranYus 25046d22a8 refactor:Use viewModel 2023-10-20 15:27:14 +08:00
AnranYus bbd4c3b7d1 style: rename 2023-10-19 17:14:29 +08:00
AnranYus d2c903ca41 style: move function 2023-10-19 17:06:20 +08:00
AnranYus 816eaa7ecf style: Adaptive adjustment 2023-10-19 16:55:21 +08:00
anrannn 1c48892cb4 Use new authority id 2022-12-05 00:45:17 +08:00
anrannn 0637278a17 Modify! Unified naming format 2022-11-05 23:13:33 +08:00
anrannn 415139d67d Modify! Move the preferences config from com.lsper.view_preferences to com.lsp.view_preferences 2022-11-05 23:07:42 +08:00
anrannn c13215a6dd Modify!Remove duplicate path judgment 2022-11-05 23:04:03 +08:00
anrannn c82509b320 Modify!drop callback param 2022-11-05 22:52:14 +08:00
anrannn ad5ef05c60 Use new applicationId 2022-11-03 17:19:39 +08:00
anrannn 8736940b9f Adapt icon from color 2022-11-02 19:03:43 +08:00
anrannn e925b14b47 Drop unused resource. 2022-11-02 18:20:14 +08:00
anrannn f28d36f0f4 Drop!Author: Vague information is used 2022-11-02 17:30:35 +08:00
anrannn 3998658726 Modify!Layout: Modify sample image layout 2022-11-02 17:25:02 +08:00
anrannn 0c9c3958fb Drop!Login: Temporarily unneeded features. 2022-11-02 15:47:58 +08:00
anrannn 7a3b7947fb Update! 2022-10-23 19:06:45 +08:00
anrannn 58f39bd610 Adapt SDK 33 2022-10-23 18:48:33 +08:00
anrannn fe605d0f89 Delete variable: searchTag, new logic in search 2022-10-23 18:41:01 +08:00
anrannn 1870bc71c3 New search bar 2022-10-23 13:55:48 +08:00
anrannn 3756900873 Revert "Use dynamic action bar"
This reverts commit e13cff7b70.
2022-10-22 17:42:42 +08:00
52 changed files with 580 additions and 682 deletions
+1
View File
@@ -19,3 +19,4 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
test
+5 -4
View File
@@ -8,7 +8,7 @@ android {
buildToolsVersion "30.0.3"
defaultConfig {
applicationId "com.lsper.view"
applicationId "com.lsp.view"
minSdkVersion 29
targetSdkVersion 33
versionCode 221022
@@ -24,12 +24,13 @@ android {
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = '1.8'
}
namespace 'com.lsp.view'
}
dependencies {
@@ -45,7 +46,7 @@ dependencies {
implementation 'androidx.preference:preference-ktx:1.2.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.core:core-ktx:1.9.0'
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
testImplementation 'junit:junit:4.13.2'
+2 -4
View File
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.lsp.view">
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
@@ -15,7 +14,7 @@
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.lsper.view.fileprovider"
android:authorities="com.lsp.view.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
@@ -37,7 +36,6 @@
android:enabled="true"
android:exported="true" />
<activity android:name=".activity.login.LoginActivity" />
<activity
android:name=".activity.pic.PicActivity"
android:configChanges="uiMode"/>
@@ -11,6 +11,7 @@ import com.google.android.material.color.DynamicColors
import android.content.Intent
import com.lsp.view.service.DownloadService
import androidx.lifecycle.LifecycleOwner
import com.lsp.view.repository.PostRepository
class YandViewApplication : Application(), DefaultLifecycleObserver {
private val connection: ServiceConnection = object : ServiceConnection {
@@ -20,6 +21,7 @@ class YandViewApplication : Application(), DefaultLifecycleObserver {
override fun onServiceDisconnected(componentName: ComponentName) {}
}
val repository = PostRepository()
override fun onCreate() {
super<Application>.onCreate()
@@ -6,6 +6,7 @@ import android.os.Bundle
import android.view.WindowManager
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat
open class BaseActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
@@ -25,8 +26,8 @@ open class BaseActivity : AppCompatActivity() {
window.statusBarColor = Color.TRANSPARENT
window.navigationBarColor = Color.TRANSPARENT
//状态栏反色
val controller = ViewCompat.getWindowInsetsController(window.decorView)
controller?.isAppearanceLightStatusBars = !isDarkMode()
WindowCompat.getInsetsController(window,window.decorView).isAppearanceLightStatusBars = !isDarkMode()
}
/**
@@ -15,7 +15,7 @@ import com.lsp.view.YandViewApplication
import com.lsp.view.R
import com.lsp.view.activity.BaseActivity
import com.lsp.view.activity.main.MainActivity
import com.lsp.view.bean.Tags
import com.lsp.view.repository.bean.Tags
class FavTagActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
@@ -9,7 +9,7 @@ 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.bean.Tags
import com.lsp.view.repository.bean.Tags
class FavTagAdapter(val tagList: ArrayList<Tags>, val context: Context) :
RecyclerView.Adapter<FavTagAdapter.ViewHolder>() {
@@ -1,57 +0,0 @@
package com.lsp.view.activity.login
import android.content.Intent
import android.os.Bundle
import android.widget.EditText
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.lsp.view.R
import com.lsp.view.activity.main.MainActivity
import okhttp3.*
import java.util.concurrent.TimeUnit
import kotlin.concurrent.thread
class LoginActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_login)
val login = findViewById<FloatingActionButton>(R.id.login)
login.setOnClickListener {
val username = findViewById<EditText>(R.id.username)
val password = findViewById<EditText>(R.id.password)
val sPassword = "choujin-steiner--${password.text}--"
val hash1Password = StrToHash1.shaEncrypt(sPassword)
thread {
conn(username.text.toString(), hash1Password)
}
val intent = Intent(this, MainActivity::class.java)
startActivity(intent)
}
}
fun conn(username: String, hash1: String): String {
val cookies = ArrayList<Cookie>()
val client = OkHttpClient.Builder()
.connectTimeout(60, TimeUnit.SECONDS)
.cookieJar(object : CookieJar {
override fun saveFromResponse(url: HttpUrl, cookies: MutableList<Cookie>) {
}
override fun loadForRequest(url: HttpUrl): MutableList<Cookie> {
return cookies
}
})
.build()
val request = Request.Builder()
.url("https://yande.re/?username=$username&password=$hash1")
.build()
return client.newCall(request).execute().body()!!.string()
}
}
@@ -1,36 +0,0 @@
package com.lsp.view.activity.login;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class StrToHash1 {
public static String shaEncrypt(String strSrc) {
MessageDigest md = null;
String strDes = null;
byte[] bt = strSrc.getBytes();
try {
md = MessageDigest.getInstance("SHA-1");
md.update(bt);
strDes = bytes2Hex(md.digest());
} catch (NoSuchAlgorithmException e) {
return null;
}
return strDes;
}
private static String bytes2Hex(byte[] bts) {
StringBuilder des = new StringBuilder();
String tmp = null;
for (byte bt : bts) {
tmp = Integer.toHexString(bt & 0xFF);
if (tmp.length() == 1) {
des.append("0");
}
des.append(tmp);
}
return des.toString();
}
}
@@ -4,146 +4,96 @@ import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.content.Intent
import android.os.*
import android.util.Log
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.view.inputmethod.EditorInfo
import android.widget.EditText
import android.widget.LinearLayout
import androidx.activity.viewModels
import androidx.appcompat.widget.Toolbar
import androidx.core.view.GravityCompat
import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat.Type.ime
import androidx.drawerlayout.widget.DrawerLayout
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.StaggeredGridLayoutManager
import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.navigation.NavigationView
import com.google.android.material.snackbar.Snackbar
import com.lsp.view.YandViewApplication
import com.lsp.view.R
import com.lsp.view.activity.BaseActivity
import com.lsp.view.activity.favtag.FavTagActivity
import com.lsp.view.activity.model.MainActivityModelImpl
import com.lsp.view.activity.setting.SettingsActivity
import com.lsp.view.bean.Post_yand
import com.lsp.view.util.CallBackStatus
import com.lsp.view.model.MainViewModel
class MainActivity : BaseActivity() {
private var searchTag: String? = null
private lateinit var search: EditText
private lateinit var searchBar: LinearLayout
private var shortAnnotationDuration: Int = 0
private var nowPage = 1
private val adapter: PostAdapter = PostAdapter(this, ArrayList())
private var username: String? = ""
private lateinit var sourceUrlArray: Array<String>
private lateinit var sourceNameArray: Array<String>
private var nowSourceName: String? = null
val TAG = javaClass.simpleName
private lateinit var layoutManager: RecyclerView.LayoutManager
private var safeMode: Boolean = true //安全模式
private lateinit var recyclerView: RecyclerView
private var barShow = false
private var tags: String? = ""
private val ISREFRESH = 1
private val ISADDDATA = 0
private lateinit var toolbar:Toolbar
private lateinit var viewModel: MainViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val toolbar = findViewById<Toolbar>(R.id.toolbar)
viewModel = MainViewModel.provideFactory((application as YandViewApplication).repository, this,this).create(MainViewModel::class.java)
toolbar = findViewById(R.id.toolbar)
setSupportActionBar(toolbar)
val appbar = findViewById<AppBarLayout>(R.id.appbar)
val nowHeight = appbar.layoutParams.height
appbar.layoutParams.height = (application as YandViewApplication).statusBarHeight()+nowHeight
supportActionBar?.setDisplayShowTitleEnabled(false)
//刷新
val refresh =
findViewById<androidx.swiperefreshlayout.widget.SwipeRefreshLayout>(R.id.swipeRefreshLayout)
//到达底部,加载更多数据
adapter.setLoadMoreListener(object : PostAdapter.OnLoadMoreListener {
//重写接口
override fun loadMore(position: Int) {
loadData(tags,++nowPage,ISADDDATA)
}
})
//初始化数据
sourceNameArray = resources.getStringArray(R.array.pic_source)
sourceUrlArray = resources.getStringArray(R.array.url_source)
val configSp = getSharedPreferences("com.lsper.view_preferences", 0)
if (configSp.getString("sourceName", null) == null) {
configSp.edit().putString("sourceName", "yande.re").apply()
configSp.edit().putString("type", "0").apply()
viewModel.uiState.value.isRefreshing.observe(this) {
refresh.isRefreshing = it
}
nowSourceName = configSp.getString("sourceName","yande.re")
safeMode = configSp.getBoolean("safe_mode",true)
refresh.setOnRefreshListener {
viewModel.fetchPostByRefresh()
}
viewModel.adapter.apply {
setLoadMoreListener(object :PostAdapter.OnScrollToBottom{
override fun event(position: Int) {
viewModel.appendPost()
}
//横屏逻辑
layoutManager = StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL)
recyclerView = findViewById(R.id.recyclerview)
recyclerView.layoutManager = layoutManager
recyclerView.adapter = adapter
//收藏Tag
val swipeRefreshLayout =
findViewById<androidx.swiperefreshlayout.widget.SwipeRefreshLayout>(
R.id.swipeRefreshLayout
)
val drawerLayout = findViewById<DrawerLayout>(R.id.drawerLayout)
})
}
findViewById<RecyclerView>(R.id.recyclerview).apply {
val layoutManager = StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL)
this.layoutManager = layoutManager
this.adapter = viewModel.adapter
}
search = findViewById(R.id.search)
//快捷搜索tag 来自PicActivity
searchTag = intent.getStringExtra("searchTag")
if (searchTag != null) {
//tag按钮搜索
this.search.setText(searchTag)
searchAction(searchTag)
} else {
//初次启动
loadData( tags,1,ISADDDATA)
}
val close = findViewById<View>(R.id.close)
supportActionBar?.let {
it.setDisplayHomeAsUpEnabled(true)
it.setHomeAsUpIndicator(R.drawable.ic_baseline_menu_24)
}
searchBar = findViewById(R.id.search_bar)
shortAnnotationDuration = resources.getInteger(android.R.integer.config_shortAnimTime)
close.setOnClickListener {
hiddenSearchBar()
hideIm()
}
search.setOnEditorActionListener { v, actionId, event ->
search.setOnEditorActionListener { _, actionId, _ ->
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
searchAction(search.text.toString())
searchTag = search.text.toString()
hiddenSearchBar()
viewModel.fetchNewPostBySearch(search.text.toString())
}
return@setOnEditorActionListener false
}
//刷新
swipeRefreshLayout.setOnRefreshListener {
nowPage = 1
loadData( searchTag, nowPage, ISREFRESH)
}
//侧边栏
val sp = getSharedPreferences("username", 0)
@@ -155,12 +105,12 @@ class MainActivity : BaseActivity() {
nav.setCheckedItem(R.id.photo)
//设置侧边栏点击逻辑
nav.setNavigationItemSelectedListener {
val drawerLayout = findViewById<DrawerLayout>(R.id.drawerLayout)
when (it.itemId) {
//画廊
R.id.photo -> {
loadData( null, 1, ISREFRESH)
viewModel.fetchPostByRefresh()
drawerLayout.closeDrawers()
searchTag = ""
true
}
//设置
@@ -186,38 +136,42 @@ class MainActivity : BaseActivity() {
override fun onResume() {
super.onResume()
//加载源改变
val configSp = getSharedPreferences("com.lsper.view_preferences", 0)
if (nowSourceName != configSp.getString("sourceName",null)){
loadData(tags,1,ISREFRESH)
nowSourceName = configSp.getString("sourceName",null)
val configSp = getSharedPreferences("com.lsp.view_preferences", 0)
if (nowSourceName != configSp.getString("source_name",null)){
viewModel.fetchPostByRefresh()
nowSourceName = configSp.getString("source_name",null)
}
if (safeMode != configSp.getBoolean("safe_mode",true)){
loadData(tags,1,ISREFRESH)
viewModel.fetchPostByRefresh()
safeMode = configSp.getBoolean("safe_mode",true)
}
search.setText("")
}
//隐藏搜索栏
private fun hiddenSearchBar() {
searchBar.animate()
supportActionBar?.setHomeAsUpIndicator(R.drawable.ic_baseline_menu_24)
search.animate()
.alpha(0f)
.setDuration(shortAnnotationDuration.toLong())
.setListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
searchBar.visibility = View.GONE
search.visibility = View.GONE
}
})
hideIm()
barShow = false
viewModel.uiState.value.switchShowSearchBar()
}
//现实搜索栏
private fun showSearchBar() {
searchBar.apply {
supportActionBar?.setHomeAsUpIndicator(R.drawable.ic_baseline_arrow_back_24)
search.apply {
alpha = 0f
visibility = View.VISIBLE
animate()
@@ -225,25 +179,27 @@ class MainActivity : BaseActivity() {
.setDuration(shortAnnotationDuration.toLong())
.setListener(null)
}
barShow = true
viewModel.uiState.value.switchShowSearchBar()
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
android.R.id.home -> {
val drawerLayout = findViewById<DrawerLayout>(R.id.drawerLayout)
drawerLayout.openDrawer(GravityCompat.START)
if (viewModel.uiState.value.showSearchBar){
hiddenSearchBar()
}else {
val drawerLayout = findViewById<DrawerLayout>(R.id.drawerLayout)
drawerLayout.openDrawer(GravityCompat.START)
}
}
R.id.search_nav -> {
if (barShow) {
searchTag = search.text.toString()
searchAction(searchTag)
hiddenSearchBar()
if (viewModel.uiState.value.showSearchBar) {
viewModel.fetchNewPostBySearch(search.text.toString())
hideIm()
}
if (searchBar.visibility == View.GONE) {
if (search.visibility == View.GONE) {
showSearchBar()
}
}
@@ -253,82 +209,9 @@ class MainActivity : BaseActivity() {
private fun hideIm() {
val controller = ViewCompat.getWindowInsetsController(window.decorView)
controller?.hide(ime())
WindowCompat.getInsetsController(this.window,window.decorView).hide(ime())
}
//执行搜索
private fun searchAction(tags: String?) {
var tag = tags
var isNum = true
try {
tags?.toInt()
}catch (e:NumberFormatException){
isNum = false
}
if (isNum){
tag = "id:"+tags
}
loadData(tag, 1, ISREFRESH)
}
//设置列表数据
private fun loadData(tags: String?,page: Int,type:Int){
//缓存搜索的tags
this.tags = tags
val swipeRefreshLayout =
findViewById<androidx.swiperefreshlayout.widget.SwipeRefreshLayout>(
R.id.swipeRefreshLayout
)
swipeRefreshLayout.isRefreshing = true
//读取配置
val configSp = getSharedPreferences("com.lsper.view_preferences", 0)
val nowSourceName: String? = configSp.getString("sourceName",null)
var source = ""
for ((index,sourceName) in sourceNameArray.withIndex()){
if (sourceName == nowSourceName){
source = sourceUrlArray[index]
}
}
//接收异步信息
val handler = object : Handler(Looper.myLooper()!!){
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
when (msg.what){
CallBackStatus.DATAISNULL.ordinal ->{
Snackbar.make(swipeRefreshLayout,R.string.toast_load_empty,Snackbar.LENGTH_SHORT).show()
search.setText("")
}
CallBackStatus.OK.ordinal -> {
if (type == ISREFRESH )
//刷新数据
adapter.refreshData(msg.obj as ArrayList<Post_yand>)
else if (type == ISADDDATA)
//加载数据
adapter.addData(msg.obj as ArrayList<Post_yand>)
}
CallBackStatus.NETWORKERROR.ordinal -> {
Snackbar.make(swipeRefreshLayout,R.string.toast_load_network_fail,Snackbar.LENGTH_SHORT).show()
}
}
swipeRefreshLayout.isRefreshing = false
}
}
MainActivityModelImpl().requestPostList(handler,source,tags,page,configSp.getBoolean("safe_mode",true))
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.toolbar_menu, menu)
@@ -1,92 +1,105 @@
package com.lsp.view.activity.main
import android.content.Context
import android.util.Log
import android.os.Handler
import android.os.Looper
import android.os.Message
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.bumptech.glide.load.model.GlideUrl
import com.bumptech.glide.load.model.LazyHeaders
import com.lsp.view.R
import com.lsp.view.activity.pic.PicActivity
import com.lsp.view.bean.Post_yand
import com.lsp.view.repository.bean.YandPost
class PostAdapter(val context: Context, private var postYandList: ArrayList<Post_yand>) :
class PostAdapter(val context: Context):
RecyclerView.Adapter<PostAdapter.ViewHolder>() {
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 postList = ArrayList<YandPost>()
inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val picImage: ImageView = view.findViewById(R.id.picImgae)
}
fun addData(list: ArrayList<Post_yand>) {
val pos = postYandList.size
postYandList.addAll(list)
notifyItemRangeInserted(pos, list.size)
fun pushNewData(list: ArrayList<YandPost>) {
val oldSize = postList.size
postList.clear()
notifyItemRangeRemoved(0,oldSize)
postList.addAll(list)
notifyItemRangeInserted(0, list.size)
}
fun refreshData(list: ArrayList<Post_yand>) {
val oldSize = postYandList.size
postYandList.clear()
notifyItemRangeRemoved(0,oldSize)
postYandList.addAll(list)
notifyItemRangeInserted(0, list.size)
fun appendDate(list: ArrayList<YandPost>){
val pos = postList.size
postList.addAll(list)
notifyItemRangeInserted(pos, list.size)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(context).inflate(R.layout.img_item_layout, parent, false)
val viewHolder = ViewHolder(view)
viewHolder.picImage.setOnClickListener {
val position = viewHolder.adapterPosition
Log.w("position", position.toString())
Log.w("url", postYandList[position].sample_url)
Log.w("rating", postYandList[position].rating)
var file_ext = postYandList[position].file_ext
// Log.w("position", position.toString())
// 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 = postYandList[position].sample_url.split(".")
val strarr = postList[position].sample_url.split(".")
file_ext = strarr[strarr.lastIndex]
}
PicActivity.actionStartActivity(context,postYandList[position].id,postYandList[position].sample_url,
postYandList[position].file_url,postYandList[position].tags,file_ext,
postYandList[position].author,postYandList[position].file_size,postYandList[position].md5)
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)
}
return viewHolder
}
private lateinit var mLoadMoreListener: OnLoadMoreListener
private lateinit var mScrollToBottom: OnScrollToBottom
interface OnLoadMoreListener {
fun loadMore(position: Int)
interface OnScrollToBottom {
fun event(position: Int)
}
fun setLoadMoreListener(mLoadMoreListener: OnLoadMoreListener) {
this.mLoadMoreListener = mLoadMoreListener
fun setLoadMoreListener(mLoadMoreListener: OnScrollToBottom) {
this.mScrollToBottom = mLoadMoreListener
}
private fun preLoad(p:Int){
if (p%20==0){
//执行预加载
val last:Int = if (postYandList.size-p<20){
postYandList.size-1
val last:Int = if (postList.size-p<20){
postList.size-1
}else{
p+19
}
for(index in p..last){
if (p>6) {
val source: String = postYandList[index].sample_url
val source: String = postList[index].sample_url
val glideUrl = GlideUrl(
source,
LazyHeaders.Builder().addHeader("User-Agent", UA)
@@ -96,21 +109,22 @@ class PostAdapter(val context: Context, private var postYandList: ArrayList<Post
}
}
}
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
preLoad(position)
val post = postYandList[position]
val post = postList[position]
val source: String = post.sample_url
val height = postYandList[position].sample_height
val width = postYandList[position].sample_width
val height = postList[position].sample_height
val width = postList[position].sample_width
//图片过长则缩减高度以保证显示完整
if (width*0.75 > height){
if (width > height){
holder.picImage.layoutParams.height = height / 2
}else {
holder.picImage.layoutParams.height = height
@@ -124,15 +138,17 @@ class PostAdapter(val context: Context, private var postYandList: ArrayList<Post
)
Glide.with(context).load(glideUrl).into(holder.picImage)
if (position == postYandList.size - 1 && postYandList.size > 6) {
if (position == postList.size - 1 && postList.size > 6) {
//到达底部
mLoadMoreListener.loadMore(position)
mScrollToBottom.event(position)
}
}
override fun getItemCount(): Int {
return postYandList.size
val size = postList.size
return size
}
@@ -1,7 +0,0 @@
package com.lsp.view.activity.model
import android.os.Handler
interface MainActivityModel {
fun requestPostList(handler: Handler, source: String, tage: String?, page: Int, safeMode: Boolean)
}
@@ -1,70 +0,0 @@
package com.lsp.view.activity.model
import android.os.Handler
import android.os.Message
import com.lsp.view.retrofit.PostService
import com.lsp.view.retrofit.ServiceCreator
import com.lsp.view.bean.Post
import com.lsp.view.bean.Post_yand
import com.lsp.view.util.CallBackStatus
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import kotlin.collections.ArrayList
class MainActivityModelImpl :MainActivityModel {
private val TAG = this::class.java.simpleName
override fun requestPostList(handler: Handler, source: String, tage: String?, page: Int,safeMode: Boolean){
val postService: PostService = ServiceCreator.create(source)
val service: Call<ArrayList<Post_yand>> = postService.getPostData("100",tage, page)
request(service,safeMode, handler)
}
fun <T:Post> callBack(handler: Handler,status: CallBackStatus,value:ArrayList<T>){
val msg = Message.obtain()
msg.what = status.ordinal
msg.obj = value
handler.sendMessage(msg)
}
fun callBack(handler: Handler,status: CallBackStatus){
val msg = Message.obtain()
msg.what = status.ordinal
handler.sendMessage(msg)
}
/**
* @param service ArrayList接收边界为Post的泛型,若直接使用Post作为泛型参数,需要强制类型转换。
*/
fun <T : Post> request(service:Call<ArrayList<T>>,safeMode: Boolean,handler: Handler){
service.enqueue(object : Callback<ArrayList<T>>{
override fun onResponse(call: Call<ArrayList<T>>, response: Response<ArrayList<T>>) {
val getValue:ArrayList<T>? = response.body()
if (getValue!=null) {
if (getValue.size==0){
callBack(handler,CallBackStatus.DATAISNULL)
return
}
if (safeMode) {
val iter = getValue.iterator()
while (iter.hasNext()) {
val post = iter.next()
if (post.rating != "s")
iter.remove()
}
}
callBack(handler,CallBackStatus.OK,getValue)
}
}
override fun onFailure(call: Call<ArrayList<T>>, t: Throwable) {
callBack(handler,CallBackStatus.NETWORKERROR)
}
})
}
}
@@ -1,33 +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.bean.Author
class AuthorAdapter(val tagList: List<Author>) : RecyclerView.Adapter<AuthorAdapter.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]
holder.tagText.text = tag.author
if (position == 0) {
holder.tagText.setBackgroundResource(R.drawable.title_bg)
}
}
override fun getItemCount(): Int {
return tagList.size
}
}
@@ -10,8 +10,8 @@ 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.bean.ID
import com.lsp.view.bean.Tags
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) {
@@ -54,9 +54,6 @@ class IdAdapter(val idList: List<ID>,val context: Context) : RecyclerView.Adapte
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val tag = idList[position]
holder.tagText.text = tag.id
if (position == 0) {
holder.tagText.setBackgroundResource(R.drawable.title_bg)
}
}
override fun getItemCount(): Int {
@@ -6,13 +6,13 @@ 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 android.widget.ProgressBar
import androidx.activity.addCallback
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.widget.Toolbar
import androidx.core.graphics.createBitmap
import com.bumptech.glide.Glide
import com.bumptech.glide.load.DataSource
import com.bumptech.glide.load.engine.GlideException
@@ -27,20 +27,16 @@ import com.google.android.material.snackbar.Snackbar
import com.lsp.view.R
import com.lsp.view.activity.BaseActivity
import com.lsp.view.activity.main.MainActivity
import com.lsp.view.bean.Author
import com.lsp.view.bean.ID
import com.lsp.view.bean.Size
import com.lsp.view.bean.Tags
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 java.io.File
import kotlin.properties.Delegates
class PicActivity : BaseActivity() {
private val tagList = ArrayList<Tags>()
private val authorList = ArrayList<Author>()
private val idList = ArrayList<ID>()
private val sizeList = ArrayList<Size>()
private lateinit var image: ImageView
@@ -51,16 +47,17 @@ class PicActivity : BaseActivity() {
companion object{
fun actionStartActivity(context: Context,id:String,sample_url:String,file_url:String,tags:String,
file_ext:String,author:String,file_size:String,md5: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("author", author)
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)
}
@@ -76,6 +73,14 @@ class PicActivity : BaseActivity() {
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")
@@ -83,29 +88,12 @@ class PicActivity : BaseActivity() {
loadTags(tags, "tags")
}
val author = intent.getStringExtra("author")
if (author != null) {
loadTags(author, "author")
}
val file_size = intent.getStringExtra("file_size")
if (file_size != null) {
loadTags(file_size, "size")
}
md5 = intent.getStringExtra("md5")
val sharedPreferences = getSharedPreferences("FirstRun", 0)
val firstRun = sharedPreferences.getBoolean("FirstRun", true)
if (firstRun) {
val sharedPreferences = getSharedPreferences("FirstRun", 0).edit()
sharedPreferences.putBoolean("FirstRun", false).apply()
val FileD =
File("${Environment.getExternalStorageDirectory()}/${Environment.DIRECTORY_PICTURES}/LspMake")
FileD.mkdirs()
}
val id = intent.getStringExtra("id")
if (id != null) {
loadTags(id, "id")
@@ -177,19 +165,22 @@ class PicActivity : BaseActivity() {
}
override fun onBackPressed() {
if (photoView.visibility == View.GONE) {
super.onBackPressed()
} else {
photoView.animate()
.alpha(0f)
.setDuration(shortAnnotationDuration.toLong())
.setListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
photoView.visibility = View.GONE
}
})
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) {
@@ -208,9 +199,7 @@ class PicActivity : BaseActivity() {
target: Target<Drawable?>?,
isFirstResource: Boolean
): Boolean {
if (e != null) {
e.printStackTrace()
}
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 {
@@ -259,6 +248,7 @@ class PicActivity : BaseActivity() {
//设置tags列表
//这写的很烂 得改
private fun loadTags(tags: String, type: String) {
val tagList = ArrayList<Tags>()
when (type) {
"tags" -> {
@@ -282,15 +272,6 @@ class PicActivity : BaseActivity() {
})
tagRecyclerView.adapter = adapter
}
"author" -> {
authorList.add(Author("Author"))
val authorRecyclerView =
findViewById<androidx.recyclerview.widget.RecyclerView>(R.id.authorRecyclerView)
authorRecyclerView.layoutManager = layoutManager()
authorList.add(Author(tags))
val adapter = AuthorAdapter(authorList)
authorRecyclerView.adapter = adapter
}
"id" -> {
idList.add(ID("ID"))
idList.add(ID(tags))
@@ -6,7 +6,7 @@ import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.lsp.view.R
import com.lsp.view.bean.Size
import com.lsp.view.repository.bean.Size
import java.math.RoundingMode
import java.text.DecimalFormat
@@ -25,7 +25,6 @@ class SizeAdapter(val tagList: List<Size>) : RecyclerView.Adapter<SizeAdapter.Vi
val tag = tagList[position]
if (position == 0) {
holder.tagText.text = tag.file_size
holder.tagText.setBackgroundResource(R.drawable.title_bg)
} else {
val sizeKb = tag.file_size.toFloat() / 1024
val format = DecimalFormat("0.##")
@@ -10,7 +10,7 @@ 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.bean.Tags
import com.lsp.view.repository.bean.Tags
class TagAdapter(val tagList: List<Tags>, val context: Context) :
RecyclerView.Adapter<TagAdapter.ViewHolder>() {
@@ -68,9 +68,6 @@ class TagAdapter(val tagList: List<Tags>, val context: Context) :
holder.tagText.setOnClickListener {
mOnItemClickListener.onItemClick(it, position)
}
if (position == 0) {
holder.tagText.setBackgroundResource(R.drawable.title_bg)
}
}
override fun getItemCount(): Int {
@@ -1,3 +0,0 @@
package com.lsp.view.bean
class Author(val author: String)
@@ -1,3 +0,0 @@
package com.lsp.view.bean
class ID(val id: String)
@@ -1,3 +0,0 @@
package com.lsp.view.bean
class Size(val file_size: String)
@@ -1,3 +0,0 @@
package com.lsp.view.bean
class Tags(val tag: String)
@@ -0,0 +1,132 @@
package com.lsp.view.model
import android.content.Context
import android.os.Bundle
import android.os.Message
import android.widget.Toast
import androidx.lifecycle.AbstractSavedStateViewModelFactory
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.lsp.view.activity.main.PostAdapter
import com.lsp.view.repository.PostRepository
import androidx.savedstate.SavedStateRegistryOwner
import com.lsp.view.repository.bean.YandPost
import com.lsp.view.repository.exception.NetworkErrorException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
class MainViewModel(private val repository: PostRepository,private val context: Context):ViewModel() {
private val _uiState = MutableStateFlow(UiState())
val uiState:StateFlow<UiState> = _uiState.asStateFlow()
private var fetchJob: Job? = null
val adapter = PostAdapter(context)
private val TAG = this::class.java.simpleName
companion object {
fun provideFactory(
repository: PostRepository,
context: Context,
owner: SavedStateRegistryOwner,
defaultArgs: Bundle? = null,
): AbstractSavedStateViewModelFactory =
object : AbstractSavedStateViewModelFactory(owner, defaultArgs) {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(
key: String,
modelClass: Class<T>,
handle: SavedStateHandle
): T {
return MainViewModel(repository, context) as T
}
}
}
init {
//初始化写入
val configSp = context.getSharedPreferences("com.lsp.view_preferences", 0)
if (configSp.getString("source_name", null) == null) {
configSp.edit().putString("source_name", "yande.re").apply()
configSp.edit().putString("type", "0").apply()
}
_uiState.update {
it.copy(nowSourceName = configSp.getString("source_name",null))
}
}
private fun fetchNewPost(){
fetchJob?.cancel()
fetchJob = viewModelScope.launch(Dispatchers.IO) {
_uiState.value.isRefreshing.postValue(true)
try {
val postList = repository.fetchPostData(
_uiState.value.nowSearchText,
_uiState.value.isSafe,
_uiState.value.nowPage
)
launch(Dispatchers.Main) {
if (_uiState.value.isAppend){
adapter.appendDate(postList)
}else{
adapter.pushNewData(postList)
}
}
}catch (e:NetworkErrorException){
launch(Dispatchers.Main){
Toast.makeText(context,e.message,Toast.LENGTH_SHORT).show()
}
}
_uiState.value.isRefreshing.postValue(false)
}
}
fun fetchNewPostBySearch(target:String){
_uiState.update {
it.copy(nowSearchText = target)
}
fetchNewPost()
}
fun fetchPostByRefresh(){
_uiState.update {
it.copy(nowPage = 1)
}
fetchNewPost()
}
fun appendPost(){
_uiState.update {
it.copy(isAppend = true)
}
fetchNewPost()
_uiState.update {
it.copy(isAppend = false)
}
}
}
@@ -0,0 +1,17 @@
package com.lsp.view.model
import androidx.lifecycle.MutableLiveData
data class UiState(
var showSearchBar:Boolean = false,
val isRefreshing:MutableLiveData<Boolean> = MutableLiveData(),
val nowSearchText:String = "",
val nowSourceName: String? = null,
val isSafe: Boolean = true, //安全模式
val nowPage:Int = 1,
val isAppend:Boolean = false
){
fun switchShowSearchBar(){
showSearchBar = !showSearchBar
}
}
@@ -0,0 +1,47 @@
package com.lsp.view.repository
import com.lsp.view.R
import com.lsp.view.YandViewApplication
import com.lsp.view.repository.exception.UnableConstructObjectException
import com.lsp.view.repository.api.PostApi
import com.lsp.view.repository.api.PostApiImpl
import com.lsp.view.repository.bean.YandPost
class PostDataSource {
private val api: PostApi = PostApiImpl()
//设置列表数据
suspend fun fetchNewPost(load: Load): ArrayList<YandPost> {
return api.fetchNewPost(load)
}
}
data class Load(
val tags: String?,
val page: Int,
val safe:Boolean
){
lateinit var source:String
companion object{
fun Builder(tags: String?, page: Int, safe:Boolean):Load{
val sourceNameArray = YandViewApplication.context!!.resources.getStringArray(R.array.pic_source)
val sourceUrlArray = YandViewApplication.context!!.resources.getStringArray(R.array.url_source)
val configSp = YandViewApplication.context?.getSharedPreferences("com.lsp.view_preferences", 0)
val nowSourceName: String? = configSp?.getString("source_name",null)
val load = Load(tags,page,safe)
//初始化数据
for ((index,sourceName) in sourceNameArray.withIndex()){
if (sourceName == nowSourceName){
load.source = sourceUrlArray[index]
return load
}
}
throw UnableConstructObjectException("Unable construct load object.")
}
}
}
@@ -0,0 +1,33 @@
package com.lsp.view.repository
import android.os.Handler
import com.lsp.view.repository.bean.YandPost
class PostRepository {
private val dataSource = PostDataSource()
//获取post
suspend fun fetchPostData(searchTarget :String?,safe:Boolean,page:Int): ArrayList<YandPost> {
var target = searchTarget
var isNum = true
//如果检索目标是纯数字,则按照id搜索
try {
target?.toLong()
}catch (e:NumberFormatException){
isNum = false
}
if (isNum){
target = "id:$searchTarget"
}
val load = Load.Builder(target,page,safe)
return dataSource.fetchNewPost(load)
}
}
@@ -0,0 +1,42 @@
package com.lsp.view.repository.api
import android.util.Log
import com.lsp.view.repository.Load
import com.lsp.view.repository.bean.Post
import com.lsp.view.repository.bean.YandPost
import com.lsp.view.repository.exception.NetworkErrorException
import retrofit2.Call
import java.net.SocketException
interface PostApi {
private val TAG: String
get() = this::class.java.simpleName
/**
* @param service ArrayList接收边界为Post的泛型,若直接使用Post作为泛型参数,需要强制类型转换。
*/
@Throws(NetworkErrorException::class)
fun <T : Post> request(service: Call<ArrayList<T>>, safeMode: Boolean):ArrayList<T> {
try {
val execute = service.execute()
return if (execute.isSuccessful) {
if (execute.body() != null) {
Log.e(TAG, execute.body().toString())
execute.body()!!
} else {
//结果为空,返回空集避免空指针
ArrayList()
}
} else {
throw NetworkErrorException(execute.errorBody().toString())
}
}catch (e: Exception){
throw NetworkErrorException(e.message)
}
}
suspend fun fetchNewPost(load:Load):ArrayList<YandPost>
}
@@ -0,0 +1,20 @@
package com.lsp.view.repository.api
import com.lsp.view.repository.Load
import com.lsp.view.retrofit.PostService
import com.lsp.view.retrofit.ServiceCreator
import com.lsp.view.repository.bean.YandPost
import retrofit2.Call
import kotlin.collections.ArrayList
class PostApiImpl :PostApi {
private val TAG = this::class.java.simpleName
private val defaultLimit = "100"
override suspend fun fetchNewPost(load: Load):ArrayList<YandPost>{
val postService: PostService = ServiceCreator.create(load.source)
val service: Call<ArrayList<YandPost>> = postService.getPostData(defaultLimit,load.tags, load.page)
return request(service, load.safe)
}
}
@@ -0,0 +1,3 @@
package com.lsp.view.repository.bean
class Author(val author: String)
@@ -0,0 +1,3 @@
package com.lsp.view.repository.bean
class ID(val id: String)
@@ -1,4 +1,4 @@
package com.lsp.view.bean
package com.lsp.view.repository.bean
open class Post(
val id: String,
@@ -0,0 +1,3 @@
package com.lsp.view.repository.bean
class Size(val file_size: String)
@@ -0,0 +1,3 @@
package com.lsp.view.repository.bean
class Tags(val tag: String)
@@ -1,6 +1,6 @@
package com.lsp.view.bean
package com.lsp.view.repository.bean
class Post_yand(
class YandPost(
val preview_url: String,
val file_url: String,
val sample_url: String,
@@ -0,0 +1,6 @@
package com.lsp.view.repository.exception
import okhttp3.ResponseBody
class NetworkErrorException(override val message: String?) : Exception() {
}
@@ -0,0 +1,6 @@
package com.lsp.view.repository.exception
import java.lang.Exception
class UnableConstructObjectException(override val message: String?):Exception(message) {
}
@@ -1,6 +1,6 @@
package com.lsp.view.retrofit
import com.lsp.view.bean.Post_yand
import com.lsp.view.repository.bean.YandPost
import retrofit2.Call
import retrofit2.http.GET
import retrofit2.http.Query
@@ -11,5 +11,5 @@ interface PostService {
@Query("limit") limit: String,
@Query("tags") tags: String?,
@Query("page") page: Int
): Call<ArrayList<Post_yand>>
): Call<ArrayList<YandPost>>
}
@@ -11,13 +11,11 @@ object ServiceCreator {
* @param source:加载根地址
*
*/
var source: String? = null
fun <T> create(serviceClass: Class<T>, source: String): T {
val retrofit = Retrofit.Builder()
.baseUrl(source)
.addConverterFactory(GsonConverterFactory.create())
.build()
ServiceCreator.source = source
return retrofit.create(serviceClass)
}
@@ -5,7 +5,7 @@ import android.content.Context
import android.content.Intent
import android.media.MediaScannerConnection
import android.os.*
import com.lsp.view.util.CallBackStatus
import com.lsp.view.util.Util
import okhttp3.OkHttpClient
import okhttp3.Request
import java.io.File
@@ -19,12 +19,9 @@ class DownloadService : Service() {
class DownloadBinder(val context: Context) : Binder() {
fun callBack(handler: Handler,status:CallBackStatus,obj:Any?=null){
fun callBack(handler: Handler,status:Int){
val msg = Message.obtain()
msg.what = status.ordinal
if (obj!=null){
msg.obj = obj
}
msg.obj = status
handler.sendMessage(msg)
}
@@ -38,7 +35,7 @@ class DownloadService : Service() {
File("${Environment.getExternalStorageDirectory()}/${Environment.DIRECTORY_PICTURES}/LspMake/$md5.$end")
if (file.exists()){
callBack(handler,CallBackStatus.FILEEXISTS)
callBack(handler,Util.FILE_EXISTS)
return@thread
}
@@ -60,24 +57,24 @@ class DownloadService : Service() {
null, null
)
if (md5 == getFileMD5(file.path)) {
callBack(handler, CallBackStatus.OK,file)
callBack(handler, Util.DOWNLOAD_SUCCESS)
} else {
file.delete()
callBack(handler,CallBackStatus.MD5COMPAREERROR)
callBack(handler,Util.MD5_COMPARE_ERROR)
}
return@thread
} else {
file.delete()
callBack(handler, CallBackStatus.NETWORKERROR)
callBack(handler, Util.NETWORK_ERROR)
return@thread
}
} catch (e: Exception) {
e.printStackTrace()
callBack(handler, CallBackStatus.DOWNLOADERROR)
callBack(handler, Util.DOWNLOAD_ERROR)
return@thread
} finally {
fos.close()
@@ -87,7 +84,7 @@ class DownloadService : Service() {
FileD.mkdirs()
downloadPic(file_url, end, handler, md5)
}
callBack(handler,CallBackStatus.DOWNLOADERROR)
callBack(handler,Util.DOWNLOAD_ERROR)
}
}
@@ -1,5 +0,0 @@
package com.lsp.view.util
enum class CallBackStatus {
OK, NETWORKERROR, DATAISNULL, DOWNLOADERROR, SETWP, MD5COMPAREERROR, FILEEXISTS
}
+20 -10
View File
@@ -5,6 +5,7 @@ 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
@@ -17,6 +18,12 @@ 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()
@@ -36,20 +43,23 @@ object Util {
val imageUri = FileProvider.getUriForFile(
context,
"com.lsper.view.fileprovider",
"com.lsp.view.fileprovider",
file
)
context.grantUriPermission("com.lsper.view",imageUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
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> = context.packageManager
.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY)
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
@@ -78,20 +88,20 @@ object Util {
private val handler = object : Handler(Looper.myLooper()!!){
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
when(msg.what){
CallBackStatus.OK.ordinal ->{
when(msg.obj){
DOWNLOAD_SUCCESS ->{
Toast.makeText(YandViewApplication.context, R.string.toast_download_success, Toast.LENGTH_SHORT).show()
}
CallBackStatus.DOWNLOADERROR.ordinal -> {
DOWNLOAD_ERROR -> {
Toast.makeText(YandViewApplication.context, R.string.toast_download_fail, Toast.LENGTH_SHORT).show()
}
CallBackStatus.MD5COMPAREERROR.ordinal -> {
MD5_COMPARE_ERROR -> {
Toast.makeText(YandViewApplication.context, R.string.toast_compar_md5_fail, Toast.LENGTH_SHORT).show()
}
CallBackStatus.FILEEXISTS.ordinal -> {
FILE_EXISTS -> {
Toast.makeText(YandViewApplication.context, R.string.toast_file_exist, Toast.LENGTH_SHORT).show()
}
@@ -1,4 +1,4 @@
<vector android:height="24dp" android:tint="#000000"
<vector 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="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
@@ -1,5 +1,5 @@
<vector android:autoMirrored="true" android:height="24dp"
android:tint="#000000" android:viewportHeight="24"
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,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="M11,7L9.6,8.4l2.6,2.6H2v2h10.2l-2.6,2.6L11,17l5,-5L11,7zM20,19h-8v2h8c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2h-8v2h8V19z"/>
</vector>
@@ -1,5 +0,0 @@
<vector android:height="40dp" android:tint="#FFFFFF"
android:viewportHeight="20" android:viewportWidth="20"
android:width="40dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M10,6L8.59,7.41 13.17,12l-4.58,4.59L10,18l6,-6z"/>
</vector>
@@ -1,41 +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:orientation="vertical"
android:layout_height="match_parent"
tools:context=".activity.login.LoginActivity">
<TextView
android:layout_marginTop="160dp"
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="44sp"
android:id="@+id/title"
android:text="@string/activity_login_login"/>
<EditText
android:hint="@string/activity_login_account"
android:layout_marginTop="20dp"
android:layout_gravity="center"
android:layout_width="240dp"
android:layout_height="wrap_content"
android:id="@+id/username"/>
<EditText
android:hint="@string/activity_login_password"
android:inputType="textPassword"
android:layout_gravity="center"
android:layout_width="240dp"
android:layout_height="wrap_content"
android:id="@+id/password"/>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:layout_width="60dp"
android:layout_height="60dp"
android:id="@+id/login"
android:background="@color/primary"
android:src="@drawable/ic_baseline_navigate_next_24"
android:layout_marginTop="30dp"
android:layout_gravity="center"
android:elevation="8dp"/>
</LinearLayout>
+29 -63
View File
@@ -19,83 +19,49 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
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
<androidx.appcompat.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:menu="@menu/toolbar_menu"
android:id="@+id/toolbar"
>
<EditText
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:menu="@menu/toolbar_menu"
android:id="@+id/toolbar"
app:layout_scrollFlags="scroll|enterAlways|snap"
/>
android:layout_height="match_parent"
android:textSize="20sp"
android:id="@+id/search"
android:imeOptions="actionSearch"
android:singleLine="true"
android:visibility="gone" />
</androidx.appcompat.widget.Toolbar>
</com.google.android.material.appbar.AppBarLayout>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
app:layout_behavior="@string/appbar_scrolling_view_behavior"
android:id="@+id/swipeRefreshLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<androidx.recyclerview.widget.RecyclerView
app:layout_behavior="@string/appbar_scrolling_view_behavior"
android:id="@+id/swipeRefreshLayout"
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>
android:layout_height="wrap_content"
android:id="@+id/recyclerview"/>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
</LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<LinearLayout
android:layout_marginTop="?attr/actionBarSize"
android:paddingTop="40dp"
android:visibility="gone"
android:id="@+id/search_bar"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<View
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"/>
<com.google.android.material.card.MaterialCardView
android:layout_width="0dp"
android:layout_weight="20"
android:elevation="15dp"
app:cardCornerRadius="3dp"
android:id="@+id/editCard"
style="?attr/materialCardViewElevatedStyle"
android:layout_height="50dp"
>
<EditText
android:background="@null"
android:hint="搜索..."
android:imeOptions="actionSearch"
android:singleLine="true"
android:paddingStart="10dp"
android:textSize="15sp"
android:layout_width="match_parent"
android:layout_height="50dp"
android:id="@+id/search"/>
<View
android:id="@+id/close"
android:layout_gravity="end"
android:layout_width="35dp"
android:layout_margin="10dp"
android:layout_height="35dp"
app:tint="?attr/colorPrimary"
android:background="@drawable/ic_baseline_close_24"/>
</com.google.android.material.card.MaterialCardView>
<View
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"/>
</LinearLayout>
</RelativeLayout>
<com.google.android.material.navigation.NavigationView
android:layout_width="240dp"
+5 -5
View File
@@ -14,13 +14,13 @@
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:fitsSystemWindows="true"
android:layout_height="match_parent"
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="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<androidx.appcompat.widget.Toolbar
@@ -34,7 +34,7 @@
android:fitsSystemWindows="true"
android:id="@+id/titleImage"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_height="wrap_content"
android:scaleType="centerCrop"
android:contentDescription="@string/appbar_scrolling_view_behavior"/>
@@ -52,13 +52,13 @@
android:orientation="vertical">
<com.google.android.material.card.MaterialCardView
style="?attr/materialCardViewElevatedStyle"
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="15dp"
android:layout_marginTop="5dp"
app:cardCornerRadius="10dp">
+13 -11
View File
@@ -1,12 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/fav_tag"
android:layout_margin="5dp"
android:padding="5dp"
android:textSize="20sp"
android:textColor="#fff"
android:background="@drawable/item_bg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<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>
+14 -9
View File
@@ -1,14 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:padding="5dp"
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content"
android:layout_height="wrap_content">
<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:textColor="#fff"
android:background="@drawable/item_bg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/tag"/>
</LinearLayout>
android:layout_height="wrap_content"/>
</com.google.android.material.card.MaterialCardView>
+1 -1
View File
@@ -8,7 +8,7 @@
app:defaultValue="yande.re"
app:entries="@array/pic_source"
app:entryValues="@array/pic_source"
app:key="sourceName"
app:key="source_name"
app:title="@string/setting_item_select_source"
app:useSimpleSummaryProvider="true" />
+1 -1
View File
@@ -6,7 +6,7 @@ buildscript {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.2.2'
classpath 'com.android.tools.build:gradle:7.3.0'
classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.10'
// NOTE: Do not place your application dependencies here; they belong