This commit is contained in:
litingting 2024-08-07 13:44:22 +08:00
commit d11595cbb8
108 changed files with 169502 additions and 0 deletions

15
.gitignore vendored Normal file
View File

@ -0,0 +1,15 @@
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
local.properties

1
app/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build

Binary file not shown.

Binary file not shown.

72
app/build.gradle.kts Normal file
View File

@ -0,0 +1,72 @@
import java.util.Date
import java.text.SimpleDateFormat
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
kotlin("kapt")
}
val timestamp = SimpleDateFormat("MM_dd_HH_mm").format(Date())
android {
namespace = "com.wall.photography.wallpaper"
compileSdk = 34
defaultConfig {
applicationId = "com.wall.photography"
minSdk = 23
targetSdk = 34
versionCode = 1
versionName = "1.0.0"
setProperty("archivesBaseName", "Photography Wallpapers_V" + versionName + "(${versionCode})_$timestamp")
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
isMinifyEnabled = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
buildFeatures {
viewBinding = true
}
}
dependencies {
val paging_version = "3.3.0"
implementation("androidx.paging:paging-runtime-ktx:$paging_version")
implementation("androidx.core:core-ktx:1.9.0")
implementation("androidx.appcompat:appcompat:1.7.0")
implementation("com.google.android.material:material:1.12.0")
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
implementation("androidx.legacy:legacy-support-v4:1.0.0")
implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.8.3")
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.3")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.2.1")
androidTestImplementation("androidx.test.espresso:espresso-core:3.6.1")
implementation("com.google.code.gson:gson:2.10.1")
implementation("com.github.bumptech.glide:glide:4.16.0")
kapt("com.github.bumptech.glide:compiler:4.16.0")
val room_version = "2.6.1"
implementation("androidx.room:room-runtime:$room_version")
kapt("androidx.room:room-compiler:$room_version")
implementation("androidx.room:room-ktx:$room_version")
implementation("androidx.room:room-paging:$room_version")
implementation("com.squareup.okhttp3:okhttp:4.11.0")
}

38
app/proguard-rules.pro vendored Normal file
View File

@ -0,0 +1,38 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
-keepclassmembers class com.wall.photography.wallpaper.MyApp {
public static final java.lang.String ROOM_NAME;
public static final int ROOM_Version;
}
-keepclassmembers class * {
@androidx.room.Query <methods>;
}
-keep class com.wall.photography.wallpaper.data.Bean { *; }
-keep class com.wall.photography.wallpaper.data.Author { *; }
-keep class com.wall.photography.wallpaper.data.Links { *; }
-keep class com.wall.photography.wallpaper.data.URLS { *; }
#-keepclassmembers class com.wall.photography.wallpaper.data { *; }
-keep class com.wall.photography.wallpaper.room.MyDataBase { *; }
-keep class com.wall.photography.wallpaper.room.BeanDao { *; }

View File

@ -0,0 +1,24 @@
package com.wall.photography.wallpaper
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.wall.photography.wallpaper", appContext.packageName)
}
}

View File

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.SET_WALLPAPER" />
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
<application
android:name=".MyApp"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/logo"
android:label="@string/app_name"
android:roundIcon="@mipmap/logo"
android:supportsRtl="true"
android:theme="@style/Theme.PhotographyWallpaper"
tools:targetApi="31">
<activity
android:name=".activivty.AboutActivity"
android:exported="false" />
<activity
android:name=".activivty.SetWallpaperActivity"
android:exported="false" />
<activity
android:name=".activivty.ListActivity"
android:exported="false" />
<activity
android:name=".activivty.MyWelComeActivity"
android:exported="true"
android:launchMode="singleTop"
android:theme="@style/Welcome.Theme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".activivty.MainActivity"
android:exported="false"
android:launchMode="singleTop"
android:screenOrientation="portrait"
android:theme="@style/Base.Theme.PhotographyWallpaper" />
</application>
</manifest>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

20702
app/src/main/assets/Film.json Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,71 @@
package com.wall.photography.wallpaper
import android.app.Application
import android.util.Log
import com.wall.photography.wallpaper.data.Bean
import com.wall.photography.wallpaper.manager.DealData
import com.wall.photography.wallpaper.room.MyDataBase
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
class MyApp : Application() {
companion object {
lateinit var application: Application
const val TAG = "-------------tt"
const val ROOM_NAME = "Photography_Wallpaper"
const val ROOM_Version = 1
const val TABLE_NAME = "wall_paper"
var data_Wallpaper: List<Bean>? = null
var data_animals: List<Bean>? = null
var data_experimental: List<Bean>? = null
var data_film: List<Bean>? = null
var data_nature: List<Bean>? = null
var data_streetphotography: List<Bean>? = null
var data_texturespatterns: List<Bean>? = null
var data_travel: List<Bean>? = null
var dataAlready: Boolean = false
const val pageSize = 10
lateinit var strings: Array<String>
}
override fun onCreate() {
super.onCreate()
application = this
Log.d(TAG, "---------------onCreate")
CoroutineScope(Dispatchers.IO).launch {
Log.d(TAG, "---------------协程开始" + Thread.currentThread().name)
strings = resources.getStringArray(R.array.category_name)
val allWallpaper = MyDataBase.getInstance().getBeanDao().getCategoryCovert(strings[7])
if (allWallpaper != null) {
Log.d(TAG, "---------------协程结束 --" + Thread.currentThread().name)
return@launch
}
for (i in 0 until 8) {
val initData = initData(strings[i] + ".json")
val subList = initData.subList(0, 500)
for (data in subList) {
MyDataBase.getInstance().getBeanDao().insertBean(data)
}
}
dataAlready = true
Log.d(TAG, "---------------协程结束 --" + Thread.currentThread().name)
}
Log.d(TAG, "---------------onCreate end")
}
private fun initData(fileName: String): List<Bean> {
Log.d(TAG, "-----------协程.......----fileName=$fileName")
val fdInput = assets.open(fileName)
val jsonStr = DealData.getJsonString(fdInput)
val cateName = fileName.substringBefore(".json")
val bean = DealData.getBean(this, jsonStr, cateName)
return bean
}
}

View File

@ -0,0 +1,63 @@
package com.wall.photography.wallpaper;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.net.Uri;
import android.os.Build;
import android.provider.MediaStore;
import java.io.File;
import java.io.FileInputStream;
import java.io.OutputStream;
public class Test {
public static Uri saveToGallery(Context context, File photoFile) {
String displayName = System.currentTimeMillis()+".jpg";
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.Images.Media.DISPLAY_NAME, displayName);
contentValues.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
contentValues.put(MediaStore.Images.Media.IS_PENDING, 1);
}
Uri collectionUri;
ContentResolver contentResolver = context.getContentResolver();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
collectionUri = MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
} else {
collectionUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
}
Uri imageUri = contentResolver.insert(collectionUri, contentValues);
if (imageUri == null) {
return null;
}
FileInputStream inputStream = null;
OutputStream outputStream = null;
try {
int byteLength = 0;
byte[] bytes = new byte[4096];
outputStream = contentResolver.openOutputStream(imageUri);
inputStream = new FileInputStream(photoFile);
while ((byteLength = inputStream.read(bytes)) != -1) {
outputStream.write(bytes, 0, byteLength);
}
inputStream.close();
outputStream.close();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
contentValues.clear();
contentValues.put(MediaStore.Images.Media.IS_PENDING, 0);
contentResolver.update(imageUri, contentValues, null, null);
}
return imageUri;
} catch (Exception exception) {
return null;
}
}
}

View File

@ -0,0 +1,56 @@
package com.wall.photography.wallpaper.activivty
import android.content.Intent
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.wall.photography.wallpaper.R
import com.wall.photography.wallpaper.databinding.ActivityAboutBinding
class AboutActivity : AppCompatActivity() {
private lateinit var vb: ActivityAboutBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
vb = ActivityAboutBinding.inflate(layoutInflater)
setContentView(vb.root)
val appVersionName = getAppVersionName()
appVersionName?.run {
vb.tvVersion.text = String.format(getString(R.string.version), this.versionName)
} ?: run {
vb.tvVersion.text = String.format(getString(R.string.version), "1.0.0")
}
vb.back.setOnClickListener { finish() }
vb.layoutRate.setOnClickListener {
try {
val uri = String.format(getString(R.string.google_link), packageName)
startActivity(Intent(Intent.ACTION_VIEW).apply {
data = Uri.parse(uri)
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
})
} catch (e: Exception) {
Toast.makeText(
this@AboutActivity,
this@AboutActivity.resources.getString(R.string.to_gp_exception),
Toast.LENGTH_SHORT
).show()
}
}
}
private fun getAppVersionName(): PackageInfo? {
return try {
packageManager.getPackageInfo(packageName, 0)
} catch (e: PackageManager.NameNotFoundException) {
null
}
}
}

View File

@ -0,0 +1,65 @@
package com.wall.photography.wallpaper.activivty
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.StaggeredGridLayoutManager
import com.wall.photography.wallpaper.MyApp
import com.wall.photography.wallpaper.R
import com.wall.photography.wallpaper.adapter.AdapterHomePaing
import com.wall.photography.wallpaper.data.Bean
import com.wall.photography.wallpaper.databinding.ActivityListBinding
import com.wall.photography.wallpaper.databinding.ActivityMainBinding
import com.wall.photography.wallpaper.fragment.HomeViewModel
import com.wall.photography.wallpaper.manager.MyItemDecoration
import com.wall.photography.wallpaper.viewmode.ListActivityViewModel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
class ListActivity : AppCompatActivity() {
private lateinit var vb: ActivityListBinding
companion object{
const val KEY = "category_name"
}
private lateinit var viewModel:ListActivityViewModel
private lateinit var adapterHomepaging: AdapterHomePaing
private lateinit var name:String
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
vb = ActivityListBinding.inflate(layoutInflater)
setContentView(vb.root)
viewModel = ViewModelProvider(this)[ListActivityViewModel::class.java]
name = intent.getStringExtra(KEY)!!
vb.tvName.text = name
vb.back.setOnClickListener {
finish()
}
adapterHomepaging = AdapterHomePaing(mContext=this@ListActivity){
startActivity(Intent(this, SetWallpaperActivity::class.java).apply {
putExtra(SetWallpaperActivity.KEY_DATA, it)
})
}
CoroutineScope(Dispatchers.Main).launch{
viewModel.getPagingData(name).collectLatest {
Log.d(MyApp.TAG,"------------collectLatest ")
adapterHomepaging.submitData(it)
}
}
initWallpaper()
}
private fun initWallpaper() {
vb.list.run {
layoutManager = StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL)
adapter = adapterHomepaging
addItemDecoration(MyItemDecoration(this@ListActivity, 8, 8, 0))
}
}
}

View File

