完成版

This commit is contained in:
lihongwei 2025-05-30 14:49:19 +08:00
parent 63af6a2ec5
commit f036f5957b
66 changed files with 11595 additions and 41 deletions

BIN
app/PureWallpaper.jks Normal file

Binary file not shown.

View File

@ -1,24 +1,31 @@
import java.text.SimpleDateFormat
import java.util.Date
plugins {
alias(libs.plugins.android.application)
}
val timestamp: String = SimpleDateFormat("MM_dd_HH_mm").format(Date())
android {
namespace = "com.pure.wallpaper.purewallpaper"
compileSdk = 35
defaultConfig {
applicationId = "com.pure.wallpaper.purewallpaper"
minSdk = 23
minSdk = 24
targetSdk = 35
versionCode = 1
versionName = "1.0"
setProperty("archivesBaseName", "Pure 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"
@ -40,4 +47,10 @@ dependencies {
testImplementation(libs.junit)
androidTestImplementation(libs.ext.junit)
androidTestImplementation(libs.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")
annotationProcessor ("androidx.room:room-compiler:2.7.1")
}

View File

@ -18,4 +18,18 @@
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
#-renamesourcefileattribute SourceFile
-keepclassmembers class com.pure.wallpaper.purewallpaper.MyApplication {
public static final java.lang.String DATABASE_FILE_NAME;
public static final int DATABASE_VERSION_CODE;
}
-keepclassmembers class * {
@androidx.room.Query <methods>;
}
-keep class com.pure.wallpaper.purewallpaper.sqlite.database.AppDatabase { *; }
-keep class com.pure.wallpaper.purewallpaper.sqlite.database.dao.PureWallpaperDao { *; }
-keep class com.pure.wallpaper.purewallpaper.sqlite.database.data.ImageSet { *; }
-keep class com.pure.wallpaper.purewallpaper.sqlite.database.data.PureWallpaper { *; }

View File

@ -2,18 +2,34 @@
<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=".MyApplication"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:roundIcon="@mipmap/ic_launcher"
android:supportsRtl="true"
android:theme="@style/Theme.PureWallpaper"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:name=".ui.activity.MainActivity"
android:exported="false" />
<activity
android:name=".ui.activity.CategoryActivity"
android:exported="false" />
<activity
android:name=".ui.activity.PureActivity"
android:exported="false" />
<activity
android:name=".ui.activity.SplashActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

File diff suppressed because it is too large Load Diff

View File

@ -1,24 +0,0 @@
package com.pure.wallpaper.purewallpaper;
import android.os.Bundle;
import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_main);
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
return insets;
});
}
}

View File

@ -0,0 +1,63 @@
package com.pure.wallpaper.purewallpaper;
import android.app.Application;
import android.content.Context;
import android.content.SharedPreferences;
import com.pure.wallpaper.purewallpaper.sqlite.database.AppDatabase;
import com.pure.wallpaper.purewallpaper.sqlite.database.dao.PureWallpaperDao;
import com.pure.wallpaper.purewallpaper.sqlite.database.data.ImageSet;
import com.pure.wallpaper.purewallpaper.sqlite.database.data.PureWallpaper;
import com.pure.wallpaper.purewallpaper.util.JsonDataParser;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class MyApplication extends Application {
public static MyApplication application;
public static final int DATABASE_VERSION_CODE = 1;
public static final String DATABASE_FILE_NAME = "art_collection";
private static final String PREFERENCES_FILE = "app_settings";
private static final String KEY_FIRST_RUN = "is_first_launch";
@Override
public void onCreate() {
super.onCreate();
application = this;
SharedPreferences appPrefs = getSharedPreferences(PREFERENCES_FILE, MODE_PRIVATE);
if (!appPrefs.getBoolean(KEY_FIRST_RUN, false)) {
initializeData();
appPrefs.edit().putBoolean(KEY_FIRST_RUN, true).apply();
}
}
public static Context getContext() {
return application.getApplicationContext();
}
private void initializeData() {
ExecutorService backgroundExec = Executors.newFixedThreadPool(2);
backgroundExec.execute(() -> {
List<ImageSet> imageData = retrieveImageData();
List<PureWallpaper> pureWallpaperList = new ArrayList<>();
AppDatabase db = AppDatabase.getInstance(getContext());
PureWallpaperDao pureWallpaperDao = db.pureWallpaperDao();
for (ImageSet category : imageData) {
pureWallpaperList.addAll(category.getList());
}
pureWallpaperDao.insertAll(pureWallpaperList);
backgroundExec.shutdown();
});
}
public static List<ImageSet> retrieveImageData() {
return JsonDataParser.extractJsonData("pure_wallpaper.json");
}
}

View File

@ -0,0 +1,32 @@
package com.pure.wallpaper.purewallpaper.sqlite.database;
import android.content.Context;
import androidx.room.Database;
import androidx.room.Room;
import androidx.room.RoomDatabase;
import com.pure.wallpaper.purewallpaper.MyApplication;
import com.pure.wallpaper.purewallpaper.sqlite.database.dao.PureWallpaperDao;
import com.pure.wallpaper.purewallpaper.sqlite.database.data.PureWallpaper;
@Database(entities = {PureWallpaper.class}, version = MyApplication.DATABASE_VERSION_CODE, exportSchema = false)
public abstract class AppDatabase extends RoomDatabase {
public abstract PureWallpaperDao pureWallpaperDao();
private static volatile AppDatabase INSTANCE;
public static AppDatabase getInstance(Context context) {
if (INSTANCE == null) {
synchronized (AppDatabase.class) {
if (INSTANCE == null) {
INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
AppDatabase.class, MyApplication.DATABASE_FILE_NAME)
.build();
}
}
}
return INSTANCE;
}
}

View File

@ -0,0 +1,33 @@
package com.pure.wallpaper.purewallpaper.sqlite.database.dao;
import androidx.lifecycle.LiveData;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.Query;
import androidx.room.Update;
import com.pure.wallpaper.purewallpaper.sqlite.database.data.PureWallpaper;
import java.util.List;
@Dao
public interface PureWallpaperDao {
@Insert
void insertAll(List<PureWallpaper> paletteData);
@Update
void update(PureWallpaper pureWallpaper);
@Query("SELECT * FROM PureWallpaper WHERE isLike = 1 ")
LiveData<List<PureWallpaper>> getFavorite();
@Query("SELECT * FROM PureWallpaper WHERE id IN (SELECT MAX(id) FROM PureWallpaper GROUP BY name) ORDER BY id ASC")
LiveData<List<PureWallpaper>> getCategory();
@Query("SELECT * FROM PureWallpaper WHERE name = :name ORDER BY id DESC")
LiveData<List<PureWallpaper>> getAllCategory(String name);
@Query("SELECT isLIKE FROM PureWallpaper WHERE source = :source AND name = :name")
LiveData<Boolean> getCollection(String source, String name);
}

View File

