V1.0.0(1)完成版

This commit is contained in:
lihongwei 2025-03-28 16:19:38 +08:00
parent 2b76217e86
commit 55b2e6d5fc
20 changed files with 566 additions and 404 deletions

BIN
app/NatureWallpaper.jks Normal file

Binary file not shown.

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.nature.wallpaper.naturewallpaper.MyApplication {
public static final java.lang.String DB_NAME;
public static final int DB_VERSION;
}
-keepclassmembers class * {
@androidx.room.Query <methods>;
}
-keep class com.nature.wallpaper.naturewallpaper.room.AppDatabase{ *; }
-keep class com.nature.wallpaper.naturewallpaper.room.NatureData { *; }
-keep class com.nature.wallpaper.naturewallpaper.room.NatureList { *; }
-keep class com.nature.wallpaper.naturewallpaper.room.NatureDataDao { *; }

View File

@ -9,45 +9,71 @@ import com.nature.wallpaper.naturewallpaper.util.JsonParse;
import com.nature.wallpaper.naturewallpaper.util.RoomUtils;
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 DB_VERSION = 1;
public static final String DB_NAME = "cool_database";
private static volatile MyApplication instance;
private static final String PREF_NAME = "app_preferences";
private static final String KEY_INIT = "isInit";
private static final String KEY_DB_INITIALIZED = "is_database_initialized";
private static final String JSON_FILE_NAME = "nature_wallpapers.json";
public static final int DB_VERSION = 1;
public static final String DB_NAME = "nature_database";
private final ExecutorService executor = Executors.newSingleThreadExecutor();
@Override
public void onCreate() {
super.onCreate();
initializeInstance();
initializeDatabaseIfNeeded();
}
application = this;
SharedPreferences sharedPreferences = getSharedPreferences(PREF_NAME, MODE_PRIVATE);
boolean isDatabaseInitialized = sharedPreferences.getBoolean(KEY_INIT, false);
if (!isDatabaseInitialized) {
initializeDatabase();
sharedPreferences.edit().putBoolean(KEY_INIT, true).apply();
}
private void initializeInstance() {
instance = this;
}
public static Context getContext() {
return application.getApplicationContext();
if (instance == null) {
throw new IllegalStateException("Application not initialized yet");
}
return instance.getApplicationContext();
}
private void initializeDatabase() {
Executors.newSingleThreadExecutor().execute(() -> {
List<NatureList> natureListList = getCategoryList();
private void initializeDatabaseIfNeeded() {
SharedPreferences prefs = getSharedPreferences(PREF_NAME, MODE_PRIVATE);
if (!prefs.getBoolean(KEY_DB_INITIALIZED, false)) {
initializeDatabaseAsync();
markDatabaseAsInitialized(prefs);
}
}
private void initializeDatabaseAsync() {
executor.execute(() -> {
List<NatureList> natureLists = loadCategoryList();
RoomUtils roomUtils = new RoomUtils(getContext());
roomUtils.insertAllImages(natureListList);
roomUtils.insertAllImages(natureLists);
});
}
public static List<NatureList> getCategoryList() {
return JsonParse.parseJson("nature_wallpapers.json");
private void markDatabaseAsInitialized(SharedPreferences prefs) {
prefs.edit()
.putBoolean(KEY_DB_INITIALIZED, true)
.apply();
}
}
public static List<NatureList> loadCategoryList() {
return JsonParse.parseJson(JSON_FILE_NAME);
}
@Override
public void onTerminate() {
super.onTerminate();
shutdownExecutor();
}
private void shutdownExecutor() {
executor.shutdown();
}
}

View File

@ -20,88 +20,84 @@ import com.nature.wallpaper.naturewallpaper.databinding.TabLayoutBinding;
public class MainActivity extends AppCompatActivity {
private ActivityMainBinding binding;
private static final int[] TAB_TITLES = {R.string.home, R.string.favorite};
private static final int[] TAB_ICONS_UNSELECTED = {R.drawable.un_home, R.drawable.un_favoriteslist};
private static final int[] TAB_ICONS_SELECTED = {R.drawable.home, R.drawable.favoriteslist};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setupEdgeToEdge();
initializeBinding();
setupWindowInsets();
setupViewPager();
setupTabLayout();
}
private void setupEdgeToEdge() {
EdgeToEdge.enable(this);
}
private void initializeBinding() {
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
}
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
private void setupWindowInsets() {
ViewCompat.setOnApplyWindowInsetsListener(binding.getRoot(), (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
return insets;
});
initData();
initEvent();
}
private void initData() {
private void setupViewPager() {
MainAdapter adapter = new MainAdapter(this);
binding.mainViewpager2.setAdapter(adapter);
}
private void initEvent() {
new TabLayoutMediator(binding.mainTabLayout, binding.mainViewpager2, (tab, position) -> {
TabLayoutBinding tabLayoutBinding = TabLayoutBinding.inflate(LayoutInflater.from(this));
tab.setCustomView(tabLayoutBinding.getRoot());
setTab(tabLayoutBinding, position);
}).attach();
private void setupTabLayout() {
new TabLayoutMediator(binding.mainTabLayout, binding.mainViewpager2,
this::configureTab).attach();
binding.mainTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
updateTab(tab, true);
updateTabState(tab, true);
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
updateTab(tab, false);
updateTabState(tab, false);
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
}
private void updateTab(TabLayout.Tab tab, boolean isSelected) {
if (tab.getCustomView() != null) {
TabLayoutBinding mainCustomBinding = TabLayoutBinding.bind(tab.getCustomView());
int iconResId = getIconResource(tab.getPosition(), isSelected);
mainCustomBinding.image.setImageResource(iconResId);
int textColor = isSelected ? R.color.black : R.color.gray;
mainCustomBinding.text.setTextColor(ContextCompat.getColor(MainActivity.this, textColor));
}
}
public void onTabReselected(TabLayout.Tab tab) {}
});
}
private void setTab(TabLayoutBinding tabLayoutBinding, int position) {
int iconResId = getIconResource(position, false);
int textColorResId = R.color.gray;
private void configureTab(TabLayout.Tab tab, int position) {
TabLayoutBinding tabBinding = TabLayoutBinding.inflate(LayoutInflater.from(this));
tab.setCustomView(tabBinding.getRoot());
switch (position) {
case 0:
tabLayoutBinding.text.setText("Home");
iconResId = R.drawable.home;
textColorResId = R.color.black;
break;
case 1:
tabLayoutBinding.text.setText("Favorite");
break;
}
tabLayoutBinding.image.setImageResource(iconResId);
tabLayoutBinding.text.setTextColor(ContextCompat.getColor(this, textColorResId));
boolean isFirstTab = position == 0;
tabBinding.text.setText(TAB_TITLES[position]);
tabBinding.image.setImageResource(isFirstTab ? TAB_ICONS_SELECTED[position] : TAB_ICONS_UNSELECTED[position]);
tabBinding.text.setTextColor(ContextCompat.getColor(this,
isFirstTab ? R.color.black : R.color.gray));
}
private int getIconResource(int position, boolean isSelected) {
if (position == 1) {
return isSelected ? R.drawable.favoriteslist : R.drawable.un_favoriteslist;
}
return isSelected ? R.drawable.home : R.drawable.un_home;
private void updateTabState(TabLayout.Tab tab, boolean isSelected) {
if (tab.getCustomView() == null) return;
TabLayoutBinding tabBinding = TabLayoutBinding.bind(tab.getCustomView());
int position = tab.getPosition();
tabBinding.image.setImageResource(isSelected
? TAB_ICONS_SELECTED[position]
: TAB_ICONS_UNSELECTED[position]);
tabBinding.text.setTextColor(ContextCompat.getColor(this,
isSelected ? R.color.black : R.color.gray));
}
@Override

View File

@ -25,7 +25,6 @@ public class NatureListActivity extends AppCompatActivity {
private ActivityNatureListBinding binding;
private WallpaperAdapter wallpaperAdapter;
private RoomUtils roomUtils;
private NatureData coolEntity;
private String name;
@Override
@ -47,9 +46,9 @@ public class NatureListActivity extends AppCompatActivity {
}
private void initData() {
coolEntity = (NatureData) getIntent().getSerializableExtra("nature");
if (coolEntity != null) {
name = coolEntity.getName();
NatureData data = (NatureData) getIntent().getSerializableExtra("nature");
if (data != null) {
name = data.getName();
} else {
Toast.makeText(this, "The picture does not exist", Toast.LENGTH_SHORT).show();
finish();
@ -62,8 +61,7 @@ public class NatureListActivity extends AppCompatActivity {
wallpaperAdapter = new WallpaperAdapter(this, new ArrayList<>(),1);
binding.recyclerView.setAdapter(wallpaperAdapter);
ItemDecoration itemDecoration = new ItemDecoration(25, 15, 10);
binding.recyclerView.addItemDecoration(itemDecoration);
binding.recyclerView.addItemDecoration(new ItemDecoration(25, 15, 10));
}
@ -72,10 +70,10 @@ public class NatureListActivity extends AppCompatActivity {
binding.back.setOnClickListener(v -> finish());
binding.text.setText(name);
loadImage();
loadAllCategory();
}
private void loadImage() {
private void loadAllCategory() {
roomUtils
.getAllCategory(name)
.observe(this, new Observer<List<NatureData>>() {

View File

@ -2,7 +2,6 @@ package com.nature.wallpaper.naturewallpaper.activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.CountDownTimer;
import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
@ -18,48 +17,70 @@ import com.nature.wallpaper.naturewallpaper.databinding.ActivityStartBinding;
public class StartActivity extends AppCompatActivity {
private ActivityStartBinding binding;
private static final long TOTAL_TIME = 1000;
private CountDownTimer countDownTimer;
private static final long TOTAL_TIME_MS = 1000;
private static final long COUNTDOWN_INTERVAL_MS = 100;
private android.os.CountDownTimer countDownTimer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setupEdgeToEdge();
initializeBinding();
setupWindowInsets();
loadSplashImage();
startCountdown();
}
private void setupEdgeToEdge() {
EdgeToEdge.enable(this);
}
private void initializeBinding() {
binding = ActivityStartBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
}
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
private void setupWindowInsets() {
ViewCompat.setOnApplyWindowInsetsListener(binding.getRoot(), (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
return insets;
});
}
private void loadSplashImage() {
Glide.with(this)
.load(R.mipmap.placeholder)
.transform(new CenterCrop(), new RoundedCorners(32))
.into(binding.image);
}
countDownTimer = new CountDownTimer(TOTAL_TIME, 100) {
private void startCountdown() {
countDownTimer = new android.os.CountDownTimer(TOTAL_TIME_MS, COUNTDOWN_INTERVAL_MS) {
@Override
public void onTick(long millisUntilFinished) {
int percentage = (int) (100 - (float) millisUntilFinished / TOTAL_TIME * 100);
binding.progressBar.setProgress(percentage);
updateProgress(millisUntilFinished);
}
@Override
public void onFinish() {
startMain();
navigateToMain();
}
};
countDownTimer.start();
}.start();
}
private void startMain() {
binding.progressBar.setProgress(100);
private void updateProgress(long millisUntilFinished) {
int progress = calculateProgress(millisUntilFinished);
binding.progressBar.setProgress(progress);
}
Intent intent = new Intent(StartActivity.this, MainActivity.class);
private int calculateProgress(long millisUntilFinished) {
return (int) (100 - ((float) millisUntilFinished / TOTAL_TIME_MS * 100));
}
private void navigateToMain() {
binding.progressBar.setProgress(100);
Intent intent = new Intent(this, MainActivity.class);
startActivity(intent);
finish();
}
@ -67,9 +88,14 @@ public class StartActivity extends AppCompatActivity {
@Override
protected void onDestroy() {
super.onDestroy();
if (countDownTimer != null) {
countDownTimer.cancel();
}
cleanupResources();
binding = null;
}
private void cleanupResources() {
if (countDownTimer != null) {
countDownTimer.cancel();
countDownTimer = null;
}
}
}

View File

@ -1,7 +1,5 @@
package com.nature.wallpaper.naturewallpaper.activity;
import static com.nature.wallpaper.naturewallpaper.util.SetAndDownloadUtils.REQUEST_CODE_WRITE_EXTERNAL_STORAGE;
import android.app.WallpaperManager;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
@ -19,7 +17,6 @@ import androidx.activity.EdgeToEdge;
import androidx.annotation.NonNull;
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;
@ -31,159 +28,168 @@ import com.nature.wallpaper.naturewallpaper.R;
import com.nature.wallpaper.naturewallpaper.databinding.ActivityWallpaperBinding;
import com.nature.wallpaper.naturewallpaper.room.NatureData;
import com.nature.wallpaper.naturewallpaper.util.RoomUtils;
import com.nature.wallpaper.naturewallpaper.util.SetAndDownloadUtils;
import com.nature.wallpaper.naturewallpaper.util.SetDownloadUtil;
import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class WallpaperActivity extends AppCompatActivity implements SetAndDownloadUtils.DownloadCallback {
public class WallpaperActivity extends AppCompatActivity implements SetDownloadUtil.DownloadCallback {
private static final int REQUEST_CODE_WRITE_EXTERNAL_STORAGE = SetDownloadUtil.REQUEST_CODE_WRITE_EXTERNAL_STORAGE;
private ActivityWallpaperBinding binding;
private NatureData data;
private String original;
private String source;
private NatureData coolEntity;
private RoomUtils roomUtils;
private String name;
private Bitmap bitmap;
private SetAndDownloadUtils setAndDownloadUtils;
private SetDownloadUtil setDownloadUtil;
private RoomUtils roomUtils;
private ExecutorService executorService;
private Handler handler;
private Handler mainHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setupUI();
if (initializeData()) {
setupEvents();
}
}
private void setupUI() {
EdgeToEdge.enable(this);
binding = ActivityWallpaperBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
int navigationBars = insets.getInsets(WindowInsetsCompat.Type.navigationBars()).bottom;
v.setPadding(0, 0, 0, navigationBars);
applyWindowInsets();
}
private void applyWindowInsets() {
ViewCompat.setOnApplyWindowInsetsListener(binding.getRoot(), (v, insets) -> {
int navigationBarsHeight = insets.getInsets(WindowInsetsCompat.Type.navigationBars()).bottom;
v.setPadding(0, 0, 0, navigationBarsHeight);
return insets;
});
initData();
initEvent();
}
private void initData() {
coolEntity = (NatureData) getIntent().getSerializableExtra("nature");
if (coolEntity != null) {
original = coolEntity.getOriginal();
source = coolEntity.getSource();
name = coolEntity.getName();
} else {
Toast.makeText(this, "Error", Toast.LENGTH_SHORT).show();
private boolean initializeData() {
data = (NatureData) getIntent().getSerializableExtra("nature");
if (data == null) {
showToast("Error loading wallpaper data");
finish();
return false;
}
setAndDownloadUtils = new SetAndDownloadUtils();
original = data.getOriginal();
source = data.getSource();
name = data.getName();
setDownloadUtil = new SetDownloadUtil();
roomUtils = new RoomUtils(this);
handler = new Handler(Looper.getMainLooper());
mainHandler = new Handler(Looper.getMainLooper());
executorService = Executors.newSingleThreadExecutor();
return true;
}
private void initEvent() {
private void setupEvents() {
showProgress();
binding.backButton.setOnClickListener(v -> finish());
binding.likeButton.setOnClickListener(v -> {
boolean newStatus = !coolEntity.getLike();
coolEntity.setLike(newStatus);
executorService.submit(() -> roomUtils.update(coolEntity));
});
binding.setWallpaperContainer.setOnClickListener(v -> {
showCustomBottomSheet();
});
binding.downloadContainer.setOnClickListener(v -> {
showProgress();
setAndDownloadUtils.setAsWallpaper(WallpaperActivity.this, source, WallpaperActivity.this);
});
binding.likeButton.setOnClickListener(v -> toggleLikeStatus());
binding.setWallpaperContainer.setOnClickListener(v -> showWallpaperOptions());
binding.downloadContainer.setOnClickListener(v -> downloadWallpaper());
loadImage();
loadFavorite();
loadFavoriteStatus();
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQUEST_CODE_WRITE_EXTERNAL_STORAGE) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
setAndDownloadUtils.setAsWallpaper(this, source, this);
} else {
Toast.makeText(this, "Permission denied", Toast.LENGTH_SHORT).show();
}
private void toggleLikeStatus() {
boolean newStatus = !data.getLike();
data.setLike(newStatus);
executorService.submit(() -> roomUtils.update(data));
updateLikeButton();
}
private void downloadWallpaper() {
showProgress();
setDownloadUtil.setAsWallpaper(this, source, this);
}
private void showWallpaperOptions() {
BottomSheetDialog dialog = new BottomSheetDialog(this);
View sheetView = LayoutInflater.from(this).inflate(R.layout.set_wallpaper_dialog, null);
setupBottomSheetListeners(sheetView, dialog);
dialog.setContentView(sheetView);
dialog.show();
}
private void setupBottomSheetListeners(View sheetView, BottomSheetDialog dialog) {
sheetView.findViewById(R.id.both).setOnClickListener(v ->
setWallpaperAndDismiss(WallpaperManager.FLAG_SYSTEM | WallpaperManager.FLAG_LOCK, sheetView, dialog));
sheetView.findViewById(R.id.lock).setOnClickListener(v ->
setWallpaperAndDismiss(WallpaperManager.FLAG_LOCK, sheetView, dialog));
sheetView.findViewById(R.id.desktop).setOnClickListener(v ->
setWallpaperAndDismiss(WallpaperManager.FLAG_SYSTEM, sheetView, dialog));
}
private void setWallpaperAndDismiss(int flag, View sheetView, BottomSheetDialog dialog) {
disableSheetButtons(sheetView);
applyWallpaperAsync(bitmap, flag, dialog);
dialog.dismiss();
}
private void disableSheetButtons(View sheetView) {
setButtonEnabled(sheetView, R.id.both, false);
setButtonEnabled(sheetView, R.id.lock, false);
setButtonEnabled(sheetView, R.id.desktop, false);
}
private void applyWallpaperAsync(Bitmap bitmap, int flag, BottomSheetDialog dialog) {
if (bitmap == null) {
showToast("No image available to set as wallpaper");
hideProgress();
return;
}
showProgress();
executorService.submit(() -> {
boolean success = setWallpaperImage(bitmap, flag);
mainHandler.post(() -> {
hideProgress();
enableSheetButtons(dialog);
showToast(success ? "Wallpaper set successfully" : "Failed to set wallpaper");
});
});
}
private void enableSheetButtons(BottomSheetDialog dialog) {
View sheetView = dialog.getCurrentFocus() != null ? dialog.getCurrentFocus().getRootView() : null;
if (sheetView != null) {
setButtonEnabled(sheetView, R.id.both, true);
setButtonEnabled(sheetView, R.id.lock, true);
setButtonEnabled(sheetView, R.id.desktop, true);
}
}
private void showCustomBottomSheet() {
BottomSheetDialog bottomSheetDialog = new BottomSheetDialog(this);
View bottomSheetView = LayoutInflater.from(this).inflate(R.layout.set_wallpaper_dialog, null);
bottomSheetView.findViewById(R.id.both).setOnClickListener(v -> {
disableButtons(bottomSheetView);
applyWallpaper(bitmap, WallpaperManager.FLAG_SYSTEM | WallpaperManager.FLAG_LOCK, bottomSheetDialog);
bottomSheetDialog.dismiss();
});
bottomSheetView.findViewById(R.id.lock).setOnClickListener(v -> {
disableButtons(bottomSheetView);
applyWallpaper(bitmap, WallpaperManager.FLAG_LOCK, bottomSheetDialog);
bottomSheetDialog.dismiss();
});
bottomSheetView.findViewById(R.id.desktop).setOnClickListener(v -> {
disableButtons(bottomSheetView);
applyWallpaper(bitmap, WallpaperManager.FLAG_SYSTEM, bottomSheetDialog);
bottomSheetDialog.dismiss();
});
bottomSheetDialog.setContentView(bottomSheetView);
bottomSheetDialog.show();
}
private void disableButtons(View bottomSheetView) {
bottomSheetView.findViewById(R.id.both).setEnabled(false);
bottomSheetView.findViewById(R.id.lock).setEnabled(false);
bottomSheetView.findViewById(R.id.desktop).setEnabled(false);
}
private void applyWallpaper(Bitmap bitmap, int flag, BottomSheetDialog bottomSheetDialog) {
showProgress();
executorService.submit(() -> {
boolean success = false;
try {
success = setWallpaperImage(bitmap, flag);
} catch (Exception e) {
e.printStackTrace();
} finally {
boolean finalSuccess = success;
handler.post(() -> {
hideProgress();
bottomSheetDialog.findViewById(R.id.both).setEnabled(true);
bottomSheetDialog.findViewById(R.id.lock).setEnabled(true);
bottomSheetDialog.findViewById(R.id.desktop).setEnabled(true);
String message = finalSuccess ? "Wallpaper set successfully" : "Failed to set wallpaper";
Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show();
});
}
});
private void setButtonEnabled(View view, int id, boolean enabled) {
View button = view.findViewById(id);
if (button != null) {
button.setEnabled(enabled);
}
}
private boolean setWallpaperImage(Bitmap bitmap, int flag) {
WallpaperManager wallpaperManager = WallpaperManager.getInstance(getApplicationContext());
WallpaperManager manager = WallpaperManager.getInstance(this);
try {
binding.wallpaperImage.setDrawingCacheEnabled(true);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
wallpaperManager.setBitmap(bitmap, null, true, flag);
manager.setBitmap(bitmap, null, true, flag);
} else {
wallpaperManager.setBitmap(bitmap);
manager.setBitmap(bitmap);
}
return true;
} catch (IOException e) {
@ -194,7 +200,6 @@ public class WallpaperActivity extends AppCompatActivity implements SetAndDownlo
}
}
private void loadImage() {
Glide.with(this)
.asBitmap()
@ -227,16 +232,39 @@ public class WallpaperActivity extends AppCompatActivity implements SetAndDownlo
return ContextCompat.getDrawable(this, R.mipmap.placeholder);
}
private void loadFavorite() {
roomUtils.getWallpaperLike(source, name).observe(this, wallpaper -> setFavoriteButton());
private void loadFavoriteStatus() {
roomUtils.getWallpaperLike(source, name).observe(this, wallpaper -> updateLikeButton());
}
private void setFavoriteButton() {
private void updateLikeButton() {
binding.likeButton.setImageResource(
coolEntity.getLike() ? R.drawable.like : R.drawable.un_like
data.getLike() ? R.drawable.like : R.drawable.un_like
);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQUEST_CODE_WRITE_EXTERNAL_STORAGE && grantResults.length > 0) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
downloadWallpaper();
} else {
showToast("Permission denied");
hideProgress();
}
}
}
private void showToast(String message) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
}
private void showProgress() {
binding.progressBar.setVisibility(View.VISIBLE);
binding.view.setVisibility(View.VISIBLE);
}
private void hideProgress() {
if (binding != null) {
binding.progressBar.setVisibility(View.GONE);
@ -244,11 +272,6 @@ public class WallpaperActivity extends AppCompatActivity implements SetAndDownlo
}
}
private void showProgress() {
binding.progressBar.setVisibility(View.VISIBLE);
binding.view.setVisibility(View.VISIBLE);
}
@Override
public void onDownloadStart() {
showProgress();
@ -257,22 +280,31 @@ public class WallpaperActivity extends AppCompatActivity implements SetAndDownlo
@Override
public void onDownloadComplete(Uri uri) {
hideProgress();
showToast("Download completed");
}
@Override
public void onDownloadFailed() {
hideProgress();
showToast("Download failed");
}
@Override
protected void onDestroy() {
super.onDestroy();
if (handler != null) {
handler.removeCallbacksAndMessages(null);
cleanupResources();
}
private void cleanupResources() {
if (mainHandler != null) {
mainHandler.removeCallbacksAndMessages(null);
}
if (executorService != null) {
if (executorService != null && !executorService.isShutdown()) {
executorService.shutdown();
}
if (setDownloadUtil != null) {
setDownloadUtil.shutdown();
}
binding = null;
}
}

View File

@ -29,7 +29,7 @@ public class WallpaperAdapter extends RecyclerView.Adapter<WallpaperAdapter.View
private List<NatureData> natureDataList;
private final Context context;
private final Executor executor;
private final int displayMode; // 0: 显示标题跳转NatureList, 其他: 隐藏标题跳转Wallpaper
private final int displayMode;
private static final int MODE_WITH_TITLE = 0;
public WallpaperAdapter(Context context, List<NatureData> natureDataList, int displayMode) {

View File

@ -43,18 +43,17 @@ public class FavoriteFragment extends Fragment {
adapter = new WallpaperAdapter(requireContext(), new ArrayList<>(),1);
binding.recyclerView.setAdapter(adapter);
ItemDecoration itemDecoration = new ItemDecoration(25, 15, 10);
binding.recyclerView.addItemDecoration(itemDecoration);
binding.recyclerView.addItemDecoration(new ItemDecoration(25, 15, 10));
}
private void initEvent() {
loadCategoryImage();
loadAllFavoriteList();
}
private void loadCategoryImage() {
private void loadAllFavoriteList() {
roomUtils
.getAllFavoriteList()
.getAllFavorite()
.observe(getViewLifecycleOwner(), new Observer<List<NatureData>>() {
@Override
public void onChanged(List<NatureData> natureDataList) {

View File

@ -10,9 +10,7 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.nature.wallpaper.naturewallpaper.R;
import com.nature.wallpaper.naturewallpaper.adapter.WallpaperAdapter;
import com.nature.wallpaper.naturewallpaper.databinding.FragmentFavoriteBinding;
import com.nature.wallpaper.naturewallpaper.databinding.FragmentNatureBinding;
import com.nature.wallpaper.naturewallpaper.room.NatureData;
import com.nature.wallpaper.naturewallpaper.util.ItemDecoration;
@ -42,21 +40,19 @@ public class NatureFragment extends Fragment {
binding.recyclerView.setLayoutManager(new GridLayoutManager(getContext(), 1));
adapter = new WallpaperAdapter(requireContext(), new ArrayList<>(),0);
adapter = new WallpaperAdapter(requireContext(), new ArrayList<>(), 0);
binding.recyclerView.setAdapter(adapter);
ItemDecoration itemDecoration = new ItemDecoration(25, 15, 10);
binding.recyclerView.addItemDecoration(itemDecoration);
binding.recyclerView.addItemDecoration(new ItemDecoration(25, 15, 10));
}
private void initEvent() {
loadCategoryImage();
loadCategoryFirst();
}
private void loadCategoryImage() {
private void loadCategoryFirst() {
roomUtils
.getCategoryFirst()
.getFirstCategory()
.observe(getViewLifecycleOwner(), new Observer<List<NatureData>>() {
@Override
public void onChanged(List<NatureData> natureDataList) {

View File

@ -19,12 +19,12 @@ public interface NatureDataDao {
@Query("SELECT * FROM NatureData WHERE isLike = 1 ")
LiveData<List<NatureData>> getAllFavoriteLive();
@Query("SELECT * FROM NatureData WHERE id IN (SELECT MIN(id) FROM NatureData GROUP BY name)")
LiveData<List<NatureData>> getFirstRecordOfEachName();
@Query("SELECT * FROM NatureData WHERE id IN (SELECT MAX(id) FROM NatureData GROUP BY name) ORDER BY id DESC")
LiveData<List<NatureData>> getFirstCategoryByFirst();
@Query("SELECT * FROM NatureData WHERE name = :name")
LiveData<List<NatureData>> getAllCategoryLive(String name);
@Query("SELECT * FROM NatureData WHERE name = :name ORDER BY id DESC")
LiveData<List<NatureData>> getAllCategoryByName(String name);
@Query("SELECT isLIKE FROM NatureData WHERE source = :source AND name = :name")
LiveData<Boolean> getWallpaperLike(String source, String name);
@Query("SELECT isLIKE FROM NatureData WHERE source = :source AND name = :name ORDER BY id DESC")
LiveData<Boolean> getWallpaperLikeBySourceAndName(String source, String name);
}

View File

@ -1,7 +1,5 @@
package com.nature.wallpaper.naturewallpaper.util;
import android.util.Log;
import com.nature.wallpaper.naturewallpaper.MyApplication;
import com.nature.wallpaper.naturewallpaper.room.NatureData;
import com.nature.wallpaper.naturewallpaper.room.NatureList;
@ -13,58 +11,70 @@ import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class JsonParse {
private static String loadJSONFromAsset(String fileName) {
private static String loadJsonFromAsset(String fileName) throws IOException {
StringBuilder jsonString = new StringBuilder();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(MyApplication.getContext().getAssets().open(fileName)))) {
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(MyApplication.getContext().getAssets().open(fileName)))) {
String line;
while ((line = reader.readLine()) != null) {
jsonString.append(line);
}
} catch (IOException e) {
e.printStackTrace();
}
Log.d("JsonParse", "Loaded JSON: " + jsonString.toString());
return jsonString.toString();
}
public static List<NatureList> parseJson(String fileName) {
List<NatureList> categoryList = new ArrayList<>();
try {
String jsonString = loadJSONFromAsset(fileName);
String jsonString = loadJsonFromAsset(fileName);
if (jsonString.isEmpty()) {
throw new IllegalArgumentException("JSON file is empty or invalid.");
}
JSONArray jsonArray = new JSONArray(jsonString);
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject categoryObject = jsonArray.getJSONObject(i);
String name = categoryObject.getString("name");
JSONArray listArray = categoryObject.getJSONArray("data");
List<NatureData> natureDataList = new ArrayList<>();
for (int j = 0; j < listArray.length(); j++) {
JSONObject imageObject = listArray.getJSONObject(j);
String original = imageObject.getString("original");
String previewThumb = imageObject.getString("previewThumb");
String source = imageObject.getString("source");
natureDataList.add(new NatureData(original, previewThumb, source, name, false));
}
categoryList.add(new NatureList(name, natureDataList));
return Collections.emptyList();
}
return parseJsonArray(new JSONArray(jsonString));
} catch (Exception e) {
e.printStackTrace();
return Collections.emptyList();
}
}
private static List<NatureList> parseJsonArray(JSONArray jsonArray) throws org.json.JSONException {
List<NatureList> categoryList = new ArrayList<>();
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject categoryObject = jsonArray.getJSONObject(i);
String categoryName = categoryObject.getString("name");
JSONArray dataArray = categoryObject.getJSONArray("data");
List<NatureData> natureDataList = parseNatureDataList(dataArray, categoryName);
categoryList.add(new NatureList(categoryName, natureDataList));
}
return categoryList;
}
private static List<NatureData> parseNatureDataList(JSONArray dataArray, String categoryName)
throws org.json.JSONException {
List<NatureData> natureDataList = new ArrayList<>();
for (int j = 0; j < dataArray.length(); j++) {
JSONObject imageObject = dataArray.getJSONObject(j);
String original = getStringSafe(imageObject, "original");
String previewThumb = getStringSafe(imageObject, "previewThumb");
String source = getStringSafe(imageObject, "source");
}
natureDataList.add(new NatureData(original, previewThumb, source, categoryName, false));
}
return natureDataList;
}
private static String getStringSafe(JSONObject jsonObject, String key) {
try {
return jsonObject.getString(key);
} catch (org.json.JSONException e) {
return "";
}
}
}

View File

@ -22,29 +22,29 @@ public class RoomUtils {
}
public void insertAllImages(List<NatureList> categoryList) {
List<NatureData> coolEntities = new ArrayList<>();
List<NatureData> dataList = new ArrayList<>();
for (NatureList category : categoryList) {
coolEntities.addAll(category.getList());
dataList.addAll(category.getList());
}
natureDataDao.insertAll(coolEntities);
natureDataDao.insertAll(dataList);
}
public LiveData<List<NatureData>> getAllFavoriteList() {
public LiveData<List<NatureData>> getAllFavorite() {
return natureDataDao.getAllFavoriteLive();
}
public LiveData<List<NatureData>> getCategoryFirst() {
return natureDataDao.getFirstRecordOfEachName();
public LiveData<List<NatureData>> getFirstCategory() {
return natureDataDao.getFirstCategoryByFirst();
}
public LiveData<List<NatureData>> getAllCategory(String name) {
return natureDataDao.getAllCategoryLive(name);
return natureDataDao.getAllCategoryByName(name);
}
public LiveData<Boolean> getWallpaperLike(String source, String name) {
return natureDataDao.getWallpaperLike(source, name);
return natureDataDao.getWallpaperLikeBySourceAndName(source, name);
}
public void update(NatureData imageEntry) {

View File

@ -1,114 +0,0 @@
package com.nature.wallpaper.naturewallpaper.util;
import android.app.Activity;
import android.content.ContentValues;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
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.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
public class SetAndDownloadUtils {
public static final int REQUEST_CODE_WRITE_EXTERNAL_STORAGE = 111;
private final ExecutorService executorService = Executors.newSingleThreadExecutor();
public void setAsWallpaper(Activity activity, String imageUrl,DownloadCallback callback) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
startDownload(activity, imageUrl,callback);
} else {
if (ContextCompat.checkSelfPermission(activity, android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(activity,
new String[]{android.Manifest.permission.WRITE_EXTERNAL_STORAGE},
REQUEST_CODE_WRITE_EXTERNAL_STORAGE);
} else {
startDownload(activity, imageUrl,callback);
}
}
}
private void startDownload(Activity activity, String imageUrl, DownloadCallback callback) {
callback.onDownloadStart();
executorService.submit(() -> {
Uri uri = downloadImage(activity, imageUrl);
activity.runOnUiThread(() -> {
if (uri != null) {
callback.onDownloadComplete(uri);
} else {
callback.onDownloadFailed();
}
});
});
}
private Uri downloadImage(Activity activity, String imageUrl) {
String displayName = UUID.randomUUID().toString() + ".jpg";
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.Images.Media.DISPLAY_NAME, displayName);
contentValues.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
contentValues.put(MediaStore.Images.Media.IS_PENDING, 1);
}
Uri collectionUri;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
collectionUri = MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
} else {
collectionUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
}
Uri imageUri = activity.getContentResolver().insert(collectionUri, contentValues);
if (imageUri != null) {
try {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(imageUrl).build();
Response response = client.newCall(request).execute();
if (response.isSuccessful()) {
InputStream inputStream = response.body().byteStream();
try (OutputStream outputStream = activity.getContentResolver().openOutputStream(imageUri)) {
if (outputStream != null) {
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
}
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
contentValues.clear();
contentValues.put(MediaStore.Images.Media.IS_PENDING, 0);
activity.getContentResolver().update(imageUri, contentValues, null, null);
}
return imageUri;
} catch (IOException e) {
activity.getContentResolver().delete(imageUri, null, null);
}
}
return null;
}
public interface DownloadCallback {
void onDownloadStart();
void onDownloadComplete(Uri uri);
void onDownloadFailed();
}
}

View File

@ -0,0 +1,174 @@
package com.nature.wallpaper.naturewallpaper.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.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.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
public class SetDownloadUtil {
public static final int REQUEST_CODE_WRITE_EXTERNAL_STORAGE = 111;
private static final int BUFFER_SIZE = 4096;
private final ExecutorService executorService;
private final OkHttpClient httpClient;
public SetDownloadUtil() {
this.executorService = Executors.newSingleThreadExecutor();
this.httpClient = new OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build();
}
public void setAsWallpaper(Activity activity, String imageUrl, DownloadCallback callback) {
if (activity == null || imageUrl == null || callback == null) {
if (callback != null) callback.onDownloadFailed();
return;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
downloadWallpaperAsync(activity, imageUrl, callback);
} else if (hasWritePermission(activity)) {
downloadWallpaperAsync(activity, imageUrl, callback);
} else {
requestWritePermission(activity);
}
}
private boolean hasWritePermission(Activity activity) {
return ContextCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE)
== PackageManager.PERMISSION_GRANTED;
}
private void requestWritePermission(Activity activity) {
ActivityCompat.requestPermissions(activity,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
REQUEST_CODE_WRITE_EXTERNAL_STORAGE);
}
private void downloadWallpaperAsync(Activity activity, String imageUrl, DownloadCallback callback) {
callback.onDownloadStart();
executorService.submit(() -> {
try {
Uri imageUri = downloadImage(activity, imageUrl);
notifyResultOnMainThread(activity, callback, imageUri);
} catch (Exception e) {
notifyFailureOnMainThread(activity, callback);
}
});
}
private Uri downloadImage(Activity activity, String imageUrl) throws IOException {
String fileName = generateUniqueFileName();
Uri imageUri = createMediaStoreEntry(activity, fileName);
if (imageUri == null) return null;
try {
downloadToMediaStore(activity, imageUrl, imageUri);
markAsComplete(activity, imageUri);
return imageUri;
} catch (IOException e) {
cleanupFailedDownload(activity, imageUri);
throw e;
}
}
private String generateUniqueFileName() {
return UUID.randomUUID().toString() + ".jpg";
}
private Uri createMediaStoreEntry(Activity activity, String fileName) {
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.DISPLAY_NAME, fileName);
values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
values.put(MediaStore.Images.Media.IS_PENDING, 1);
}
Uri collectionUri = getMediaStoreCollectionUri();
return activity.getContentResolver().insert(collectionUri, values);
}
private Uri getMediaStoreCollectionUri() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
return MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
}
return MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
}
private void downloadToMediaStore(Activity activity, String imageUrl, Uri imageUri) throws IOException {
Request request = new Request.Builder().url(imageUrl).build();
try (Response response = httpClient.newCall(request).execute();
InputStream inputStream = response.body().byteStream();
OutputStream outputStream = activity.getContentResolver().openOutputStream(imageUri)) {
if (!response.isSuccessful() || outputStream == null) {
throw new IOException("Download failed: " + response.code());
}
byte[] buffer = new byte[BUFFER_SIZE];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
}
}
private void markAsComplete(Activity activity, Uri imageUri) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.IS_PENDING, 0);
activity.getContentResolver().update(imageUri, values, null, null);
}
}
private void cleanupFailedDownload(Activity activity, Uri imageUri) {
activity.getContentResolver().delete(imageUri, null, null);
}
private void notifyResultOnMainThread(Activity activity, DownloadCallback callback, Uri imageUri) {
activity.runOnUiThread(() -> {
if (imageUri != null) {
callback.onDownloadComplete(imageUri);
} else {
callback.onDownloadFailed();
}
});
}
private void notifyFailureOnMainThread(Activity activity, DownloadCallback callback) {
activity.runOnUiThread(callback::onDownloadFailed);
}
public void shutdown() {
if (!executorService.isShutdown()) {
executorService.shutdown();
}
}
public interface DownloadCallback {
void onDownloadStart();
void onDownloadComplete(Uri uri);
void onDownloadFailed();
}
}

View File

@ -27,7 +27,7 @@
android:textSize="25sp"
android:textStyle="bold"
android:gravity="center"
android:textColor="@color/white"
android:textColor="@color/black"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/image" />

View File

@ -13,18 +13,15 @@
android:layout_height="wrap_content"
android:padding="4dp">
<!-- 图片 -->
<ImageView
android:id="@+id/image_view"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_width="match_parent"
android:layout_height="250dp"
android:adjustViewBounds="true"
app:layout_constraintDimensionRatio="16:9"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<!-- 标题 -->
<TextView
android:id="@+id/title_text"
android:layout_width="0dp"
@ -40,13 +37,12 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<!-- 收藏按钮 -->
<ImageView
android:id="@+id/favorite"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/un_like"
android:layout_margin="4dp"
android:layout_margin="8dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent" />

View File

@ -4,6 +4,7 @@
android:layout_width="250dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:id="@+id/set_wallpaper_dialog"
android:background="@drawable/rounded_control_bar_background">
<TextView

View File

@ -2,4 +2,6 @@
<string name="app_name">Nature Wallpaper</string>
<!-- TODO: Remove or change this placeholder text -->
<string name="hello_blank_fragment">Hello blank fragment</string>
<string name="home">Home</string>
<string name="favorite">Favorite</string>
</resources>

6
keystore.properties Normal file
View File

@ -0,0 +1,6 @@
app_name=Nature Wallpaper
package_name=com.nature.wallpaper.naturewallpaper
keystoreFile=app/NatureWallpaper.jks
key_alias=NatureWallpaperkey0
key_store_password=NatureWallpaper
key_password=NatureWallpaper