@ -0,0 +1,88 @@
package com.wall.photography.wallpaper.activivty
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import androidx.fragment.app.Fragment
import androidx.viewpager2.adapter.FragmentStateAdapter
import androidx.viewpager2.widget.ViewPager2
import com.wall.photography.wallpaper.MyApp
import com.wall.photography.wallpaper.R
import com.wall.photography.wallpaper.databinding.ActivityMainBinding
import com.wall.photography.wallpaper.fragment.CategoryFragment
import com.wall.photography.wallpaper.fragment.HomeFragment
import com.wall.photography.wallpaper.fragment.LoveFragment
import com.wall.photography.wallpaper.manager.DealData
class MainActivity : AppCompatActivity() {
private lateinit var vb: ActivityMainBinding
private lateinit var listOf: List<Fragment>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d(MyApp.TAG, "------------MainActivity onCreate=$this")
vb = ActivityMainBinding.inflate(layoutInflater)
setContentView(vb.root)
listOf = listOf<Fragment>(
HomeFragment.newInstance(),
CategoryFragment.newInstance(),
LoveFragment.newInstance()
)
initVp2()
vb.iconSet.setOnClickListener {
startActivity(Intent(this@MainActivity,AboutActivity::class.java).apply {
})
}
vb.bottomNavigation.setOnItemSelectedListener { menu ->
when (menu.itemId) {
R.id.home -> vb.viewpager2.currentItem = 0
R.id.category -> {
vb.viewpager2.currentItem = 1
val cateFragment = listOf[1] as CategoryFragment
cateFragment.refreshCate()
}
R.id.love -> {
vb.viewpager2.currentItem = 2
val loveFragment = listOf[2] as LoveFragment
loveFragment.refreshLoves()
}
}
true
}
}
private fun initVp2() {
vb.viewpager2.run {
adapter = object : FragmentStateAdapter(this@MainActivity) {
override fun getItemCount(): Int = listOf.size
override fun createFragment(position: Int): Fragment = listOf[position]
}
registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
super.onPageSelected(position)
when (position) {
0 -> vb.bottomNavigation.selectedItemId = R.id.home
1 -> {
vb.bottomNavigation.selectedItemId = R.id.category
val cateFragment = listOf[1] as CategoryFragment
cateFragment.refreshCate()
}
2 -> {
vb.bottomNavigation.selectedItemId = R.id.love
val loveFragment = listOf[2] as LoveFragment
loveFragment.refreshLoves()
}
}
}
})
}
}
}

View File

@ -0,0 +1,44 @@
package com.wall.photography.wallpaper.activivty
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.os.CountDownTimer
import com.wall.photography.wallpaper.MyApp
import com.wall.photography.wallpaper.R
import com.wall.photography.wallpaper.databinding.ActivityMainBinding
import com.wall.photography.wallpaper.databinding.ActivityWelcomeBinding
class MyWelComeActivity : AppCompatActivity() {
private lateinit var vb:ActivityWelcomeBinding
private lateinit var goTimer:CountDownTimer
private val countTime = 2000L
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
vb = ActivityWelcomeBinding.inflate(layoutInflater)
setContentView(vb.root)
init()
}
private fun init(){
goTimer = object :CountDownTimer(countTime,500){
override fun onTick(millisUntilFinished: Long) {
}
override fun onFinish() {
enterMain()
}
}
goTimer.start()
}
private fun enterMain(){
startActivity(Intent(this@MyWelComeActivity, MainActivity::class.java))
finish()
}
}

View File

@ -0,0 +1,59 @@
package com.wall.photography.wallpaper.activivty
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.DialogFragment
import com.wall.photography.wallpaper.databinding.SelectDialogBinding
class SelectDialog(private var clickAction:(clickPos:Int)->Unit) : DialogFragment() {
private lateinit var vb: SelectDialogBinding
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
vb = SelectDialogBinding.inflate(layoutInflater)
dialog?.run {
setCanceledOnTouchOutside(true)
window?.run {
setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
setLayout(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.WRAP_CONTENT)
}
}
initCLick()
return vb.root
}
private fun initCLick(){
vb.home.setOnClickListener {
clickAction.invoke(clickType_home)
dismiss()
}
vb.lock.setOnClickListener {
clickAction.invoke(clickType_lock)
dismiss()
}
vb.both.setOnClickListener {
clickAction.invoke(clickType_both)
dismiss()
}
}
companion object {
const val clickType_home = 0
const val clickType_lock = 1
const val clickType_both = 2
}
}

View File

@ -0,0 +1,414 @@
package com.wall.photography.wallpaper.activivty
import android.app.WallpaperManager
import android.content.pm.PackageManager
import android.graphics.BitmapFactory
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.view.View
import android.view.WindowManager
import android.widget.RelativeLayout
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.isVisible
import androidx.core.view.marginBottom
import com.bumptech.glide.Glide
import com.bumptech.glide.load.DataSource
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.load.engine.GlideException
import com.bumptech.glide.request.RequestListener
import com.bumptech.glide.request.target.Target
import com.wall.photography.wallpaper.MyApp
import com.wall.photography.wallpaper.R
import com.wall.photography.wallpaper.Test
import com.wall.photography.wallpaper.data.Bean
import com.wall.photography.wallpaper.databinding.ActivitySetWallpaperBinding
import com.wall.photography.wallpaper.manager.DealData
import com.wall.photography.wallpaper.manager.Other
import com.wall.photography.wallpaper.room.MyDataBase
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File
class SetWallpaperActivity : AppCompatActivity(), View.OnClickListener {
companion object {
const val KEY_DATA = "key_data"
}
private var code = 1
private var dialog: SelectDialog? = null
private lateinit var wallpaperManager: WallpaperManager
private lateinit var data: Bean
private lateinit var vb: ActivitySetWallpaperBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d(MyApp.TAG, "----------SetWallpaperActivity--onCreate=$this")
vb = ActivitySetWallpaperBinding.inflate(layoutInflater)
setContentView(vb.root)
vb.loadingView.isVisible = true
initBar()
data = intent.getSerializableExtra(KEY_DATA) as Bean
wallpaperManager = WallpaperManager.getInstance(this)
CoroutineScope(Dispatchers.IO).launch {
data = MyDataBase.getInstance().getBeanDao().getCurWallpaper(data.id)
vb.imLove.isSelected = data.love
data.downloadFilePath?.let {
val file = File(it)
if (file.exists()) {
withContext(Dispatchers.Main) {
Log.d(MyApp.TAG, "-------------load file =${it}")
Glide.with(MyApp.application)
.asDrawable()
.load(file)
.skipMemoryCache(true)
.thumbnail(0.2f)
.error(R.drawable.placeholder)
.placeholder(R.drawable.placeholder)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(vb.preview)
}
}
}
}
initView()
setClick()
relayout()
}
private fun relayout() {
val navigationBarHeight = Other.getNavigationBarHeight(MyApp.application)
Log.d(MyApp.TAG, "-------navigationBarHeight=${navigationBarHeight}")
if (navigationBarHeight != 0) {
val newLayoutParams = vb.relayout.layoutParams as ConstraintLayout.LayoutParams
vb.relayout.layoutParams = newLayoutParams.apply {
bottomMargin = navigationBarHeight+Other.dpToPx(20,MyApp.application)
}
}
}
private fun setClick() {
vb.run {
back.setOnClickListener(this@SetWallpaperActivity)
imDownload.setOnClickListener(this@SetWallpaperActivity)
imSetWallpaper.setOnClickListener(this@SetWallpaperActivity)
imLove.setOnClickListener(this@SetWallpaperActivity)
}
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == code) {
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
startSaveIm()
} else {
Toast.makeText(this, getString(R.string.grant_permission), Toast.LENGTH_SHORT)
.show()
}
}
}
private fun startSaveIm() {
vb.loadingView.isVisible = true
data.downloadFilePath?.let { path ->
Log.d(MyApp.TAG, "-------------has downloadFilePath =${path}")
val file = File(path)
if (file.exists()) {
val saveToGallery = DealData.saveToGallery(this@SetWallpaperActivity, file)
Log.d(MyApp.TAG, "------------file exist")
saveResultToast(
if (saveToGallery != null) getString(R.string.save_ok) else getString(
R.string.save_fail
)
)
} else {
Log.d(MyApp.TAG, "-------------file no exist")
downloadIm {
var msg: String = if (it == null) {
getString(R.string.save_exception)
} else {
val newFile = File(it)
val saveToGallery =
DealData.saveToGallery(this@SetWallpaperActivity, newFile)
if (saveToGallery != null) getString(R.string.save_ok) else getString(
R.string.save_fail
)
}
saveResultToast(msg)
}
}
} ?: run {
downloadIm {
var msg: String = if (it == null) {
getString(R.string.save_exception)
} else {
val newFile = File(it)
// val saveToGallery = DealData.saveToGallery(this@SetWallpaperActivity, newFile)
val saveToGallery = Test.saveToGallery(this@SetWallpaperActivity, newFile)
if (saveToGallery != null) getString(R.string.save_ok) else getString(
R.string.save_fail
)
}
saveResultToast(msg)
}
}
}
private fun saveResultToast(message: String) {
runOnUiThread {
Toast.makeText(
this@SetWallpaperActivity,
message,
Toast.LENGTH_SHORT
).show()
vb.loadingView.isVisible = false
}
}
private fun downloadIm(result: (filePath: String?) -> Unit) {
val filePath = Other.getInternalFilePath(this)
Log.d(MyApp.TAG, "-------------start downloadIm =${filePath}")
DealData.downloadFile(data.download, filePath) { isSave, inputStream ->
val file = File(filePath)
if (!isSave || inputStream == null || !file.exists()) {
Log.d(MyApp.TAG, "-------------downloadIm fail")
result.invoke(null)
return@downloadFile
}
MyDataBase.getInstance().getBeanDao().updateBean(data.apply {
downloadFilePath = filePath
})
result.invoke(filePath)
Log.d(
MyApp.TAG,
"-------------updateBean=${data.id} ${data.downloadFilePath} thread=${Thread.currentThread().name}"
)
}
}
private fun initView() {
vb.run {
data.run {
authorName.text = user.name
Log.d(MyApp.TAG, "-------------load reguar initView")
loadRegular()
Glide.with(MyApp.application)
.asDrawable()
.load(user.header_medium)
.thumbnail(0.2f)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.error(R.drawable.placeholder)
.placeholder(R.drawable.placeholder)
.listener(object : RequestListener<Drawable>{
override fun onLoadFailed(
e: GlideException?,
model: Any?,
target: Target<Drawable>,
isFirstResource: Boolean
): Boolean {
vb.loadingView.isVisible = false
return false
}
override fun onResourceReady(
resource: Drawable,
model: Any,
target: Target<Drawable>?,
dataSource: DataSource,
isFirstResource: Boolean
): Boolean {
vb.loadingView.isVisible = false
return false
}
})
.into(header)
}
}
}
private fun loadRegular() {
Glide.with(MyApp.application)
.asDrawable()
.load(data.urls.regular)
.thumbnail(0.2f)
.error(R.drawable.placeholder)
.skipMemoryCache(true)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.placeholder(R.drawable.placeholder)
.into(vb.preview)
}
private fun initBar() {
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
vb.root.systemUiVisibility =
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
}
override fun onPause() {
super.onPause()
Log.d(MyApp.TAG, "-------------onPause")
CoroutineScope(Dispatchers.IO).launch {
vb.imLove.isSelected.let {
MyDataBase.getInstance().getBeanDao().updateBean(data.apply {
love = it
})
}
}
}
override fun onClick(v: View?) {
v?.run {
vb.run {
when (v) {
back -> {
finish()
}
imLove -> {
imLove.isSelected = !imLove.isSelected
}
imSetWallpaper -> {
if (dialog == null) {
dialog = SelectDialog {
vb.loadingView.visibility = View.VISIBLE
Log.d(MyApp.TAG, "-------------isVisible ${Thread.currentThread().name}")
data.downloadFilePath?.let { filePath ->
val file = File(filePath)
if (file.exists()) {
launchSet(it, file)
} else {
downloadIm { downloadpath ->
if (downloadpath == null) {
saveResultToast(getString(R.string.download_exception))
} else {
val file1 = File(downloadpath)
launchSet(it, file1)
}
}
}
} ?: run {
downloadIm { downloadpath ->
if (downloadpath == null) {
saveResultToast(getString(R.string.download_exception))
} else {
val file1 = File(downloadpath)
launchSet(it, file1)
}
}
}
}
}
if (!isFinishing) {
dialog?.show(supportFragmentManager, "")
}
}
imDownload -> {
if (Other.requestPermission(this@SetWallpaperActivity, code)) {
startSaveIm()
} else {
}
}
else -> {}
}
}
}
}
private fun launchSet(type: Int, file: File) {
CoroutineScope(Dispatchers.Main).launch {
var msg: String = try {
startSetWallpaper(type, file)
getString(R.string.set_success)
} catch (ex: Exception) {
getString(R.string.set_fail)
}
saveResultToast(msg)
Log.d(MyApp.TAG, "------------end SetWallpaper")
}
}
private fun startSetWallpaper(type: Int, file: File) {
Log.d(MyApp.TAG, "------------start SetWallpaper")
when (type) {
SelectDialog.clickType_home -> {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
wallpaperManager.setStream(file.inputStream())
} else {
wallpaperManager.setStream(
file.inputStream(),
null,
true,
WallpaperManager.FLAG_SYSTEM
)
}
}
SelectDialog.clickType_lock -> {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
wallpaperManager.setStream(file.inputStream())
} else {
wallpaperManager.setStream(
file.inputStream(),
null,
true,
WallpaperManager.FLAG_LOCK
)
}
}
SelectDialog.clickType_both -> {
wallpaperManager.setStream(file.inputStream())
}
}
}
override fun onDestroy() {
super.onDestroy()
dialog?.dismiss()
Glide.with(MyApp.application).clear(vb.preview)
}
}