@ -0,0 +1,29 @@
package com.pure.wallpaper.purewallpaper.sqlite.database.data;
import java.util.List;
public class ImageSet {
private String name;
private List<PureWallpaper> list;
public ImageSet(String name, List<PureWallpaper> list) {
this.name = name;
this.list = list;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<PureWallpaper> getList() {
return list;
}
public void setList(List<PureWallpaper> list) {
this.list = list;
}
}

View File

@ -0,0 +1,74 @@
package com.pure.wallpaper.purewallpaper.sqlite.database.data;
import androidx.room.Entity;
import androidx.room.PrimaryKey;
import java.io.Serializable;
@Entity
public class PureWallpaper implements Serializable {
@PrimaryKey(autoGenerate = true)
private int id;
private String original;
private String previewThumb;
private String source;
private String name;
private Boolean isLike;
public PureWallpaper(String original, String previewThumb, String source, String name, Boolean isLike) {
this.original = original;
this.previewThumb = previewThumb;
this.source = source;
this.name = name;
this.isLike = isLike;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getOriginal() {
return original;
}
public void setOriginal(String original) {
this.original = original;
}
public String getPreviewThumb() {
return previewThumb;
}
public void setPreviewThumb(String previewThumb) {
this.previewThumb = previewThumb;
}
public String getSource() {
return source;
}
public void setSource(String source) {
this.source = source;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Boolean getLike() {
return isLike;
}
public void setLike(Boolean like) {
isLike = like;
}
}

View File

@ -0,0 +1,91 @@
package com.pure.wallpaper.purewallpaper.ui.activity;
import android.os.Bundle;
import android.view.View;
import android.view.animation.AnimationUtils;
import android.view.animation.LayoutAnimationController;
import android.widget.Toast;
import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.StaggeredGridLayoutManager;
import com.pure.wallpaper.purewallpaper.R;
import com.pure.wallpaper.purewallpaper.databinding.ActivityCategoryBinding;
import com.pure.wallpaper.purewallpaper.ui.adapter.JumpToPureAdapter;
import com.pure.wallpaper.purewallpaper.util.PaletteViewModel;
import com.pure.wallpaper.purewallpaper.util.StaggeredGridItemDecoration;
import java.util.ArrayList;
public class CategoryActivity extends AppCompatActivity {
private ActivityCategoryBinding layoutBinding;
private JumpToPureAdapter dataAdapter;
private PaletteViewModel dataModel;
private String groupTitle;
@Override
protected void onCreate(Bundle savedState) {
super.onCreate(savedState);
EdgeToEdge.enable(this);
layoutBinding = ActivityCategoryBinding.inflate(getLayoutInflater());
setContentView(layoutBinding.getRoot());
View mainView = findViewById(R.id.main);
ViewCompat.setOnApplyWindowInsetsListener(mainView, (v, windowInsets) -> {
Insets bars = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(bars.left, bars.top, bars.right, bars.bottom);
return windowInsets;
});
setupComponents();
bindListeners();
monitorDataUpdates();
}
private void setupComponents() {
groupTitle = getIntent().getStringExtra("name");
if (groupTitle == null) {
Toast.makeText(this, "Data not found", Toast.LENGTH_SHORT).show();
finish();
return;
}
dataModel = new ViewModelProvider(this).get(PaletteViewModel.class);
dataAdapter = new JumpToPureAdapter(this, new ArrayList<>());
layoutBinding.recyclerView.setLayoutManager(new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL));
layoutBinding.recyclerView.setAdapter(dataAdapter);
LayoutAnimationController animation = AnimationUtils.loadLayoutAnimation(this, R.anim.layout_animation_fall_down);
layoutBinding.recyclerView.setLayoutAnimation(animation);
StaggeredGridItemDecoration decoration = new StaggeredGridItemDecoration(20, 10);
layoutBinding.recyclerView.addItemDecoration(decoration);
layoutBinding.title.setText(groupTitle);
}
private void bindListeners() {
layoutBinding.back.setOnClickListener(v -> finish());
}
private void monitorDataUpdates() {
dataModel.getAllCategory(groupTitle).observe(this, items -> {
if (items != null) {
dataAdapter.updateDataSet(items);
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
layoutBinding = null;
}
}

View File

@ -0,0 +1,99 @@
package com.pure.wallpaper.purewallpaper.ui.activity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import com.google.android.material.tabs.TabLayout;
import com.google.android.material.tabs.TabLayoutMediator;
import com.pure.wallpaper.purewallpaper.R;
import com.pure.wallpaper.purewallpaper.databinding.ActivityMainBinding;
import com.pure.wallpaper.purewallpaper.databinding.CustomTabBinding;
import com.pure.wallpaper.purewallpaper.ui.adapter.ViewPager2Adapter;
public class MainActivity extends AppCompatActivity {
private ActivityMainBinding layoutBinding;
@Override
protected void onCreate(Bundle savedState) {
super.onCreate(savedState);
EdgeToEdge.enable(this);
layoutBinding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(layoutBinding.getRoot());
View mainView = findViewById(R.id.main);
ViewCompat.setOnApplyWindowInsetsListener(mainView, (v, windowInsets) -> {
Insets systemBars = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
return windowInsets;
});
configurePagerAndTabs();
}
private void configurePagerAndTabs() {
ViewPager2Adapter adapter = new ViewPager2Adapter(this);
layoutBinding.viewPager.setAdapter(adapter);
TabLayoutMediator mediator = new TabLayoutMediator(layoutBinding.tabLayout, layoutBinding.viewPager, (tab, position) -> {
CustomTabBinding tabView = CustomTabBinding.inflate(LayoutInflater.from(this));
tab.setCustomView(tabView.getRoot());
setupTabContent(tabView, position);
});
mediator.attach();
layoutBinding.tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
updateTabAppearance(tab, true);
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
updateTabAppearance(tab, false);
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
}
private void setupTabContent(CustomTabBinding tabBinding, int index) {
tabBinding.text.setText(index == 0 ? "CATEGORY" : "COLLECTION");
tabBinding.image.setImageResource(selectIcon(index, false, 1));
int colorRes = index == 0 ? R.color.black : R.color.gray;
tabBinding.text.setTextColor(ContextCompat.getColor(this, colorRes));
}
private void updateTabAppearance(TabLayout.Tab tab, boolean isActive) {
View customView = tab.getCustomView();
if (customView == null) return;
CustomTabBinding tabBinding = CustomTabBinding.bind(customView);
int tabIndex = tab.getPosition();
tabBinding.image.setImageResource(selectIcon(tabIndex, isActive, 0));
int colorRes = isActive ? R.color.black : R.color.gray;
tabBinding.text.setTextColor(ContextCompat.getColor(this, colorRes));
}
private int selectIcon(int index, boolean isActive, int mode) {
if (index == 0 && mode == 1) return R.drawable.main;
if (index == 1) return isActive ? R.drawable.collection : R.drawable.un_collection;
return isActive ? R.drawable.main : R.drawable.un_main;
}
@Override
protected void onDestroy() {
super.onDestroy();
layoutBinding = null;
}
}

View File

@ -0,0 +1,261 @@
package com.pure.wallpaper.purewallpaper.ui.activity;
import static com.pure.wallpaper.purewallpaper.util.PureUtils.PERMISSION_CODE_STORAGE;
import android.app.WallpaperManager;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Toast;
import androidx.activity.EdgeToEdge;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;
import androidx.lifecycle.ViewModelProvider;
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.pure.wallpaper.purewallpaper.R;
import com.pure.wallpaper.purewallpaper.databinding.ActivityPureBinding;
import com.pure.wallpaper.purewallpaper.sqlite.database.data.PureWallpaper;
import com.pure.wallpaper.purewallpaper.util.PaletteViewModel;
import com.pure.wallpaper.purewallpaper.util.PureListener;
import com.pure.wallpaper.purewallpaper.util.PureUtils;
import java.io.IOException;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class PureActivity extends AppCompatActivity implements PureListener {
private ActivityPureBinding displayBinding;
private String mainImageLink;
private String imageOrigin;
private PureWallpaper imageData;
private PaletteViewModel dataHandler;
private String displayTitle;
private Bitmap loadedBitmap;
private PureUtils imageProcessor;
private ExecutorService taskRunner;
private Handler mainThreadHandler;
@Override
protected void onCreate(Bundle savedState) {
super.onCreate(savedState);
EdgeToEdge.enable(this);
displayBinding = ActivityPureBinding.inflate(getLayoutInflater());
setContentView(displayBinding.getRoot());
initData();
setupControls();
}
private void initData() {
imageData = (PureWallpaper) getIntent().getSerializableExtra("entry");
if (imageData == null) {
Toast.makeText(this, "Load failure", Toast.LENGTH_SHORT).show();
finish();
return;
}
mainImageLink = imageData.getOriginal();
imageOrigin = imageData.getSource();
displayTitle = imageData.getName();
imageProcessor = new PureUtils();
dataHandler = new ViewModelProvider(this).get(PaletteViewModel.class);
mainThreadHandler = new Handler(Looper.getMainLooper());
taskRunner = Executors.newFixedThreadPool(1);
}
private void setupControls() {
showProgress();
displayBinding.back.setOnClickListener(v -> finish());
displayBinding.like.setOnClickListener(v -> {
boolean newState = !imageData.getLike();
imageData.setLike(newState);
taskRunner.execute(() -> dataHandler.update(imageData));
});
displayBinding.set.setOnClickListener(v -> launchOptionsMenu());
displayBinding.down.setOnClickListener(v -> {
showProgress();
imageProcessor.setWallpaper(this, imageOrigin, this);
});
loadImageContent();
trackLikeStatus();
}
@Override
public void onRequestPermissionsResult(int code, @NonNull String[] perms, @NonNull int[] results) {
super.onRequestPermissionsResult(code, perms, results);
if (code == PERMISSION_CODE_STORAGE) {
if (results.length > 0 && results[0] == PackageManager.PERMISSION_GRANTED) {
imageProcessor.setWallpaper(this, imageOrigin, this);
} else {
Toast.makeText(this, "Permission rejected", Toast.LENGTH_SHORT).show();
}
}
}
private void launchOptionsMenu() {
BottomSheetDialog menu = new BottomSheetDialog(this);
View menuLayout = LayoutInflater.from(this).inflate(R.layout.detail_dialog, null);
menuLayout.findViewById(R.id.both).setOnClickListener(v -> {
toggleOptions(menuLayout, false);
applyImageAsWallpaper(loadedBitmap, WallpaperManager.FLAG_SYSTEM | WallpaperManager.FLAG_LOCK, menu, menuLayout);
});
menuLayout.findViewById(R.id.lock).setOnClickListener(v -> {
toggleOptions(menuLayout, false);
applyImageAsWallpaper(loadedBitmap, WallpaperManager.FLAG_LOCK, menu, menuLayout);
});
menuLayout.findViewById(R.id.desktop).setOnClickListener(v -> {
toggleOptions(menuLayout, false);
applyImageAsWallpaper(loadedBitmap, WallpaperManager.FLAG_SYSTEM, menu, menuLayout);
});
menu.setContentView(menuLayout);
menu.show();
}
private void toggleOptions(View menuLayout, boolean enable) {
menuLayout.findViewById(R.id.both).setEnabled(enable);
menuLayout.findViewById(R.id.lock).setEnabled(enable);
menuLayout.findViewById(R.id.desktop).setEnabled(enable);
}
private void applyImageAsWallpaper(Bitmap bitmap, int flagSet, BottomSheetDialog menu, View menuLayout) {
showProgress();
taskRunner.execute(() -> {
boolean success = false;
try {
success = processWallpaper(bitmap, flagSet);
} catch (Exception e) {
e.printStackTrace();
}
boolean outcome = success;
mainThreadHandler.post(() -> {
clearProgress();
String text = outcome ? "Wallpaper applied" : "Failed to apply wallpaper";
Toast.makeText(this, text, Toast.LENGTH_SHORT).show();
toggleOptions(menuLayout, true); // 确保在 Toast 显示后重新启用按钮
menu.dismiss(); // 如果您希望在设置成功/失败后关闭菜单
});
});
}
private boolean processWallpaper(Bitmap bitmap, int flagSet) {
WallpaperManager manager = WallpaperManager.getInstance(this);
try {
displayBinding.imageView.setDrawingCacheEnabled(true);
manager.setBitmap(bitmap, null, true, flagSet);
return true;
} catch (IOException e) {
e.printStackTrace();
return false;
} finally {
displayBinding.imageView.setDrawingCacheEnabled(false);
}
}
private void loadImageContent() {
Glide.with(this)
.asBitmap()
.load(mainImageLink)
.error(ContextCompat.getDrawable(this, R.mipmap.placeholder))
.override(1080, 1920)
.centerInside()
.into(new CustomTarget<Bitmap>() {
@Override
public void onResourceReady(@NonNull Bitmap resource, Transition<? super Bitmap> transition) {
displayBinding.imageView.setImageBitmap(resource);
loadedBitmap = resource;
clearProgress();
}
@Override
public void onLoadCleared(Drawable fallback) {
displayBinding.imageView.setImageDrawable(fallback != null ? fallback : fetchPlaceholder());
}
@Override
public void onLoadFailed(Drawable error) {
displayBinding.imageView.setImageDrawable(error != null ? error : fetchPlaceholder());
clearProgress();
}
});
}
private Drawable fetchPlaceholder() {
return ContextCompat.getDrawable(this, R.mipmap.placeholder);
}
private void trackLikeStatus() {
dataHandler.getLike(imageOrigin, displayTitle).observe(this, item -> refreshLikeButton());
}
private void refreshLikeButton() {
displayBinding.like.setImageResource(
imageData.getLike() ? R.drawable.favorite : R.drawable.un_favorite
);
}
private void clearProgress() {
if (displayBinding != null) {
displayBinding.progressBar.setVisibility(View.GONE);
displayBinding.view.setVisibility(View.GONE);
}
}
private void showProgress() {
displayBinding.progressBar.setVisibility(View.VISIBLE);
displayBinding.view.setVisibility(View.VISIBLE);
}
@Override
public void onPrepare() {
showProgress();
}
@Override
public void onSuccess(Uri uri) {
clearProgress();
Toast.makeText(this, "Download successful", Toast.LENGTH_SHORT).show();
}
@Override
public void onError() {
clearProgress();
Toast.makeText(this, "Download failed", Toast.LENGTH_SHORT).show();
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mainThreadHandler != null) {
mainThreadHandler.removeCallbacksAndMessages(null);
}
if (taskRunner != null) {
taskRunner.shutdown();
}
displayBinding = null;
}
}

View File

@ -0,0 +1,75 @@
package com.pure.wallpaper.purewallpaper.ui.activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.CountDownTimer;
import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
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.pure.wallpaper.purewallpaper.R;
import com.pure.wallpaper.purewallpaper.databinding.ActivitySplashBinding;
public class SplashActivity extends AppCompatActivity {
private ActivitySplashBinding binding;
private static final long TOTAL_TIME = 1000;
private CountDownTimer countDownTimer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
binding = ActivitySplashBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
return insets;
});
Glide.with(this)
.load(R.mipmap.placeholder)
.transform(new CenterCrop(), new RoundedCorners(32))
.into(binding.image);
countDownTimer = new CountDownTimer(TOTAL_TIME, 100) {
@Override
public void onTick(long millisUntilFinished) {
int percentage = (int) (100 - (float) millisUntilFinished / TOTAL_TIME * 100);
binding.progressBar.setProgress(percentage);
}
@Override
public void onFinish() {
jumpToMain();
}
};
countDownTimer.start();
}
private void jumpToMain() {
binding.progressBar.setProgress(100);
Intent intent = new Intent(SplashActivity.this, MainActivity.class);
startActivity(intent);
finish();
}
@Override
protected void onDestroy() {
super.onDestroy();
if (countDownTimer != null) {
countDownTimer.cancel();
}
binding = null;
}
}

View File

@ -0,0 +1,88 @@
package com.pure.wallpaper.purewallpaper.ui.adapter;
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.annotation.NonNull;
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.pure.wallpaper.purewallpaper.R;
import com.pure.wallpaper.purewallpaper.sqlite.database.data.PureWallpaper;
import com.pure.wallpaper.purewallpaper.ui.activity.CategoryActivity;
import java.util.List;
public class JumpToFragmentAdapter extends RecyclerView.Adapter<JumpToFragmentAdapter.GalleryViewHolder> {
private List<PureWallpaper> itemCollection;
private final Context appContext;
public JumpToFragmentAdapter(Context ctx, List<PureWallpaper> startingData) {
this.appContext = ctx;
this.itemCollection = startingData;
}
public void swapData(List<PureWallpaper> newItems) {
this.itemCollection = newItems;
notifyDataSetChanged();
}
@NonNull
@Override
public GalleryViewHolder onCreateViewHolder(@NonNull ViewGroup container, int type) {
View layout = LayoutInflater.from(appContext).inflate(R.layout.item_to_fragment, container, false);
return new GalleryViewHolder(layout);
}
@Override
public void onBindViewHolder(@NonNull GalleryViewHolder holder, int index) {
holder.fillContent(itemCollection.get(index));
}
@Override
public int getItemCount() {
return itemCollection.size();
}
public static class GalleryViewHolder extends RecyclerView.ViewHolder {
private final ImageView previewImage;
private final TextView caption;
GalleryViewHolder(View layout) {
super(layout);
previewImage = layout.findViewById(R.id.imageview);
caption = layout.findViewById(R.id.category_title);
}
void fillContent(PureWallpaper data) {
caption.setText(data.getName());
renderImage(data.getOriginal());
bindClickAction(data.getName());
}
private void renderImage(String imageUrl) {
Glide.with(previewImage.getContext())
.load(imageUrl)
.thumbnail(0.15f)
.transform(new CenterCrop(), new RoundedCorners(32))
.placeholder(R.mipmap.placeholder)
.error(R.mipmap.placeholder)
.into(previewImage);
}
private void bindClickAction(String groupName) {
previewImage.setOnClickListener(v -> {
Intent navigate = new Intent(v.getContext(), CategoryActivity.class);
navigate.putExtra("name", groupName);
v.getContext().startActivity(navigate);
});
}
}
}

View File

@ -0,0 +1,117 @@
package com.pure.wallpaper.purewallpaper.ui.adapter;
import android.annotation.SuppressLint;
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 androidx.annotation.NonNull;
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.pure.wallpaper.purewallpaper.R;
import com.pure.wallpaper.purewallpaper.sqlite.database.AppDatabase;
import com.pure.wallpaper.purewallpaper.sqlite.database.data.PureWallpaper;
import com.pure.wallpaper.purewallpaper.ui.activity.PureActivity;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
public class JumpToPureAdapter extends RecyclerView.Adapter<JumpToPureAdapter.ArtHolder> {
private final List<PureWallpaper> dataCollection;
private final Context appContext;
private final Executor taskQueue = Executors.newFixedThreadPool(1);
public JumpToPureAdapter(Context ctx, List<PureWallpaper> startingData) {
this.appContext = ctx;
this.dataCollection = startingData;
}
@SuppressLint("NotifyDataSetChanged")
public void updateDataSet(List<PureWallpaper> freshData) {
dataCollection.clear();
dataCollection.addAll(freshData);
notifyDataSetChanged();
}
@NonNull
@Override
public ArtHolder onCreateViewHolder(@NonNull ViewGroup container, int type) {
View view = LayoutInflater.from(appContext).inflate(R.layout.item_to_pure, container, false);
return new ArtHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ArtHolder holder, int index) {
holder.renderItem(dataCollection.get(index));
}
@Override
public int getItemCount() {
return dataCollection.size();
}
public class ArtHolder extends RecyclerView.ViewHolder {
private final ImageView artViewer;
private final ImageView favButton;
ArtHolder(View layout) {
super(layout);
artViewer = layout.findViewById(R.id.image_view);
favButton = layout.findViewById(R.id.favorite);
}
void renderItem(PureWallpaper entry) {
showImage(entry.getOriginal());
updateFavoriteIcon(entry);
attachListeners(entry);
}
private void showImage(String url) {
Glide.with(artViewer.getContext())
.load(url)
.dontAnimate()
.transform(new CenterCrop(), new RoundedCorners(32))
.placeholder(R.mipmap.placeholder)
.error(R.mipmap.placeholder)
.into(artViewer);
}
private void updateFavoriteIcon(PureWallpaper entry) {
favButton.setImageResource(entry.getLike() ? R.drawable.favorite : R.drawable.un_favorite);
}
private void attachListeners(PureWallpaper entry) {
artViewer.setOnClickListener(v -> {
Intent detailIntent = new Intent(v.getContext(), PureActivity.class);
detailIntent.putExtra("entry", entry);
v.getContext().startActivity(detailIntent);
});
favButton.setOnClickListener(v -> switchFavorite(entry));
}
private void switchFavorite(PureWallpaper entry) {
boolean newValue = !entry.getLike();
entry.setLike(newValue);
favButton.setImageResource(newValue ? R.drawable.favorite : R.drawable.un_favorite);
notifyItemChanged(getAdapterPosition());
persistData(entry);
}
private void persistData(PureWallpaper entry) {
taskQueue.execute(() -> {
AppDatabase
.getInstance(appContext.getApplicationContext())
.pureWallpaperDao()
.update(entry);
});
}
}
}

View File

@ -0,0 +1,34 @@
package com.pure.wallpaper.purewallpaper.ui.adapter;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.viewpager2.adapter.FragmentStateAdapter;
import com.pure.wallpaper.purewallpaper.ui.fragment.CollectionFragment;
import com.pure.wallpaper.purewallpaper.ui.fragment.HomeFragment;
import java.util.ArrayList;
import java.util.List;
public class ViewPager2Adapter extends FragmentStateAdapter {
private final List<Fragment> fragmentList = new ArrayList<>();
public ViewPager2Adapter(@NonNull FragmentActivity fragmentActivity) {
super(fragmentActivity);
fragmentList.add(new HomeFragment());
fragmentList.add(new CollectionFragment());
}
@NonNull
@Override
public Fragment createFragment(int position) {
return fragmentList.get(position);
}
@Override
public int getItemCount() {
return fragmentList.size();
}
}

View File

@ -0,0 +1,70 @@
package com.pure.wallpaper.purewallpaper.ui.fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AnimationUtils;
import android.view.animation.LayoutAnimationController;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.StaggeredGridLayoutManager;
import com.pure.wallpaper.purewallpaper.R;
import com.pure.wallpaper.purewallpaper.databinding.FragmentCollectionBinding;
import com.pure.wallpaper.purewallpaper.sqlite.database.data.PureWallpaper;
import com.pure.wallpaper.purewallpaper.ui.adapter.JumpToPureAdapter;
import com.pure.wallpaper.purewallpaper.util.PaletteViewModel;
import com.pure.wallpaper.purewallpaper.util.StaggeredGridItemDecoration;
import java.util.ArrayList;
import java.util.List;
public class CollectionFragment extends Fragment {
private FragmentCollectionBinding binding;
private JumpToPureAdapter jumpToPureAdapter;
private PaletteViewModel paletteViewModel;
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
binding = FragmentCollectionBinding.inflate(inflater, container, false);
initializeData();
fetchFavoriteItems();
return binding.getRoot();
}
private void initializeData() {
paletteViewModel = new ViewModelProvider(this).get(PaletteViewModel.class);
jumpToPureAdapter = new JumpToPureAdapter(requireContext(), new ArrayList<>());
binding.recyclerView.setLayoutManager(new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL));
binding.recyclerView.setAdapter(jumpToPureAdapter);
LayoutAnimationController animation = AnimationUtils.loadLayoutAnimation(requireContext(), R.anim.layout_animation_fall_down);
binding.recyclerView.setLayoutAnimation(animation);
StaggeredGridItemDecoration decoration = new StaggeredGridItemDecoration(20, 10);
binding.recyclerView.addItemDecoration(decoration);
}
private void fetchFavoriteItems() {
paletteViewModel.getAllFavorite()
.observe(getViewLifecycleOwner(), new Observer<List<PureWallpaper>>() {
@Override
public void onChanged(List<PureWallpaper> updatedList) {
jumpToPureAdapter.updateDataSet(updatedList);
}
});
}
@Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
}

