V1.0.0(1) 完成版
BIN
app/LumaWallpaper.jks
Normal file
@ -1,8 +1,13 @@
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.android.application)
|
||||
alias(libs.plugins.kotlin.android)
|
||||
id("com.google.devtools.ksp")
|
||||
id("kotlin-parcelize")
|
||||
}
|
||||
|
||||
val timestamp: String = SimpleDateFormat("MM_dd_HH_mm").format(Date())
|
||||
android {
|
||||
namespace = "com.wallpaper.lumawallpaper"
|
||||
compileSdk = 35
|
||||
@ -12,14 +17,21 @@ android {
|
||||
minSdk = 23
|
||||
targetSdk = 35
|
||||
versionCode = 1
|
||||
versionName = "1.0"
|
||||
|
||||
versionName = "1.0.0"
|
||||
setProperty(
|
||||
"archivesBaseName",
|
||||
"Luma Wallpaper_V" + versionName + "(${versionCode})_$timestamp"
|
||||
)
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
viewBinding = true
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
isMinifyEnabled = false
|
||||
isMinifyEnabled = true
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro"
|
||||
@ -45,4 +57,18 @@ dependencies {
|
||||
testImplementation(libs.junit)
|
||||
androidTestImplementation(libs.androidx.junit)
|
||||
androidTestImplementation(libs.androidx.espresso.core)
|
||||
|
||||
implementation("com.github.bumptech.glide:glide:4.16.0")
|
||||
annotationProcessor("com.github.bumptech.glide:compiler:4.16.0")
|
||||
|
||||
implementation("androidx.room:room-runtime:2.7.1")
|
||||
ksp("androidx.room:room-compiler:2.7.1")
|
||||
implementation("androidx.room:room-ktx:2.7.1")
|
||||
|
||||
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.9.0")
|
||||
|
||||
implementation ("com.squareup.okhttp3:okhttp:4.12.0")
|
||||
|
||||
implementation ("androidx.activity:activity-ktx:1.10.1")
|
||||
implementation ("androidx.lifecycle:lifecycle-runtime-ktx:2.9.0")
|
||||
}
|
||||
15
app/proguard-rules.pro
vendored
@ -18,4 +18,17 @@
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
#-renamesourcefileattribute SourceFile
|
||||
|
||||
-keepclassmembers class com.wallpaper.lumawallpaper.App {
|
||||
public static final java.lang.String DB_NAME;
|
||||
public static final int DB_VERSION;
|
||||
}
|
||||
|
||||
-keepclassmembers class * {
|
||||
@androidx.room.Query <methods>;
|
||||
}
|
||||
|
||||
-keep class com.wallpaper.lumawallpaper.database.AppDatabase { *; }
|
||||
-keep class com.wallpaper.lumawallpaper.database.LumaEntity { *; }
|
||||
-keep class com.wallpaper.lumawallpaper.database.LumaDao { *; }
|
||||
@ -2,7 +2,13 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission android:name="android.permission.SET_WALLPAPER" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
|
||||
<application
|
||||
android:name=".App"
|
||||
android:allowBackup="true"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
android:fullBackupContent="@xml/backup_rules"
|
||||
@ -13,7 +19,16 @@
|
||||
android:theme="@style/Theme.LumaWallpaper"
|
||||
tools:targetApi="31">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:name=".ui.activity.LumaActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".ui.activity.CategoryActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".ui.activity.MainActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".ui.activity.SplashActivity"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
12208
app/src/main/assets/wallpaper.json
Normal file
51
app/src/main/java/com/wallpaper/lumawallpaper/App.kt
Normal file
@ -0,0 +1,51 @@
|
||||
package com.wallpaper.lumawallpaper
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import androidx.core.content.edit
|
||||
import com.wallpaper.lumawallpaper.database.AppDatabase
|
||||
import com.wallpaper.lumawallpaper.utils.JsonUtils
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class App : Application() {
|
||||
companion object {
|
||||
@Volatile
|
||||
private lateinit var instance: App
|
||||
|
||||
const val DB_VERSION = 1
|
||||
const val DB_NAME = "luma_database"
|
||||
private const val PREF_NAME = "luma_preferences"
|
||||
private const val INIT_DATABASE = "InitDatabase"
|
||||
|
||||
@JvmStatic
|
||||
fun getContext(): Context {
|
||||
return instance.applicationContext
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
instance = this
|
||||
val preferences: SharedPreferences = getSharedPreferences(PREF_NAME, MODE_PRIVATE)
|
||||
val init = preferences.getBoolean(INIT_DATABASE, false)
|
||||
|
||||
if (!init) {
|
||||
initDatabase()
|
||||
preferences.edit() { putBoolean(INIT_DATABASE, true) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun initDatabase() {
|
||||
val lumaDao = AppDatabase.getDatabase(this).LumaDao()
|
||||
|
||||
val lumaEntityList = JsonUtils.parseJson("wallpaper.json")
|
||||
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
lumaDao.insertAll(lumaEntityList)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,20 +0,0 @@
|
||||
package com.wallpaper.lumawallpaper
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
setContentView(R.layout.activity_main)
|
||||
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
|
||||
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
|
||||
insets
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,6 @@
|
||||
package com.wallpaper.lumawallpaper.call
|
||||
|
||||
data class DownloadCall(
|
||||
val isSuccessful: Boolean,
|
||||
val message: String
|
||||
)
|
||||
@ -0,0 +1,30 @@
|
||||
package com.wallpaper.lumawallpaper.database
|
||||
|
||||
import android.content.Context
|
||||
import androidx.room.Database
|
||||
import androidx.room.Room
|
||||
import androidx.room.RoomDatabase
|
||||
import com.wallpaper.lumawallpaper.App
|
||||
|
||||
@Database(entities = [LumaEntity::class], version = App.DB_VERSION, exportSchema = false)
|
||||
abstract class AppDatabase : RoomDatabase() {
|
||||
|
||||
abstract fun LumaDao(): LumaDao
|
||||
|
||||
companion object {
|
||||
@Volatile
|
||||
private var INSTANCE: AppDatabase? = null
|
||||
|
||||
fun getDatabase(context: Context): AppDatabase {
|
||||
return INSTANCE ?: synchronized(this) {
|
||||
val instance = Room.databaseBuilder(
|
||||
context.applicationContext,
|
||||
AppDatabase::class.java,
|
||||
App.DB_NAME
|
||||
).build()
|
||||
INSTANCE = instance
|
||||
instance
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,60 @@
|
||||
package com.wallpaper.lumawallpaper.database
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import androidx.room.Update
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
interface LumaDao {
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun insertAll(lumaEntityList: List<LumaEntity>)
|
||||
|
||||
@Update
|
||||
suspend fun update(lumaEntity: LumaEntity)
|
||||
|
||||
@Query("SELECT * FROM LumaEntity WHERE isFavorite = 1")
|
||||
fun getFavoriteLumaEntities(): Flow<List<LumaEntity>>
|
||||
|
||||
@Query("SELECT * FROM LumaEntity WHERE id IN (SELECT MAX(id) FROM LumaEntity GROUP BY name)")
|
||||
fun getUniqueLumaEntities(): Flow<List<LumaEntity>>
|
||||
|
||||
@Query("SELECT * FROM LumaEntity WHERE name = :name ORDER BY id DESC")
|
||||
fun getLumaEntitiesByName(name: String): Flow<List<LumaEntity>>
|
||||
|
||||
@Query("SELECT isFavorite FROM LumaEntity WHERE source = :imagePath AND name = :name ")
|
||||
fun getFavoriteStatus(imagePath: String, name: String): Flow<Boolean>
|
||||
|
||||
|
||||
@Query("SELECT * FROM lumaentity WHERE id IN (SELECT MIN(id) FROM lumaentity GROUP BY name)")
|
||||
suspend fun getMinIdLumaEntities(): List<LumaEntity>
|
||||
|
||||
@Query("SELECT * FROM lumaentity WHERE id IN (:lumaEntityIds) LIMIT :pageSize OFFSET (:page - 1) * :pageSize")
|
||||
suspend fun getLumaEntitiesFromLumaMinIdPaged(
|
||||
page: Int,
|
||||
pageSize: Int,
|
||||
lumaEntityIds: List<Int>
|
||||
): List<LumaEntity>
|
||||
|
||||
@Query("SELECT COUNT(*) FROM lumaentity")
|
||||
suspend fun getTotalLumaEntities(): Int
|
||||
|
||||
@Query("SELECT * FROM lumaentity WHERE name = :name LIMIT :pageSize OFFSET (:page - 1) * :pageSize")
|
||||
suspend fun getLumaEntitiesByNamePaged(
|
||||
name: String,
|
||||
page: Int,
|
||||
pageSize: Int
|
||||
): List<LumaEntity>
|
||||
|
||||
@Query("SELECT * FROM lumaentity WHERE isFavorite = 1 LIMIT :pageSize OFFSET (:page - 1) * :pageSize")
|
||||
suspend fun getFavoriteLumaEntitiesPaged(page: Int, pageSize: Int): List<LumaEntity>
|
||||
|
||||
@Query("SELECT * FROM lumaentity WHERE isFavorite = 1")
|
||||
fun getFavoriteMagicEntities(): Flow<List<LumaEntity>>
|
||||
|
||||
@Query("SELECT COUNT(*) FROM lumaentity WHERE name = :name")
|
||||
suspend fun getTotalLumaEntitiesByName(name: String): Int
|
||||
}
|
||||
@ -0,0 +1,17 @@
|
||||
package com.wallpaper.lumawallpaper.database
|
||||
|
||||
import android.os.Parcelable
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
@Parcelize
|
||||
@Entity
|
||||
data class LumaEntity(
|
||||
@PrimaryKey(autoGenerate = true) val id: Int = 0,
|
||||
val name: String,
|
||||
val original: String,
|
||||
val previewThumb: String,
|
||||
val source: String,
|
||||
var isFavorite: Boolean
|
||||
) : Parcelable
|
||||
@ -0,0 +1,91 @@
|
||||
package com.wallpaper.lumawallpaper.ui.activity
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.wallpaper.lumawallpaper.R
|
||||
import com.wallpaper.lumawallpaper.databinding.ActivityCategoryBinding
|
||||
import com.wallpaper.lumawallpaper.ui.adapter.LumaAdapter
|
||||
import com.wallpaper.lumawallpaper.ui.viewmodel.LumaViewModel
|
||||
import com.wallpaper.lumawallpaper.utils.ItemDecoration
|
||||
|
||||
class CategoryActivity : AppCompatActivity() {
|
||||
private lateinit var binding: ActivityCategoryBinding
|
||||
private lateinit var adapter: LumaAdapter
|
||||
private lateinit var lumaViewModel: LumaViewModel
|
||||
private lateinit var name: String
|
||||
private val layoutManager by lazy { GridLayoutManager(this, 2) }
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = ActivityCategoryBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
enableEdgeToEdge()
|
||||
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
|
||||
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
|
||||
insets
|
||||
}
|
||||
initData()
|
||||
initEvent()
|
||||
}
|
||||
|
||||
private fun initData() {
|
||||
name = intent.getStringExtra("name").toString()
|
||||
|
||||
lumaViewModel = ViewModelProvider(this)[LumaViewModel::class.java]
|
||||
|
||||
binding.recyclerView.layoutManager = layoutManager
|
||||
|
||||
adapter = LumaAdapter(
|
||||
lumaViewModel,
|
||||
this,
|
||||
mutableListOf(),
|
||||
this,
|
||||
1
|
||||
)
|
||||
binding.recyclerView.adapter = adapter
|
||||
|
||||
val itemDecoration = ItemDecoration(20, 15, 20)
|
||||
binding.recyclerView.addItemDecoration(itemDecoration)
|
||||
|
||||
binding.recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
super.onScrolled(recyclerView, dx, dy)
|
||||
|
||||
val visibleItemCount = layoutManager.childCount
|
||||
val totalItemCount = layoutManager.itemCount
|
||||
val firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition()
|
||||
val isLoading = lumaViewModel.isLoading.value ?: false
|
||||
val hasMore = lumaViewModel.hasMore.value ?: true
|
||||
val threshold = 5
|
||||
|
||||
if (!isLoading && hasMore && (visibleItemCount + firstVisibleItemPosition + threshold) >= totalItemCount && firstVisibleItemPosition >= 0) {
|
||||
lumaViewModel.loadNextPage(name)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
lumaViewModel.lumaListLiveData.observe(this) { newList ->
|
||||
val isFirstLoad = adapter.itemCount == 0
|
||||
adapter.updateData(newList, !isFirstLoad)
|
||||
}
|
||||
|
||||
lumaViewModel.hasMore.observe(this) { hasMore ->
|
||||
adapter.setHasMoreData(hasMore)
|
||||
}
|
||||
|
||||
lumaViewModel.loadInitialDataByName(name)
|
||||
}
|
||||
|
||||
private fun initEvent() {
|
||||
binding.back.setOnClickListener {
|
||||
finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,208 @@
|
||||
package com.wallpaper.lumawallpaper.ui.activity
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.WallpaperManager
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.request.target.CustomTarget
|
||||
import com.bumptech.glide.request.transition.Transition
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import com.wallpaper.lumawallpaper.R
|
||||
import com.wallpaper.lumawallpaper.databinding.ActivityLumaBinding
|
||||
import com.wallpaper.lumawallpaper.database.LumaEntity
|
||||
import com.wallpaper.lumawallpaper.ui.viewmodel.LumaViewModel
|
||||
import com.wallpaper.lumawallpaper.utils.WallpaperUtil
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class LumaActivity : AppCompatActivity() {
|
||||
private lateinit var lumaBinding: ActivityLumaBinding
|
||||
private lateinit var imageSourceUrl: String
|
||||
private lateinit var originalImageUrl: String
|
||||
private lateinit var lumaEntity: LumaEntity
|
||||
private lateinit var imageTitle: String
|
||||
private lateinit var loadedBitmap: Bitmap
|
||||
private lateinit var utilHelper: WallpaperUtil
|
||||
private lateinit var wallpaperService: WallpaperManager
|
||||
private lateinit var viewModel: LumaViewModel
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.N)
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
lumaBinding = ActivityLumaBinding.inflate(layoutInflater)
|
||||
setContentView(lumaBinding.root)
|
||||
configureWindowInsets()
|
||||
initializeComponents()
|
||||
registerEventListeners()
|
||||
}
|
||||
|
||||
private fun configureWindowInsets() {
|
||||
ViewCompat.setOnApplyWindowInsetsListener(lumaBinding.main) { v, insets ->
|
||||
val navigationBars = insets.getInsets(WindowInsetsCompat.Type.navigationBars())
|
||||
v.setPadding(0, 0, 0, navigationBars.bottom)
|
||||
insets
|
||||
}
|
||||
}
|
||||
|
||||
private fun initializeComponents() {
|
||||
val receivedData: LumaEntity? = intent.getParcelableExtra("lumaEntity")
|
||||
if (receivedData == null) {
|
||||
finish()
|
||||
return
|
||||
}
|
||||
lumaEntity = receivedData
|
||||
imageSourceUrl = lumaEntity.source
|
||||
originalImageUrl = lumaEntity.original
|
||||
imageTitle = lumaEntity.name
|
||||
viewModel = ViewModelProvider(this)[LumaViewModel::class.java]
|
||||
utilHelper = WallpaperUtil(lumaBinding.progressBar, lumaBinding.view)
|
||||
wallpaperService = WallpaperManager.getInstance(this)
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.N)
|
||||
private fun registerEventListeners() {
|
||||
showProgress()
|
||||
lumaBinding.back.setOnClickListener { finishActivity() }
|
||||
lumaBinding.like.setOnClickListener {
|
||||
toggleFavoriteStatus()
|
||||
refreshFavoriteIcon()
|
||||
}
|
||||
lumaBinding.set.setOnClickListener { showWallpaperDialog() }
|
||||
lumaBinding.downPicture.setOnClickListener {
|
||||
showProgress()
|
||||
utilHelper.downloadWallpaper(this, originalImageUrl)
|
||||
}
|
||||
loadImageContent()
|
||||
monitorFavoriteChanges()
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(
|
||||
requestCode: Int, permissions: Array<String>, grantResults: IntArray
|
||||
) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||
if (requestCode == WallpaperUtil.PERMISSION_REQUEST_CODE && grantResults.isNotEmpty() &&
|
||||
grantResults[0] == PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
utilHelper.downloadWallpaper(this, originalImageUrl)
|
||||
} else {
|
||||
Toast.makeText(this, "Permission denied for storage", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("InflateParams")
|
||||
@RequiresApi(Build.VERSION_CODES.N)
|
||||
private fun showWallpaperDialog() {
|
||||
val bottomDialog = BottomSheetDialog(this)
|
||||
val dialogView = LayoutInflater.from(this).inflate(R.layout.set_dialog, null)
|
||||
dialogView.findViewById<View>(R.id.both).setOnClickListener {
|
||||
configureWallpaper(loadedBitmap, WallpaperManager.FLAG_SYSTEM or WallpaperManager.FLAG_LOCK)
|
||||
bottomDialog.dismiss()
|
||||
}
|
||||
dialogView.findViewById<View>(R.id.lock).setOnClickListener {
|
||||
configureWallpaper(loadedBitmap, WallpaperManager.FLAG_LOCK)
|
||||
bottomDialog.dismiss()
|
||||
}
|
||||
dialogView.findViewById<View>(R.id.desktop).setOnClickListener {
|
||||
configureWallpaper(loadedBitmap, WallpaperManager.FLAG_SYSTEM)
|
||||
bottomDialog.dismiss()
|
||||
}
|
||||
bottomDialog.setContentView(dialogView)
|
||||
bottomDialog.show()
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.N)
|
||||
private fun configureWallpaper(bitmap: Bitmap, flags: Int) {
|
||||
showProgress()
|
||||
lifecycleScope.launch {
|
||||
try {
|
||||
setWallpaperImage(bitmap, flags)
|
||||
withContext(Dispatchers.Main) {
|
||||
hideProgress()
|
||||
Toast.makeText(this@LumaActivity, "Wallpaper applied", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
withContext(Dispatchers.Main) {
|
||||
hideProgress()
|
||||
Toast.makeText(this@LumaActivity, "Failed to apply wallpaper", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.N)
|
||||
private suspend fun setWallpaperImage(bitmap: Bitmap, flags: Int) {
|
||||
withContext(Dispatchers.IO) {
|
||||
wallpaperService.setBitmap(bitmap, null, true, flags)
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadImageContent() {
|
||||
Glide.with(this)
|
||||
.asBitmap()
|
||||
.load(imageSourceUrl)
|
||||
.error(R.mipmap.placeholder)
|
||||
.override(1080, 1920)
|
||||
.centerInside()
|
||||
.into(object : CustomTarget<Bitmap>() {
|
||||
override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
|
||||
lumaBinding.imageView.setImageBitmap(resource)
|
||||
loadedBitmap = resource
|
||||
hideProgress()
|
||||
}
|
||||
override fun onLoadCleared(placeholder: Drawable?) {
|
||||
lumaBinding.imageView.setImageDrawable(placeholder)
|
||||
}
|
||||
override fun onLoadFailed(errorDrawable: Drawable?) {
|
||||
lumaBinding.imageView.setImageDrawable(errorDrawable)
|
||||
hideProgress()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun monitorFavoriteChanges() {
|
||||
lifecycleScope.launch {
|
||||
viewModel.getFavoriteStatus(imageSourceUrl, imageTitle).collect { refreshFavoriteIcon() }
|
||||
}
|
||||
}
|
||||
|
||||
private fun toggleFavoriteStatus() {
|
||||
lumaEntity.isFavorite = !lumaEntity.isFavorite
|
||||
viewModel.update(lumaEntity)
|
||||
}
|
||||
|
||||
private fun refreshFavoriteIcon() {
|
||||
lumaBinding.like.setImageResource(
|
||||
if (lumaEntity.isFavorite) R.drawable.like else R.drawable.un_like
|
||||
)
|
||||
}
|
||||
|
||||
private fun showProgress() {
|
||||
lumaBinding.progressBar.visibility = View.VISIBLE
|
||||
lumaBinding.view.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
private fun hideProgress() {
|
||||
lumaBinding.progressBar.visibility = View.GONE
|
||||
lumaBinding.view.visibility = View.GONE
|
||||
}
|
||||
|
||||
private fun finishActivity() {
|
||||
finish()
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,112 @@
|
||||
package com.wallpaper.lumawallpaper.ui.activity
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import com.google.android.material.tabs.TabLayout.OnTabSelectedListener
|
||||
import com.google.android.material.tabs.TabLayoutMediator
|
||||
import com.wallpaper.lumawallpaper.R
|
||||
import com.wallpaper.lumawallpaper.databinding.ActivityMainBinding
|
||||
import com.wallpaper.lumawallpaper.databinding.MainTabBinding
|
||||
import com.wallpaper.lumawallpaper.ui.adapter.MainAdapter
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
private lateinit var binding: ActivityMainBinding
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
this.enableEdgeToEdge()
|
||||
binding = ActivityMainBinding.inflate(layoutInflater)
|
||||
setContentView(binding.getRoot())
|
||||
|
||||
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v: View, insets: WindowInsetsCompat ->
|
||||
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
|
||||
insets
|
||||
}
|
||||
|
||||
initData()
|
||||
initEvent()
|
||||
}
|
||||
|
||||
private fun initData() {
|
||||
val adapter = MainAdapter(this)
|
||||
binding.mainViewpager2.setAdapter(adapter)
|
||||
}
|
||||
|
||||
private fun initEvent() {
|
||||
TabLayoutMediator(
|
||||
binding.mainTabLayout,
|
||||
binding.mainViewpager2
|
||||
) { tab: TabLayout.Tab, position: Int ->
|
||||
val mainTabBinding: MainTabBinding =
|
||||
MainTabBinding.inflate(LayoutInflater.from(this))
|
||||
tab.setCustomView(mainTabBinding.getRoot())
|
||||
setTab(mainTabBinding, position)
|
||||
}.attach()
|
||||
|
||||
binding.mainTabLayout.addOnTabSelectedListener(object : OnTabSelectedListener {
|
||||
override fun onTabSelected(tab: TabLayout.Tab) {
|
||||
updateTab(tab, true)
|
||||
}
|
||||
|
||||
override fun onTabUnselected(tab: TabLayout.Tab) {
|
||||
updateTab(tab, false)
|
||||
}
|
||||
|
||||
override fun onTabReselected(tab: TabLayout.Tab) {
|
||||
}
|
||||
|
||||
fun updateTab(tab: TabLayout.Tab, isSelected: Boolean) {
|
||||
if (tab.customView != null) {
|
||||
val mainTabBinding: MainTabBinding =
|
||||
MainTabBinding.bind(tab.customView!!)
|
||||
|
||||
val iconResId = getIconResource(tab.position, isSelected)
|
||||
mainTabBinding.image.setImageResource(iconResId)
|
||||
|
||||
val textColor = if (isSelected) R.color.white else R.color.black
|
||||
mainTabBinding.text.setTextColor(
|
||||
ContextCompat.getColor(
|
||||
this@MainActivity,
|
||||
textColor
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
private fun setTab(mainTabBinding: MainTabBinding, position: Int) {
|
||||
var iconResId = getIconResource(position, false)
|
||||
var textColorResId = R.color.black
|
||||
|
||||
when (position) {
|
||||
0 -> {
|
||||
mainTabBinding.text.text = "Home"
|
||||
iconResId = R.drawable.home
|
||||
textColorResId = R.color.white
|
||||
}
|
||||
|
||||
1 -> mainTabBinding.text.text = "Collection"
|
||||
}
|
||||
|
||||
mainTabBinding.image.setImageResource(iconResId)
|
||||
mainTabBinding.text.setTextColor(ContextCompat.getColor(this, textColorResId))
|
||||
}
|
||||
|
||||
private fun getIconResource(position: Int, isSelected: Boolean): Int {
|
||||
if (position == 1) {
|
||||
return if (isSelected) R.drawable.collection else R.drawable.un_collection
|
||||
}
|
||||
return if (isSelected) R.drawable.home else R.drawable.un_home
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,75 @@
|
||||
package com.wallpaper.lumawallpaper.ui.activity
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.os.CountDownTimer
|
||||
import android.view.View
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.load.resource.bitmap.CenterCrop
|
||||
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
|
||||
import com.wallpaper.lumawallpaper.R
|
||||
import com.wallpaper.lumawallpaper.databinding.ActivitySplashBinding
|
||||
|
||||
@SuppressLint("CustomSplashScreen")
|
||||
class SplashActivity : AppCompatActivity() {
|
||||
private lateinit var binding: ActivitySplashBinding
|
||||
private lateinit var countDownTimer: CountDownTimer
|
||||
|
||||
companion object {
|
||||
private const val TOTAL_TIME: Long = 1000
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
this.enableEdgeToEdge()
|
||||
binding = ActivitySplashBinding.inflate(layoutInflater)
|
||||
setContentView(binding.getRoot())
|
||||
|
||||
ViewCompat.setOnApplyWindowInsetsListener(
|
||||
findViewById(R.id.main)
|
||||
) { v: View, insets: WindowInsetsCompat ->
|
||||
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
|
||||
insets
|
||||
}
|
||||
|
||||
Glide.with(this)
|
||||
.load(R.mipmap.placeholder)
|
||||
.transform(CenterCrop(), RoundedCorners(32))
|
||||
.into(binding.image)
|
||||
|
||||
countDownTimer = object : CountDownTimer(TOTAL_TIME, 50) {
|
||||
override fun onTick(millisUntilFinished: Long) {
|
||||
val percentage = (100 - millisUntilFinished.toFloat() / TOTAL_TIME * 100).toInt()
|
||||
binding.progressBar.progress = percentage
|
||||
}
|
||||
|
||||
override fun onFinish() {
|
||||
startMain()
|
||||
}
|
||||
}.start()
|
||||
|
||||
countDownTimer.start()
|
||||
}
|
||||
|
||||
private fun startMain() {
|
||||
binding.progressBar.progress = 100
|
||||
|
||||
val intent = Intent(
|
||||
this@SplashActivity,
|
||||
MainActivity::class.java
|
||||
)
|
||||
startActivity(intent)
|
||||
finish()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
countDownTimer.cancel()
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,136 @@
|
||||
package com.wallpaper.lumawallpaper.ui.adapter
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.load.resource.bitmap.CenterCrop
|
||||
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
|
||||
import com.wallpaper.lumawallpaper.R
|
||||
import com.wallpaper.lumawallpaper.database.LumaEntity
|
||||
import com.wallpaper.lumawallpaper.ui.activity.LumaActivity
|
||||
import com.wallpaper.lumawallpaper.ui.activity.CategoryActivity
|
||||
import com.wallpaper.lumawallpaper.ui.viewmodel.LumaViewModel
|
||||
|
||||
class LumaAdapter(
|
||||
private val lumaViewModel: LumaViewModel,
|
||||
private val context: Context,
|
||||
private var lumaList: MutableList<LumaEntity> = mutableListOf(),
|
||||
private val activity: Activity,
|
||||
private val type: Int
|
||||
) :
|
||||
RecyclerView.Adapter<LumaAdapter.ViewHolder>() {
|
||||
|
||||
private var isLoadingMore = false
|
||||
private var hasMoreData = true
|
||||
|
||||
fun updateData(newList: List<LumaEntity>, isLoadMore: Boolean = false) {
|
||||
if (isLoadMore) {
|
||||
val previousSize = lumaList.size
|
||||
lumaList.addAll(newList)
|
||||
notifyItemRangeInserted(previousSize, newList.size)
|
||||
isLoadingMore = false
|
||||
} else {
|
||||
lumaList.clear()
|
||||
lumaList.addAll(newList)
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
fun setLoadingMore(loading: Boolean) {
|
||||
isLoadingMore = loading
|
||||
}
|
||||
|
||||
fun setHasMoreData(hasMore: Boolean) {
|
||||
hasMoreData = hasMore
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
val view: View =
|
||||
LayoutInflater.from(context).inflate(R.layout.item_luma, parent, false)
|
||||
return ViewHolder(view)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
val lumaEntity: LumaEntity = lumaList[position]
|
||||
holder.bind(lumaEntity)
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return lumaList.size
|
||||
}
|
||||
|
||||
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
private val imageView: ImageView = itemView.findViewById(R.id.item_image_view)
|
||||
private val favorite: ImageView = itemView.findViewById(R.id.item_favorite)
|
||||
private val title: TextView = itemView.findViewById(R.id.item_title)
|
||||
|
||||
fun bind(lumaEntity: LumaEntity) {
|
||||
val imagePath: String = lumaEntity.previewThumb
|
||||
loadImage(imagePath)
|
||||
|
||||
if (type == 0) {
|
||||
title.text = lumaEntity.name
|
||||
favorite.visibility = View.GONE
|
||||
} else {
|
||||
title.visibility = View.GONE
|
||||
setFavoriteButton(lumaEntity)
|
||||
}
|
||||
|
||||
setClickListeners(lumaEntity)
|
||||
}
|
||||
|
||||
private fun loadImage(imagePath: String) {
|
||||
Glide.with(context)
|
||||
.load(imagePath)
|
||||
.thumbnail(
|
||||
Glide.with(context)
|
||||
.load(imagePath)
|
||||
.override(100, 100)
|
||||
.transform(CenterCrop(), RoundedCorners(32))
|
||||
)
|
||||
.transform(CenterCrop(), RoundedCorners(32))
|
||||
.error(R.mipmap.placeholder)
|
||||
.placeholder(R.mipmap.placeholder)
|
||||
.into(imageView)
|
||||
}
|
||||
|
||||
private fun setFavoriteButton(lumaEntity: LumaEntity) {
|
||||
favorite.setImageResource(if (lumaEntity.isFavorite) R.drawable.like else R.drawable.un_like)
|
||||
}
|
||||
|
||||
private fun setClickListeners(lumaEntity: LumaEntity) {
|
||||
imageView.setOnClickListener {
|
||||
val intent: Intent
|
||||
if (type == 0) {
|
||||
intent =
|
||||
Intent(activity, CategoryActivity::class.java)
|
||||
intent.putExtra("name", lumaEntity.name)
|
||||
} else {
|
||||
intent = Intent(activity, LumaActivity::class.java)
|
||||
intent.putExtra("lumaEntity", lumaEntity)
|
||||
}
|
||||
activity.startActivity(intent)
|
||||
}
|
||||
|
||||
favorite.setOnClickListener { toggleFavorite(lumaEntity) }
|
||||
}
|
||||
|
||||
private fun toggleFavorite(lumaEntity: LumaEntity) {
|
||||
val newStatus: Boolean = !lumaEntity.isFavorite
|
||||
lumaEntity.isFavorite = newStatus
|
||||
updateImageInDatabase(lumaEntity)
|
||||
notifyItemChanged(adapterPosition)
|
||||
}
|
||||
|
||||
private fun updateImageInDatabase(lumaEntity: LumaEntity) {
|
||||
lumaViewModel.update(lumaEntity)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
package com.wallpaper.lumawallpaper.ui.adapter
|
||||
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||
import com.wallpaper.lumawallpaper.ui.fragment.CategoryFragment
|
||||
import com.wallpaper.lumawallpaper.ui.fragment.FavoriteFragment
|
||||
|
||||
class MainAdapter(fragmentActivity: FragmentActivity) : FragmentStateAdapter(fragmentActivity) {
|
||||
private val fragmentList: MutableList<Fragment> = ArrayList()
|
||||
|
||||
init {
|
||||
fragmentList.add(CategoryFragment())
|
||||
fragmentList.add(FavoriteFragment())
|
||||
}
|
||||
|
||||
override fun createFragment(position: Int): Fragment {
|
||||
return fragmentList[position]
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return fragmentList.size
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,79 @@
|
||||
package com.wallpaper.lumawallpaper.ui.fragment
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.wallpaper.lumawallpaper.databinding.FragmentCategoryBinding
|
||||
import com.wallpaper.lumawallpaper.ui.adapter.LumaAdapter
|
||||
import com.wallpaper.lumawallpaper.ui.viewmodel.LumaViewModel
|
||||
import com.wallpaper.lumawallpaper.utils.ItemDecoration
|
||||
|
||||
class CategoryFragment : Fragment() {
|
||||
private lateinit var binding: FragmentCategoryBinding
|
||||
private lateinit var adapter: LumaAdapter
|
||||
private lateinit var lumaViewModel: LumaViewModel
|
||||
private val layoutManager by lazy { GridLayoutManager(context, 2) }
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
binding = FragmentCategoryBinding.inflate(inflater, container, false)
|
||||
initData()
|
||||
initEvent()
|
||||
return binding.getRoot()
|
||||
}
|
||||
|
||||
private fun initData() {
|
||||
lumaViewModel = ViewModelProvider(this)[LumaViewModel::class.java]
|
||||
|
||||
binding.recyclerView.layoutManager = layoutManager
|
||||
|
||||
adapter = LumaAdapter(
|
||||
lumaViewModel,
|
||||
requireContext(),
|
||||
mutableListOf(),
|
||||
requireActivity(),
|
||||
0
|
||||
)
|
||||
binding.recyclerView.adapter = adapter
|
||||
|
||||
val itemDecoration = ItemDecoration(20, 15, 20)
|
||||
binding.recyclerView.addItemDecoration(itemDecoration)
|
||||
|
||||
binding.recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
super.onScrolled(recyclerView, dx, dy)
|
||||
|
||||
val visibleItemCount = layoutManager.childCount
|
||||
val totalItemCount = layoutManager.itemCount
|
||||
val firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition()
|
||||
val isLoading = lumaViewModel.isLoading.value ?: false
|
||||
val hasMore = lumaViewModel.hasMore.value ?: true
|
||||
val threshold = 5
|
||||
|
||||
if (!isLoading && hasMore && (visibleItemCount + firstVisibleItemPosition + threshold) >= totalItemCount && firstVisibleItemPosition >= 0) {
|
||||
lumaViewModel.loadNextPage()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
lumaViewModel.lumaListLiveData.observe(viewLifecycleOwner) { newList ->
|
||||
val isFirstLoad = adapter.itemCount == 0
|
||||
adapter.updateData(newList, !isFirstLoad)
|
||||
}
|
||||
}
|
||||
|
||||
private fun initEvent() {
|
||||
loadCategory()
|
||||
}
|
||||
|
||||
private fun loadCategory() {
|
||||
lumaViewModel.loadInitialData()
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,98 @@
|
||||
package com.wallpaper.lumawallpaper.ui.fragment
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.wallpaper.lumawallpaper.databinding.FragmentFavoriteBinding
|
||||
import com.wallpaper.lumawallpaper.ui.adapter.LumaAdapter
|
||||
import com.wallpaper.lumawallpaper.ui.viewmodel.LumaViewModel
|
||||
import com.wallpaper.lumawallpaper.utils.ItemDecoration
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class FavoriteFragment : Fragment() {
|
||||
private lateinit var binding: FragmentFavoriteBinding
|
||||
private lateinit var adapter: LumaAdapter
|
||||
private lateinit var lumaViewModel: LumaViewModel
|
||||
private val layoutManager by lazy { GridLayoutManager(context, 2) }
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
binding = FragmentFavoriteBinding.inflate(inflater, container, false)
|
||||
initData()
|
||||
return binding.getRoot()
|
||||
}
|
||||
|
||||
private fun initData() {
|
||||
lumaViewModel = ViewModelProvider(this)[LumaViewModel::class.java]
|
||||
|
||||
binding.recyclerView.layoutManager = layoutManager
|
||||
|
||||
adapter = LumaAdapter(
|
||||
lumaViewModel,
|
||||
requireContext(),
|
||||
mutableListOf(),
|
||||
requireActivity(),
|
||||
1
|
||||
)
|
||||
binding.recyclerView.adapter = adapter
|
||||
|
||||
val itemDecoration = ItemDecoration(20, 15, 20)
|
||||
binding.recyclerView.addItemDecoration(itemDecoration)
|
||||
|
||||
binding.recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
super.onScrolled(recyclerView, dx, dy)
|
||||
|
||||
val visibleItemCount = layoutManager.childCount
|
||||
val totalItemCount = layoutManager.itemCount
|
||||
val firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition()
|
||||
val isLoading = lumaViewModel.isLoading.value ?: false
|
||||
val hasMore = lumaViewModel.hasMore.value ?: true
|
||||
val threshold = 5
|
||||
|
||||
if (!isLoading && hasMore && (visibleItemCount + firstVisibleItemPosition + threshold) >= totalItemCount && firstVisibleItemPosition >= 0) {
|
||||
lumaViewModel.loadNextPage(isFavorite = true)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
lumaViewModel.lumaListLiveData.observe(viewLifecycleOwner) { newList ->
|
||||
val isFirstLoad = adapter.itemCount == 0
|
||||
adapter.updateData(newList, !isFirstLoad)
|
||||
if (newList.isEmpty() && isFirstLoad) {
|
||||
binding.tip.visibility = View.VISIBLE
|
||||
} else {
|
||||
binding.tip.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
lumaViewModel.hasMore.observe(viewLifecycleOwner) { hasMore ->
|
||||
adapter.setHasMoreData(hasMore)
|
||||
}
|
||||
|
||||
lumaViewModel.loadInitialFavoriteData()
|
||||
|
||||
loadFavorite()
|
||||
}
|
||||
|
||||
private fun loadFavorite() {
|
||||
lifecycleScope.launch {
|
||||
lumaViewModel.favoriteMagicEntities.collect { lumaEntity ->
|
||||
if (lumaEntity.isEmpty()) {
|
||||
binding.tip.visibility = View.VISIBLE
|
||||
} else {
|
||||
binding.tip.visibility = View.GONE
|
||||
}
|
||||
adapter.updateData(lumaEntity)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,128 @@
|
||||
package com.wallpaper.lumawallpaper.ui.viewmodel
|
||||
|
||||
import android.app.Application
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.wallpaper.lumawallpaper.database.AppDatabase
|
||||
import com.wallpaper.lumawallpaper.database.LumaDao
|
||||
import com.wallpaper.lumawallpaper.database.LumaEntity
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class LumaViewModel(application: Application) : AndroidViewModel(application) {
|
||||
|
||||
private val lumaDao: LumaDao = AppDatabase.getDatabase(application).LumaDao()
|
||||
|
||||
private val _lumaListLiveData = MutableLiveData<MutableList<LumaEntity>>()
|
||||
val lumaListLiveData: LiveData<MutableList<LumaEntity>> = _lumaListLiveData
|
||||
|
||||
val favoriteMagicEntities: Flow<List<LumaEntity>> = lumaDao.getFavoriteLumaEntities()
|
||||
|
||||
private val _isLoading = MutableLiveData<Boolean>()
|
||||
val isLoading: LiveData<Boolean> = _isLoading
|
||||
|
||||
private val _hasMore = MutableLiveData<Boolean>(true)
|
||||
val hasMore: LiveData<Boolean> = _hasMore
|
||||
|
||||
private var currentPage = 1
|
||||
private val pageSize = 20
|
||||
|
||||
private var currentQueryName: String? = null
|
||||
|
||||
private var isCurrentlyLoadingFavorites = false
|
||||
|
||||
init {
|
||||
_lumaListLiveData.value = mutableListOf()
|
||||
}
|
||||
|
||||
fun loadInitialData() {
|
||||
isCurrentlyLoadingFavorites = false
|
||||
currentPage = 1
|
||||
_hasMore.value = true
|
||||
loadMagicEntitiesPaged(currentPage, pageSize)
|
||||
}
|
||||
|
||||
fun loadInitialFavoriteData() {
|
||||
isCurrentlyLoadingFavorites = true
|
||||
currentPage = 1
|
||||
_hasMore.value = true
|
||||
loadFavoriteMagicEntitiesPaged(currentPage, pageSize)
|
||||
}
|
||||
|
||||
fun loadInitialDataByName(name: String) {
|
||||
currentQueryName = name
|
||||
currentPage = 1
|
||||
_hasMore.value = true
|
||||
loadMagicEntitiesByNamePaged(name, currentPage, pageSize)
|
||||
}
|
||||
|
||||
fun loadNextPage(name: String? = null, isFavorite: Boolean = false) {
|
||||
if (_isLoading.value == true || _hasMore.value == false) {
|
||||
return
|
||||
}
|
||||
currentPage++
|
||||
_isLoading.value = true
|
||||
if (isFavorite) {
|
||||
loadFavoriteMagicEntitiesPaged(currentPage, pageSize)
|
||||
} else if (!name.isNullOrEmpty()) {
|
||||
loadMagicEntitiesByNamePaged(name, currentPage, pageSize)
|
||||
} else {
|
||||
loadMagicEntitiesPaged(currentPage, pageSize)
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadMagicEntitiesPaged(page: Int, size: Int) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
val newData = getLumaEntitiesFromUniqueLumaPaged(page = page, pageSize = size)
|
||||
updateLiveData(newData)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getLumaEntitiesFromUniqueLumaPaged(page: Int, pageSize: Int): List<LumaEntity> {
|
||||
val minIdLumaEntities = lumaDao.getMinIdLumaEntities()
|
||||
val lumaEntityIds = minIdLumaEntities.map { it.id }
|
||||
|
||||
return lumaDao.getLumaEntitiesFromLumaMinIdPaged(page, pageSize, lumaEntityIds)
|
||||
}
|
||||
|
||||
private fun loadMagicEntitiesByNamePaged(name: String, page: Int, size: Int) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
val newData =
|
||||
lumaDao.getLumaEntitiesByNamePaged(name = name, page = page, pageSize = size)
|
||||
updateLiveData(newData)
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadFavoriteMagicEntitiesPaged(page: Int, size: Int) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
val newData = lumaDao.getFavoriteLumaEntitiesPaged(page = page, pageSize = size)
|
||||
updateLiveData(newData)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun updateLiveData(newData: List<LumaEntity>) {
|
||||
withContext(Dispatchers.Main) {
|
||||
val currentList = _lumaListLiveData.value ?: mutableListOf()
|
||||
currentList.addAll(newData)
|
||||
_lumaListLiveData.value = currentList
|
||||
_isLoading.value = false
|
||||
if (newData.size < pageSize) {
|
||||
_hasMore.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getFavoriteStatus(imagePath: String, name: String): Flow<Boolean> {
|
||||
return lumaDao.getFavoriteStatus(imagePath, name)
|
||||
}
|
||||
|
||||
fun update(lumaEntity: LumaEntity) {
|
||||
viewModelScope.launch {
|
||||
lumaDao.update(lumaEntity)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,76 @@
|
||||
package com.wallpaper.lumawallpaper.utils;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.recyclerview.widget.StaggeredGridLayoutManager;
|
||||
|
||||
import com.wallpaper.lumawallpaper.App;
|
||||
|
||||
public class ItemDecoration extends RecyclerView.ItemDecoration {
|
||||
|
||||
private final int verticalSpacing;
|
||||
private final int horizontalSpacing;
|
||||
private final int extraSpacing;
|
||||
|
||||
public ItemDecoration(int verticalSpacingDp, int horizontalSpacingDp, int extraSpacingDp) {
|
||||
this.verticalSpacing = Math.round(dpToPx(verticalSpacingDp));
|
||||
this.horizontalSpacing = Math.round(dpToPx(horizontalSpacingDp));
|
||||
this.extraSpacing = Math.round(dpToPx(extraSpacingDp));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
|
||||
int spanCount = 1;
|
||||
int spanSize = 1;
|
||||
int spanIndex = 0;
|
||||
|
||||
int position = parent.getChildAdapterPosition(view);
|
||||
RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
|
||||
|
||||
if (layoutManager instanceof StaggeredGridLayoutManager) {
|
||||
StaggeredGridLayoutManager staggeredGridLayoutManager = (StaggeredGridLayoutManager) layoutManager;
|
||||
StaggeredGridLayoutManager.LayoutParams layoutParams = (StaggeredGridLayoutManager.LayoutParams) view.getLayoutParams();
|
||||
spanCount = staggeredGridLayoutManager.getSpanCount();
|
||||
spanSize = layoutParams.isFullSpan() ? spanCount : 1;
|
||||
spanIndex = layoutParams.getSpanIndex();
|
||||
} else if (layoutManager instanceof GridLayoutManager) {
|
||||
GridLayoutManager gridLayoutManager = (GridLayoutManager) layoutManager;
|
||||
GridLayoutManager.LayoutParams layoutParams = (GridLayoutManager.LayoutParams) view.getLayoutParams();
|
||||
spanCount = gridLayoutManager.getSpanCount();
|
||||
spanSize = gridLayoutManager.getSpanSizeLookup().getSpanSize(position);
|
||||
spanIndex = layoutParams.getSpanIndex();
|
||||
} else if (layoutManager instanceof LinearLayoutManager) {
|
||||
outRect.left = horizontalSpacing;
|
||||
outRect.right = horizontalSpacing;
|
||||
outRect.bottom = verticalSpacing;
|
||||
return;
|
||||
}
|
||||
|
||||
if (spanSize == spanCount) {
|
||||
outRect.left = horizontalSpacing + extraSpacing;
|
||||
outRect.right = horizontalSpacing + extraSpacing;
|
||||
} else {
|
||||
int totalSpacing = (horizontalSpacing * (spanCount + 1) + extraSpacing * 2) / spanCount;
|
||||
int leftSpacing = horizontalSpacing * (spanIndex + 1) - totalSpacing * spanIndex + extraSpacing;
|
||||
int rightSpacing = totalSpacing - leftSpacing;
|
||||
outRect.left = leftSpacing;
|
||||
outRect.right = rightSpacing;
|
||||
}
|
||||
outRect.bottom = verticalSpacing;
|
||||
|
||||
if (position < spanCount) {
|
||||
outRect.top = verticalSpacing;
|
||||
}
|
||||
}
|
||||
|
||||
public static float dpToPx(float dpValue) {
|
||||
float density = App.getContext().getResources().getDisplayMetrics().density;
|
||||
return dpValue * density + 0.5f;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,71 @@
|
||||
package com.wallpaper.lumawallpaper.utils
|
||||
|
||||
import com.wallpaper.lumawallpaper.App
|
||||
import com.wallpaper.lumawallpaper.database.LumaEntity
|
||||
import org.json.JSONArray
|
||||
import java.io.BufferedReader
|
||||
import java.io.IOException
|
||||
import java.io.InputStreamReader
|
||||
|
||||
object JsonUtils {
|
||||
|
||||
private fun loadJSONFromAsset(fileName: String): String {
|
||||
val jsonString = StringBuilder()
|
||||
try {
|
||||
App.getContext().assets.open(fileName).use { inputStream ->
|
||||
BufferedReader(InputStreamReader(inputStream)).use { reader ->
|
||||
reader.forEachLine { line ->
|
||||
jsonString.append(line)
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
return jsonString.toString()
|
||||
}
|
||||
|
||||
fun parseJson(fileName: String): List<LumaEntity> {
|
||||
val audioDataList = mutableListOf<LumaEntity>()
|
||||
try {
|
||||
val jsonString = loadJSONFromAsset(fileName)
|
||||
if (jsonString.isEmpty()) {
|
||||
throw IllegalArgumentException("JSON file is empty or invalid.")
|
||||
}
|
||||
|
||||
val jsonArray = JSONArray(jsonString)
|
||||
|
||||
for (i in 0 until jsonArray.length()) {
|
||||
val categoryObject = jsonArray.getJSONObject(i)
|
||||
|
||||
val name = categoryObject.getString("name")
|
||||
|
||||
val listArray = categoryObject.getJSONArray("data")
|
||||
|
||||
for (j in 0 until listArray.length()) {
|
||||
val itemObject = listArray.getJSONObject(j)
|
||||
|
||||
val original = itemObject.getString("original")
|
||||
val previewThumb = itemObject.getString("previewThumb")
|
||||
val source = itemObject.getString("source")
|
||||
|
||||
audioDataList.add(
|
||||
LumaEntity(
|
||||
name = name,
|
||||
original = original,
|
||||
previewThumb = previewThumb,
|
||||
source = source,
|
||||
isFavorite = false
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
return audioDataList
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,105 @@
|
||||
package com.wallpaper.lumawallpaper.utils
|
||||
|
||||
import android.Manifest
|
||||
import android.app.Activity
|
||||
import android.content.ContentValues
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import android.provider.MediaStore
|
||||
import android.view.View
|
||||
import android.widget.ProgressBar
|
||||
import android.widget.Toast
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.wallpaper.lumawallpaper.call.DownloadCall
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import java.io.IOException
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class WallpaperUtil(
|
||||
private val loadingBar: ProgressBar,
|
||||
private val overlayView: View
|
||||
) {
|
||||
companion object {
|
||||
const val PERMISSION_REQUEST_CODE = 456
|
||||
}
|
||||
|
||||
fun downloadWallpaper(context: Activity, imageUrl: String) {
|
||||
if (ContextCompat.checkSelfPermission(
|
||||
context, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
startDownload(context, imageUrl)
|
||||
} else {
|
||||
ActivityCompat.requestPermissions(
|
||||
context, arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), PERMISSION_REQUEST_CODE)
|
||||
}
|
||||
}
|
||||
|
||||
private fun startDownload(context: Activity, url: String) {
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
try {
|
||||
showLoading()
|
||||
val result = withContext(Dispatchers.IO) { downloadToGallery(context, url) }
|
||||
Toast.makeText(context, result.message, Toast.LENGTH_SHORT).show()
|
||||
} finally {
|
||||
hideLoading()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun downloadToGallery(context: Activity, url: String): DownloadCall {
|
||||
val fileName = "wallpaper_${System.currentTimeMillis()}.jpg"
|
||||
val contentValues = ContentValues().apply {
|
||||
put(MediaStore.Images.Media.DISPLAY_NAME, fileName)
|
||||
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 collectionUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
|
||||
val imageUri = context.contentResolver.insert(collectionUri, contentValues)
|
||||
?: return DownloadCall(false, "Failed to initialize storage")
|
||||
|
||||
return try {
|
||||
val client = OkHttpClient.Builder()
|
||||
.connectTimeout(30, TimeUnit.SECONDS)
|
||||
.readTimeout(30, TimeUnit.SECONDS)
|
||||
.build()
|
||||
val request = Request.Builder().url(url).build()
|
||||
val response = client.newCall(request).execute()
|
||||
if (!response.isSuccessful) throw IOException("Unexpected code ${response.code}")
|
||||
|
||||
response.body?.byteStream()?.use { inputStream ->
|
||||
context.contentResolver.openOutputStream(imageUri)?.use { outputStream ->
|
||||
inputStream.copyTo(outputStream)
|
||||
} ?: throw IOException("Failed to open output stream")
|
||||
} ?: throw IOException("Empty response body")
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
contentValues.clear()
|
||||
contentValues.put(MediaStore.Images.Media.IS_PENDING, 0)
|
||||
context.contentResolver.update(imageUri, contentValues, null, null)
|
||||
}
|
||||
|
||||
DownloadCall(true, "Image saved to gallery")
|
||||
} catch (e: Exception) {
|
||||
context.contentResolver.delete(imageUri, null, null)
|
||||
DownloadCall(false, "Failed to save image: ${e.localizedMessage}")
|
||||
}
|
||||
}
|
||||
private fun showLoading() {
|
||||
loadingBar.visibility = View.VISIBLE
|
||||
overlayView.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
private fun hideLoading() {
|
||||
loadingBar.visibility = View.GONE
|
||||
overlayView.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
9
app/src/main/res/drawable/back.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="32dp"
|
||||
android:height="32dp"
|
||||
android:viewportWidth="1024"
|
||||
android:viewportHeight="1024">
|
||||
<path
|
||||
android:fillColor="@color/white"
|
||||
android:pathData="M707.5,62.1c-17.4,0 -33.8,6.5 -46.1,18.2L279.6,431.9c-18,17.2 -28.3,40.6 -28.3,65.4s10.2,48.3 28.6,65.9l381.4,350.4c24.6,23.5 67.6,23.5 92.2,0 12.3,-11.7 19.1,-27.3 19.1,-43.8s-6.8,-32.2 -19.1,-43.9l-278,-262.6c-18.5,-17.7 -28.7,-41 -28.7,-65.9s10.2,-48.2 28.6,-65.8l278.1,-263.6c12.3,-11.7 19,-27.3 19,-43.8s-6.8,-32.2 -19.1,-43.9a66.3,66.3 0,0 0,-46.1 -18.2zM707.5,993.8a132.7,132.7 0,0 1,-92.2 -36.4l-52.3,-49.8 -328.5,-300.1c-31.3,-29.8 -48.2,-68.7 -48.2,-110.2s16.9,-80.4 47.7,-109.7L615.7,36a132.6,132.6 0,0 1,91.7 -36c34.8,0 67.6,12.9 92.1,36.4 24.6,23.4 38.2,54.6 38.2,87.7s-13.6,64.4 -38.2,87.8l-278.1,263.6a30.1,30.1 0,0 0,0.1 43.8l278,262.6c24.7,23.6 38.3,54.8 38.3,87.9s-13.6,64.3 -38.2,87.7a132.9,132.9 0,0 1,-92.1 36.4z"/>
|
||||
</vector>
|
||||
4
app/src/main/res/drawable/circle_background.xml
Normal file
@ -0,0 +1,4 @@
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="#009688" />
|
||||
<corners android:radius="100dp" />
|
||||
</shape>
|
||||
9
app/src/main/res/drawable/collection.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="32dp"
|
||||
android:height="32dp"
|
||||
android:viewportWidth="1024"
|
||||
android:viewportHeight="1024">
|
||||
<path
|
||||
android:fillColor="@color/white"
|
||||
android:pathData="M914.2,189.4L577.3,15.8a143.7,143.7 0,0 0,-131 0L109.4,190.4A135.3,135.3 0,0 0,33.4 314.7v393.5a143.6,143.6 0,0 0,76 125.2l339,174.6a137,137 0,0 0,65.5 15.9,143.1 143.1,0 0,0 65.5,-15.9l334.8,-173.6a140.8,140.8 0,0 0,76 -125.2L990.2,313.6a139.9,139.9 0,0 0,-76 -124.3zM480.1,77.9a71.4,71.4 0,0 1,31.7 -8.4,64 64,0 0,1 31.7,7.4l336.9,173.6a95.8,95.8 0,0 1,13.7 8.4l-383.4,197.8L127.4,262a30.3,30.3 0,0 1,13.7 -8.4zM143.2,770.1a69.3,69.3 0,0 1,-39.1 -63.1L104.1,327.2l141.5,72.6v158.9a34.3,34.3 0,0 0,19.1 31.5l25.5,12.7a10.1,10.1 0,0 0,7.4 2.1,17.1 17.1,0 0,0 17.9,-17.9L315.6,435.6l159.3,81v426.1zM880.4,770.1l-333.7,173.6v-427l372.8,-189.4v379.8a69.3,69.3 0,0 1,-39.1 63.1z"/>
|
||||
</vector>
|
||||
9
app/src/main/res/drawable/download.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="32dp"
|
||||
android:height="32dp"
|
||||
android:viewportWidth="1024"
|
||||
android:viewportHeight="1024">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M768,768q0,-14.9 -10.9,-25.7t-25.7,-10.9 -25.7,10.9 -10.9,25.7 10.9,25.7 25.7,10.9 25.7,-10.9 10.9,-25.7zM914.3,768q0,-14.9 -10.9,-25.7t-25.7,-10.9 -25.7,10.9 -10.9,25.7 10.9,25.7 25.7,10.9 25.7,-10.9 10.9,-25.7zM987.4,640v182.9q0,22.9 -16,38.9t-38.9,16L91.4,877.7q-22.9,0 -38.9,-16t-16,-38.9v-182.9q0,-22.9 16,-38.9t38.9,-16h265.7l77.1,77.7q33.1,32 77.7,32t77.7,-32l77.7,-77.7h265.1q22.9,0 38.9,16t16,38.9zM801.7,314.9q9.7,23.4 -8,40l-256,256q-10.3,10.9 -25.7,10.9t-25.7,-10.9L230.3,354.9q-17.7,-16.6 -8,-40 9.7,-22.3 33.7,-22.3h146.3L402.3,36.6q0,-14.9 10.9,-25.7t25.7,-10.9h146.3q14.9,0 25.7,10.9t10.9,25.7v256h146.3q24,0 33.7,22.3z"/>
|
||||
</vector>
|
||||
9
app/src/main/res/drawable/home.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="32dp"
|
||||
android:height="32dp"
|
||||
android:viewportWidth="1024"
|
||||
android:viewportHeight="1024">
|
||||
<path
|
||||
android:fillColor="@color/white"
|
||||
android:pathData="M875.2,572.5V874.9q0,16.4 -12,28.4T834.9,915.2H592.9V673.3H431.7v241.9H189.8q-16.4,0 -28.4,-12t-12,-28.4V572.5q0,-0.6 0.3,-1.9t0.3,-1.9l362.3,-298.6 362.3,298.6q0.6,1.3 0.6,3.8zM1015.7,529l-39.1,46.6q-5,5.7 -13.2,6.9h-1.9q-8.2,0 -13.2,-4.4L512.3,214.6 76.3,578.1q-7.5,5 -15.1,4.4 -8.2,-1.3 -13.2,-6.9l-39.1,-46.6q-5,-6.3 -4.4,-14.8t6.9,-13.5l453,-377.4q20.1,-16.4 47.9,-16.4t47.9,16.4L713.8,251.8V129q0,-8.8 5.7,-14.5t14.5,-5.7h121q8.8,0 14.5,5.7t5.7,14.5v257.1l138,114.7q6.3,5 6.9,13.5t-4.4,14.8z"/>
|
||||
</vector>
|
||||
9
app/src/main/res/drawable/like.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="32dp"
|
||||
android:height="32dp"
|
||||
android:viewportWidth="1024"
|
||||
android:viewportHeight="1024">
|
||||
<path
|
||||
android:pathData="M725.3,192c-89.6,0 -168.5,44.8 -213.3,115.2C467.2,236.8 388.3,192 298.7,192 157.9,192 42.7,307.2 42.7,448c0,253.9 469.3,512 469.3,512s469.3,-256 469.3,-512c0,-140.8 -115.2,-256 -256,-256z"
|
||||
android:fillColor="#F44336"/>
|
||||
</vector>
|
||||
30
app/src/main/res/drawable/progress_color.xml
Normal file
@ -0,0 +1,30 @@
|
||||
<!-- res/drawable/seekbar_progress_drawable.xml -->
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:id="@android:id/background">
|
||||
<shape>
|
||||
<corners android:radius="5dp" />
|
||||
<solid android:color="#D3D3D3" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
<item android:id="@android:id/secondaryProgress">
|
||||
<clip>
|
||||
<shape>
|
||||
<corners android:radius="5dp" />
|
||||
<solid android:color="#FFD700" />
|
||||
</shape>
|
||||
</clip>
|
||||
</item>
|
||||
|
||||
<item android:id="@android:id/progress">
|
||||
<clip>
|
||||
<shape>
|
||||
<corners android:radius="5dp" />
|
||||
<gradient
|
||||
android:startColor="#4891FF"
|
||||
android:endColor="#6CE89E"
|
||||
android:angle="0" />
|
||||
</shape>
|
||||
</clip>
|
||||
</item>
|
||||
</layer-list>
|
||||
9
app/src/main/res/drawable/rounded_rectangle_gradient.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<gradient
|
||||
android:angle="45"
|
||||
android:endColor="#8BC34A"
|
||||
android:startColor="#E8ECEF"
|
||||
android:type="linear"
|
||||
android:useLevel="false" />
|
||||
<corners android:radius="16sp" />
|
||||
</shape>
|
||||
@ -0,0 +1,4 @@
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<corners android:radius="12dp" />
|
||||
<solid android:color="#33FFFFFF" />
|
||||
</shape>
|
||||
9
app/src/main/res/drawable/set.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="32dp"
|
||||
android:height="32dp"
|
||||
android:viewportWidth="1024"
|
||||
android:viewportHeight="1024">
|
||||
<path
|
||||
android:pathData="M783.9,401.3a128,128 0,0 1,128 128v256a128,128 0,0 1,-128 128h-256a128,128 0,0 1,-128 -128v-256a128,128 0,0 1,128 -128zM603.6,112.1a106.7,106.7 0,0 1,106.6 102l0.1,4.6v64a42.7,42.7 0,0 1,-85.2 3.2l-0.1,-3.2v-64a21.3,21.3 0,0 0,-18.9 -21.2l-2.5,-0.1L553.8,197.4l-12.3,65.4c-4.4,29.2 -27.9,52.1 -57.5,54l-3.9,0.1h-142c-30,0 -54.6,-21.7 -60.4,-49l-0.6,-3.6 -12.6,-66.9h-44.8a21.3,21.3 0,0 0,-21.2 18.8l-0.1,2.5v586.3a21.3,21.3 0,0 0,18.8 21.2l2.5,0.1h72.9a42.7,42.7 0,0 1,3.2 85.2l-3.2,0.1h-72.9a106.7,106.7 0,0 1,-106.6 -102l-0.1,-4.6L112.9,218.8a106.7,106.7 0,0 1,102 -106.6l4.6,-0.1h384zM783.9,486.6h-256a42.7,42.7 0,0 0,-42.7 42.7v256a42.7,42.7 0,0 0,42.7 42.7h91.6v-0.4c3.1,-113 94.1,-204 207,-207.3L826.5,529.3a42.7,42.7 0,0 0,-42.7 -42.7zM704.9,827.9h79a42.7,42.7 0,0 0,42.7 -42.7v-79.7a128,128 0,0 0,-121.7 122.3zM596.5,545.5a53.3,53.3 0,1 1,0 106.7,53.3 53.3,0 0,1 0,-106.7zM467,197.4h-115.8l6.4,34.2h102.9l6.4,-34.2z"
|
||||
android:fillColor="@color/black"/>
|
||||
</vector>
|
||||
8
app/src/main/res/drawable/set_background.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="@color/gray" />
|
||||
<corners android:radius="6dp" />
|
||||
</shape>
|
||||
9
app/src/main/res/drawable/un_collection.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="32dp"
|
||||
android:height="32dp"
|
||||
android:viewportWidth="1024"
|
||||
android:viewportHeight="1024">
|
||||
<path
|
||||
android:fillColor="@color/black"
|
||||
android:pathData="M914.2,189.4L577.3,15.8a143.7,143.7 0,0 0,-131 0L109.4,190.4A135.3,135.3 0,0 0,33.4 314.7v393.5a143.6,143.6 0,0 0,76 125.2l339,174.6a137,137 0,0 0,65.5 15.9,143.1 143.1,0 0,0 65.5,-15.9l334.8,-173.6a140.8,140.8 0,0 0,76 -125.2L990.2,313.6a139.9,139.9 0,0 0,-76 -124.3zM480.1,77.9a71.4,71.4 0,0 1,31.7 -8.4,64 64,0 0,1 31.7,7.4l336.9,173.6a95.8,95.8 0,0 1,13.7 8.4l-383.4,197.8L127.4,262a30.3,30.3 0,0 1,13.7 -8.4zM143.2,770.1a69.3,69.3 0,0 1,-39.1 -63.1L104.1,327.2l141.5,72.6v158.9a34.3,34.3 0,0 0,19.1 31.5l25.5,12.7a10.1,10.1 0,0 0,7.4 2.1,17.1 17.1,0 0,0 17.9,-17.9L315.6,435.6l159.3,81v426.1zM880.4,770.1l-333.7,173.6v-427l372.8,-189.4v379.8a69.3,69.3 0,0 1,-39.1 63.1z"/>
|
||||
</vector>
|
||||
9
app/src/main/res/drawable/un_home.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="32dp"
|
||||
android:height="32dp"
|
||||
android:viewportWidth="1024"
|
||||
android:viewportHeight="1024">
|
||||
<path
|
||||
android:fillColor="@color/black"
|
||||
android:pathData="M875.2,572.5V874.9q0,16.4 -12,28.4T834.9,915.2H592.9V673.3H431.7v241.9H189.8q-16.4,0 -28.4,-12t-12,-28.4V572.5q0,-0.6 0.3,-1.9t0.3,-1.9l362.3,-298.6 362.3,298.6q0.6,1.3 0.6,3.8zM1015.7,529l-39.1,46.6q-5,5.7 -13.2,6.9h-1.9q-8.2,0 -13.2,-4.4L512.3,214.6 76.3,578.1q-7.5,5 -15.1,4.4 -8.2,-1.3 -13.2,-6.9l-39.1,-46.6q-5,-6.3 -4.4,-14.8t6.9,-13.5l453,-377.4q20.1,-16.4 47.9,-16.4t47.9,16.4L713.8,251.8V129q0,-8.8 5.7,-14.5t14.5,-5.7h121q8.8,0 14.5,5.7t5.7,14.5v257.1l138,114.7q6.3,5 6.9,13.5t-4.4,14.8z"/>
|
||||
</vector>
|
||||
9
app/src/main/res/drawable/un_like.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="32dp"
|
||||
android:height="32dp"
|
||||
android:viewportWidth="1024"
|
||||
android:viewportHeight="1024">
|
||||
<path
|
||||
android:pathData="M725.3,192c-89.6,0 -168.5,44.8 -213.3,115.2C467.2,236.8 388.3,192 298.7,192 157.9,192 42.7,307.2 42.7,448c0,253.9 469.3,512 469.3,512s469.3,-256 469.3,-512c0,-140.8 -115.2,-256 -256,-256z"
|
||||
android:fillColor="@color/gray"/>
|
||||
</vector>
|
||||
43
app/src/main/res/layout/activity_category.xml
Normal file
@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/soft_black"
|
||||
tools:context=".ui.activity.CategoryActivity">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/back"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:layout_marginStart="25dp"
|
||||
android:src="@drawable/back"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/title"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/title" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="25dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/app_name"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="24sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recycler_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginTop="25dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/title" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
101
app/src/main/res/layout/activity_luma.xml
Normal file
@ -0,0 +1,101 @@
|
||||
<?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:id="@+id/main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/soft_black"
|
||||
tools:context=".ui.activity.LumaActivity">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/image_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerCrop" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/back"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:layout_marginStart="32dp"
|
||||
android:layout_marginTop="32dp"
|
||||
android:src="@drawable/back"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/background_container"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="50dp"
|
||||
android:layout_marginStart="50dp"
|
||||
android:layout_marginEnd="50dp"
|
||||
android:layout_marginBottom="32dp"
|
||||
android:background="@drawable/rounded_rectangle_transparent"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal"
|
||||
android:padding="8dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/like"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="34dp"
|
||||
android:layout_weight="1"
|
||||
android:src="@drawable/un_like" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/down_picture"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1">
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/down_progress"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:indeterminateTint="@color/black"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="34dp"
|
||||
android:layout_height="34dp"
|
||||
android:src="@drawable/download"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/set"
|
||||
android:layout_width="34dp"
|
||||
android:layout_height="34dp"
|
||||
android:layout_weight="1"
|
||||
android:src="@drawable/set" />
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/gray"
|
||||
android:focusable="true"
|
||||
android:visibility="gone" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progress_bar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@ -5,15 +5,38 @@
|
||||
android:id="@+id/main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".MainActivity">
|
||||
android:background="@color/soft_black"
|
||||
tools:context=".ui.activity.MainActivity">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Hello World!"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:layout_marginTop="24sp"
|
||||
android:text="@string/app_name"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="24sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<androidx.viewpager2.widget.ViewPager2
|
||||
android:id="@+id/main_viewpager2"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/main_tab_layout"
|
||||
app:layout_constraintTop_toBottomOf="@+id/title" />
|
||||
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/main_tab_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="100dp"
|
||||
android:layout_marginBottom="25dp"
|
||||
android:background="@android:color/transparent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:tabIndicatorHeight="0dp"
|
||||
app:tabRippleColor="@android:color/transparent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
50
app/src/main/res/layout/activity_splash.xml
Normal file
@ -0,0 +1,50 @@
|
||||
<?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:id="@+id/main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/soft_black"
|
||||
tools:context=".ui.activity.SplashActivity">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/image"
|
||||
android:layout_width="150dp"
|
||||
android:layout_height="150dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.388" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/splash_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="32dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/app_name"
|
||||
android:textSize="24sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@color/white"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/image" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progress_bar"
|
||||
style="?android:attr/progressBarStyleHorizontal"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="5dp"
|
||||
android:layout_marginStart="53dp"
|
||||
android:layout_marginEnd="53dp"
|
||||
android:layout_marginBottom="80dp"
|
||||
android:max="100"
|
||||
android:progress="0"
|
||||
android:progressDrawable="@drawable/progress_color"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
17
app/src/main/res/layout/fragment_category.xml
Normal file
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
tools:context=".ui.fragment.CategoryFragment">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recycler_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="25dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/title" />
|
||||
|
||||
</FrameLayout>
|
||||
28
app/src/main/res/layout/fragment_favorite.xml
Normal file
@ -0,0 +1,28 @@
|
||||
<?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=".ui.fragment.FavoriteFragment">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recycler_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="25dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:text="Aww, you haven’t liked any wallpapers yet! C’mon, cutie, spread some love! 🥰💖"
|
||||
android:textColor="@color/white"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
32
app/src/main/res/layout/item_luma.xml
Normal file
@ -0,0 +1,32 @@
|
||||
<androidx.constraintlayout.widget.ConstraintLayout 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="256dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/item_image_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerCrop" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/item_favorite"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:src="@drawable/un_like"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/item_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
52
app/src/main/res/layout/main_tab.xml
Normal file
@ -0,0 +1,52 @@
|
||||
<?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"
|
||||
android:id="@+id/main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@drawable/rounded_rectangle_gradient">
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#10FFFFFF"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/image"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:background="@drawable/circle_background"
|
||||
android:elevation="4dp"
|
||||
android:src="@drawable/home"
|
||||
android:scaleType="centerInside"
|
||||
app:layout_constraintBottom_toTopOf="@+id/text"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_chainStyle="packed" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:padding="8dp"
|
||||
android:shadowColor="#80000000"
|
||||
android:shadowDx="1"
|
||||
android:shadowDy="1"
|
||||
android:shadowRadius="2"
|
||||
android:text="@string/app_name"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/image"
|
||||
app:layout_constraintVertical_bias="0.1" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
96
app/src/main/res/layout/set_dialog.xml
Normal file
@ -0,0 +1,96 @@
|
||||
<?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"
|
||||
android:layout_width="250dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/rounded_rectangle_transparent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:text=""
|
||||
android:textColor="@color/black"
|
||||
android:textSize="20sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/both"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="24dp"
|
||||
android:background="@drawable/set_background"
|
||||
android:padding="12dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/title">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Both"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/lock"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="24dp"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:background="@drawable/set_background"
|
||||
android:padding="12dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/both">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/lock_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Lock"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/desktop"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="24dp"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:background="@drawable/set_background"
|
||||
android:padding="12dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/lock">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Desktop"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@ -1,6 +0,0 @@
|
||||
<?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>
|
||||
BIN
app/src/main/res/mipmap-hdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 7.9 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
BIN
app/src/main/res/mipmap-hdpi/placeholder.png
Normal file
|
After Width: | Height: | Size: 357 KiB |
BIN
app/src/main/res/mipmap-mdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 982 B |
BIN
app/src/main/res/mipmap-xhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 2.8 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 3.8 KiB |
@ -2,4 +2,6 @@
|
||||
<resources>
|
||||
<color name="black">#FF000000</color>
|
||||
<color name="white">#FFFFFFFF</color>
|
||||
<color name="soft_black">#0D1B2A</color>
|
||||
<color name="gray">#9C979D</color>
|
||||
</resources>
|
||||
@ -2,4 +2,6 @@
|
||||
plugins {
|
||||
alias(libs.plugins.android.application) apply false
|
||||
alias(libs.plugins.kotlin.android) apply false
|
||||
|
||||
id("com.google.devtools.ksp") version "2.0.21-1.0.27" apply false
|
||||
}
|
||||
6
keystore.properties
Normal file
@ -0,0 +1,6 @@
|
||||
app_name=Luma Wallpaper
|
||||
package_name=com.wallpaper.lumawallpaper
|
||||
keystoreFile=app/LumaWallpaper.jks
|
||||
key_alias=LumaWallpaperkey0
|
||||
key_store_password=LumaWallpaper
|
||||
key_password=LumaWallpaper
|
||||