View File

@ -0,0 +1,107 @@
package com.wall.photography.wallpaper.adapter
import android.content.Context
import android.graphics.drawable.Drawable
import android.util.Log
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.bumptech.glide.load.DataSource
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.load.engine.GlideException
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
import com.bumptech.glide.request.RequestListener
import com.bumptech.glide.request.RequestOptions
import com.bumptech.glide.request.target.Target
import com.wall.photography.wallpaper.MyApp
import com.wall.photography.wallpaper.R
import com.wall.photography.wallpaper.data.Bean
import com.wall.photography.wallpaper.databinding.ItemCategoryBinding
import com.wall.photography.wallpaper.databinding.ItemHomeBinding
import com.wall.photography.wallpaper.manager.DealData
import com.wall.photography.wallpaper.manager.Other
class AdapterCategory(private var mContext: Context,private var goList:(item:Bean)->Unit) : RecyclerView.Adapter<AdapterCategory.VH>() {
private var data: List<Bean> = emptyList()
inner class VH(var vb: ItemCategoryBinding) : RecyclerView.ViewHolder(vb.root)
fun updateData(newData: List<Bean>) {
val diffCallback = DiffCallback(data, newData)
val calculateDiff = DiffUtil.calculateDiff(diffCallback)
calculateDiff.dispatchUpdatesTo(this)
data = newData
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH {
val inflate = ItemCategoryBinding.inflate(LayoutInflater.from(parent.context),parent,false)
return VH(inflate)
}
override fun getItemCount(): Int = data.size
override fun onBindViewHolder(holder: VH, position: Int) {
data[position].run {
holder.vb.tvName.text = cateName
holder.vb.cardView.setOnClickListener {
goList.invoke(this)
}
Log.d(MyApp.TAG, "--------AdapterCategory-----urls.small=${urls.small}")
Glide.with(MyApp.application)
.asDrawable()
.load(urls.small)
.thumbnail(0.2f)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.error(R.drawable.placeholder)
.placeholder(R.drawable.placeholder)
.listener(object : RequestListener<Drawable> {
override fun onLoadFailed(
e: GlideException?,
model: Any?,
target: Target<Drawable>,
isFirstResource: Boolean
): Boolean {
Log.d(MyApp.TAG, "----------AdapterCategory-onLoadFailed----urls.thumb=${urls.thumb} e=${e?.message}")
return false
}
override fun onResourceReady(
resource: Drawable,
model: Any,
target: Target<Drawable>?,
dataSource: DataSource,
isFirstResource: Boolean
): Boolean {
return false
}
})
.into(holder.vb.wallpaper)
}
}
inner class DiffCallback(var old: List<Bean>, var new: List<Bean>) : DiffUtil.Callback() {
override fun getOldListSize(): Int = old.size
override fun getNewListSize(): Int = new.size
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val thumb = old[oldItemPosition].urls.thumb
val newThumb = new[newItemPosition].urls.thumb
return thumb == newThumb
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val love = old[oldItemPosition].love
val newLove = new[newItemPosition].love
return love == newLove
}
}
}

View File

@ -0,0 +1,135 @@
package com.wall.photography.wallpaper.adapter
import android.content.Context
import android.graphics.drawable.Drawable
import android.util.Log
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.ImageView
import androidx.paging.PagingDataAdapter
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import com.bumptech.glide.Glide
import com.bumptech.glide.load.DataSource
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.load.engine.GlideException
import com.bumptech.glide.request.RequestListener
import com.bumptech.glide.request.target.Target
import com.wall.photography.wallpaper.MyApp
import com.wall.photography.wallpaper.R
import com.wall.photography.wallpaper.data.Bean
import com.wall.photography.wallpaper.databinding.ItemHomeBinding
import com.wall.photography.wallpaper.manager.Other
class AdapterHomePaing(
private var isLove: Boolean = false,
private var mContext: Context,
var clickAction: (data: Bean) -> Unit
) : PagingDataAdapter<Bean, AdapterHomePaing.VH>(diff) {
// private var data: List<Bean> = emptyList()
inner class VH(var vb: ItemHomeBinding) : RecyclerView.ViewHolder(vb.root)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH {
val inflate = ItemHomeBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return VH(inflate)
}
override fun onViewRecycled(holder: VH) {
super.onViewRecycled(holder)
Glide.with(MyApp.application).clear(holder.vb.wallpaper)
Log.d(MyApp.TAG, "----------onViewRecycled")
}
override fun onBindViewHolder(holder: VH, position: Int) {
Log.d(MyApp.TAG, "----------onBindViewHolder---- position=${position}")
val item = getItem(position)
item?.run {
var curheight: Int
if (!isLove) {
if (position <= 1) {
curheight = Other.dpToPx(200, mContext)
} else if (position <= 3) {
curheight = Other.dpToPx(120, mContext)
} else if (position % 3 == 0) {
curheight = Other.dpToPx(240, mContext)
} else {
curheight = Other.dpToPx(200, mContext)
}
holder.vb.wallpaper.run {
layoutParams = layoutParams.apply {
height = curheight
width = RecyclerView.LayoutParams.MATCH_PARENT
}
}
}
Log.d(
MyApp.TAG,
"----------onLoad----- urls.small=${urls.small}"
)
Glide.with(MyApp.application)
.asDrawable()
.load(urls.small)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.thumbnail(0.2f)
.error(R.drawable.placeholder)
.skipMemoryCache(true)
.centerCrop()
.placeholder(R.drawable.placeholder)
.listener(object : RequestListener<Drawable> {
override fun onLoadFailed(
e: GlideException?,
model: Any?,
target: Target<Drawable>,
isFirstResource: Boolean
): Boolean {
Log.d(
MyApp.TAG,
"----------onLoadFailed-----urls.thumb=${urls.regular} e=${e?.message}"
)
return false
}
override fun onResourceReady(
resource: Drawable,
model: Any,
target: Target<Drawable>?,
dataSource: DataSource,
isFirstResource: Boolean
): Boolean {
return false
}
})
.into(holder.vb.wallpaper)
holder.vb.cardView.setOnClickListener {
clickAction.invoke(this)
}
}
}
companion object {
private val diff = object : DiffUtil.ItemCallback<Bean>() {
override fun areItemsTheSame(oldItem: Bean, newItem: Bean): Boolean {
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: Bean, newItem: Bean): Boolean {
return oldItem.love == newItem.love
}
}
}
}

View File

@ -0,0 +1,107 @@
package com.wall.photography.wallpaper.adapter
import android.content.Context
import android.graphics.drawable.Drawable
import android.util.Log
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.bumptech.glide.load.DataSource
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.load.engine.GlideException
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
import com.bumptech.glide.request.RequestListener
import com.bumptech.glide.request.RequestOptions
import com.bumptech.glide.request.target.Target
import com.wall.photography.wallpaper.MyApp
import com.wall.photography.wallpaper.R
import com.wall.photography.wallpaper.data.Bean
import com.wall.photography.wallpaper.databinding.ItemCategoryBinding
import com.wall.photography.wallpaper.databinding.ItemHomeBinding
import com.wall.photography.wallpaper.databinding.ItemLoveBinding
import com.wall.photography.wallpaper.manager.DealData
import com.wall.photography.wallpaper.manager.Other
class AdapterLoves(private var mContext: Context, private var goList:(item:Bean)->Unit) : RecyclerView.Adapter<AdapterLoves.VH>() {
private var data: List<Bean> = emptyList()
inner class VH(var vb: ItemLoveBinding) : RecyclerView.ViewHolder(vb.root)
fun updateData(newData: List<Bean>) {
val diffCallback = DiffCallback(data, newData)
val calculateDiff = DiffUtil.calculateDiff(diffCallback)
calculateDiff.dispatchUpdatesTo(this)
data = newData
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH {
val inflate = ItemLoveBinding.inflate(LayoutInflater.from(parent.context),parent,false)
return VH(inflate)
}
override fun getItemCount(): Int = data.size
override fun onBindViewHolder(holder: VH, position: Int) {
data[position].run {
holder.vb.cardView.setOnClickListener {
goList.invoke(this)
}
Log.d(MyApp.TAG, "--------LoveAdapter-----urls.regular=${urls.regular}")
Glide.with(MyApp.application)
.asDrawable()
.diskCacheStrategy(DiskCacheStrategy.ALL)
.load(urls.regular)
.thumbnail(0.2f)
.error(R.drawable.placeholder)
.placeholder(R.drawable.placeholder)
.listener(object : RequestListener<Drawable> {
override fun onLoadFailed(
e: GlideException?,
model: Any?,
target: Target<Drawable>,
isFirstResource: Boolean
): Boolean {
Log.d(MyApp.TAG, "----------LoveAdapter-onLoadFailed----urls.thumb=${urls.thumb} e=${e?.message}")
return false
}
override fun onResourceReady(
resource: Drawable,
model: Any,
target: Target<Drawable>?,
dataSource: DataSource,
isFirstResource: Boolean
): Boolean {
return false
}
})
.into(holder.vb.loveWallpaper)
}
}
inner class DiffCallback(var old: List<Bean>, var new: List<Bean>) : DiffUtil.Callback() {
override fun getOldListSize(): Int = old.size
override fun getNewListSize(): Int = new.size
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val thumb = old[oldItemPosition].urls.thumb
val newThumb = new[newItemPosition].urls.thumb
return thumb == newThumb
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val love = old[oldItemPosition].love
val newLove = new[newItemPosition].love
return love == newLove
}
}
}