View File

@ -0,0 +1,73 @@
package com.pure.wallpaper.purewallpaper.ui.fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AnimationUtils;
import android.view.animation.LayoutAnimationController;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.StaggeredGridLayoutManager;
import com.pure.wallpaper.purewallpaper.R;
import com.pure.wallpaper.purewallpaper.databinding.FragmentHomeBinding;
import com.pure.wallpaper.purewallpaper.sqlite.database.data.PureWallpaper;
import com.pure.wallpaper.purewallpaper.ui.adapter.JumpToFragmentAdapter;
import com.pure.wallpaper.purewallpaper.util.PaletteViewModel;
import com.pure.wallpaper.purewallpaper.util.StaggeredGridItemDecoration;
import java.util.ArrayList;
import java.util.List;
public class HomeFragment extends Fragment {
private FragmentHomeBinding fragmentHomeBinding;
private JumpToFragmentAdapter jumpToFragmentAdapter;
private PaletteViewModel paletteViewModel;
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
fragmentHomeBinding = FragmentHomeBinding.inflate(inflater, container, false);
initData();
getFirstByName();
return fragmentHomeBinding.getRoot();
}
private void initData() {
paletteViewModel = new ViewModelProvider(this).get(PaletteViewModel.class);
fragmentHomeBinding.recyclerView.setLayoutManager(new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL));
jumpToFragmentAdapter = new JumpToFragmentAdapter(requireContext(), new ArrayList<>());
fragmentHomeBinding.recyclerView.setAdapter(jumpToFragmentAdapter);
LayoutAnimationController animation = AnimationUtils.loadLayoutAnimation(requireContext(), R.anim.layout_animation_fall_down);
fragmentHomeBinding.recyclerView.setLayoutAnimation(animation);
StaggeredGridItemDecoration decoration = new StaggeredGridItemDecoration(20, 10);
fragmentHomeBinding.recyclerView.addItemDecoration(decoration);
}
private void getFirstByName() {
paletteViewModel
.getFirstByName()
.observe(getViewLifecycleOwner(), new Observer<List<PureWallpaper>>() {
@Override
public void onChanged(List<PureWallpaper> coolEntities) {
jumpToFragmentAdapter.swapData(coolEntities);
}
});
}
@Override
public void onDestroyView() {
super.onDestroyView();
fragmentHomeBinding = null;
}
}

