From d3985cb6bcef67cdf8e8f57e463deb386c772823 Mon Sep 17 00:00:00 2001 From: Mihai-Cristian Condrea Date: Sun, 31 Aug 2025 11:19:10 +0300 Subject: [PATCH] Refactor home screen state management --- .../java/ui/screens/home/HomeFragment.java | 44 ++++++------ .../java/ui/screens/home/HomeUiState.java | 16 +++++ .../java/ui/screens/home/HomeViewModel.java | 70 ++++++++----------- 3 files changed, 67 insertions(+), 63 deletions(-) create mode 100644 app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/home/HomeUiState.java diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/home/HomeFragment.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/home/HomeFragment.java index 824edd46..f97776e2 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/home/HomeFragment.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/home/HomeFragment.java @@ -32,13 +32,27 @@ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); homeViewModel = new ViewModelProvider(this).get(HomeViewModel.class); - homeViewModel.getAnnouncementTitle().observe(getViewLifecycleOwner(), title -> binding.announcementTitle.setText(title)); - homeViewModel.getAnnouncementSubtitle().observe(getViewLifecycleOwner(), subtitle -> binding.announcementSubtitle.setText(subtitle)); - homeViewModel.getDailyTip().observe(getViewLifecycleOwner(), tip -> { - binding.tipText.setText(tip); - binding.shareTipButton.setOnClickListener(v -> shareTip(tip)); + LayoutInflater inflater = LayoutInflater.from(requireContext()); + homeViewModel.getUiState().observe(getViewLifecycleOwner(), state -> { + binding.announcementTitle.setText(state.announcementTitle()); + binding.announcementSubtitle.setText(state.announcementSubtitle()); + binding.tipText.setText(state.dailyTip()); + binding.shareTipButton.setOnClickListener(v -> shareTip(state.dailyTip())); + + ViewGroup promotedContainer = binding.promotedAppsContainer; + binding.scrollView.clearFocus(); + promotedContainer.clearFocus(); + promotedContainer.removeAllViews(); + for (com.d4rk.androidtutorials.java.data.model.PromotedApp app : state.promotedApps()) { + com.d4rk.androidtutorials.java.databinding.PromotedAppItemBinding itemBinding = + com.d4rk.androidtutorials.java.databinding.PromotedAppItemBinding.inflate(inflater, promotedContainer, false); + loadImage(app.iconUrl(), itemBinding.appIcon); + itemBinding.appName.setText(app.name()); + itemBinding.appDescription.setVisibility(android.view.View.GONE); + itemBinding.appButton.setOnClickListener(v -> startActivity(homeViewModel.getPromotedAppIntent(app.packageName()))); + promotedContainer.addView(itemBinding.getRoot()); + } }); - setupPromotions(LayoutInflater.from(requireContext())); new FastScrollerBuilder(binding.scrollView) .useMd2Style() .build(); @@ -67,24 +81,6 @@ private void shareTip(String tip) { startActivity(android.content.Intent.createChooser(shareIntent, getString(com.d4rk.androidtutorials.java.R.string.share_using))); } - private void setupPromotions(LayoutInflater inflater) { - ViewGroup container = binding.promotedAppsContainer; - homeViewModel.getPromotedApps().observe(getViewLifecycleOwner(), apps -> { - binding.scrollView.clearFocus(); - container.clearFocus(); - container.removeAllViews(); - for (com.d4rk.androidtutorials.java.data.model.PromotedApp app : apps) { - com.d4rk.androidtutorials.java.databinding.PromotedAppItemBinding itemBinding = - com.d4rk.androidtutorials.java.databinding.PromotedAppItemBinding.inflate(inflater, container, false); - loadImage(app.iconUrl(), itemBinding.appIcon); - itemBinding.appName.setText(app.name()); - itemBinding.appDescription.setVisibility(android.view.View.GONE); - itemBinding.appButton.setOnClickListener(v -> startActivity(homeViewModel.getPromotedAppIntent(app.packageName()))); - container.addView(itemBinding.getRoot()); - } - }); - } - private void loadImage(String url, android.widget.ImageView imageView) { com.android.volley.toolbox.ImageRequest request = new com.android.volley.toolbox.ImageRequest( url, diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/home/HomeUiState.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/home/HomeUiState.java new file mode 100644 index 00000000..a4814ec0 --- /dev/null +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/home/HomeUiState.java @@ -0,0 +1,16 @@ +package com.d4rk.androidtutorials.java.ui.screens.home; + +import java.util.List; + +import com.d4rk.androidtutorials.java.data.model.PromotedApp; + +/** + * Represents the UI state for the Home screen. + */ +public record HomeUiState( + String announcementTitle, + String announcementSubtitle, + String dailyTip, + List promotedApps +) {} + diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/home/HomeViewModel.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/home/HomeViewModel.java index c19960a7..4309a41f 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/home/HomeViewModel.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/home/HomeViewModel.java @@ -4,9 +4,9 @@ import android.content.Intent; import android.net.Uri; -import androidx.lifecycle.ViewModel; import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; +import androidx.lifecycle.ViewModel; import com.d4rk.androidtutorials.java.R; import com.d4rk.androidtutorials.java.data.model.PromotedApp; @@ -29,10 +29,7 @@ public class HomeViewModel extends ViewModel { private final GetDailyTipUseCase getDailyTipUseCase; private final GetPromotedAppsUseCase getPromotedAppsUseCase; - private final MutableLiveData announcementTitle = new MutableLiveData<>(); - private final MutableLiveData announcementSubtitle = new MutableLiveData<>(); - private final MutableLiveData dailyTip = new MutableLiveData<>(); - private final MutableLiveData> promotedApps = new MutableLiveData<>(new ArrayList<>()); + private final MutableLiveData uiState = new MutableLiveData<>(); @Inject public HomeViewModel(Application application, @@ -44,43 +41,45 @@ public HomeViewModel(Application application, this.getDailyTipUseCase = getDailyTipUseCase; this.getPromotedAppsUseCase = getPromotedAppsUseCase; - announcementTitle.setValue(application.getString(R.string.announcement_title)); - announcementSubtitle.setValue(application.getString(R.string.announcement_subtitle)); - dailyTip.setValue(getDailyTipUseCase.invoke()); + HomeUiState initialState = new HomeUiState( + application.getString(R.string.announcement_title), + application.getString(R.string.announcement_subtitle), + getDailyTipUseCase.invoke(), + new ArrayList<>() + ); + uiState.setValue(initialState); getPromotedAppsUseCase.invoke(apps -> { + List result; if (apps.isEmpty()) { - promotedApps.postValue(apps); - return; + result = apps; + } else { + int startIndex = (int) ((System.currentTimeMillis() / (24L * 60 * 60 * 1000)) % apps.size()); + result = new ArrayList<>(); + for (int i = 0; i < Math.min(4, apps.size()); i++) { + result.add(apps.get((startIndex + i) % apps.size())); + } } - int startIndex = (int) ((System.currentTimeMillis() / (24L * 60 * 60 * 1000)) % apps.size()); - List rotated = new ArrayList<>(); - for (int i = 0; i < Math.min(4, apps.size()); i++) { - rotated.add(apps.get((startIndex + i) % apps.size())); + HomeUiState current = uiState.getValue(); + if (current == null) { + current = new HomeUiState("", "", "", result); + } else { + current = new HomeUiState( + current.announcementTitle(), + current.announcementSubtitle(), + current.dailyTip(), + result + ); } - promotedApps.postValue(rotated); + uiState.postValue(current); }); } /** - * Provides a LiveData for the announcement title. - */ - public LiveData getAnnouncementTitle() { - return announcementTitle; - } - - /** - * Provides a LiveData for the announcement subtitle. - */ - public LiveData getAnnouncementSubtitle() { - return announcementSubtitle; - } - - /** - * Provides a LiveData for the tip of the day text. + * Exposes the UI state for the Home screen. */ - public LiveData getDailyTip() { - return dailyTip; + public LiveData getUiState() { + return uiState; } /** @@ -91,13 +90,6 @@ public Intent getOpenPlayStoreIntent() { return buildPlayStoreIntent(homeRepository.getPlayStoreUrl()); } - /** - * List of apps to promote on the Home screen. - */ - public LiveData> getPromotedApps() { - return promotedApps; - } - /** * Builds an intent to open the Google Play listing for the provided package. */