View File

@ -0,0 +1,12 @@
package com.wall.photography.wallpaper.data
import java.io.Serializable
class Author(
var name: String,
var portfolio_url: String,
var authorHtml: String,
var header_large: String,
var header_medium: String,
var header_small: String
) : Serializable

View File

@ -0,0 +1,25 @@
package com.wall.photography.wallpaper.data
import androidx.room.Entity
import androidx.room.PrimaryKey
import androidx.room.TypeConverters
import com.wall.photography.wallpaper.MyApp
import com.wall.photography.wallpaper.room.AuthorTypeCovert
import com.wall.photography.wallpaper.room.UrlsTypeCovert
import java.io.Serializable
@TypeConverters(AuthorTypeCovert::class, UrlsTypeCovert::class)
@Entity(tableName = MyApp.TABLE_NAME)
class Bean(
@PrimaryKey(autoGenerate = true)
var id: Int = 0,
var alt_description: String,
var links: String,
var urls: URLS,
var user: Author,
var love: Boolean = false,
var cateName: String,
var download: String,
var downloadFilePath: String? = null
) : Serializable

View File

@ -0,0 +1,8 @@
package com.wall.photography.wallpaper.data
class Links(
var download: String,
var download_location: String,
var html: String
) {
}

View File

@ -0,0 +1,12 @@
package com.wall.photography.wallpaper.data
import java.io.Serializable
class URLS(
var full: String,
var raw: String,
var regular: String,
var small: String,
var thumb: String
) :Serializable{
}

View File

@ -0,0 +1,70 @@
package com.wall.photography.wallpaper.fragment
import android.content.Intent
import androidx.lifecycle.ViewModelProvider
import android.os.Bundle
import android.util.Log
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import com.wall.photography.wallpaper.MyApp
import com.wall.photography.wallpaper.R
import com.wall.photography.wallpaper.activivty.ListActivity
import com.wall.photography.wallpaper.adapter.AdapterCategory
import com.wall.photography.wallpaper.databinding.FragmentCategoryBinding
import com.wall.photography.wallpaper.databinding.FragmentHomeBinding
import com.wall.photography.wallpaper.manager.MyItemDecoration
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
class CategoryFragment : Fragment() {
companion object {
fun newInstance() = CategoryFragment()
}
private var viewModel: CategoryViewModel? = null
private lateinit var vb: FragmentCategoryBinding
private lateinit var mAdapter: AdapterCategory
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
vb = FragmentCategoryBinding.inflate(layoutInflater)
return vb.root
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
viewModel = ViewModelProvider(this)[CategoryViewModel::class.java]
CoroutineScope(Dispatchers.IO).launch {
viewModel?.getData()
}
mAdapter = AdapterCategory(requireContext()){
startActivity(Intent(requireActivity(),ListActivity::class.java).apply {
putExtra(ListActivity.KEY,it.cateName)
})
}
viewModel?.data?.observe(requireActivity()) {
Log.d(MyApp.TAG, "----------category ${it.size}- ${Thread.currentThread().name}")
mAdapter.updateData(it)
}
vb.categoryRecycler.run {
adapter = mAdapter
layoutManager = GridLayoutManager(requireContext(), 2)
addItemDecoration(MyItemDecoration(requireContext(), 8, 8, 0))
}
}
fun refreshCate(){
CoroutineScope(Dispatchers.IO).launch {
viewModel?.getData()
}
}
}

View File

@ -0,0 +1,35 @@
package com.wall.photography.wallpaper.fragment
import android.util.Log
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.wall.photography.wallpaper.MyApp
import com.wall.photography.wallpaper.R
import com.wall.photography.wallpaper.data.Bean
import com.wall.photography.wallpaper.room.MyDataBase
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
class CategoryViewModel : ViewModel() {
var data: MutableLiveData<List<Bean>> = MutableLiveData()
suspend fun getData() {
return run {
val mutableListOf = mutableListOf<Bean>()
val strings = MyApp.application.resources.getStringArray(R.array.category_name)
for (i in 1 until 8) {
val categoryCovert =
MyDataBase.getInstance().getBeanDao().getCategoryCovert(strings[i])
Log.d(MyApp.TAG, "-------categoryCovert=${categoryCovert?.cateName} ${Thread.currentThread().name}")
if (categoryCovert != null) {
mutableListOf.add(categoryCovert)
}
}
withContext(Dispatchers.Main){
data.value = mutableListOf
}
}
}
}

View File

@ -0,0 +1,98 @@
package com.wall.photography.wallpaper.fragment
import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.paging.PagingData
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.OnScrollListener
import androidx.recyclerview.widget.StaggeredGridLayoutManager
import com.bumptech.glide.Glide
import com.wall.photography.wallpaper.MyApp
import com.wall.photography.wallpaper.activivty.SetWallpaperActivity
import com.wall.photography.wallpaper.adapter.AdapterCategory
import com.wall.photography.wallpaper.adapter.AdapterHomePaing
import com.wall.photography.wallpaper.data.Bean
import com.wall.photography.wallpaper.databinding.FragmentHomeBinding
import com.wall.photography.wallpaper.manager.MyItemDecoration
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
class HomeFragment : Fragment() {
companion object {
fun newInstance() = HomeFragment()
}
private lateinit var viewModel: HomeViewModel
private lateinit var adapterHomepaging: AdapterHomePaing
private lateinit var vb: FragmentHomeBinding
private var list :MutableList<PagingData<Bean>> = mutableListOf()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
vb = FragmentHomeBinding.inflate(layoutInflater)
return vb.root
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
Log.d(MyApp.TAG, "------------onActivityCreated homeFragment=$this")
if (savedInstanceState != null) {
// 恢复之前的状态
Log.d(MyApp.TAG, "------------onActivityCreated homeFragment list=${list.size}")
}
adapterHomepaging = AdapterHomePaing(mContext = requireContext()) {
startActivity(Intent(requireActivity(), SetWallpaperActivity::class.java).apply {
putExtra(SetWallpaperActivity.KEY_DATA, it)
})
}
viewModel = ViewModelProvider(this)[HomeViewModel::class.java]
CoroutineScope(Dispatchers.Main).launch {
viewModel.getPagingData(MyApp.strings[0]).collectLatest {
Log.d(MyApp.TAG, "------------collectLatest ")
adapterHomepaging.submitData(it)
list.add(it)
}
}
initWallpaper()
}
private fun initWallpaper() {
vb.wallpaperRecycler.run {
layoutManager = StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL)
adapter = adapterHomepaging
addItemDecoration(MyItemDecoration(requireContext(), 8, 8, 0))
addOnScrollListener(object : OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
Glide.with(context).resumeRequests()
} else {
Glide.with(context).pauseRequests()
}
}
})
}
}
}

View File

@ -0,0 +1,38 @@
package com.wall.photography.wallpaper.fragment
//import com.wall.photography.wallpaper.paging.MySource
import android.util.Log
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import androidx.paging.cachedIn
import com.wall.photography.wallpaper.MyApp
import com.wall.photography.wallpaper.data.Bean
import com.wall.photography.wallpaper.paging.MySource
import kotlinx.coroutines.flow.Flow
import java.util.Random
class HomeViewModel : ViewModel() {
fun getPaging(cateName:String): Flow<PagingData<Bean>> {
val nextInt = Random().nextInt(100)
Log.d(MyApp.TAG,"----------nextInt=${nextInt}")
return Pager(
config = PagingConfig(pageSize = MyApp.pageSize, initialLoadSize = MyApp.pageSize),
pagingSourceFactory = { MySource(cateName,nextInt) }
).flow
}
fun getPagingData(cateName:String): Flow<PagingData<Bean>> {
return getPaging(cateName).cachedIn(viewModelScope)
}
}

View File

@ -0,0 +1,108 @@
package com.wall.photography.wallpaper.fragment
import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.paging.LoadState
import androidx.paging.PagingData
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.StaggeredGridLayoutManager
import com.wall.photography.wallpaper.MyApp
import com.wall.photography.wallpaper.activivty.SetWallpaperActivity
import com.wall.photography.wallpaper.adapter.AdapterHomePaing
import com.wall.photography.wallpaper.adapter.AdapterLoves
import com.wall.photography.wallpaper.data.Bean
import com.wall.photography.wallpaper.databinding.FragmentLoveBinding
import com.wall.photography.wallpaper.manager.MyItemDecoration
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
class LoveFragment : Fragment() {
companion object {
fun newInstance() = LoveFragment()
}
private lateinit var viewModel: LoveViewModel
private lateinit var vb: FragmentLoveBinding
private lateinit var adapterLove: AdapterLoves
private var adapterLovePaging: AdapterHomePaing? = null
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
vb = FragmentLoveBinding.inflate(layoutInflater)
return vb.root
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
viewModel = ViewModelProvider(this).get(LoveViewModel::class.java)
viewModel.setData()
initWallpaper()
}
private fun initWallpaper() {
// adapterLove = AdapterLoves(requireContext()) {
// startActivity(Intent(requireActivity(), SetWallpaperActivity::class.java).apply {
// putExtra(SetWallpaperActivity.KEY_DATA, it)
// })
// }
adapterLovePaging = AdapterHomePaing(true,requireContext()) {
startActivity(Intent(requireActivity(), SetWallpaperActivity::class.java).apply {
putExtra(SetWallpaperActivity.KEY_DATA, it)
})
}.apply {
addLoadStateListener {
if(it.source.refresh is LoadState.NotLoading && adapterLovePaging?.itemCount == 0){
Log.d(MyApp.TAG, "------------love-空")
vb.tvNoLove.isVisible = true
vb.loveRecycler.isVisible = false
}else{
Log.d(MyApp.TAG, "------------love-不空")
vb.tvNoLove.isVisible = false
vb.loveRecycler.isVisible = true
}
}
}
CoroutineScope(Dispatchers.Main).launch {
viewModel.getPagingData().collectLatest {
Log.d(MyApp.TAG, "------------love-update 分页")
adapterLovePaging?.submitData(it)
}
}
// viewModel.getLoves().observe(viewLifecycleOwner) {
// Log.d(MyApp.TAG, "-----------love-update ")
// adapterLove.updateData(it)
// }
vb.loveRecycler.run {
layoutManager = LinearLayoutManager(requireContext())
adapter = adapterLovePaging
addItemDecoration(MyItemDecoration(requireContext(), 8, 8, 0))
}
}
fun refreshLoves(){
adapterLovePaging?.refresh()
}
override fun onResume() {
super.onResume()
Log.d(MyApp.TAG, "------------love onResume")
}
}