View File

@ -0,0 +1,69 @@
package com.pure.wallpaper.purewallpaper.util;
import android.util.Log;
import com.pure.wallpaper.purewallpaper.MyApplication;
import com.pure.wallpaper.purewallpaper.sqlite.database.data.ImageSet;
import com.pure.wallpaper.purewallpaper.sqlite.database.data.PureWallpaper;
import org.json.JSONArray;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
public class JsonDataParser {
private static String retrieveJsonFromFile(String assetName) {
StringBuilder dataBuilder = new StringBuilder();
try (InputStream is = MyApplication.getContext().getAssets().open(assetName);
BufferedReader br = new BufferedReader(new InputStreamReader(is))) {
int charCode;
while ((charCode = br.read()) != -1) {
dataBuilder.append((char) charCode);
}
} catch (IOException e) {
e.printStackTrace();
}
Log.d("JsonLoad", "Retrieved JSON: " + dataBuilder.toString());
return dataBuilder.toString();
}
public static List<ImageSet> extractJsonData(String assetName) {
List<ImageSet> resultList = new ArrayList<>();
try {
String jsonData = retrieveJsonFromFile(assetName);
if (jsonData.isEmpty()) {
throw new IllegalStateException("Empty or invalid JSON content.");
}
JSONArray dataArray = new JSONArray(jsonData);
for (int idx = 0; idx < dataArray.length(); idx++) {
JSONObject groupItem = dataArray.getJSONObject(idx);
String categoryName = groupItem.getString("name");
JSONArray itemsArray = groupItem.getJSONArray("data");
List<PureWallpaper> imageItems = new ArrayList<>();
for (int k = 0; k < itemsArray.length(); k++) {
JSONObject entry = itemsArray.getJSONObject(k);
String fullImage = entry.getString("original");
String thumbImage = entry.getString("previewThumb");
String origin = entry.getString("source");
imageItems.add(new PureWallpaper(fullImage, thumbImage, origin, categoryName, false));
}
resultList.add(new ImageSet(categoryName, imageItems));
}
} catch (Exception e) {
e.printStackTrace();
}
return resultList;
}
}

View File

@ -0,0 +1,43 @@
package com.pure.wallpaper.purewallpaper.util;
import android.app.Application;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData;
import com.pure.wallpaper.purewallpaper.sqlite.database.AppDatabase;
import com.pure.wallpaper.purewallpaper.sqlite.database.dao.PureWallpaperDao;
import com.pure.wallpaper.purewallpaper.sqlite.database.data.PureWallpaper;
import java.util.List;
public class PaletteViewModel extends AndroidViewModel {
private final PureWallpaperDao pureWallpaperDao;
public PaletteViewModel(Application application) {
super(application);
AppDatabase db = AppDatabase.getInstance(application);
pureWallpaperDao = db.pureWallpaperDao();
}
public LiveData<List<PureWallpaper>> getAllFavorite() {
return pureWallpaperDao.getFavorite();
}
public LiveData<List<PureWallpaper>> getFirstByName() {
return pureWallpaperDao.getCategory();
}
public LiveData<List<PureWallpaper>> getAllCategory(String name) {
return pureWallpaperDao.getAllCategory(name);
}
public LiveData<Boolean> getLike(String source, String name) {
return pureWallpaperDao.getCollection(source, name);
}
public void update(PureWallpaper pureWallpaper) {
pureWallpaperDao.update(pureWallpaper);
}
}

View File

@ -0,0 +1,9 @@
package com.pure.wallpaper.purewallpaper.util;
import android.net.Uri;
public interface PureListener {
void onPrepare();
void onSuccess(Uri uri);
void onError();
}

View File

@ -0,0 +1,107 @@
package com.pure.wallpaper.purewallpaper.util;
import android.Manifest;
import android.app.Activity;
import android.content.ContentValues;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.provider.MediaStore;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class PureUtils {
public static final int PERMISSION_CODE_STORAGE = 302;
private final ExecutorService taskExecutor = Executors.newFixedThreadPool(1);
public void setWallpaper(Activity activity, String imageUrl, PureListener callback) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
initiateDownload(activity, imageUrl, callback);
} else {
if (ContextCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(
activity,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
PERMISSION_CODE_STORAGE
);
} else {
initiateDownload(activity, imageUrl, callback);
}
}
}
private void initiateDownload(Activity activity, String sourceUrl, PureListener callback) {
callback.onPrepare();
taskExecutor.submit(() -> {
Uri savedUri = saveMediaImage(activity, sourceUrl);
new Handler(Looper.getMainLooper()).post(() -> {
if (savedUri != null) {
callback.onSuccess(savedUri);
} else {
callback.onError();
}
});
});
}
private Uri saveMediaImage(Activity activity, String imageUrl) {
String name = "photo_" + System.nanoTime() + ".jpeg";
ContentValues metadata = new ContentValues();
metadata.put(MediaStore.Images.Media.DISPLAY_NAME, name);
metadata.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
metadata.put(MediaStore.Images.Media.IS_PENDING, 1);
}
Uri destinationUri = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
? MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL)
: MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
Uri imageUri = activity.getContentResolver().insert(destinationUri, metadata);
if (imageUri == null) return null;
try {
URL url = new URL(imageUrl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.connect();
try (InputStream inStream = conn.getInputStream();
OutputStream outStream = activity.getContentResolver().openOutputStream(imageUri)) {
if (outStream == null) return null;
byte[] data = new byte[8192];
int bytesRead;
while ((bytesRead = inStream.read(data)) > 0) {
outStream.write(data, 0, bytesRead);
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
metadata.clear();
metadata.put(MediaStore.Images.Media.IS_PENDING, 0);
activity.getContentResolver().update(imageUri, metadata, null, null);
}
return imageUri;
} catch (IOException e) {
activity.getContentResolver().delete(imageUri, null, null);
return null;
}
}
}