View File

@ -0,0 +1,44 @@
package com.wall.photography.wallpaper.fragment
import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import androidx.paging.cachedIn
import com.wall.photography.wallpaper.MyApp
import com.wall.photography.wallpaper.data.Bean
import com.wall.photography.wallpaper.paging.MyLoveSource
//import com.wall.photography.wallpaper.paging.MyLoveSource
import com.wall.photography.wallpaper.room.BeanDao
import com.wall.photography.wallpaper.room.MyDataBase
import kotlinx.coroutines.flow.Flow
class LoveViewModel : ViewModel() {
private lateinit var userDao: BeanDao
private lateinit var allLove: LiveData<List<Bean>>
private fun getPaging(): Flow<PagingData<Bean>> {
return Pager(
config = PagingConfig(pageSize = MyApp.pageSize, initialLoadSize = MyApp.pageSize),
pagingSourceFactory = { MyLoveSource() }
).flow
}
fun getPagingData(): Flow<PagingData<Bean>> {
return getPaging().cachedIn(viewModelScope)
}
fun setData() {
userDao = MyDataBase.getInstance().getBeanDao()
allLove = userDao.getLoveWallpaper()
}
fun getLoves(): LiveData<List<Bean>> {
return allLove
}
}

View File

@ -0,0 +1,318 @@
package com.wall.photography.wallpaper.manager
import android.content.ContentValues
import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.drawable.BitmapDrawable
import android.net.Uri
import android.os.Build
import android.provider.MediaStore
import android.util.Log
import android.widget.Toast
import com.wall.photography.wallpaper.MyApp
import com.wall.photography.wallpaper.data.Author
import com.wall.photography.wallpaper.data.Bean
import com.wall.photography.wallpaper.data.URLS
import okhttp3.Call
import okhttp3.Callback
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import org.json.JSONArray
import java.io.BufferedReader
import java.io.ByteArrayOutputStream
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.IOException
import java.io.InputStream
import java.io.InputStreamReader
import java.io.OutputStream
import java.io.StringWriter
import java.nio.charset.StandardCharsets
object DealData {
fun getJsonString(input: InputStream): String {
val stringWriter = StringWriter()
val charArray = CharArray(input.available())
var l = 0;
val bufReader = BufferedReader(InputStreamReader(input, StandardCharsets.UTF_8))
while (bufReader.read(charArray).also {
l = it
} != -1) {
stringWriter.write(charArray, 0, l)
}
return stringWriter.toString()
}
fun getBean(context: Context, jsonStr: String, cateName: String): MutableList<Bean> {
val jsonArray = JSONArray(jsonStr)
var data = mutableListOf<Bean>()
for (i in 0 until jsonArray.length()) {
val get = jsonArray.getJSONObject(i)
val description = get.getString("alt_description")
val links = get.getJSONObject("links")
val download = links.getString("download")
val downloadLocation = links.getString("download_location")
val html = links.getString("html")
val urls = get.getJSONObject("urls")
val full = urls.getString("full")
val raw = urls.getString("raw")
val regular = urls.getString("regular")
val small = urls.getString("small")
val thumb = urls.getString("thumb")
val users = get.getJSONObject("user")
val portfolio_url = users.getString("portfolio_url")
val name = users.getString("name")
val header_large = users.getString("header_large")
val header_medium = users.getString("header_medium")
val header_small = users.getString("header_small")
val user_html = users.getString("authorHtml")
val bean = Bean(
download = download,
cateName = cateName,
alt_description = description,
links = html,
urls = URLS(full, raw, regular, small, thumb),
user = Author(
name,
portfolio_url,
user_html,
header_large,
header_medium,
header_small
)
)
data.add(bean)
}
return data
}
fun downloadFile(
url: String,
savePath: String,
result: (Boolean, input: InputStream?) -> Unit
) {
var client = OkHttpClient()
var request = Request.Builder().url(url).build()
var call = client.newCall(request)
call.enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
result.invoke(false, null)
}
override fun onResponse(call: Call, response: Response) {
response.body?.run {
val byteStream = byteStream()
val writeFile = writeFile(byteStream, savePath)
result.invoke(writeFile, byteStream)
} ?: run {
result.invoke(false, null)
}
}
})
}
fun writeFile(input: InputStream, filePath: String): Boolean {
try {
val byte = ByteArray(4096)
val output = ByteArrayOutputStream()
var l: Int
while (input.read(byte).also { l = it } != -1) {
output.write(byte, 0, l)
}
val fileDe = File(filePath)
if (!fileDe.exists()) {
fileDe.createNewFile()
}
val fileOutputStream = FileOutputStream(filePath)
fileOutputStream.write(output.toByteArray())
output.close()
fileOutputStream.close()
return true
} catch (ex: Exception) {
Log.d("-----------", "---------ex=" + ex.message)
return false
}
}
private fun saveImageQ(imagePath: String, context: Context): Boolean {
return try {
val contentResolver = context.contentResolver
val contentValues =
getImageContentValues(File(imagePath))
val localUri = contentResolver.insert(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
contentValues
)
// 拷贝到指定uri,如果没有这步操作android11不会在相册显示
val out = context.contentResolver.openOutputStream(localUri!!)
copyFile(imagePath, out)
contentValues.clear();
contentValues.put(MediaStore.MediaColumns.IS_PENDING, 0);
contentValues.putNull(MediaStore.MediaColumns.DATE_EXPIRES);
contentResolver.update(localUri, contentValues, null, null);
true
} catch (e: Exception) {
e.printStackTrace()
false
}
}
private fun getImageContentValues(paramFile: File): ContentValues {
val time = System.currentTimeMillis()
val localContentValues = ContentValues()
localContentValues.put(MediaStore.Images.Media.TITLE, paramFile.name)
localContentValues.put(MediaStore.Images.Media.DISPLAY_NAME, paramFile.name)
localContentValues.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")
localContentValues.put(MediaStore.Images.Media.RELATIVE_PATH, getSavePath())
// localContentValues.put(MediaStore.Images.Media.DATE_MODIFIED, time / 1000)
// localContentValues.put(MediaStore.Images.Media.DATE_ADDED, time / 1000)
return localContentValues
}
private fun getSavePath(): String {
return ""
}
private fun saveImage(imagePath: String, context: Context): Boolean {
return try {
val path = getSavePath() + File.separator + File(imagePath).name
val newFile = File(path)
if (!newFile!!.parentFile.exists()) {
newFile!!.parentFile.mkdirs()
}
val out = newFile.outputStream()
copyFile(imagePath, out)
val uri = Uri.fromFile(newFile)
context.sendBroadcast(Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri))
out.close()
true
} catch (e: Exception) {
e.printStackTrace()
false
}
}
private fun copyFile(oldPath: String, out: OutputStream?): Boolean {
try {
var bytesum = 0
var byteread = 0
val oldFile = File(oldPath)
if (oldFile.exists()) {
// 读入原文件
val inStream: InputStream = FileInputStream(oldPath)
val buffer = ByteArray(1444)
while (inStream.read(buffer).also { byteread = it } != -1) {
bytesum += byteread //字节数 文件大小
println(bytesum)
out!!.write(buffer, 0, byteread)
}
inStream.close()
out!!.close()
return true
} else {
}
} catch (e: java.lang.Exception) {
e.printStackTrace()
}
return false
}
fun saveToGallery(context: Context, photoFile: File): Uri? {
val displayName = "${System.currentTimeMillis()}.jpg"
val contentValues = ContentValues().apply {
put(MediaStore.Images.Media.DISPLAY_NAME, displayName)
put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
put(MediaStore.Images.Media.IS_PENDING, 1)
}
}
val contentResolver = context.contentResolver
val collectionUri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
} else {
MediaStore.Images.Media.EXTERNAL_CONTENT_URI
}
val imageUri = contentResolver.insert(collectionUri, contentValues)
imageUri?.let { uri ->
try {
contentResolver.openOutputStream(uri)?.use { outputStream ->
val inputStream = FileInputStream(photoFile)
inputStream.copyTo(outputStream)
inputStream.close()
outputStream.close()
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
contentValues.clear()
contentValues.put(MediaStore.Images.Media.IS_PENDING, 0)
contentResolver.update(uri, contentValues, null, null)
} else {
}
return uri
} catch (e: IOException) {
Log.d(MyApp.TAG, "-----------------e=${e.printStackTrace()}")
return null
}
} ?: run {
Log.d(MyApp.TAG, "----------------false")
return null
}
}
fun saveFreshAppImageToGallery(context: Context, bitmap: Bitmap) {
val bitmap = bitmap
val displayName = "${System.currentTimeMillis()}.jpg"
val values = ContentValues().apply {
put(MediaStore.Images.Media.DISPLAY_NAME, displayName)
put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")
put(MediaStore.Images.Media.DATE_ADDED, System.currentTimeMillis() / 1000)
}
val contentResolver = context.contentResolver
val collection =
MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
val item = contentResolver.insert(collection, values)
item?.let { uri ->
try {
val outputStream = contentResolver.openOutputStream(uri)
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream!!)
outputStream.close()
Log.d(MyApp.TAG, "------------------Save successfully!")
val mediaScanIntent = Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE)
mediaScanIntent.data = uri
context.sendBroadcast(mediaScanIntent)
} catch (e: Exception) {
Log.d(MyApp.TAG, "------------------Save Failed!")
e.printStackTrace()
}
}
}
}

View File