View File

@ -0,0 +1,33 @@
package com.pure.wallpaper.purewallpaper.util;
import android.graphics.Rect;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.StaggeredGridLayoutManager;
public class StaggeredGridItemDecoration extends RecyclerView.ItemDecoration {
private final int spacing;
private final int spanCount;
public StaggeredGridItemDecoration(int spacing, int spanCount) {
this.spacing = spacing;
this.spanCount = spanCount;
}
@Override
public void getItemOffsets(Rect outRect, @NonNull View view, RecyclerView parent, @NonNull RecyclerView.State state) {
int position = parent.getChildAdapterPosition(view);
StaggeredGridLayoutManager.LayoutParams params = (StaggeredGridLayoutManager.LayoutParams) view.getLayoutParams();
int spanIndex = params.getSpanIndex();
outRect.left = spacing - (spanIndex * spacing) / spanCount;
outRect.right = ((spanIndex + 1) * spacing) / spanCount;
if (position < spanCount) {
outRect.top = spacing;
}
outRect.bottom = spacing;
}
}

View File

@ -0,0 +1,11 @@
<!-- res/anim/item_fall_down.xml -->
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromYDelta="-50%"
android:toYDelta="0%"
android:duration="500" />
<alpha
android:fromAlpha="0.0"
android:toAlpha="1.0"
android:duration="500" />
</set>

View File

@ -0,0 +1,5 @@
<!-- res/anim/layout_animation_fall_down.xml -->
<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
android:animation="@anim/item_fall_down"
android:animationOrder="normal"
android:delay="15%" />

View File

@ -0,0 +1,15 @@
<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/gray"
android:pathData="M912.2,637.2a30.7,30.7 0,0 0,-22.9 9.5,32.9 32.9,0 0,0 -9.4,22.9v59.7L258.9,729.4L258.9,141.8h116.5a32.6,32.6 0,0 0,32.4 -32.4,32.2 32.2,0 0,0 -31.9,-31.9L226,77.5a30.7,30.7 0,0 0,-30.7 30.7v121.7h-85.8a30.7,30.7 0,0 0,-30.7 30.7v654.3a30.7,30.7 0,0 0,30.7 30.7h687.7a30.7,30.7 0,0 0,30.7 -30.7L827.9,793.6h85.7a30.7,30.7 0,0 0,30.7 -30.7v-92.2a33.2,33.2 0,0 0,-32.4 -32.9zM225.3,792.7h537.5L762.8,880.6L141.8,880.6L141.8,293.2L194.6,293.2v468.6a30.7,30.7 0,0 0,30.7 30.7zM225.3,792.7"/>
<path
android:fillColor="@color/gray"
android:pathData="M457,571.6a282.7,282.7 0,1 0,-82.6 -199.8,277.2 277.2,0 0,0 82.6,199.8zM439.1,371.8a217.6,217.6 0,1 1,217.6 217.7,217.8 217.8,0 0,1 -217.6,-217.7zM439.1,371.8"/>
<path
android:fillColor="@color/gray"
android:pathData="M614.4,457a38.9,38.9 0,0 0,26.8 11.5,36 36,0 0,0 26.9,-11.5l117.6,-117.6a37.7,37.7 0,0 0,11.5 -26.8,36.9 36.9,0 0,0 -11.5,-26.9 37.9,37.9 0,0 0,-26.9 -11.5,36.4 36.4,0 0,0 -26.9,11.5l-90.6,90.6 -40.2,-41a37.8,37.8 0,0 0,-26.9 -11.4,37 37,0 0,0 -26.9,11.4 37.9,37.9 0,0 0,-11.5 26.9A36.1,36.1 0,0 0,547.1 389.1zM614.4,457"/>
</vector>

View 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="M800,480H268.8l233.6,-233.6c12.8,-12.8 12.8,-32 0,-44.8 -12.8,-12.8 -32,-12.8 -44.8,0l-284.8,288c-12.8,12.8 -12.8,32 0,44.8h3.2l284.8,288c6.4,6.4 16,9.6 22.4,9.6 9.6,0 16,-3.2 22.4,-9.6 12.8,-12.8 12.8,-32 0,-44.8L272,544h528c19.2,0 32,-12.8 32,-32s-16,-32 -32,-32z"/>
</vector>

View File

@ -0,0 +1,18 @@
<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="M42.7,469.3C42.7,233.7 233.7,42.7 469.3,42.7s426.7,191 426.7,426.7a42.7,42.7 0,1 1,-85.3 0,341.3 341.3,0 1,0 -341.3,341.3 42.7,42.7 0,1 1,0 85.3C233.7,896 42.7,705 42.7,469.3z"
android:fillColor="#000000"
android:fillAlpha="0.85"/>
<path
android:pathData="M597.3,722C597.3,659.5 642,597.3 710.4,597.3c21.6,0 41.1,6.5 57.6,17.3A104.5,104.5 0,0 1,825.6 597.3c68.4,0 113.1,62.2 113.1,124.7 0,55.1 -27.5,103.8 -57.3,138.9 -29.7,35 -67.2,63 -97.9,74.9a42.7,42.7 0,0 1,-30.8 0c-30.7,-11.9 -68.2,-39.9 -97.9,-74.9 -29.8,-35.1 -57.3,-83.8 -57.3,-138.8zM710.4,682.7c-9.4,0 -27.7,11.3 -27.7,39.4 0,26.9 14.1,56.6 37,83.6 16.3,19.1 34.2,33.6 48.3,42.5a217.8,217.8 0,0 0,48.3 -42.5c23,-27 37,-56.7 37,-83.6 0,-28.1 -18.3,-39.3 -27.7,-39.3 -6.4,0 -14.7,3.6 -21.1,14.3a42.7,42.7 0,0 1,-73 0c-6.4,-10.6 -14.7,-14.3 -21.1,-14.3zM469.3,320a149.3,149.3 0,1 0,0 298.7,149.3 149.3,0 0,0 0,-298.7zM405.3,469.3a64,64 0,1 1,128 0,64 64,0 0,1 -128,0z"
android:fillColor="#000000"
android:fillAlpha="0.85"/>
<path
android:pathData="M469.3,405.3a64,64 0,1 0,0 128,64 64,0 0,0 0,-128z"
android:fillColor="#000000"
android:fillAlpha="0.85"/>
</vector>

View 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>

View 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="M469.3,570.2v-256h85.4v256h128L512,756.3 341.3,570.2h128zM1024,640.1C1024,782.9 919.9,896 787.6,896h-512C123.9,896 0,761.6 0,597.5 0,452 94.7,331.5 226.4,303 284.2,195.5 391.8,128 512,128c152.3,0 282.1,108.4 323.4,261.1C941.9,413.4 1024,519 1024,640.2zM764.8,434.8c-24.4,-129 -128.9,-222.7 -252.8,-222.7 -97.3,0 -183,57.3 -224.6,147.5l-9.3,20.2 -20.9,2.9c-103.4,14.4 -178.4,104.3 -178.4,214.7 0,118 88.8,214.4 196.9,214.4h512c88.3,0 157.5,-75.1 157.5,-171.7 0,-88.1 -65.9,-164.9 -145,-171.8l-29.5,-2.6 -5.9,-31z"
android:fillColor="@color/gray"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="56.9dp"
android:height="32dp"
android:viewportWidth="1821"
android:viewportHeight="1024">
<path
android:fillColor="#FF0000"
android:pathData="M596.5,402.5h-65.6c-34,0 -63.1,29.1 -63.1,63.1v398.1c0,36.4 26.7,65.6 63.1,65.6h65.6c21.9,0 41.2,-12.1 41.2,-26.7L637.7,429.3c0,-14.7 -19.5,-26.8 -41.2,-26.8zM1264,385.6L1067.3,385.6c-14.5,-2.4 -19.5,-16.9 -21.9,-24.3L1045.5,184.1c0,-48.5 -38.8,-87.3 -87.3,-87.3 -43.7,0 -80.1,34 -87.3,75.2 -26.7,167.5 -128.7,223.3 -194.1,240.3 2.4,7.3 2.4,12.1 2.4,16.9v470.9c0,9.7 -2.4,19.5 -7.3,29.1h475.7c46.1,-9.7 77.7,-26.7 99.5,-72.8l99.5,-349.5c19.5,-63.1 -12,-123.7 -82.4,-121.3zM1150.3,682.4l-24,24 0.3,0.3 -131.7,131.7 -0.3,-0.3 -0.3,0.3 -131.7,-131.7 0.3,-0.3 -24,-24c-36.1,-36.1 -36.1,-95.2 0,-131.3l0.3,-0.3c36.1,-36.1 95.2,-36.1 131.3,0l24,24 24,-24c36.1,-36.1 95.2,-36.1 131.3,0l0.3,0.3c36.4,36 36.4,95.2 0.3,131.3z"/>
</vector>

View File

@ -0,0 +1,7 @@
<!-- res/drawable/gradient_overlay.xml -->
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<gradient
android:startColor="#00000000"
android:endColor="#CC000000"
android:angle="270" />
</shape>

View File

@ -0,0 +1,18 @@
<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="M148.4,107l282.2,0c22.3,0 40.3,18 40.3,40.3l0,282.2c0,22.3 -18,40.3 -40.3,40.3L148.4,469.8c-22.3,0 -40.3,-18 -40.3,-40.3L108.1,147.3C108.1,125 126.1,107 148.4,107z"/>
<path
android:fillColor="#FF000000"
android:pathData="M147.6,553.5 L429.7,553.5c22.3,0 40.3,18 40.3,40.3l0,282.2c0,22.3 -18,40.3 -40.3,40.3L147.6,916.3c-22.3,0 -40.3,-18 -40.3,-40.3L107.2,593.8C107.2,571.5 125.3,553.5 147.6,553.5z"/>
<path
android:fillColor="#FF000000"
android:pathData="M593.9,107l282.2,0c22.3,0 40.3,18 40.3,40.3l0,282.2c0,22.3 -18,40.3 -40.3,40.3L593.9,469.8c-22.3,0 -40.3,-18 -40.3,-40.3L553.6,147.3C553.6,125 571.7,107 593.9,107z"/>
<path
android:fillColor="#FF000000"
android:pathData="M730.2,920.5 L623.9,920.5c-40.9,0 -74.2,-33.4 -74.2,-74.4L549.7,624c0,-41 33.4,-74.4 74.4,-74.4l222.1,0c41,0 74.4,33.2 74.4,74.1L920.6,737.9c0,10.2 -8.3,18.5 -18.5,18.5s-18.5,-8.3 -18.5,-18.5L883.5,623.6c0,-20.4 -16.7,-37 -37.3,-37L624.1,586.7c-20.6,0 -37.3,16.7 -37.3,37.3l0,222.1c0,20.6 16.7,37.3 37.1,37.3l106.3,0c10.2,0 18.5,8.3 18.5,18.5C748.8,912.2 740.5,920.5 730.2,920.5z"/>
</vector>

View 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>

View File

@ -0,0 +1,9 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<gradient
android:angle="45"
android:endColor="#BBDEFB"
android:startColor="#F5F5F5"
android:type="linear"
android:useLevel="false" />
<corners android:radius="16sp" />
</shape>

View File

@ -0,0 +1,18 @@
<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="M42.7,469.3C42.7,233.7 233.7,42.7 469.3,42.7s426.7,191 426.7,426.7a42.7,42.7 0,1 1,-85.3 0,341.3 341.3,0 1,0 -341.3,341.3 42.7,42.7 0,1 1,0 85.3C233.7,896 42.7,705 42.7,469.3z"
android:fillColor="@color/gray"
android:fillAlpha="0.85"/>
<path
android:pathData="M597.3,722C597.3,659.5 642,597.3 710.4,597.3c21.6,0 41.1,6.5 57.6,17.3A104.5,104.5 0,0 1,825.6 597.3c68.4,0 113.1,62.2 113.1,124.7 0,55.1 -27.5,103.8 -57.3,138.9 -29.7,35 -67.2,63 -97.9,74.9a42.7,42.7 0,0 1,-30.8 0c-30.7,-11.9 -68.2,-39.9 -97.9,-74.9 -29.8,-35.1 -57.3,-83.8 -57.3,-138.8zM710.4,682.7c-9.4,0 -27.7,11.3 -27.7,39.4 0,26.9 14.1,56.6 37,83.6 16.3,19.1 34.2,33.6 48.3,42.5a217.8,217.8 0,0 0,48.3 -42.5c23,-27 37,-56.7 37,-83.6 0,-28.1 -18.3,-39.3 -27.7,-39.3 -6.4,0 -14.7,3.6 -21.1,14.3a42.7,42.7 0,0 1,-73 0c-6.4,-10.6 -14.7,-14.3 -21.1,-14.3zM469.3,320a149.3,149.3 0,1 0,0 298.7,149.3 149.3,0 0,0 0,-298.7zM405.3,469.3a64,64 0,1 1,128 0,64 64,0 0,1 -128,0z"
android:fillColor="@color/gray"
android:fillAlpha="0.85"/>
<path
android:pathData="M469.3,405.3a64,64 0,1 0,0 128,64 64,0 0,0 0,-128z"
android:fillColor="@color/gray"
android:fillAlpha="0.85"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="56.9dp"
android:height="32dp"
android:viewportWidth="1821"
android:viewportHeight="1024">
<path
android:fillColor="@color/gray"
android:pathData="M596.5,402.5h-65.6c-34,0 -63.1,29.1 -63.1,63.1v398.1c0,36.4 26.7,65.6 63.1,65.6h65.6c21.9,0 41.2,-12.1 41.2,-26.7L637.7,429.3c0,-14.7 -19.5,-26.8 -41.2,-26.8zM1264,385.6L1067.3,385.6c-14.5,-2.4 -19.5,-16.9 -21.9,-24.3L1045.5,184.1c0,-48.5 -38.8,-87.3 -87.3,-87.3 -43.7,0 -80.1,34 -87.3,75.2 -26.7,167.5 -128.7,223.3 -194.1,240.3 2.4,7.3 2.4,12.1 2.4,16.9v470.9c0,9.7 -2.4,19.5 -7.3,29.1h475.7c46.1,-9.7 77.7,-26.7 99.5,-72.8l99.5,-349.5c19.5,-63.1 -12,-123.7 -82.4,-121.3zM1150.3,682.4l-24,24 0.3,0.3 -131.7,131.7 -0.3,-0.3 -0.3,0.3 -131.7,-131.7 0.3,-0.3 -24,-24c-36.1,-36.1 -36.1,-95.2 0,-131.3l0.3,-0.3c36.1,-36.1 95.2,-36.1 131.3,0l24,24 24,-24c36.1,-36.1 95.2,-36.1 131.3,0l0.3,0.3c36.4,36 36.4,95.2 0.3,131.3z"/>
</vector>