@ -0,0 +1,85 @@
package com.wall.photography.wallpaper.manager
import android.content.Context
import android.graphics.Rect
import android.view.View
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.StaggeredGridLayoutManager
class MyItemDecoration(
private var context: Context,
private var v: Int,
private var h: Int,
private var ex: Int
) :
RecyclerView.ItemDecoration() {
// item占满一行时该item是否需要左右间距
var mVerticalSpacing = true
var mHorizontalSpaci = true
init {
v = dpToPx(v, context)
h = dpToPx(h, context)
ex = dpToPx(ex, context)
}
override fun getItemOffsets(
outRect: Rect,
view: View,
parent: RecyclerView,
state: RecyclerView.State
) {
super.getItemOffsets(outRect, view, parent, state)
val position = parent.getChildAdapterPosition(view)
var spanCount = 1
var spanSize = 1
var spanIndex = 0
parent.layoutManager?.run {
if (this is StaggeredGridLayoutManager) {
spanCount = this.spanCount
(view.layoutParams as StaggeredGridLayoutManager.LayoutParams)?.run {
if (isFullSpan) spanSize = spanCount
spanIndex = this.spanIndex
}
} else if (this is GridLayoutManager) {
spanCount = this.spanCount
spanSize = this.spanSizeLookup.getSpanSize(position)
spanIndex = (view.layoutParams as GridLayoutManager.LayoutParams).spanIndex
} else if (this is LinearLayoutManager) {
outRect.left = v
outRect.right = v
outRect.bottom = h
}
}
if (spanSize == spanCount) {
outRect.left =
if (mVerticalSpacing) v + ex else 0
outRect.right =
if (mVerticalSpacing) v + ex else 0
outRect.bottom = if (mHorizontalSpaci) h else 0
} else {
val itemAllSpacing = (v * (spanCount + 1) + ex * 2) / spanCount
val left = v * (spanIndex + 1) - itemAllSpacing * spanIndex + ex
val right = itemAllSpacing - left
outRect.left = left
outRect.right = right
outRect.bottom = h
}
}
fun dpToPx(dp: Int, context: Context): Int {
val density = context.resources.displayMetrics.density
return (dp * density).toInt()
}
}

View File

@ -0,0 +1,55 @@
package com.wall.photography.wallpaper.manager
import android.Manifest
import android.app.Activity
import android.content.Context
import android.content.pm.PackageManager
import android.os.Build
import androidx.core.app.ActivityCompat
object Other {
fun dpToPx(dp: Int, context: Context): Int {
val density = context.resources.displayMetrics.density
return (dp * density).toInt()
}
fun pxToPx(context: Context, pxValue: Float): Int {
val scale = context.resources.displayMetrics.density
return (pxValue / scale + 0.5f).toInt()
}
fun getInternalFilePath(context: Context): String {
val filePath = "${context.cacheDir}/${System.currentTimeMillis()}.jpg"
return filePath
}
fun requestPermission(context: Activity, requestCode: Int): Boolean {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
return true
}
return if (context.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(
context,
arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
requestCode
)
false
} else {
true
}
}
fun getNavigationBarHeight(context: Context): Int {
val resources = context.resources
val resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android")
return if (resourceId > 0) {
resources.getDimensionPixelSize(resourceId)
} else 0
}
}

View File

@ -0,0 +1,54 @@
package com.wall.photography.wallpaper.paging
import android.util.Log
import androidx.paging.PagingSource
import androidx.paging.PagingState
import com.wall.photography.wallpaper.MyApp
import com.wall.photography.wallpaper.data.Bean
import com.wall.photography.wallpaper.room.MyDataBase
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class MyLoveSource( var diff:Int = 0) : PagingSource<Int, Bean>() {
override fun getRefreshKey(state: PagingState<Int, Bean>): Int? {
return null
}
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Bean> {
val page = params.key ?: 1
var pageSize = params.loadSize
if(page == 1){
pageSize = MyApp.pageSize
}
val indexStart = pageSize * (page - 1)+diff
// val rspRepository = MyApp.data_Wallpaper?.subList(indexStart, indexStart + pageSize)
return withContext(Dispatchers.IO) {
val rspRepository =
MyDataBase.getInstance().getBeanDao().getPagingLoveWallpaper(true,pageSize, indexStart)
val items = rspRepository
val preKey = if (page > 1) page - 1 else null
val nextKey = if (items.isNotEmpty()) page + 1 else null
Log.d(
MyApp.TAG,
"-----------------Love--indexStart=${indexStart} pageSize=${pageSize} page=${page} items.size=${items.size}"
)
withContext(Dispatchers.Main) {
Log.d(
MyApp.TAG,
"--------------Love-----return thread=${Thread.currentThread().name}"
)
LoadResult.Page(items, preKey, nextKey)
}
}
}
}

View File

@ -0,0 +1,52 @@
package com.wall.photography.wallpaper.paging
import android.util.Log
import androidx.paging.PagingSource
import androidx.paging.PagingState
import com.wall.photography.wallpaper.MyApp
import com.wall.photography.wallpaper.data.Bean
import com.wall.photography.wallpaper.room.MyDataBase
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class MySource(var name:String,var diff:Int = 0) : PagingSource<Int, Bean>() {
override fun getRefreshKey(state: PagingState<Int, Bean>): Int? {
return null
}
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Bean> {
val page = params.key ?: 1
var pageSize = params.loadSize
if(page == 1){
pageSize = MyApp.pageSize
}
val indexStart = pageSize * (page - 1)+diff
// val rspRepository = MyApp.data_Wallpaper?.subList(indexStart, indexStart + pageSize)
return withContext(Dispatchers.IO) {
val rspRepository =
MyDataBase.getInstance().getBeanDao().getNextWallpaper(name,pageSize, indexStart)
val items = rspRepository
val preKey = if (page > 1) page - 1 else null
val nextKey = if (items.isNotEmpty()) page + 1 else null
Log.d(
MyApp.TAG,
"-------------------indexStart=${indexStart} pageSize=${pageSize} page=${page} items.size=${items.size}"
)
withContext(Dispatchers.Main) {
Log.d(
MyApp.TAG,
"-------------------return thread=${Thread.currentThread().name}"
)
LoadResult.Page(items, preKey, nextKey)
}
}
}
}

View File

@ -0,0 +1,19 @@
package com.wall.photography.wallpaper.room
import androidx.room.TypeConverter
import com.google.gson.Gson
import com.wall.photography.wallpaper.data.Author
class AuthorTypeCovert {
@TypeConverter
fun getAuthor(value: String): Author {
return Gson().fromJson(value, Author::class.java)
}
@TypeConverter
fun authorToString(author: Author): String {
return Gson().toJson(author)
}
}

View File

@ -0,0 +1,45 @@
package com.wall.photography.wallpaper.room
import androidx.lifecycle.LiveData
import androidx.paging.PagingSource
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import androidx.room.Update
import com.wall.photography.wallpaper.data.Bean
@Dao
interface BeanDao {
@Insert(onConflict = OnConflictStrategy.IGNORE)
fun insertBean(bean: Bean)
@Update
fun updateBean(bean: Bean)
@Query("delete from wall_paper")
fun deleteTBData()
@Query("select * from wall_paper order by id desc")
fun getAllWallpaper(): List<Bean>
@Query("select * from wall_paper where id==:myId")
fun getCurWallpaper(myId: Int): Bean
@Query("select * from wall_paper where love==:isLove order by id desc ")
fun getLoveWallpaper(isLove:Boolean = true): LiveData<List<Bean>>
@Query("select * from wall_paper where love==:isLove order by id desc LIMIT :pageSize OFFSET :pageNUmber")
suspend fun getPagingLoveWallpaper(isLove:Boolean = true,pageSize: Int, pageNUmber: Int): List<Bean>
@Query("select * from wall_paper where cateName==:cateName order by id desc LIMIT 1 OFFSET 2")
suspend fun getCategoryCovert(cateName: String): Bean?
@Query("select * from wall_paper where cateName==:cateName order by id desc LIMIT :pageSize OFFSET :pageNUmber")
suspend fun getNextWallpaper(cateName: String, pageSize: Int, pageNUmber: Int): List<Bean>
}

View File

@ -0,0 +1,33 @@
package com.wall.photography.wallpaper.room
import android.content.Context
import androidx.room.Database
import androidx.room.Room.databaseBuilder
import androidx.room.RoomDatabase
import com.wall.photography.wallpaper.MyApp
import com.wall.photography.wallpaper.data.Bean
@Database(entities = [Bean::class], version = MyApp.ROOM_Version, exportSchema = false)
abstract class MyDataBase : RoomDatabase() {
abstract fun getBeanDao(): BeanDao
companion object {
private var myDataBase: MyDataBase? = null
fun getInstance(): MyDataBase {
return if (myDataBase == null) {
myDataBase = databaseBuilder(
MyApp.application,
MyDataBase::class.java, MyApp.ROOM_NAME
).build()
myDataBase!!
}else{
myDataBase!!
}
}
}
}

View File

@ -0,0 +1,24 @@
package com.wall.photography.wallpaper.room
import androidx.room.TypeConverter
import com.google.gson.Gson
import com.wall.photography.wallpaper.data.URLS
class UrlsTypeCovert {
@TypeConverter
fun getUrls(value: String): URLS {
return Gson().fromJson(value, URLS::class.java)
}
@TypeConverter
fun urlsToString(urls: URLS): String {
return Gson().toJson(urls)
}
}

View File

@ -0,0 +1,32 @@
package com.wall.photography.wallpaper.viewmode
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import androidx.paging.cachedIn
import com.wall.photography.wallpaper.MyApp
import com.wall.photography.wallpaper.data.Bean
import com.wall.photography.wallpaper.paging.MySource
import kotlinx.coroutines.flow.Flow
class ListActivityViewModel : ViewModel() {
fun getPaging(cateName:String): Flow<PagingData<Bean>> {
return Pager(
config = PagingConfig(pageSize = MyApp.pageSize, initialLoadSize = MyApp.pageSize),
pagingSourceFactory = { MySource(cateName) }
).flow
}
fun getPagingData(cateName:String): Flow<PagingData<Bean>> {
return getPaging(cateName).cachedIn(viewModelScope)
}
}

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/gray" android:state_checked="false" />
<item android:color="@color/main_color" />
</selector>

View File

@ -0,0 +1,30 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="64dp"
android:height="64dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:pathData="M941.3,553.2 L240.4,553.2l347.2,347.2 -58.3,58.3L82.7,512 529.4,65.3l58.3,58.3 -347.2,347.2 700.9,0L941.3,553.2zM199.2,512l0.6,0.6 0,-1.1L199.2,512z"
android:fillColor="#020202"/>
</vector>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@color/set_loading_color"/>
</shape>

View File

@ -0,0 +1,170 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="64dp"
android:height="64dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:pathData="M487,347.4C487,424.5 424.5,487 347.4,487L237.6,487C160.5,487 98,424.5 98,347.4L98,237.6C98,160.5 160.5,98 237.6,98h109.9C424.5,98 487,160.5 487,237.6v109.9zM487,786.4C487,863.5 424.5,926 347.4,926L237.6,926C160.5,926 98,863.5 98,786.4L98,676.6C98,599.5 160.5,537 237.6,537h109.9C424.5,537 487,599.5 487,676.6v109.9zM926,347.4C926,424.5 863.5,487 786.4,487L676.6,487C599.5,487 537,424.5 537,347.4L537,237.6C537,160.5 599.5,98 676.6,98h109.9C863.5,98 926,160.5 926,237.6v109.9zM730.7,533.6c-107.9,0 -195.3,87.4 -195.3,195.3s87.4,195.3 195.3,195.3S926,836.8 926,728.9s-87.4,-195.3 -195.3,-195.3zM730.7,843.3c-63.2,0 -114.4,-51.2 -114.4,-114.4S667.5,614.5 730.7,614.5 845.1,665.7 845.1,728.9 793.9,843.3 730.7,843.3z"
android:fillColor="#666666"/>
</vector>