View File

@ -0,0 +1,18 @@
<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/gray"
android:pathData="M148.4,107l282.2,0c22.3,0 40.3,18 40.3,40.3l0,282.2c0,22.3 -18,40.3 -40.3,40.3L148.4,469.8c-22.3,0 -40.3,-18 -40.3,-40.3L108.1,147.3C108.1,125 126.1,107 148.4,107z"/>
<path
android:fillColor="@color/gray"
android:pathData="M147.6,553.5 L429.7,553.5c22.3,0 40.3,18 40.3,40.3l0,282.2c0,22.3 -18,40.3 -40.3,40.3L147.6,916.3c-22.3,0 -40.3,-18 -40.3,-40.3L107.2,593.8C107.2,571.5 125.3,553.5 147.6,553.5z"/>
<path
android:fillColor="@color/gray"
android:pathData="M593.9,107l282.2,0c22.3,0 40.3,18 40.3,40.3l0,282.2c0,22.3 -18,40.3 -40.3,40.3L593.9,469.8c-22.3,0 -40.3,-18 -40.3,-40.3L553.6,147.3C553.6,125 571.7,107 593.9,107z"/>
<path
android:fillColor="@color/gray"
android:pathData="M730.2,920.5 L623.9,920.5c-40.9,0 -74.2,-33.4 -74.2,-74.4L549.7,624c0,-41 33.4,-74.4 74.4,-74.4l222.1,0c41,0 74.4,33.2 74.4,74.1L920.6,737.9c0,10.2 -8.3,18.5 -18.5,18.5s-18.5,-8.3 -18.5,-18.5L883.5,623.6c0,-20.4 -16.7,-37 -37.3,-37L624.1,586.7c-20.6,0 -37.3,16.7 -37.3,37.3l0,222.1c0,20.6 16.7,37.3 37.1,37.3l106.3,0c10.2,0 18.5,8.3 18.5,18.5C748.8,912.2 740.5,920.5 730.2,920.5z"/>
</vector>

View File

@ -0,0 +1,41 @@
<?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"
tools:context=".ui.activity.CategoryActivity">
<ImageView
android:id="@+id/back"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginStart="24dp"
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="24dp"
android:gravity="center"
android:textColor="@color/black"
android:textSize="24sp"
android:text="@string/app_name"
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>

View File

@ -5,15 +5,37 @@
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
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="16dp"
android:gravity="center"
android:text="@string/app_name"
android:textColor="@color/black"
android:textSize="24sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.tabs.TabLayout
android:id="@+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="75dp"
android:layout_marginTop="16dp"
android:background="@android:color/transparent"
app:layout_constraintTop_toBottomOf="@+id/title"
app:tabIndicatorHeight="0dp"
app:tabRippleColor="@android:color/transparent" />
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/view_pager"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tab_layout" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,123 @@
<?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"
tools:context=".ui.activity.PureActivity">
<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="36dp"
android:layout_height="36dp"
android:layout_marginStart="16dp"
android:layout_marginTop="32dp"
android:background="@drawable/dialog_background"
android:clickable="true"
android:focusable="true"
android:padding="6dp"
android:src="@drawable/back"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout
android:id="@+id/top_action_bar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:layout_marginEnd="32dp"
android:gravity="center_vertical"
android:orientation="horizontal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="@+id/like"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:background="?attr/selectableItemBackgroundBorderless"
android:clickable="true"
android:focusable="true"
android:padding="6dp"
android:src="@drawable/un_favorite" />
<FrameLayout
android:id="@+id/down"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackgroundBorderless"
android:clickable="true"
android:focusable="true"
android:padding="6dp">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/download" />
<ProgressBar
android:id="@+id/down_progress"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_gravity="center"
android:indeterminateTint="@android:color/white"
android:visibility="gone" />
</FrameLayout>
<ImageView
android:id="@+id/set"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:background="?attr/selectableItemBackgroundBorderless"
android:clickable="true"
android:focusable="true"
android:padding="6dp"
android:src="@drawable/apply_to" />
</LinearLayout>
<TextView
android:id="@+id/bottom_hint"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
android:alpha="0.7"
android:gravity="center"
android:paddingVertical="8dp"
android:text="双击屏幕点赞,滑动切换壁纸"
android:textColor="@android:color/white"
android:textSize="12sp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<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>

View File

@ -0,0 +1,49 @@
<?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"
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/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:text="@string/app_name"
android:textSize="25sp"
android:textStyle="bold"
android:gravity="center"
android:textColor="@color/black"
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="10dp"
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>

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"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="5dp"
android:paddingTop="5dp">
<ImageView
android:id="@+id/image"
android:layout_width="32dp"
android:layout_height="32dp"
app:layout_constraintBottom_toTopOf="@+id/text"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textStyle="bold"
android:gravity="center"
android:layout_marginTop="5dp"
android:text="@string/app_name"
android:textSize="14sp"
app:layout_constraintTop_toBottomOf="@+id/image" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,98 @@
<?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:layout_gravity="center"
android:background="@drawable/rounded_gradient">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:text="@string/set_as_wallpaper"
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/dialog_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/dialog_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/dialog_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>

View File

@ -0,0 +1,16 @@
<?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.CollectionFragment">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,17 @@
<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.HomeFragment">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="0dp"
android:clipToPadding="false"
android:paddingBottom="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,40 @@
<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:layout_height="wrap_content"
app:cardCornerRadius="16dp"
app:cardElevation="8dp"
app:cardUseCompatPadding="true">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="250dp">
<ImageView
android:id="@+id/imageview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop" />
<View
android:id="@+id/gradient_overlay"
android:layout_width="match_parent"
android:layout_height="80dp"
android:background="@drawable/gradient_overlay"
app:layout_constraintBottom_toBottomOf="parent" />
<TextView
android:id="@+id/category_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginBottom="16dp"
android:maxLines="2"
android:textColor="@color/white"
android:textSize="20sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>

View File

@ -0,0 +1,30 @@
<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:layout_height="wrap_content"
app:cardCornerRadius="16dp"
app:cardElevation="8dp"
app:cardUseCompatPadding="true">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="250dp">
<ImageView
android:id="@+id/image_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop" />
<ImageView
android:id="@+id/favorite"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:src="@drawable/un_favorite"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>

View File

@ -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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 463 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 982 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

View File

@ -2,4 +2,5 @@
<resources>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
<color name="gray">#9C979D</color>
</resources>

View File

@ -1,3 +1,4 @@
<resources>
<string name="app_name">Pure Wallpaper</string>
<string name="set_as_wallpaper">Set as Wallpaper</string>
</resources>

View File

@ -1,5 +1,5 @@
[versions]
agp = "8.9.1"
agp = "8.10.1"
junit = "4.13.2"
junitVersion = "1.2.1"
espressoCore = "3.6.1"

6
keystore.properties Normal file
View File

@ -0,0 +1,6 @@
app_name=Pure Wallpaper
package_name=com.pure.wallpaper.purewallpaper
keystoreFile=app/PureWallpaper.jks
key_alias=PureWallpaperkey0
key_store_password=PureWallpaper
key_password=PureWallpaper