View File

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="64dp"
android:height="64dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:pathData="M819.2,405.6c0,-169.7 -137.5,-307.2 -307.2,-307.2s-307.2,137.5 -307.2,307.2c-113.1,0 -204.8,91.7 -204.8,204.8s91.7,204.8 204.8,204.8h102.4V733.3h-102.4c-67.8,0 -122.9,-55.1 -122.9,-122.9 0,-67.8 55.1,-122.9 122.9,-122.9h81.9v-81.9c0,-124.2 101.1,-225.3 225.3,-225.3 124.2,0 225.3,101.1 225.3,225.3v81.9h81.9c67.8,0 122.9,55.1 122.9,122.9 0,67.8 -55.1,122.9 -122.9,122.9h-102.4v81.9h102.4c113.1,0 204.8,-91.7 204.8,-204.8s-91.7,-204.8 -204.8,-204.8z"
android:fillColor="#040000"/>
<path
android:pathData="M511.4,925.5l221.3,-238 -64.4,-60 -110.8,119.2V410.2h-92.2v336.5L354.5,627.5l-64.4,60z"
android:fillColor="#040000"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="64dp"
android:height="64dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:pathData="M938.6,420.1 L544.9,89.1c-0.1,-0.1 -0.1,-0.1 -0.2,-0.2 -0.4,-0.3 -0.8,-0.7 -1.2,-1 -0.2,-0.1 -0.3,-0.3 -0.5,-0.4 -0.5,-0.4 -1,-0.7 -1.5,-1.1 -0.1,-0.1 -0.2,-0.1 -0.2,-0.2 -0.6,-0.4 -1.2,-0.8 -1.8,-1.2 -0,-0 -0,-0 -0,-0 -18.3,-11.6 -42.9,-10.2 -59.7,4.1L86,420.1c-20.6,17.3 -22.7,47.4 -4.7,67.1 18,19.8 49.3,21.8 69.8,4.5l33.9,-28.5 0,279.2c0,65.7 55.6,119.2 124,119.2l73.9,0c27.3,0 49.5,-21.3 49.5,-47.6L432.5,589.5c0,-13.3 11.2,-24.1 25,-24.1l109.5,0c13.8,0 25.1,10.8 25.1,24.1l0,219c0,3.3 0.3,6.5 1,9.7l1.2,5.6c4.8,22 25,37.9 48.4,37.9l72.7,0c68.4,0 124,-53.5 124,-119.2l0,-279.2 33.9,28.5c9.4,7.9 21,11.7 32.6,11.7 13.8,0 27.5,-5.5 37.3,-16.3C961.2,467.4 959.1,437.4 938.6,420.1z"
android:fillColor="#272636"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="64dp"
android:height="64dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:fillColor="#FF000000"
android:pathData="M533.5,268.3q33.8,-42 71.7,-75.8 32.8,-27.6 74.2,-50.2t86.5,-19.5q63.5,5.1 106,30.2t67.6,63.5 34.3,87 6.1,99.8 -17.9,97.8 -36.9,87 -48.6,74.8 -53.2,62q-41,42 -85.5,78.3t-85,62.5 -73.7,41.5 -51.7,15.4q-20.5,1 -52.2,-14.3t-69.6,-41.5 -79.9,-62 -82.9,-75.8q-26.6,-25.6 -57.3,-59.4t-57.9,-74.2 -46.6,-87.6 -21.5,-100.4 11.3,-99.8 39.9,-83.5 65.5,-62 88.1,-35.3q24.6,-5.1 49.2,-1.5t48.1,12.3 45.1,22 41,27.6q45.1,33.8 86,80.9z"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="64dp"
android:height="64dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:pathData="M938.7,553.9L938.7,768c0,64.8 -52.5,117.3 -117.3,117.3L202.7,885.3c-64.8,0 -117.3,-52.5 -117.3,-117.3L85.3,256c0,-64.8 52.5,-117.3 117.3,-117.3h618.7c64.8,0 117.3,52.5 117.3,117.3v297.9zM874.7,479.3L874.7,256a53.3,53.3 0,0 0,-53.3 -53.3L202.7,202.7a53.3,53.3 0,0 0,-53.3 53.3v344.5A290.1,290.1 0,0 1,192 597.3a286.9,286.9 0,0 1,183.3 65.8C427,528.4 556.9,437.3 704,437.3c65.7,0 127,16.8 170.7,42zM874.7,561.5c-5.3,-8.3 -21.1,-21.7 -43.6,-32.9C796.8,511.5 753,501.3 704,501.3c-121.8,0 -229.1,76.3 -270.4,188.7 -2.7,7.4 -7.4,20.3 -14,38.6 -7.7,21.3 -34.5,28.1 -51.4,13.1 -16.4,-14.6 -28.6,-25.1 -36.1,-31.1A222.9,222.9 0,0 0,192 661.3c-14.5,0 -28.7,1.4 -42.7,4.1L149.3,768a53.3,53.3 0,0 0,53.3 53.3h618.7a53.3,53.3 0,0 0,53.3 -53.3L874.7,561.5zM320,480a96,96 0,1 1,0 -192,96 96,0 0,1 0,192zM320,416a32,32 0,1 0,0 -64,32 32,0 0,0 0,64z"
android:fillColor="#000000"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="64dp"
android:height="64dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:pathData="M951.7,428.8c-0.2,-1.8 -0.4,-3.3 -0.7,-4.2v-0.8l-0.4,-2.2c-7.1,-34.6 -30,-56.9 -58.5,-56.9h-4.7c-48.6,0 -88,-39.6 -88,-88 0,-11.2 5.2,-27.1 7.4,-32.7 13.8,-32.2 -0.9,-68.9 -35,-87.6L664.5,95.7l-2,-0.7c-8,-2.6 -17.3,-5.7 -27.7,-5.7 -19.4,0 -41.2,9 -54.7,22.5 -16.9,16.7 -51.2,41.6 -71.6,41.6 -20.3,0 -54.7,-24.8 -71.6,-41.6 -14.4,-14.2 -34.3,-22.5 -54.7,-22.5 -10.7,0 -19.7,3 -27.7,5.7l-1.8,0.7 -112.5,60.9 -0.7,0.4c-27.3,17.1 -38.4,56.3 -24.5,87.4l0.2,0.4 0.2,0.4c2.2,4.9 9,21.5 9,36 0,48.6 -39.6,88 -88,88h-4.7c-29.8,0 -52.1,22 -58.5,57.3l-0.4,2v0.7c0,1 -0.4,2.4 -0.7,4.2 -2.5,15.1 -8.5,50.7 -8.5,79.8 0,29.1 5.9,64.7 8.5,79.8 0.2,1.8 0.4,3.3 0.7,4.2v0.8l0.4,2.2c7.1,34.6 30,56.9 58.5,56.9h2.4c48.6,0 88,39.6 88,88 0,11.2 -5.2,27.1 -7.4,32.7 -13.3,30.3 -0.7,69.2 28.7,88.8l0.8,0.4 106,59 2,0.7c8,2.6 17.1,5.7 27.5,5.7 22.2,0 42.2,-8.5 54.7,-22.5 1.2,-0.9 2.4,-2.1 3.8,-3.3 12.8,-11.2 47.2,-40.8 69.9,-40.8 16.9,0 45.2,17.7 73.7,46.2 14.4,14.2 34.3,22.5 54.7,22.5 13.8,0 24,-3.8 35.6,-9.5l0.4,-0.2 108.7,-60.1 0.4,-0.4c27.3,-17.1 38.4,-56.3 24.5,-87.4l-0.2,-0.4 -0.2,-0.4c-0.2,-0.1 -8.7,-17.8 -7.1,-33.7l0.2,-1v-1c0,-48.6 39.6,-88 88,-88h5c29.8,0 52.1,-22 58.5,-57.3l0.4,-2v-0.7c0.2,-0.8 0.4,-2 0.7,-3.6 2.6,-14.7 8.6,-49 8.6,-80.4 0.2,-29 -5.7,-64.5 -8.3,-79.6zM511.7,651.2c-76.9,0 -139.2,-62.3 -139.2,-139.2s62.3,-139.2 139.2,-139.2S650.9,435.1 650.9,512s-62.3,139.2 -139.2,139.2z"
android:fillColor="@color/main_color"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="64dp"
android:height="64dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:pathData="M667.8,117.3C832.9,117.3 938.7,249.7 938.7,427.9c0,138.3 -125.1,290.5 -371.6,461.6a96.8,96.8 0,0 1,-110.2 0C210.4,718.4 85.3,566.1 85.3,427.9 85.3,249.7 191.1,117.3 356.2,117.3c59.6,0 100.1,20.8 155.8,68.1C567.7,138.2 608.2,117.3 667.8,117.3zM667.8,180.5c-41.4,0 -70.3,15.2 -117,55 -2.2,1.8 -14.4,12.4 -17.9,15.4a32.3,32.3 0,0 1,-41.8 0c-3.5,-3 -15.8,-13.5 -17.9,-15.4 -46.7,-39.9 -75.5,-55 -117,-55C230.2,180.5 149.3,281.3 149.3,426.7 149.3,537.6 262.9,675.2 493.6,834.8a32.4,32.4 0,0 0,36.7 0C761.1,675.3 874.7,537.6 874.7,426.7c0,-145.4 -80.9,-246.2 -206.9,-246.2z"
android:fillColor="#000000"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="64dp"
android:height="64dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:pathData="M667.8,117.3C832.9,117.3 938.7,249.7 938.7,427.9c0,138.3 -125.1,290.5 -371.6,461.6a96.8,96.8 0,0 1,-110.2 0C210.4,718.4 85.3,566.1 85.3,427.9 85.3,249.7 191.1,117.3 356.2,117.3c59.6,0 100.1,20.8 155.8,68.1C567.7,138.2 608.2,117.3 667.8,117.3z"
android:fillColor="#000000"/>
</vector>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/im_love_selected" android:state_selected="true" />
<item android:drawable="@drawable/im_love_normal" android:state_selected="false" />
</selector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="12dp"/>
<stroke android:color="@color/main_color" android:width="1dp"/>
</shape>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/white" />
<corners android:radius="9dp" />
</shape>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="18dp" />
<solid android:color="@color/category_aplah" />
</shape>

View File

@ -0,0 +1,86 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".activivty.AboutActivity">
<ImageView
android:id="@+id/back"
android:layout_width="44dp"
android:layout_height="44dp"
android:layout_marginStart="12dp"
android:layout_marginTop="23dp"
android:padding="10dp"
android:src="@drawable/back"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.cardview.widget.CardView
android:id="@+id/card_logo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="63dp"
app:cardCornerRadius="6dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/back">
<ImageView
android:layout_width="86dp"
android:layout_height="86dp"
android:src="@mipmap/logo" />
</androidx.cardview.widget.CardView>
<TextView
android:id="@+id/tv_version"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:text="@string/version"
android:textColor="@color/black"
android:textSize="15sp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/card_logo" />
<LinearLayout
android:id="@+id/layout_rate"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="25dp"
android:layout_marginTop="35dp"
android:layout_marginEnd="35dp"
android:orientation="vertical"
android:padding="5dp"
app:layout_constraintTop_toBottomOf="@id/tv_version">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/rate_us"
android:textColor="@color/black"
android:textSize="16sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:text="@string/rate_us_content"
android:textColor="@color/black"
android:textSize="14sp" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:background="@color/set_loading_color"
app:layout_constraintTop_toBottomOf="@id/layout_rate"
android:layout_marginStart="25dp"
android:layout_marginEnd="25dp"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingTop="10dp"
tools:context=".activivty.ListActivity">
<ImageView
android:id="@+id/back"
android:layout_width="44dp"
android:layout_height="44dp"
android:layout_marginStart="12dp"
android:padding="10dp"
android:src="@drawable/back" />
<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/black"
android:textSize="18sp"
android:textStyle="bold"
android:layout_centerHorizontal="true"
android:padding="10dp" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/tv_name"
android:layout_marginTop="10dp" />
</RelativeLayout>

View File

@ -0,0 +1,62 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/bg_color"
tools:context=".activivty.MainActivity">
<RelativeLayout
android:id="@+id/logo_layout"
android:layout_width="match_parent"
android:layout_height="70dp"
app:layout_constraintTop_toTopOf="parent">
<androidx.cardview.widget.CardView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginStart="12dp"
app:cardCornerRadius="10dp">
<ImageView
android:id="@+id/logo_icon"
android:layout_width="45dp"
android:layout_height="45dp"
android:src="@mipmap/logo" />
</androidx.cardview.widget.CardView>
<ImageView
android:id="@+id/icon_set"
android:layout_width="54dp"
android:layout_height="54dp"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:layout_marginStart="22dp"
android:layout_marginEnd="12dp"
android:padding="13dp"
android:src="@drawable/icon_setting" />
</RelativeLayout>
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/viewpager2"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@id/bottom_navigation"
app:layout_constraintTop_toBottomOf="@id/logo_layout" />
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottom_navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/white"
app:itemIconTint="@color/bottom_color"
app:labelVisibilityMode="unlabeled"
app:layout_constraintBottom_toBottomOf="parent"
app:menu="@menu/bottom_navigation" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,117 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".activivty.SetWallpaperActivity">
<ImageView
android:id="@+id/preview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop" />
<ImageView
android:id="@+id/back"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginStart="12dp"
android:layout_marginTop="36dp"
android:padding="8dp"
android:background="@drawable/back_bg"
android:src="@drawable/back"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="25dp"
android:layout_marginEnd="25dp"
android:id="@+id/relayout"
android:background="@drawable/set_wallpaper_shape"
android:padding="20dp"
app:layout_constraintBottom_toBottomOf="parent">
<androidx.cardview.widget.CardView
android:id="@+id/header_card"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:cardCornerRadius="8dp">
<ImageView
android:id="@+id/header"
android:layout_width="33dp"
android:layout_height="33dp"
android:src="@mipmap/ic_launcher" />
</androidx.cardview.widget.CardView>
<TextView
android:id="@+id/author_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="@id/header_card"
android:layout_alignBottom="@id/header_card"
android:layout_marginStart="12dp"
android:layout_toEndOf="@id/header_card"
android:gravity="center"
android:text="@string/app_name"
android:textColor="@color/black"
android:textSize="18sp"
android:textStyle="bold" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/header_card"
android:layout_marginTop="10dp"
android:orientation="horizontal">
<ImageView
android:id="@+id/im_download"
android:layout_width="35dp"
android:layout_height="45dp"
android:layout_weight="1"
android:paddingTop="10dp"
android:paddingBottom="10dp"
android:src="@drawable/icon_download" />
<ImageView
android:id="@+id/im_set_wallpaper"
android:layout_width="35dp"
android:layout_height="45dp"
android:layout_weight="1"
android:paddingTop="10dp"
android:paddingBottom="10dp"
android:src="@drawable/icon_set_wallapper" />
<ImageView
android:id="@+id/im_love"
android:layout_width="35dp"
android:layout_height="45dp"
android:layout_weight="1"
android:paddingTop="10dp"
android:paddingBottom="10dp"
android:src="@drawable/love_selector" />
</LinearLayout>
</RelativeLayout>
<FrameLayout
android:id="@+id/loading_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"
android:clickable="true"
android:background="@color/set_loading_color">
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:indeterminateTint="@color/white" />
</FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".activivty.MyWelComeActivity">
<ImageView
android:id="@+id/icon"
android:layout_width="90dp"
android:layout_height="50dp"
android:src="@mipmap/welcome_icon"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/welcome_text"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:layout_marginTop="12dp"
android:textColor="@color/white"
android:textSize="18sp"
app:layout_constraintTop_toBottomOf="@id/icon" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,13 @@
<?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.CategoryFragment">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/category_recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</FrameLayout>

View File

@ -0,0 +1,13 @@
<?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.HomeFragment">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/wallpaper_recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</FrameLayout>

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
android:orientation="vertical"
android:gravity="center"
tools:context=".fragment.LoveFragment">
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/loveRecycler"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/black"
android:textSize="16sp"
android:id="@+id/tv_no_love"
android:visibility="gone"
android:layout_centerInParent="true"
android:text="@string/no_love"/>
</RelativeLayout>

View File

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="260dp"
android:id="@+id/card_view"
app:cardCornerRadius="8dp">
<ImageView
android:id="@+id/wallpaper"
android:layout_width="match_parent"
android:layout_height="260dp"
android:scaleType="centerCrop" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_gravity="bottom"
android:background="@color/category_aplah">
<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="@string/app_name"
android:textColor="@color/white"
android:textSize="17sp" />
</FrameLayout>
</androidx.cardview.widget.CardView>
</FrameLayout>

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:id="@+id/card_view"
android:layout_height="wrap_content"
app:cardCornerRadius="8dp">
<ImageView
android:id="@+id/wallpaper"
android:layout_width="match_parent"
android:scaleType="centerCrop"
android:layout_height="200dp" />
</androidx.cardview.widget.CardView>

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/card_view"
android:layout_width="match_parent"
android:layout_height="260dp"
app:cardCornerRadius="8dp">
<ImageView
android:id="@+id/love_wallpaper"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="centerCrop" />
</androidx.cardview.widget.CardView>

View File

@ -0,0 +1,59 @@
<?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="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
android:background="@drawable/select_dialog_bg"
android:orientation="vertical">
<TextView
android:id="@+id/home"
android:layout_width="match_parent"
android:layout_height="54dp"
android:gravity="center"
android:text="@string/home"
android:textColor="@color/black"
android:textSize="18sp" />
<View
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:layout_gravity="center_horizontal"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
android:background="@color/main_color" />
<TextView
android:id="@+id/lock"
android:layout_width="match_parent"
android:layout_height="54dp"
android:gravity="center"
android:text="@string/lock"
android:textColor="@color/black"
android:textSize="18sp" />
<View
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:layout_gravity="center_horizontal"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
android:background="@color/main_color" />
<TextView
android:id="@+id/both"
android:layout_width="match_parent"
android:layout_height="54dp"
android:gravity="center"
android:text="@string/both"
android:textColor="@color/black"
android:textSize="18sp" />
</LinearLayout>
</FrameLayout>

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/home"
android:icon="@drawable/icon_home"
android:title="." />
<item
android:id="@+id/category"
android:icon="@drawable/icon_category"
android:title="" />
<item
android:id="@+id/love"
android:icon="@drawable/icon_love"
android:title="" />
</menu>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 982 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 575 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -0,0 +1,7 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Base.Theme.PhotographyWallpaper" parent="Theme.Material3.DayNight.NoActionBar">
<!-- Customize your dark theme here. -->
<!-- <item name="colorPrimary">@color/my_dark_primary</item> -->
</style>
</resources>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
<color name="main_color">#414548</color>
<color name="bg_color">#F5F5F5</color>
<color name="gray">#cdd5d5</color>
<color name="category_aplah">#80FFFFFF</color>
<color name="set_loading_color">#80414548</color>
</resources>

View File

@ -0,0 +1,30 @@
<resources>
<string name="app_name">Photography Wallpapers</string>
<string name="welcome_text">Wallpaper</string>
<string name="grant_permission">Please grant permission to use related functions</string>
<string name="save_ok">Save to album successfully</string>
<string name="save_fail">Sorry,Save to album fail</string>
<string name="save_exception">Sorry,An error occurred while saving. Please try again.</string>
<string name="download_exception">Sorry,An error occurred while downloading. Please try again.</string>
<string name="home">Home screen</string>
<string name="lock">Lock screen</string>
<string name="both">Both</string>
<string name="set_success">Set wallpaper successfully</string>
<string name="set_fail">Failed to set wallpaper, please try again</string>
<string name="no_love">You haven not collected any wallpapers yet</string>
<string name="version">App Version V%s</string>
<string name="rate_us">Rate App</string>
<string name="rate_us_content">Consider giving us a review on the Google Play Store</string>
<string name="google_link">https://play.google.com/store/apps/details?id=%s</string>
<string name="to_gp_exception">An exception occurred. Please try again later.</string>
<string-array name="category_name">
<item>Wallpaper</item>
<item>Animals</item>
<item>Experimental</item>
<item>Film</item>
<item>Nature</item>
<item>Street</item>
<item>Patterns</item>
<item>Travel</item>
</string-array>
</resources>

View File

@ -0,0 +1,15 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Base.Theme.PhotographyWallpaper" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your light theme here. -->
<item name="colorPrimary">@color/main_color</item>
</style>
<style name="Theme.PhotographyWallpaper" parent="Base.Theme.PhotographyWallpaper" />
<style name="Welcome.Theme" parent="Base.Theme.PhotographyWallpaper">
<item name="android:windowBackground">@mipmap/wel_bg</item>
<item name="windowNoTitle">true</item>
</style>
</resources>

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?><!--
Sample backup rules file; uncomment and customize as necessary.
See https://developer.android.com/guide/topics/data/autobackup
for details.
Note: This file is ignored for devices older that API 31
See https://developer.android.com/about/versions/12/backup-restore
-->
<full-backup-content>
<!--
<include domain="sharedpref" path="."/>
<exclude domain="sharedpref" path="device.xml"/>
-->
</full-backup-content>

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?><!--
Sample data extraction rules file; uncomment and customize as necessary.
See https://developer.android.com/about/versions/12/backup-restore#xml-changes
for details.
-->
<data-extraction-rules>
<cloud-backup>
<!-- TODO: Use <include> and <exclude> to control what is backed up.
<include .../>
<exclude .../>
-->
</cloud-backup>
<!--
<device-transfer>
<include .../>
<exclude .../>
</device-transfer>
-->
</data-extraction-rules>

View File

@ -0,0 +1,17 @@
package com.wall.photography.wallpaper
import org.junit.Test
import org.junit.Assert.*
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}

Some files were not shown because too many files have changed in this diff Show More