From 8865e826c340b38f809e2fd95920cd3524caffbb Mon Sep 17 00:00:00 2001 From: Mihai-Cristian Condrea Date: Sat, 13 Sep 2025 11:25:50 +0300 Subject: [PATCH 01/67] Use Material and AppCompat widgets in layouts --- .../main/res/layout/activity_onboarding.xml | 4 ++-- app/src/main/res/layout/activity_settings.xml | 4 ++-- .../main/res/layout/activity_table_layout.xml | 16 ++++++++-------- app/src/main/res/layout/ad_about.xml | 8 ++++---- .../main/res/layout/ad_android_studio_list.xml | 18 +++++++++--------- app/src/main/res/layout/ad_attribution.xml | 2 +- app/src/main/res/layout/ad_bottom_app_bar.xml | 16 ++++++++-------- .../main/res/layout/ad_home_banner_large.xml | 16 ++++++++-------- .../main/res/layout/ad_home_banner_small.xml | 18 +++++++++--------- app/src/main/res/layout/ad_lesson_code.xml | 18 +++++++++--------- app/src/main/res/layout/ad_support.xml | 14 +++++++------- .../fragment_onboarding_bottom_labels.xml | 8 ++++---- .../res/layout/fragment_onboarding_data.xml | 10 +++++----- .../res/layout/fragment_onboarding_done.xml | 10 +++++----- .../res/layout/fragment_onboarding_font.xml | 8 ++++---- .../layout/fragment_onboarding_selection.xml | 2 +- .../layout/item_android_studio_category.xml | 10 +++++----- .../res/layout/item_android_studio_lesson.xml | 12 ++++++------ 18 files changed, 97 insertions(+), 97 deletions(-) diff --git a/app/src/main/res/layout/activity_onboarding.xml b/app/src/main/res/layout/activity_onboarding.xml index 3347fec9..37b0c7db 100644 --- a/app/src/main/res/layout/activity_onboarding.xml +++ b/app/src/main/res/layout/activity_onboarding.xml @@ -26,7 +26,7 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/buttonSkip" /> - - + diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml index ec7d0231..d51bc310 100644 --- a/app/src/main/res/layout/activity_settings.xml +++ b/app/src/main/res/layout/activity_settings.xml @@ -1,5 +1,5 @@ - - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_table_layout.xml b/app/src/main/res/layout/activity_table_layout.xml index 18fa1ddb..035390b3 100644 --- a/app/src/main/res/layout/activity_table_layout.xml +++ b/app/src/main/res/layout/activity_table_layout.xml @@ -29,7 +29,7 @@ android:layout_marginTop="12dp" app:cardCornerRadius="24dp"> - - + @@ -82,7 +82,7 @@ android:layout_marginTop="12dp" app:cardCornerRadius="24dp"> - - + @@ -135,7 +135,7 @@ android:layout_marginTop="12dp" app:cardCornerRadius="24dp"> - - + @@ -188,7 +188,7 @@ android:layout_marginTop="12dp" app:cardCornerRadius="24dp"> - - + diff --git a/app/src/main/res/layout/ad_about.xml b/app/src/main/res/layout/ad_about.xml index e9c8543b..e473dd45 100644 --- a/app/src/main/res/layout/ad_about.xml +++ b/app/src/main/res/layout/ad_about.xml @@ -14,7 +14,7 @@ app:cardUseCompatPadding="false" app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.CardViewBottomRounded"> - - - - + diff --git a/app/src/main/res/layout/ad_android_studio_list.xml b/app/src/main/res/layout/ad_android_studio_list.xml index 124f1e85..946fff1b 100644 --- a/app/src/main/res/layout/ad_android_studio_list.xml +++ b/app/src/main/res/layout/ad_android_studio_list.xml @@ -12,7 +12,7 @@ android:layout_marginHorizontal="16dp" app:cardCornerRadius="4dp"> - - - - - - - + - - + + diff --git a/app/src/main/res/layout/ad_attribution.xml b/app/src/main/res/layout/ad_attribution.xml index 32680d1a..bbcdc62a 100644 --- a/app/src/main/res/layout/ad_attribution.xml +++ b/app/src/main/res/layout/ad_attribution.xml @@ -1,5 +1,5 @@ - - - - - - - + - - + + diff --git a/app/src/main/res/layout/ad_home_banner_large.xml b/app/src/main/res/layout/ad_home_banner_large.xml index efb4216d..8b8136b0 100644 --- a/app/src/main/res/layout/ad_home_banner_large.xml +++ b/app/src/main/res/layout/ad_home_banner_large.xml @@ -11,7 +11,7 @@ android:layout_height="wrap_content" app:cardCornerRadius="24dp"> - - - - - - + - - + + diff --git a/app/src/main/res/layout/ad_home_banner_small.xml b/app/src/main/res/layout/ad_home_banner_small.xml index 7d7004a4..fee0eb2f 100644 --- a/app/src/main/res/layout/ad_home_banner_small.xml +++ b/app/src/main/res/layout/ad_home_banner_small.xml @@ -14,7 +14,7 @@ android:layout_marginBottom="16dp" app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.CardViewBottomRounded"> - - - - - - - + - - + + diff --git a/app/src/main/res/layout/ad_lesson_code.xml b/app/src/main/res/layout/ad_lesson_code.xml index 81883277..77f70519 100644 --- a/app/src/main/res/layout/ad_lesson_code.xml +++ b/app/src/main/res/layout/ad_lesson_code.xml @@ -11,7 +11,7 @@ android:layout_height="wrap_content" app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.CardViewBottomRounded"> - - - - - - - + - - + + diff --git a/app/src/main/res/layout/ad_support.xml b/app/src/main/res/layout/ad_support.xml index c8dce2dc..71038652 100644 --- a/app/src/main/res/layout/ad_support.xml +++ b/app/src/main/res/layout/ad_support.xml @@ -11,7 +11,7 @@ android:layout_height="wrap_content" app:cardCornerRadius="24dp"> - - - - - + - - + diff --git a/app/src/main/res/layout/fragment_onboarding_bottom_labels.xml b/app/src/main/res/layout/fragment_onboarding_bottom_labels.xml index b6d52ffd..682a5a8e 100644 --- a/app/src/main/res/layout/fragment_onboarding_bottom_labels.xml +++ b/app/src/main/res/layout/fragment_onboarding_bottom_labels.xml @@ -5,7 +5,7 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - - - + - + diff --git a/app/src/main/res/layout/fragment_onboarding_data.xml b/app/src/main/res/layout/fragment_onboarding_data.xml index d2a96c39..c933c10f 100644 --- a/app/src/main/res/layout/fragment_onboarding_data.xml +++ b/app/src/main/res/layout/fragment_onboarding_data.xml @@ -4,7 +4,7 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - - - - + - + diff --git a/app/src/main/res/layout/fragment_onboarding_done.xml b/app/src/main/res/layout/fragment_onboarding_done.xml index c6b83733..4f62ea62 100644 --- a/app/src/main/res/layout/fragment_onboarding_done.xml +++ b/app/src/main/res/layout/fragment_onboarding_done.xml @@ -5,7 +5,7 @@ android:layout_height="match_parent" android:padding="24dp"> - - - - + - + diff --git a/app/src/main/res/layout/fragment_onboarding_font.xml b/app/src/main/res/layout/fragment_onboarding_font.xml index bb3821ea..dc87f3bf 100644 --- a/app/src/main/res/layout/fragment_onboarding_font.xml +++ b/app/src/main/res/layout/fragment_onboarding_font.xml @@ -5,7 +5,7 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - - - - + + diff --git a/app/src/main/res/layout/fragment_onboarding_selection.xml b/app/src/main/res/layout/fragment_onboarding_selection.xml index ff0f2530..ab187775 100644 --- a/app/src/main/res/layout/fragment_onboarding_selection.xml +++ b/app/src/main/res/layout/fragment_onboarding_selection.xml @@ -144,7 +144,7 @@ - - - + - - - + diff --git a/app/src/main/res/layout/item_android_studio_lesson.xml b/app/src/main/res/layout/item_android_studio_lesson.xml index 842ee898..fd2bd08e 100644 --- a/app/src/main/res/layout/item_android_studio_lesson.xml +++ b/app/src/main/res/layout/item_android_studio_lesson.xml @@ -8,7 +8,7 @@ android:layout_marginHorizontal="16dp" app:cardCornerRadius="4dp"> - - - - - - + + From 4cd86be9fd4e77e13a39f17109469dfd3e3af59c Mon Sep 17 00:00:00 2001 From: Mihai-Cristian Condrea Date: Sat, 13 Sep 2025 11:33:14 +0300 Subject: [PATCH 02/67] Refactor layouts and resolve FIXMEs --- .../android/AndroidStudioFragment.java | 8 +- ...vigationAndSearchingShortcutsActivity.java | 87 ++- .../android/lessons/data/room/Note.java | 2 +- .../onboarding/BottomLabelsFragment.java | 3 +- .../ui/screens/onboarding/FontFragment.java | 3 +- .../onboarding/OnboardingActivity.java | 10 +- ...ity_shortcuts_navigation_and_searching.xml | 627 ++---------------- .../layout/item_android_studio_category.xml | 25 +- app/src/main/res/layout/item_shortcut.xml | 28 + .../ui/screens/home/HomeViewModelTest.java | 37 +- 10 files changed, 184 insertions(+), 646 deletions(-) create mode 100644 app/src/main/res/layout/item_shortcut.xml diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/AndroidStudioFragment.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/AndroidStudioFragment.java index b624aef3..05dd1f76 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/AndroidStudioFragment.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/AndroidStudioFragment.java @@ -13,7 +13,6 @@ import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import android.widget.ImageView; import android.widget.TextView; import androidx.annotation.NonNull; @@ -450,21 +449,18 @@ private void applyCorners(boolean first, boolean last) { } static class CategoryHolder extends RecyclerView.ViewHolder { - final ImageView icon; final TextView title; CategoryHolder(@NonNull View itemView) { super(itemView); - icon = itemView.findViewById(R.id.category_icon); title = itemView.findViewById(R.id.category_title); } void bind(Category category) { if (category.iconRes != 0) { - icon.setImageResource(category.iconRes); - icon.setVisibility(View.VISIBLE); + title.setCompoundDrawablesRelativeWithIntrinsicBounds(category.iconRes, 0, 0, 0); } else { - icon.setVisibility(View.GONE); + title.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, 0, 0); } title.setText(category.title); } diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/tabs/NavigationAndSearchingShortcutsActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/tabs/NavigationAndSearchingShortcutsActivity.java index 36939060..7ae63be5 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/tabs/NavigationAndSearchingShortcutsActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/tabs/NavigationAndSearchingShortcutsActivity.java @@ -1,27 +1,108 @@ package com.d4rk.androidtutorials.java.ui.screens.android.lessons.basics.shortcuts.tabs; import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import com.d4rk.androidtutorials.java.R; import com.d4rk.androidtutorials.java.databinding.ActivityShortcutsNavigationAndSearchingBinding; import com.d4rk.androidtutorials.java.ui.components.navigation.UpNavigationActivity; import com.d4rk.androidtutorials.java.utils.EdgeToEdgeDelegate; import com.google.android.gms.ads.AdRequest; import com.google.android.gms.ads.MobileAds; +import java.util.List; + import me.zhanghai.android.fastscroll.FastScrollerBuilder; public class NavigationAndSearchingShortcutsActivity extends UpNavigationActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - com.d4rk.androidtutorials.java.databinding.ActivityShortcutsNavigationAndSearchingBinding binding = ActivityShortcutsNavigationAndSearchingBinding.inflate(getLayoutInflater()); + ActivityShortcutsNavigationAndSearchingBinding binding = ActivityShortcutsNavigationAndSearchingBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); MobileAds.initialize(this); EdgeToEdgeDelegate edgeToEdgeDelegate = new EdgeToEdgeDelegate(this); - edgeToEdgeDelegate.applyEdgeToEdge(binding.scrollView); + edgeToEdgeDelegate.applyEdgeToEdge(binding.shortcutList); binding.adView.loadAd(new AdRequest.Builder().build()); - new FastScrollerBuilder(binding.scrollView).useMd2Style().build(); + new FastScrollerBuilder(binding.shortcutList).useMd2Style().build(); + + List shortcuts = List.of( + new Shortcut(getString(R.string.press_shift_twice), getString(R.string.search_everything)), + new Shortcut("Ctrl + F", getString(R.string.find)), + new Shortcut("F3", getString(R.string.find_next)), + new Shortcut("Shift + F3", getString(R.string.find_previous)), + new Shortcut("Ctrl + R", getString(R.string.replace)), + new Shortcut("Ctrl + Shift + A", getString(R.string.find_action)), + new Shortcut("Ctrl + Alt + Shift + N", getString(R.string.search_by_symbol_name)), + new Shortcut("Ctrl + N", getString(R.string.find_class)), + new Shortcut("Ctrl + Shift + N", getString(R.string.find_file)), + new Shortcut("Ctrl + Shift + F", getString(R.string.find_path)), + new Shortcut("Ctrl + F12", getString(R.string.open_file_structure)), + new Shortcut("Alt + Right/Left Arrow", getString(R.string.navigate_between_open_tabs)), + new Shortcut("F4/Ctrl +Enter", getString(R.string.jump_to_source)), + new Shortcut("Shift + F4", getString(R.string.open_current_editor_tab_in_new_window)), + new Shortcut("Ctrl + E", getString(R.string.recently_opened_files)), + new Shortcut("Ctrl + Shift + E", getString(R.string.recently_edited_files)), + new Shortcut("Ctrl + Shift + Backspace", getString(R.string.go_to_last_edit_location)), + new Shortcut("Ctrl + F4", getString(R.string.close_active_editor_tabs)), + new Shortcut("Esc", getString(R.string.return_to_editor_window)), + new Shortcut("Shift + Esc", getString(R.string.hide_active_window)), + new Shortcut("Ctrl + G", getString(R.string.go_to_line)), + new Shortcut("Ctrl + H", getString(R.string.open_type_hierarchy)), + new Shortcut("Ctrl + Shift + H", getString(R.string.open_v_hierarchy)), + new Shortcut("Ctrl + Alt + H", getString(R.string.open_call_hierarchy)) + ); + + binding.shortcutList.setLayoutManager(new LinearLayoutManager(this)); + binding.shortcutList.setAdapter(new ShortcutsAdapter(shortcuts)); } + + private static class ShortcutsAdapter extends RecyclerView.Adapter { + private final List items; + + ShortcutsAdapter(List items) { + this.items = items; + } + + @NonNull + @Override + public ShortcutHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_shortcut, parent, false); + return new ShortcutHolder(view); + } + + @Override + public void onBindViewHolder(@NonNull ShortcutHolder holder, int position) { + Shortcut item = items.get(position); + holder.key.setText(item.key); + holder.description.setText(item.description); + } + + @Override + public int getItemCount() { + return items.size(); + } + + static class ShortcutHolder extends RecyclerView.ViewHolder { + final TextView key; + final TextView description; + + ShortcutHolder(@NonNull View itemView) { + super(itemView); + key = itemView.findViewById(R.id.shortcut_key); + description = itemView.findViewById(R.id.shortcut_description); + } + } + } + + private record Shortcut(String key, String description) { } } \ No newline at end of file diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/data/room/Note.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/data/room/Note.java index f4e3ad10..68471c5f 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/data/room/Note.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/data/room/Note.java @@ -10,7 +10,7 @@ public class Note { @PrimaryKey(autoGenerate = true) public int id; - public String text; // FIXME: Declaration can have final modifier + public final String text; public Note(String text) { this.text = text; diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/onboarding/BottomLabelsFragment.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/onboarding/BottomLabelsFragment.java index ad320688..3928f813 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/onboarding/BottomLabelsFragment.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/onboarding/BottomLabelsFragment.java @@ -20,7 +20,6 @@ public class BottomLabelsFragment extends Fragment { private FragmentOnboardingBottomLabelsBinding binding; private OnboardingViewModel viewModel; private MaterialRadioButton[] radioButtons; - private View[] optionCards; // FIXME: Field can be converted to a local variable @Nullable @Override @@ -40,7 +39,7 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat binding.optionUnlabeled.radioButton }; - optionCards = new View[]{ + View[] optionCards = new View[]{ binding.cardLabeled, binding.cardSelected, binding.cardUnlabeled diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/onboarding/FontFragment.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/onboarding/FontFragment.java index 057caafb..987f45a8 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/onboarding/FontFragment.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/onboarding/FontFragment.java @@ -21,7 +21,6 @@ public class FontFragment extends Fragment { private FragmentOnboardingFontBinding binding; private OnboardingViewModel viewModel; private MaterialRadioButton[] radioButtons; - private View[] optionCards; // FIXME: Field can be converted to a local variable @Nullable @Override @@ -45,7 +44,7 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat binding.optionGoogleSansCode.radioButton }; - optionCards = new View[]{ + View[] optionCards = new View[]{ binding.cardAudiowide, binding.cardFiraCode, binding.cardJetbrainsMono, diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/onboarding/OnboardingActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/onboarding/OnboardingActivity.java index 7832ce3d..7bcd301f 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/onboarding/OnboardingActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/onboarding/OnboardingActivity.java @@ -81,8 +81,9 @@ public void onPageSelected(int position) { new TabLayoutMediator(binding.tabIndicator, binding.viewPager, (tab, position) -> { ImageView dot = new ImageView(this); - dot.setImageResource(R.drawable.onboarding_dot_unselected); - int margin = dpToPx(2); + dot.setImageResource(R.drawable.onboarding_dot_unselected); + int margin = Math.round(TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, 2, getResources().getDisplayMetrics())); LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT); @@ -173,10 +174,7 @@ private void updateButtons(int position) { } } - private int dpToPx(int dp) { // FIXME: Value of parameter 'dp' is always '2' - return Math.round(TypedValue.applyDimension( - TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics())); - } + private static class OnboardingPagerAdapter extends FragmentStateAdapter { diff --git a/app/src/main/res/layout/activity_shortcuts_navigation_and_searching.xml b/app/src/main/res/layout/activity_shortcuts_navigation_and_searching.xml index 9388e66d..b1f70efc 100644 --- a/app/src/main/res/layout/activity_shortcuts_navigation_and_searching.xml +++ b/app/src/main/res/layout/activity_shortcuts_navigation_and_searching.xml @@ -6,597 +6,43 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - - - + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + app:layout_constraintEnd_toEndOf="parent" /> - \ No newline at end of file + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toEndOf="parent" /> + + diff --git a/app/src/main/res/layout/item_android_studio_category.xml b/app/src/main/res/layout/item_android_studio_category.xml index 20755fca..01b112a3 100644 --- a/app/src/main/res/layout/item_android_studio_category.xml +++ b/app/src/main/res/layout/item_android_studio_category.xml @@ -1,25 +1,10 @@ - - - - - - - - - + android:textAppearance="@style/TextAppearance.Material3.BodySmall" /> diff --git a/app/src/main/res/layout/item_shortcut.xml b/app/src/main/res/layout/item_shortcut.xml new file mode 100644 index 00000000..b33648ef --- /dev/null +++ b/app/src/main/res/layout/item_shortcut.xml @@ -0,0 +1,28 @@ + + + + + + + diff --git a/app/src/test/java/com/d4rk/androidtutorials/java/ui/screens/home/HomeViewModelTest.java b/app/src/test/java/com/d4rk/androidtutorials/java/ui/screens/home/HomeViewModelTest.java index a17f52e0..7673d9da 100644 --- a/app/src/test/java/com/d4rk/androidtutorials/java/ui/screens/home/HomeViewModelTest.java +++ b/app/src/test/java/com/d4rk/androidtutorials/java/ui/screens/home/HomeViewModelTest.java @@ -27,7 +27,7 @@ public class HomeViewModelTest { @Test public void uiStateUpdatesWithData() { List promoted = List.of(new PromotedApp("App", "pkg", "icon")); - FakeHomeRepository repo = new FakeHomeRepository("tip", promoted); + FakeHomeRepository repo = new FakeHomeRepository(promoted); HomeViewModel viewModel = new HomeViewModel( new GetDailyTipUseCase(repo), new GetPromotedAppsUseCase(repo), @@ -46,7 +46,7 @@ public void uiStateUpdatesWithData() { @Test public void uiStateHandlesEmptyPromotedApps() { - FakeHomeRepository repo = new FakeHomeRepository("tip", List.of()); + FakeHomeRepository repo = new FakeHomeRepository(List.of()); HomeViewModel viewModel = new HomeViewModel( new GetDailyTipUseCase(repo), new GetPromotedAppsUseCase(repo), @@ -60,21 +60,26 @@ public void uiStateHandlesEmptyPromotedApps() { assertTrue(state.promotedApps().isEmpty()); } - record FakeHomeRepository(String dailyTip, List apps) implements HomeRepository { // FIXME: Value for parameter 'dailyTip' is always "tip" + record FakeHomeRepository(List apps) implements HomeRepository { @Override - public String getPlayStoreUrl() { - return ""; - } - - @Override - public String getAppPlayStoreUrl(String packageName) { - return ""; - } - - @Override - public void fetchPromotedApps(PromotedAppsCallback callback) { - callback.onResult(apps); - } + public String dailyTip() { + return "tip"; } + + @Override + public String getPlayStoreUrl() { + return ""; + } + + @Override + public String getAppPlayStoreUrl(String packageName) { + return ""; + } + + @Override + public void fetchPromotedApps(PromotedAppsCallback callback) { + callback.onResult(apps); + } + } } From 0ed51681d2098ee80d34eb5faf2fda0f06cc548f Mon Sep 17 00:00:00 2001 From: Mihai-Cristian Condrea Date: Sat, 13 Sep 2025 11:44:14 +0300 Subject: [PATCH 03/67] Use view binding in Android Studio lesson list --- .../android/AndroidStudioFragment.java | 58 +++++++++++-------- 1 file changed, 35 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/AndroidStudioFragment.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/AndroidStudioFragment.java index 05dd1f76..a8dfbb95 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/AndroidStudioFragment.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/AndroidStudioFragment.java @@ -13,10 +13,10 @@ import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.appcompat.widget.AppCompatImageView; import androidx.appcompat.widget.SearchView; import androidx.core.view.MenuHost; import androidx.core.view.MenuProvider; @@ -29,10 +29,14 @@ import com.d4rk.androidtutorials.java.R; import com.d4rk.androidtutorials.java.ads.views.NativeAdBannerView; import com.d4rk.androidtutorials.java.utils.ConsentUtils; +import com.d4rk.androidtutorials.java.databinding.FragmentAndroidStudioBinding; +import com.d4rk.androidtutorials.java.databinding.ItemAndroidStudioCategoryBinding; +import com.d4rk.androidtutorials.java.databinding.ItemAndroidStudioLessonBinding; import com.google.android.gms.ads.AdListener; import com.google.android.gms.ads.LoadAdError; import com.google.android.gms.ads.MobileAds; import com.google.android.material.card.MaterialCardView; +import com.google.android.material.textview.MaterialTextView; import com.google.android.material.shape.CornerFamily; import com.google.android.material.shape.ShapeAppearanceModel; @@ -54,12 +58,14 @@ public class AndroidStudioFragment extends Fragment { private final List allItems = new ArrayList<>(); private LessonsAdapter adapter; private boolean showAds; + private FragmentAndroidStudioBinding binding; @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - return inflater.inflate(R.layout.fragment_android_studio, container, false); + binding = FragmentAndroidStudioBinding.inflate(inflater, container, false); + return binding.getRoot(); } @Override @@ -69,7 +75,7 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat if (showAds) { ensureMobileAdsInitialized(); } - RecyclerView list = view.findViewById(R.id.lessons_list); + RecyclerView list = binding.lessonsList; list.setLayoutManager(new LinearLayoutManager(requireContext())); adapter = new LessonsAdapter(); list.setAdapter(adapter); @@ -124,6 +130,12 @@ public boolean onMenuItemSelected(@NonNull MenuItem menuItem) { }, getViewLifecycleOwner(), Lifecycle.State.RESUMED); } + @Override + public void onDestroyView() { + super.onDestroyView(); + binding = null; + } + private void ensureMobileAdsInitialized() { if (!mobileAdsInitialized) { MobileAds.initialize(requireContext()); @@ -350,13 +362,13 @@ public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int adView.setNativeAdLayout(R.layout.ad_android_studio_list); return new AdHolder(adView); } else if (viewType == TYPE_CATEGORY) { - View view = LayoutInflater.from(parent.getContext()) - .inflate(R.layout.item_android_studio_category, parent, false); - return new CategoryHolder(view); + ItemAndroidStudioCategoryBinding binding = ItemAndroidStudioCategoryBinding.inflate( + LayoutInflater.from(parent.getContext()), parent, false); + return new CategoryHolder(binding); } else { - View view = LayoutInflater.from(parent.getContext()) - .inflate(R.layout.item_android_studio_lesson, parent, false); - return new LessonHolder(view); + ItemAndroidStudioLessonBinding binding = ItemAndroidStudioLessonBinding.inflate( + LayoutInflater.from(parent.getContext()), parent, false); + return new LessonHolder(binding); } } @@ -400,16 +412,16 @@ static class AdHolder extends RecyclerView.ViewHolder { static class LessonHolder extends RecyclerView.ViewHolder { final MaterialCardView card; - final ImageView icon; - final TextView title; - final TextView summary; - - LessonHolder(@NonNull View itemView) { - super(itemView); - card = (MaterialCardView) itemView; - icon = itemView.findViewById(R.id.lesson_icon); - title = itemView.findViewById(R.id.lesson_title); - summary = itemView.findViewById(R.id.lesson_summary); + final AppCompatImageView icon; + final MaterialTextView title; + final MaterialTextView summary; + + LessonHolder(@NonNull ItemAndroidStudioLessonBinding binding) { + super(binding.getRoot()); + card = binding.lessonCard; + icon = binding.lessonIcon; + title = binding.lessonTitle; + summary = binding.lessonSummary; } void bind(Lesson lesson, boolean first, boolean last) { @@ -449,11 +461,11 @@ private void applyCorners(boolean first, boolean last) { } static class CategoryHolder extends RecyclerView.ViewHolder { - final TextView title; + final MaterialTextView title; - CategoryHolder(@NonNull View itemView) { - super(itemView); - title = itemView.findViewById(R.id.category_title); + CategoryHolder(@NonNull ItemAndroidStudioCategoryBinding binding) { + super(binding.getRoot()); + title = binding.categoryTitle; } void bind(Category category) { From e50a570fb936801a2ea57a01ff84bf6695681424 Mon Sep 17 00:00:00 2001 From: Mihai-Cristian Condrea Date: Sat, 13 Sep 2025 12:05:10 +0300 Subject: [PATCH 04/67] Translate onboarding strings across locales --- app/src/main/res/values-ar-rEG/strings.xml | 22 +++++++++++++++++++++ app/src/main/res/values-bg-rBG/strings.xml | 22 +++++++++++++++++++++ app/src/main/res/values-bn-rBD/strings.xml | 22 +++++++++++++++++++++ app/src/main/res/values-de-rDE/strings.xml | 22 +++++++++++++++++++++ app/src/main/res/values-es-rGQ/strings.xml | 22 +++++++++++++++++++++ app/src/main/res/values-es-rMX/strings.xml | 22 +++++++++++++++++++++ app/src/main/res/values-fil-rPH/strings.xml | 22 +++++++++++++++++++++ app/src/main/res/values-fr-rFR/strings.xml | 22 +++++++++++++++++++++ app/src/main/res/values-hi-rIN/strings.xml | 22 +++++++++++++++++++++ app/src/main/res/values-hu-rHU/strings.xml | 22 +++++++++++++++++++++ app/src/main/res/values-in-rID/strings.xml | 22 +++++++++++++++++++++ app/src/main/res/values-it-rIT/strings.xml | 22 +++++++++++++++++++++ app/src/main/res/values-ja-rJP/strings.xml | 22 +++++++++++++++++++++ app/src/main/res/values-ko-rKR/strings.xml | 22 +++++++++++++++++++++ app/src/main/res/values-pl-rPL/strings.xml | 22 +++++++++++++++++++++ app/src/main/res/values-pt-rBR/strings.xml | 22 +++++++++++++++++++++ app/src/main/res/values-ro-rRO/strings.xml | 22 +++++++++++++++++++++ app/src/main/res/values-ru-rRU/strings.xml | 22 +++++++++++++++++++++ app/src/main/res/values-sv-rSE/strings.xml | 22 +++++++++++++++++++++ app/src/main/res/values-th-rTH/strings.xml | 22 +++++++++++++++++++++ app/src/main/res/values-tr-rTR/strings.xml | 22 +++++++++++++++++++++ app/src/main/res/values-uk-rUA/strings.xml | 22 +++++++++++++++++++++ app/src/main/res/values-ur-rPK/strings.xml | 22 +++++++++++++++++++++ app/src/main/res/values-vi-rVN/strings.xml | 22 +++++++++++++++++++++ app/src/main/res/values-zh-rTW/strings.xml | 22 +++++++++++++++++++++ 25 files changed, 550 insertions(+) diff --git a/app/src/main/res/values-ar-rEG/strings.xml b/app/src/main/res/values-ar-rEG/strings.xml index 177ac047..67fe7831 100644 --- a/app/src/main/res/values-ar-rEG/strings.xml +++ b/app/src/main/res/values-ar-rEG/strings.xml @@ -226,6 +226,28 @@ الوضع الداكن الوضع الداكن التلقائي حسب البطارية التبويب الافتراضي + اختر علامة التبويب التي تظهر عند فتح التطبيق + أفضل القصص والتحديثات الحديثة + أدوات استوديو Android ، نصائح ، وكتالوج البرنامج التعليمي الكامل + تفاصيل التطبيق والإصدار والائتمانات + يتخطى + اختر أسلوبك + اختر كيف يبدو التطبيق + مظهر مشرق ونظيف + مريح في الضوء المنخفض + يتبع موضوع جهازك + ساعدنا في تحسين تجربتك + ساعدنا في جعل التطبيق أفضل لك من خلال مشاركة تقارير التعطل المجهول وإحصائيات الاستخدام. + تمكين التقارير تحطم + من خلال تمكين هذا ، يمكنك مساعدتنا في تحديد وإصلاح الأخطاء بشكل أسرع. نحن لا نجمع أي معلومات شخصية. + السماح الإعلانات الشخصية + السماح بتخزين بيانات الإعلان ، ومعلومات المستخدم ، والتخصيص للحفاظ على إعلانات ذات صلة. + اقرأ سياسة الخصوصية الخاصة بنا + أيقونة الخصوصية + أنت كل مجموعة! + الإعداد الخاص بك اكتمل. أنت الآن مستعد لاستكشاف جميع الميزات. + ابدأ + أيقونة النجاح عناوين شريط التنقل السفلي معنون المحدد %1$s diff --git a/app/src/main/res/values-bg-rBG/strings.xml b/app/src/main/res/values-bg-rBG/strings.xml index 27e03e10..29132d28 100644 --- a/app/src/main/res/values-bg-rBG/strings.xml +++ b/app/src/main/res/values-bg-rBG/strings.xml @@ -252,6 +252,28 @@ Тъмен режим Автоматичен тъмен режим при ниска батерия Раздел по подразбиране + Изберете кой раздел се появява, когато приложението се отвори + Топ истории и скорошни актуализации + Андроид студио инструменти, съвети и пълния каталог на урока + Подробности за приложението, версия и кредити + Пропуснете + Изберете вашия стил + Изберете как изглежда приложението + Ярък, чист външен вид + Удобно при ниска светлина + Следва темата на вашето устройство + Помогнете ни да подобрим вашия опит + Помогнете ни да направим приложението по -добро за вас, като споделяте анонимни отчети за сривове и статистика за използване. + Активиране на отчитане на катастрофи + Като активирате това, вие ни помагате да идентифицираме и поправим грешки по -бързо. Ние не събираме лична информация. + Позволяват персонализирани реклами + Разрешаване на съхранение на рекламни данни, информация за потребителя и персонализация, за да се поддържат рекламните реклами. + Прочетете нашата Политика за поверителност + Икона за поверителност + Вие сте готови! + Вашата настройка е пълна. Вече сте готови да проучите всички функции. + Започнете + Икона за успех Етикети на долната навигационна лента С етикети Избрани %1$s diff --git a/app/src/main/res/values-bn-rBD/strings.xml b/app/src/main/res/values-bn-rBD/strings.xml index 9282ab2e..423a91ad 100644 --- a/app/src/main/res/values-bn-rBD/strings.xml +++ b/app/src/main/res/values-bn-rBD/strings.xml @@ -252,6 +252,28 @@ ডার্ক মোড অটো ব্যাটারি ডার্ক মোড ডিফল্ট ট্যাব + অ্যাপটি খোলার পরে কোন ট্যাবটি প্রদর্শিত হবে তা চয়ন করুন + শীর্ষ গল্প এবং সাম্প্রতিক আপডেটগুলি + অ্যান্ড্রয়েড স্টুডিও সরঞ্জাম, টিপস এবং সম্পূর্ণ টিউটোরিয়াল ক্যাটালগ + অ্যাপ্লিকেশন বিশদ, সংস্করণ এবং ক্রেডিট + এড়িয়ে যান + আপনার স্টাইল চয়ন করুন + অ্যাপটি কেমন দেখাচ্ছে তা চয়ন করুন + উজ্জ্বল, পরিষ্কার চেহারা + কম আলোতে আরামদায়ক + আপনার ডিভাইস থিম অনুসরণ করে + আমাদের আপনার অভিজ্ঞতা উন্নত করতে সহায়তা করুন + বেনামে ক্র্যাশ রিপোর্ট এবং ব্যবহারের পরিসংখ্যান ভাগ করে আপনার জন্য অ্যাপ্লিকেশনটিকে আরও উন্নত করতে আমাদের সহায়তা করুন। + ক্র্যাশ রিপোর্টিং সক্ষম করুন + এটি সক্ষম করে আপনি আমাদের বাগগুলি দ্রুত সনাক্ত করতে এবং ঠিক করতে সহায়তা করেন। আমরা কোনও ব্যক্তিগত তথ্য সংগ্রহ করি না। + ব্যক্তিগতকৃত বিজ্ঞাপনগুলির অনুমতি দিন + বিজ্ঞাপনগুলি প্রাসঙ্গিক রাখতে বিজ্ঞাপন ডেটা, ব্যবহারকারীর তথ্য এবং ব্যক্তিগতকরণের সংরক্ষণের অনুমতি দিন। + আমাদের গোপনীয়তা নীতি পড়ুন + গোপনীয়তা আইকন + আপনি সব সেট! + আপনার সেটআপ সম্পূর্ণ। আপনি এখন সমস্ত বৈশিষ্ট্য অন্বেষণ করতে প্রস্তুত। + শুরু করুন + সাফল্য আইকন নিচের নেভিগেশন বার লেবেল লেবেলযুক্ত নির্বাচিত %1$s diff --git a/app/src/main/res/values-de-rDE/strings.xml b/app/src/main/res/values-de-rDE/strings.xml index 4ec55d47..716e18ac 100644 --- a/app/src/main/res/values-de-rDE/strings.xml +++ b/app/src/main/res/values-de-rDE/strings.xml @@ -252,6 +252,28 @@ Dunkler Modus Automatischer Dunkelmodus bei niedrigem Akkustand Standard-Tab + Wählen Sie aus, welche Registerkarte angezeigt wird, wenn die App geöffnet wird + Top -Geschichten und aktuelle Updates + Android Studio Tools, Tipps und der vollständige Tutorial -Katalog + App -Details, Version und Credits + Überspringen + Wählen Sie Ihren Stil + Wählen Sie, wie die App aussieht + Helles, sauberes Aussehen + Bequem bei schwacher Licht + Folgen Sie Ihrem Gerätethema + Helfen Sie uns, Ihre Erfahrung zu verbessern + Helfen Sie uns, die App für Sie besser zu machen, indem Sie anonyme Absturzberichte und Nutzungsstatistiken teilen. + Absturzberichterstattung aktivieren + Wenn Sie dies aktivieren, helfen Sie uns, Fehler schneller zu identifizieren und zu beheben. Wir sammeln keine persönlichen Informationen. + Personalisierte Anzeigen zulassen + Ermöglichen Sie das Speichern von Anzeigendaten, Benutzerinformationen und Personalisierung, um Anzeigen relevant zu halten. + Lesen Sie unsere Datenschutzrichtlinie + Datenschutz -Ikone + Sie sind alle gesetzt! + Ihr Setup ist abgeschlossen. Sie sind jetzt bereit, alle Funktionen zu erkunden. + Fangen an + Erfolgsikone Beschriftungen der unteren Navigationsleiste Beschriftet Ausgewählt %1$s diff --git a/app/src/main/res/values-es-rGQ/strings.xml b/app/src/main/res/values-es-rGQ/strings.xml index 90a734e8..b4ad8624 100644 --- a/app/src/main/res/values-es-rGQ/strings.xml +++ b/app/src/main/res/values-es-rGQ/strings.xml @@ -252,6 +252,28 @@ Modo oscuro Modo oscuro automático por batería Pestaña predeterminada + Elija qué pestaña aparece cuando se abre la aplicación + Las principales historias y actualizaciones recientes + Android Studio Tools, consejos y el catálogo de tutorial completo + Detalles, versión y créditos de la aplicación + Saltar + Elige tu estilo + Elija cómo se ve la aplicación + Apariencia brillante y limpia + Cómodo con poca luz + Sigue el tema de tu dispositivo + Ayúdanos a mejorar tu experiencia + Ayúdanos a mejorar la aplicación para compartir informes anónimos de accidentes y estadísticas de uso. + Habilitar informes de accidentes + Al habilitar esto, nos ayuda a identificar y solucionar errores más rápido. No recopilamos ninguna información personal. + Permitir anuncios personalizados + Permitir almacenar datos de anuncios, información del usuario y personalización para mantener los anuncios relevantes. + Lea nuestra Política de privacidad + Icono de privacidad + ¡Estás listo! + Tu configuración está completa. Ahora estás listo para explorar todas las características. + Empezar + Icono de éxito Etiquetas de la barra de navegación inferior Etiquetado Seleccionado %1$s diff --git a/app/src/main/res/values-es-rMX/strings.xml b/app/src/main/res/values-es-rMX/strings.xml index c6947ea1..d461d23b 100644 --- a/app/src/main/res/values-es-rMX/strings.xml +++ b/app/src/main/res/values-es-rMX/strings.xml @@ -226,6 +226,28 @@ Modo Oscuro Modo Oscuro Automático por Batería Pestaña predeterminada + Elija qué pestaña aparece cuando se abre la aplicación + Las principales historias y actualizaciones recientes + Android Studio Tools, consejos y el catálogo de tutorial completo + Detalles, versión y créditos de la aplicación + Saltar + Elige tu estilo + Elija cómo se ve la aplicación + Apariencia brillante y limpia + Cómodo con poca luz + Sigue el tema de tu dispositivo + Ayúdanos a mejorar tu experiencia + Ayúdanos a mejorar la aplicación para compartir informes anónimos de accidentes y estadísticas de uso. + Habilitar informes de accidentes + Al habilitar esto, nos ayuda a identificar y solucionar errores más rápido. No recopilamos ninguna información personal. + Permitir anuncios personalizados + Permitir almacenar datos de anuncios, información del usuario y personalización para mantener los anuncios relevantes. + Lea nuestra Política de privacidad + Icono de privacidad + ¡Estás listo! + Tu configuración está completa. Ahora estás listo para explorar todas las características. + Empezar + Icono de éxito Etiquetas de la barra de navegación inferior Etiquetado Seleccionado %1$s diff --git a/app/src/main/res/values-fil-rPH/strings.xml b/app/src/main/res/values-fil-rPH/strings.xml index 4bfa500a..c3d54733 100644 --- a/app/src/main/res/values-fil-rPH/strings.xml +++ b/app/src/main/res/values-fil-rPH/strings.xml @@ -226,6 +226,28 @@ Dark Mode Auto Battery Dark Mode Default na tab + Piliin kung aling tab ang lilitaw kapag magbubukas ang app + Nangungunang mga kwento at kamakailang mga pag -update + Mga tool sa Android Studio, mga tip, at ang buong katalogo ng tutorial + Mga detalye ng app, bersyon, at kredito + Laktawan + Piliin ang iyong estilo + Piliin kung paano ang hitsura ng app + Maliwanag, malinis na hitsura + Kumportable sa mababang ilaw + Sumusunod sa tema ng iyong aparato + Tulungan kaming mapagbuti ang iyong karanasan + Tulungan kaming gawing mas mahusay ang app para sa iyo sa pamamagitan ng pagbabahagi ng hindi nagpapakilalang mga ulat ng pag -crash at mga istatistika ng paggamit. + Paganahin ang pag -uulat ng pag -crash + Sa pamamagitan ng pagpapagana nito, tinutulungan mo kaming kilalanin at ayusin ang mga bug nang mas mabilis. Hindi namin kinokolekta ang anumang personal na impormasyon. + Payagan ang mga personalized na ad + Pahintulutan ang pag -iimbak ng data ng ad, impormasyon ng gumagamit, at pag -personalize upang mapanatili ang mga ad na may kaugnayan. + Basahin ang aming Patakaran sa Pagkapribado + Icon ng privacy + Nakatakda kayong lahat! + Kumpleto ang iyong pag -setup. Handa ka na upang galugarin ang lahat ng mga tampok. + Magsimula + Tagumpay icon Mga label ng bottom navigation bar May label Napili %1$s diff --git a/app/src/main/res/values-fr-rFR/strings.xml b/app/src/main/res/values-fr-rFR/strings.xml index 0fe1bc23..8315e281 100644 --- a/app/src/main/res/values-fr-rFR/strings.xml +++ b/app/src/main/res/values-fr-rFR/strings.xml @@ -252,6 +252,28 @@ Mode sombre Mode sombre auto (batterie) Onglet par défaut + Choisissez l\'onglet apparaît lorsque l\'application s\'ouvre + Les meilleures histoires et les mises à jour récentes + Outils de studio Android, conseils et catalogue de tutoriel complet + Détails de l\'application, version et crédits + Sauter + Choisissez votre style + Choisissez à quoi ressemble l\'application + Apparence brillante et propre + Confortable en basse lumière + Suit le thème de votre appareil + Aidez-nous à améliorer votre expérience + Aidez-nous à améliorer l\'application pour vous en partageant des rapports de crash anonymes et des statistiques d\'utilisation. + Activer les rapports de crash + En permettant cela, vous nous aidez à identifier et à corriger les bogues plus rapidement. Nous ne recueillons aucune information personnelle. + Autoriser les annonces personnalisées + Perminez le stockage des données publicitaires, des informations d\'utilisateur et de la personnalisation pour garder les annonces pertinentes. + Lisez notre politique de confidentialité + Icône de confidentialité + Vous êtes prêt! + Votre configuration est complète. Vous êtes maintenant prêt à explorer toutes les fonctionnalités. + Commencer + Icône de succès Étiquettes de la barre de navigation inférieure Étiqueté Sélectionné %1$s diff --git a/app/src/main/res/values-hi-rIN/strings.xml b/app/src/main/res/values-hi-rIN/strings.xml index 41eda870..bac968f1 100644 --- a/app/src/main/res/values-hi-rIN/strings.xml +++ b/app/src/main/res/values-hi-rIN/strings.xml @@ -253,6 +253,28 @@ डार्क मोड ऑटो बैटरी डार्क मोड डिफ़ॉल्ट टैब + ऐप खुलने पर कौन सा टैब दिखाई देता है, चुनें + शीर्ष कहानियाँ और हाल के अपडेट + एंड्रॉइड स्टूडियो टूल्स, टिप्स और फुल ट्यूटोरियल कैटलॉग + ऐप विवरण, संस्करण और क्रेडिट + छोडना + अपनी शैली चुनें + चुनें कि ऐप कैसा दिखता है + उज्ज्वल, स्वच्छ उपस्थिति + कम रोशनी में आरामदायक + अपने डिवाइस थीम का अनुसरण करता है + हमें अपने अनुभव को बेहतर बनाने में मदद करें + अनाम क्रैश रिपोर्ट और उपयोग के आंकड़े साझा करके हमारे लिए ऐप को बेहतर बनाने में हमारी मदद करें। + क्रैश रिपोर्टिंग सक्षम करें + इसे सक्षम करके, आप हमें तेजी से कीड़े को पहचानने और ठीक करने में मदद करते हैं। हम कोई व्यक्तिगत जानकारी एकत्र नहीं करते हैं। + व्यक्तिगत विज्ञापनों की अनुमति दें + विज्ञापन डेटा, उपयोगकर्ता की जानकारी और विज्ञापन को प्रासंगिक रखने के लिए वैयक्तिकरण की अनुमति दें। + हमारी गोपनीयता नीति पढ़ें + गोपनीयता आइकन + तुम सब सेट हो! + आपका सेटअप पूरा हो गया है। अब आप सभी सुविधाओं का पता लगाने के लिए तैयार हैं। + शुरू हो जाओ + सफलता चिह्न बॉटम नेविगेशन बार लेबल लेबल किया हुआ चयनित %1$s diff --git a/app/src/main/res/values-hu-rHU/strings.xml b/app/src/main/res/values-hu-rHU/strings.xml index 442a1d39..0e644507 100644 --- a/app/src/main/res/values-hu-rHU/strings.xml +++ b/app/src/main/res/values-hu-rHU/strings.xml @@ -273,6 +273,28 @@ Sötét mód Automatikus akkumulátor-alapú sötét mód Alapértelmezett lap + Válassza ki, melyik lap jelenik meg, amikor az alkalmazás megnyílik + A legnépszerűbb történetek és a legújabb frissítések + Android Studio Tools, Tippek és a teljes bemutató katalógus + Az alkalmazás részletei, verziója és kreditjei + Kihagyás + Válassza ki a stílusát + Válassza ki, hogyan néz ki az alkalmazás + Fényes, tiszta megjelenés + Kényelmes gyenge fényben + Követi az eszköz témáját + Segítsen javítani tapasztalatait + Segítsen nekünk, hogy az alkalmazás jobbá tegye az Ön számára az anonim összeomlási jelentések és a használati statisztikák megosztásával. + Engedélyezze az ütközésjelentést + Ennek engedélyezésével segít a hibák gyorsabb azonosításában és javításában. Nem gyűjtünk személyes információkat. + Engedélyezze a személyre szabott hirdetéseket + Engedélyezze a hirdetési adatok, a felhasználói információk és a személyre szabás tárolását, hogy a hirdetések relevánsak maradjanak. + Olvassa el adatvédelmi irányelveinket + Adatvédelmi ikon + Minden készen állsz! + A beállítás teljes. Most már készen áll az összes szolgáltatás felfedezésére. + Elindul + Sikerikon Alsó navigációs sáv címkék Címkézett Kiválasztva %1$s diff --git a/app/src/main/res/values-in-rID/strings.xml b/app/src/main/res/values-in-rID/strings.xml index 0af9a58a..af5cc87c 100644 --- a/app/src/main/res/values-in-rID/strings.xml +++ b/app/src/main/res/values-in-rID/strings.xml @@ -252,6 +252,28 @@ Mode Gelap Mode Gelap Baterai Otomatis Tab default + Pilih tab mana yang muncul saat aplikasi terbuka + Cerita teratas dan pembaruan terbaru + Alat studio android, tips, dan katalog tutorial lengkap + Detail aplikasi, versi, dan kredit + Melewati + Pilih Gaya Anda + Pilih penampilan aplikasi + Penampilan yang cerah dan bersih + Nyaman dalam cahaya rendah + Mengikuti tema perangkat Anda + Bantu kami meningkatkan pengalaman Anda + Bantu kami membuat aplikasi lebih baik untuk Anda dengan berbagi laporan kecelakaan anonim dan statistik penggunaan. + Aktifkan pelaporan crash + Dengan mengaktifkan ini, Anda membantu kami mengidentifikasi dan memperbaiki bug lebih cepat. Kami tidak mengumpulkan informasi pribadi apa pun. + Izinkan iklan yang dipersonalisasi + Izin menyimpan data iklan, info pengguna, dan personalisasi agar iklan tetap relevan. + Baca Kebijakan Privasi Kami + Ikon Privasi + Anda siap! + Pengaturan Anda selesai. Anda sekarang siap untuk menjelajahi semua fitur. + Mulai + Ikon Sukses Label bilah navigasi bawah Berlabel Terpilih %1$s diff --git a/app/src/main/res/values-it-rIT/strings.xml b/app/src/main/res/values-it-rIT/strings.xml index 0d6d07ef..f4289358 100644 --- a/app/src/main/res/values-it-rIT/strings.xml +++ b/app/src/main/res/values-it-rIT/strings.xml @@ -273,6 +273,28 @@ Modalità scura Modalità scura automatica in base alla batteria Scheda predefinita + Scegli quale scheda appare quando si apre l\'app + Storie migliori e aggiornamenti recenti + Strumenti di studio Android, suggerimenti e catalogo tutorial completo + Dettagli, versione e crediti dell\'app + Saltare + Scegli il tuo stile + Scegli come appare l\'app + Aspetto luminoso e pulito + Comodo in condizioni di scarsa luminosità + Segue il tema del tuo dispositivo + Aiutaci a migliorare la tua esperienza + Aiutaci a migliorare l\'app per te condividendo rapporti anonimi e statistiche sull\'uso. + Abilita i report di crash + Abilitando questo, ci aiuti a identificare e correggere i bug più velocemente. Non raccogliamo alcuna informazione personale. + Consenti annunci personalizzati + Consentire la memorizzazione di dati di annunci, informazioni sull\'utente e personalizzazione per mantenere gli annunci pertinenti. + Leggi la nostra politica sulla privacy + Icona della privacy + Siete tutti pronti! + La tua configurazione è completa. Ora sei pronto per esplorare tutte le funzionalità. + Inizia + Icona di successo Etichette barra di navigazione inferiore Con etichetta Selezionato %1$s diff --git a/app/src/main/res/values-ja-rJP/strings.xml b/app/src/main/res/values-ja-rJP/strings.xml index 74039e23..caa8f30b 100644 --- a/app/src/main/res/values-ja-rJP/strings.xml +++ b/app/src/main/res/values-ja-rJP/strings.xml @@ -273,6 +273,28 @@ ダークモード 自動バッテリーダークモード デフォルトタブ + アプリが開いたときに表示されるタブを選択します + トップストーリーと最近の更新 + Android Studioツール、ヒント、および完全なチュートリアルカタログ + アプリの詳細、バージョン、クレジット + スキップ + あなたのスタイルを選択してください + アプリの見た目を選択してください + 明るくてきれいな外観 + 低光の中で快適 + デバイスのテーマに従います + 私たちがあなたの経験を改善するのを手伝ってください + 匿名のクラッシュレポートと使用統計を共有することにより、アプリをより良くするのに役立ちます。 + クラッシュレポートを有効にします + これを有効にすることで、バグをより速く特定して修正するのに役立ちます。個人情報は収集しません。 + パーソナライズされた広告を許可します + 広告データ、ユーザー情報、およびパーソナライズを保存して、広告を関連させます。 + プライバシーポリシーをお読みください + プライバシーアイコン + あなたはすべて設定されています! + セットアップが完了しました。これで、すべての機能を探索する準備ができました。 + 始めましょう + 成功アイコン ボトムナビゲーションバーのラベル ラベル付き 選択済み %1$s diff --git a/app/src/main/res/values-ko-rKR/strings.xml b/app/src/main/res/values-ko-rKR/strings.xml index a2b890d4..96c9c0cb 100644 --- a/app/src/main/res/values-ko-rKR/strings.xml +++ b/app/src/main/res/values-ko-rKR/strings.xml @@ -226,6 +226,28 @@ 다크 모드 배터리 절약 시 자동 다크 모드 기본 탭 + 앱이 열릴 때 나타나는 탭을 선택하십시오 + 최고의 이야기와 최근 업데이트 + 안드로이드 스튜디오 도구, 팁 및 전체 자습서 카탈로그 + 앱 세부 사항, 버전 및 크레딧 + 건너뛰다 + 스타일을 선택하십시오 + 앱의 모양을 선택하십시오 + 밝고 깨끗한 외관 + 낮은 조명에서 편안합니다 + 장치 테마를 따릅니다 + 경험을 향상시키는 데 도움이됩니다 + 익명의 충돌 보고서 및 사용 통계를 공유하여 앱을 더 좋게 만들도록 도와줍니다. + 충돌보고를 활성화합니다 + 이를 활성화하면 버그를 더 빨리 식별하고 수정하는 데 도움이됩니다. 우리는 개인 정보를 수집하지 않습니다. + 개인화 된 광고를 허용합니다 + 광고 데이터, 사용자 정보 및 개인화를 저장하여 광고를 관련성있게 유지할 수 있습니다. + 개인 정보 보호 정책을 읽으십시오 + 프라이버시 아이콘 + 당신은 모두 설정되었습니다! + 설정이 완료되었습니다. 이제 모든 기능을 탐색 할 준비가되었습니다. + 시작하세요 + 성공 아이콘 하단 탐색 메뉴 라벨 라벨 표시 선택됨 %1$s diff --git a/app/src/main/res/values-pl-rPL/strings.xml b/app/src/main/res/values-pl-rPL/strings.xml index a40d51e1..579435c3 100644 --- a/app/src/main/res/values-pl-rPL/strings.xml +++ b/app/src/main/res/values-pl-rPL/strings.xml @@ -273,6 +273,28 @@ Tryb ciemny Automatyczny tryb ciemny baterii Domyślna karta + Wybierz, która karta pojawi się, gdy aplikacja się otworzy + Najlepsze historie i najnowsze aktualizacje + Narzędzia Studio Android, wskazówki i pełny katalog samouczka + Szczegóły aplikacji, wersja i kredyty + Pominąć + Wybierz swój styl + Wybierz, jak wygląda aplikacja + Jasny, czysty wygląd + Wygodne w słabym świetle + Śledzi motyw urządzenia + Pomóż nam poprawić swoje doświadczenie + Pomóż nam ulepszyć aplikację, udostępniając anonimowe raporty katastrofy i statystyki użytkowania. + Włącz raportowanie o awarie + Umożliwiając to, pomagasz nam szybciej identyfikować i naprawić błędy. Nie zbieramy żadnych danych osobowych. + Zezwalaj na spersonalizowane reklamy + Zezwalaj na przechowywanie danych AD, informacje o użytkowniku i personalizacja, aby zapewnić odpowiednie reklamy. + Przeczytaj naszą politykę prywatności + Ikona prywatności + Jesteś gotowy! + Twoja konfiguracja jest kompletna. Jesteś teraz gotowy do zbadania wszystkich funkcji. + Zacznij + Ikona sukcesu Etykiety dolnego paska nawigacji Z etykietą Wybrane %1$s diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 3d2c8c2f..c3be3546 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -226,6 +226,28 @@ Modo Escuro Modo Escuro Automático por Bateria Aba padrão + Escolha qual guia aparece quando o aplicativo é aberto + Principais histórias e atualizações recentes + Android Studio Tools, dicas e o catálogo completo do tutorial + Detalhes do aplicativo, versão e créditos + Pular + Escolha o seu estilo + Escolha como o aplicativo parece + Aparência brilhante e limpa + Confortável com pouca luz + Segue o tema do seu dispositivo + Ajude -nos a melhorar sua experiência + Ajude -nos a melhorar o aplicativo para você, compartilhando relatórios anônimos e estatísticas de uso. + Ativar relatórios de falhas + Ao ativar isso, você nos ajuda a identificar e corrigir bugs mais rapidamente. Não coletamos nenhuma informação pessoal. + Permitir anúncios personalizados + Permita armazenar dados de anúncios, informações do usuário e personalização para manter os anúncios relevantes. + Leia nossa Política de Privacidade + Ícone de privacidade + Você está pronto! + Sua configuração está completa. Agora você está pronto para explorar todos os recursos. + Comece + Ícone de sucesso Rótulos da barra de navegação inferior Rotulado Selecionado %1$s diff --git a/app/src/main/res/values-ro-rRO/strings.xml b/app/src/main/res/values-ro-rRO/strings.xml index 81991c7d..051f1ec1 100644 --- a/app/src/main/res/values-ro-rRO/strings.xml +++ b/app/src/main/res/values-ro-rRO/strings.xml @@ -260,6 +260,28 @@ Mod Întunecat Mod întunecat automat la baterie scăzută Filă implicită + Alegeți ce filă apare când se deschide aplicația + Povești de top și actualizări recente + Instrumente Android Studio, Sfaturi și Catalogul Tutorial complet + Detalii despre aplicație, versiune și credite + Skip + Alege -ți stilul + Alegeți cum arată aplicația + Aspect luminos, curat + Confortabil în lumină scăzută + Urmează tema dispozitivului + Ajută -ne să -ți îmbunătățim experiența + Ajută -ne să îmbunătățim aplicația pentru dvs. prin partajarea rapoartelor de accidente anonime și a statisticilor de utilizare. + Activați raportarea accidentelor + Activând acest lucru, ne ajutați să identificăm și să remediem mai repede erorile. Nu colectăm nicio informație personală. + Permiteți reclame personalizate + Permiterea stocării datelor publicitare, informațiile utilizatorilor și personalizarea pentru a menține anunțurile relevante. + Citiți Politica noastră de confidențialitate + Pictograma confidențialității + Ești cu toții setat! + Configurarea dvs. este completă. Acum sunteți gata să explorați toate caracteristicile. + Începeți + Pictograma de succes Etichete bară de navigare inferioară Etichetate Selectat %1$s diff --git a/app/src/main/res/values-ru-rRU/strings.xml b/app/src/main/res/values-ru-rRU/strings.xml index 58438280..337d2540 100644 --- a/app/src/main/res/values-ru-rRU/strings.xml +++ b/app/src/main/res/values-ru-rRU/strings.xml @@ -252,6 +252,28 @@ Темный режим Автоматический темный режим по заряду батареи Вкладка по умолчанию + Выберите, какая вкладка появляется, когда приложение откроется + Лучшие истории и последние обновления + Инструменты Android Studio, советы и полный учебный каталог + Детали приложения, версия и кредиты + Пропускать + Выберите свой стиль + Выберите, как выглядит приложение + Яркий, чистый внешний вид + Удобно при слабом освещении + Следует за темой вашего устройства + Помогите нам улучшить ваш опыт + Помогите нам сделать приложение лучше для вас, поделившись анонимными отчетами об аварии и статистике использования. + Включить отчет о сбоях + Включив это, вы помогаете нам быстрее определять и исправлять ошибки. Мы не собираем никакой личной информации. + Разрешить персонализированную рекламу + Разрешение на хранение данных рекламы, информация пользователя и персонализация, чтобы поддерживать актуальность объявлений. + Прочитайте нашу политику конфиденциальности + Значок конфиденциальности + Ты все готово! + Ваша установка завершена. Теперь вы готовы исследовать все функции. + Начните + Иконка успеха Подписи нижней панели навигации С подписями Выбрано %1$s diff --git a/app/src/main/res/values-sv-rSE/strings.xml b/app/src/main/res/values-sv-rSE/strings.xml index fabf9af2..fb259fe1 100644 --- a/app/src/main/res/values-sv-rSE/strings.xml +++ b/app/src/main/res/values-sv-rSE/strings.xml @@ -226,6 +226,28 @@ Mörkt läge Automatiskt mörkt läge vid lågt batteri Standardflik + Välj vilken flik som visas när appen öppnas + Topphistorier och senaste uppdateringar + Android Studio -verktyg, tips och hela handledningskatalogen + Appdetaljer, version och krediter + Hoppa + Välj din stil + Välj hur appen ser ut + Ljust, rent utseende + Bekvämt i svagt ljus + Följer ditt enhetstema + Hjälp oss att förbättra din upplevelse + Hjälp oss att göra appen bättre för dig genom att dela anonyma kraschrapporter och användningsstatistik. + Aktivera kraschrapportering + Genom att aktivera detta hjälper du oss att identifiera och fixa buggar snabbare. Vi samlar inte in någon personlig information. + Tillåt personliga annonser + Tillåt lagring av annonsdata, användarinformation och personalisering för att hålla annonser relevanta. + Läs vår integritetspolicy + Integritetsikon + Ni är alla redo! + Din installation är klar. Du är nu redo att utforska alla funktioner. + Komma igång + Framgångsikon Etiketter i nedre navigeringsfältet Med etikett Vald %1$s diff --git a/app/src/main/res/values-th-rTH/strings.xml b/app/src/main/res/values-th-rTH/strings.xml index cd8954de..d31472b5 100644 --- a/app/src/main/res/values-th-rTH/strings.xml +++ b/app/src/main/res/values-th-rTH/strings.xml @@ -228,6 +228,28 @@ โหมดมืด โหมดมืดอัตโนมัติตามแบตเตอรี่ แท็บเริ่มต้น + เลือกแท็บที่จะปรากฏขึ้นเมื่อแอปเปิดขึ้น + เรื่องยอดนิยมและการอัปเดตล่าสุด + เครื่องมือ Android Studio, เคล็ดลับและแคตตาล็อกการสอนเต็มรูปแบบ + รายละเอียดแอพเวอร์ชันและเครดิต + ข้าม + เลือกสไตล์ของคุณ + เลือกรูปลักษณ์ของแอป + รูปลักษณ์ที่สดใสและสะอาด + สะดวกสบายในแสงน้อย + ทำตามธีมอุปกรณ์ของคุณ + ช่วยเราปรับปรุงประสบการณ์ของคุณ + ช่วยเราทำให้แอพดีขึ้นสำหรับคุณโดยการแบ่งปันรายงานความผิดพลาดที่ไม่ระบุชื่อและสถิติการใช้งาน + เปิดใช้งานการรายงานข้อผิดพลาด + ด้วยการเปิดใช้งานสิ่งนี้คุณจะช่วยเราระบุและแก้ไขข้อบกพร่องได้เร็วขึ้น เราไม่รวบรวมข้อมูลส่วนบุคคลใด ๆ + อนุญาตให้โฆษณาส่วนบุคคล + อนุญาตให้จัดเก็บข้อมูลโฆษณาข้อมูลผู้ใช้และการปรับเปลี่ยนส่วนบุคคลเพื่อให้โฆษณาที่เกี่ยวข้อง + อ่านนโยบายความเป็นส่วนตัวของเรา + ไอคอนความเป็นส่วนตัว + คุณพร้อมแล้ว! + การตั้งค่าของคุณเสร็จสมบูรณ์ ตอนนี้คุณพร้อมที่จะสำรวจคุณสมบัติทั้งหมด + เริ่มต้นใช้งาน + ไอคอนความสำเร็จ ป้ายกำกับแถบนำทางด้านล่าง มีป้ายกำกับ ที่เลือก %1$s diff --git a/app/src/main/res/values-tr-rTR/strings.xml b/app/src/main/res/values-tr-rTR/strings.xml index b0473ce9..d05368d4 100644 --- a/app/src/main/res/values-tr-rTR/strings.xml +++ b/app/src/main/res/values-tr-rTR/strings.xml @@ -226,6 +226,28 @@ Karanlık Mod Otomatik Pil Karanlık Modu Varsayılan sekme + Uygulama açıldığında hangi sekmenin göründüğünü seçin + En iyi hikayeler ve son güncellemeler + Android stüdyo araçları, ipuçları ve tam öğretici katalog + Uygulama ayrıntıları, sürüm ve krediler + Atlamak + Stilinizi Seçin + Uygulamanın nasıl göründüğünü seçin + Parlak, temiz görünüm + Düşük ışıkta rahat + Cihaz temanızı takip eder + Deneyiminizi geliştirmemize yardımcı olun + Anonim çarpışma raporlarını ve kullanım istatistiklerini paylaşarak uygulamayı sizin için daha iyi hale getirmemize yardımcı olun. + Kaza raporlamasını etkinleştirin + Bunu etkinleştirerek, hataları daha hızlı tanımlamamıza ve düzeltmemize yardımcı olursunuz. Herhangi bir kişisel bilgi toplamıyoruz. + Kişiselleştirilmiş Reklamlara İzin Ver + Reklamları, kullanıcı bilgilerinin ve kişiselleştirmenin reklamları alakalı tutmak için saklanmasına izin verin. + Gizlilik Politikamızı Okuyun + Gizlilik simgesi + Hepiniz hazırsınız! + Kurulumunuz tamamlandı. Artık tüm özellikleri keşfetmeye hazırsınız. + Başlamak + Başarı simgesi Alt gezinme çubuğu etiketleri Etiketli Seçili %1$s diff --git a/app/src/main/res/values-uk-rUA/strings.xml b/app/src/main/res/values-uk-rUA/strings.xml index 7b77e12c..f0220946 100644 --- a/app/src/main/res/values-uk-rUA/strings.xml +++ b/app/src/main/res/values-uk-rUA/strings.xml @@ -226,6 +226,28 @@ Темний режим Автоматичний темний режим при низькому заряді Вкладка за замовчуванням + Виберіть, яка вкладка відображається, коли додаток відкривається + Найпопулярніші історії та останні оновлення + Інструменти, поради Android Studio, поради та повний каталог підручників + Деталі програми, версія та кредити + Пропустити + Виберіть свій стиль + Виберіть, як виглядає додаток + Яскравий, чистий зовнішній вигляд + Зручно при слабкому освітленні + Слідкує за темою вашого пристрою + Допоможіть нам покращити ваш досвід + Допоможіть нам покращити додаток, поділившись анонімними звітами про аварію та статистикою використання. + Увімкнути звітність про аварію + Увімкнувши це, ви допомагаєте нам швидше визначити та виправити помилки. Ми не збираємо жодної особистої інформації. + Дозволити персоналізовані оголошення + Дозволити зберігати дані реклами, інформацію про користувачів та персоналізацію, щоб підтримувати рекламу актуальними. + Прочитайте нашу Політику конфіденційності + Значок конфіденційності + Ви всі налаштовані! + Ваша установка завершена. Тепер ви готові вивчити всі функції. + Розпочати + Значок успіху Підписи нижньої навігаційної панелі З підписами Вибрані %1$s diff --git a/app/src/main/res/values-ur-rPK/strings.xml b/app/src/main/res/values-ur-rPK/strings.xml index 6a53ed62..1f538f7b 100644 --- a/app/src/main/res/values-ur-rPK/strings.xml +++ b/app/src/main/res/values-ur-rPK/strings.xml @@ -226,6 +226,28 @@ ڈارک موڈ آٹو بیٹری ڈارک موڈ پہلے سے طے شدہ ٹیب + جب ایپ کھلتی ہے تو کون سا ٹیب ظاہر ہوتا ہے اس کا انتخاب کریں + اعلی کہانیاں اور حالیہ تازہ کارییں + اینڈروئیڈ اسٹوڈیو ٹولز ، اشارے ، اور مکمل ٹیوٹوریل کیٹلاگ + ایپ کی تفصیلات ، ورژن ، اور کریڈٹ + چھوڑ دیں + اپنے انداز کا انتخاب کریں + منتخب کریں کہ ایپ کیسی دکھتی ہے + روشن ، صاف ظاہری شکل + کم روشنی میں آرام دہ + آپ کے آلے کے تھیم کی پیروی کرتا ہے + اپنے تجربے کو بہتر بنانے میں ہماری مدد کریں + گمنام کریش رپورٹس اور استعمال کے اعدادوشمار کا اشتراک کرکے آپ کے لئے ایپ کو بہتر بنانے میں ہماری مدد کریں۔ + کریش رپورٹنگ کو فعال کریں + اس کو چالو کرنے سے ، آپ کیڑے کی تیزی سے شناخت اور ٹھیک کرنے میں ہماری مدد کرتے ہیں۔ ہم کوئی ذاتی معلومات اکٹھا نہیں کرتے ہیں۔ + ذاتی نوعیت کے اشتہارات کی اجازت دیں + اشتہارات کو متعلقہ رکھنے کے لئے اشتہار کے اعداد و شمار ، صارف کی معلومات ، اور ذاتی نوعیت کو ذخیرہ کرنے کی اجازت دیں۔ + ہماری رازداری کی پالیسی پڑھیں + رازداری کا آئیکن + آپ سب تیار ہیں! + آپ کا سیٹ اپ مکمل ہے۔ اب آپ تمام خصوصیات کو دریافت کرنے کے لئے تیار ہیں۔ + شروع کریں + کامیابی کا آئیکن نیچے نیویگیشن بار کے لیبلز لیبل شدہ منتخب %1$s diff --git a/app/src/main/res/values-vi-rVN/strings.xml b/app/src/main/res/values-vi-rVN/strings.xml index d13ec20c..bce59630 100644 --- a/app/src/main/res/values-vi-rVN/strings.xml +++ b/app/src/main/res/values-vi-rVN/strings.xml @@ -226,6 +226,28 @@ Chế độ tối Chế độ tối tự động theo pin Tab mặc định + Chọn tab nào xuất hiện khi ứng dụng mở + Những câu chuyện hàng đầu và các bản cập nhật gần đây + Android Studio Tools, Mẹo và Danh mục hướng dẫn đầy đủ + Chi tiết ứng dụng, phiên bản và tín dụng + Nhảy + Chọn phong cách của bạn + Chọn cách ứng dụng trông như thế nào + Xuất hiện tươi sáng, sạch sẽ + Thoải mái trong ánh sáng yếu + Theo chủ đề thiết bị của bạn + Giúp chúng tôi cải thiện trải nghiệm của bạn + Giúp chúng tôi làm cho ứng dụng tốt hơn cho bạn bằng cách chia sẻ các báo cáo sự cố ẩn danh và thống kê sử dụng. + Cho phép báo cáo sự cố + Bằng cách cho phép điều này, bạn giúp chúng tôi xác định và sửa lỗi nhanh hơn. Chúng tôi không thu thập bất kỳ thông tin cá nhân. + Cho phép quảng cáo được cá nhân hóa + Cho phép lưu trữ dữ liệu quảng cáo, thông tin người dùng và cá nhân hóa để giữ quảng cáo có liên quan. + Đọc chính sách bảo mật của chúng tôi + Biểu tượng riêng tư + Tất cả các bạn đã thiết lập! + Thiết lập của bạn đã hoàn tất. Bây giờ bạn đã sẵn sàng để khám phá tất cả các tính năng. + Bắt đầu + Biểu tượng thành công Nhãn thanh điều hướng dưới cùng Có nhãn Đã chọn %1$s diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 97049248..0a66fbb7 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -226,6 +226,28 @@ 深色模式 自動省電深色模式 預設分頁 + 選擇應用程序打開時出現哪個選項卡 + 頂級故事和最新更新 + Android Studio工具,技巧和完整的教程目錄 + 應用詳細信息,版本和積分 + 跳過 + 選擇您的風格 + 選擇應用的外觀 + 明亮,乾淨的外觀 + 在弱光中舒適 + 遵循您的設備主題 + 幫助我們改善您的經驗 + 通過共享匿名崩潰報告和使用統計信息,幫助我們為您提供更好的應用程序。 + 啟用崩潰報告 + 通過啟用此功能,您可以幫助我們更快地識別和修復錯誤。我們沒有收集任何個人信息。 + 允許個性化廣告 + 允許存儲廣告數據,用戶信息和個性化,以使廣告相關。 + 閱讀我們的隱私政策 + 隱私圖標 + 你們都設定了! + 您的設置已完成。您現在準備探索所有功能。 + 開始 + 成功偶像 底部導覽列標籤 有標籤 已選取 %1$s From 06e43e4b2e74f4c94821722a6227d0f89bf689f6 Mon Sep 17 00:00:00 2001 From: Mihai-Cristian Condrea Date: Sat, 13 Sep 2025 12:36:12 +0300 Subject: [PATCH 05/67] Add localized translations for onboarding strings --- app/src/main/res/values-de-rDE/strings.xml | 2 ++ app/src/main/res/values-es-rGQ/strings.xml | 2 ++ app/src/main/res/values-es-rMX/strings.xml | 2 ++ app/src/main/res/values-fr-rFR/strings.xml | 2 ++ app/src/main/res/values-ja-rJP/strings.xml | 2 ++ app/src/main/res/values-ru-rRU/strings.xml | 4 ++++ app/src/main/res/values-zh-rTW/strings.xml | 4 ++++ 7 files changed, 18 insertions(+) diff --git a/app/src/main/res/values-de-rDE/strings.xml b/app/src/main/res/values-de-rDE/strings.xml index 716e18ac..2ce722f6 100644 --- a/app/src/main/res/values-de-rDE/strings.xml +++ b/app/src/main/res/values-de-rDE/strings.xml @@ -283,6 +283,7 @@ Navigationsleiste Dashboard Code-Schriftart + Wähle die Schriftart für Codeausschnitte Sprache Benachrichtigungen Benachrichtigungseinstellungen @@ -360,6 +361,7 @@ Weiter Zurück Bottom Navigation Labels + Wähle, wie Beschriftungen in der unteren Navigationsleiste erscheinen Ermöglicht der App, auf die Benachrichtigungsrichtlinie des Geräts zuzugreifen und diese zu ändern, wodurch gesteuert wird, wie und wann Benachrichtigungen dem Benutzer angezeigt werden, und benutzerdefinierte Benachrichtigungsverwaltungsfunktionen bereitgestellt werden. Ermöglicht der App, Dienste zu erstellen und zu verwenden, die im Vordergrund ausgeführt werden, wodurch sie Vorrang vor anderen Hintergrundprozessen erhalten und Leistung sowie Zuverlässigkeit verbessert werden. Anwendungssprache festlegen. diff --git a/app/src/main/res/values-es-rGQ/strings.xml b/app/src/main/res/values-es-rGQ/strings.xml index b4ad8624..716afd9b 100644 --- a/app/src/main/res/values-es-rGQ/strings.xml +++ b/app/src/main/res/values-es-rGQ/strings.xml @@ -283,6 +283,7 @@ Panel de navegación Panel de control Fuente del código + Elige la fuente usada para fragmentos de código Idioma Notificaciones Configuración de notificaciones @@ -360,6 +361,7 @@ Siguiente Atrás Etiquetas de navegación inferior + Elige cómo aparecen las etiquetas en la barra de navegación inferior Permite a la aplicación acceder y modificar la política de notificaciones del dispositivo, controlando cómo y cuándo se muestran las notificaciones al usuario y proporcionando funciones personalizadas de gestión de notificaciones. Permite a la aplicación crear y usar servicios que se ejecutan en primer plano, dándoles prioridad sobre otros procesos en segundo plano y mejorando el rendimiento y la fiabilidad. Establecer el idioma de la aplicación. diff --git a/app/src/main/res/values-es-rMX/strings.xml b/app/src/main/res/values-es-rMX/strings.xml index d461d23b..0d38de7c 100644 --- a/app/src/main/res/values-es-rMX/strings.xml +++ b/app/src/main/res/values-es-rMX/strings.xml @@ -257,6 +257,7 @@ Panel de navegación Panel de control Fuente del código + Elige la fuente usada para fragmentos de código Idioma Notificaciones Configuración de notificaciones @@ -334,6 +335,7 @@ Siguiente Atrás Etiquetas de navegación inferior + Elige cómo aparecen las etiquetas en la barra de navegación inferior Permite que la app acceda y modifique la política de notificaciones del dispositivo, controlando cómo y cuándo se muestran las notificaciones al usuario y proporcionando funciones de gestión de notificaciones personalizadas. Permite que la app cree y use servicios que se ejecutan en primer plano, dándoles prioridad sobre otros procesos en segundo plano y mejorando el rendimiento y la fiabilidad. Establecer el idioma de la aplicación. diff --git a/app/src/main/res/values-fr-rFR/strings.xml b/app/src/main/res/values-fr-rFR/strings.xml index 8315e281..5c495abf 100644 --- a/app/src/main/res/values-fr-rFR/strings.xml +++ b/app/src/main/res/values-fr-rFR/strings.xml @@ -283,6 +283,7 @@ Tiroir de navigation Tableau de bord Police du code + Choisissez la police utilisée pour les extraits de code Langue Notifications Paramètres des notifications @@ -360,6 +361,7 @@ Suivant Retour Étiquettes de navigation inférieure + Choisissez la manière dont les libellés apparaissent dans la barre de navigation inférieure Permet à l\'application d\'accéder et de modifier la politique de notification de l\'appareil, contrôlant comment et quand les notifications sont affichées à l\'utilisateur et fournissant des fonctionnalités de gestion des notifications personnalisées. Permet à l\'application de créer et d\'utiliser des services qui s\'exécutent en premier plan, leur donnant la priorité sur d\'autres processus d\'arrière-plan et améliorant les performances et la fiabilité. Définir la langue de l\'application. diff --git a/app/src/main/res/values-ja-rJP/strings.xml b/app/src/main/res/values-ja-rJP/strings.xml index caa8f30b..102a5f76 100644 --- a/app/src/main/res/values-ja-rJP/strings.xml +++ b/app/src/main/res/values-ja-rJP/strings.xml @@ -304,6 +304,7 @@ ナビゲーションドロワー ダッシュボード コードフォント + コードスニペットで使用するフォントを選択 言語 通知 通知設定 @@ -381,6 +382,7 @@ 戻る ボトムナビゲーションラベル + 下部ナビゲーションバーのラベル表示方法を選択 アプリがデバイスの通知ポリシーにアクセスして変更できるようにし、通知が表示される方法やタイミングを制御して、カスタム通知管理機能を提供します。 アプリがフォアグラウンドで実行されるサービスを作成・使用できるようにし、他のバックグラウンドプロセスより優先度を与えてパフォーマンスと信頼性を向上させます。 アプリの言語を設定します。 diff --git a/app/src/main/res/values-ru-rRU/strings.xml b/app/src/main/res/values-ru-rRU/strings.xml index 337d2540..2d13de8e 100644 --- a/app/src/main/res/values-ru-rRU/strings.xml +++ b/app/src/main/res/values-ru-rRU/strings.xml @@ -283,6 +283,7 @@ Выдвижное меню Панель управления Шрифт кода + Выберите шрифт, используемый для фрагментов кода Язык Уведомления Настройки уведомлений @@ -360,6 +361,7 @@ Следующий Назад Нижняя навигационная метка + Выберите, как отображаются подписи в нижней панели навигации Позволяет приложению получать доступ и изменять политику уведомлений устройства, контролируя, как и когда уведомления отображаются пользователю, и предоставляя настраиваемые функции управления уведомлениями. Позволяет приложению создавать и использовать службы, которые работают на переднем плане, предоставляя им приоритет над другими фоновыми процессами и улучшая производительность и надежность. Установить язык приложения. @@ -417,6 +419,8 @@ Linear Layout Compat — это класс в библиотеке поддержки Android appCompat v7. Он был добавлен для поддержки методов, которые были добавлены в более новые уровни API для старых (например, разделителей). Если вы сравните методы в LinearLayout и LinearLayoutCompat, вы увидите, что макет Compat имеет все методы LinearLayout без каких-либо ограничений по уровню API. Например, метод setShowDividers был введен на уровне API 11. Таким образом, в этом случае setShowDividers (и его параметры) следует вызывать с использованием Linear Layout Compat вместо LinearLayout, если вы ориентируетесь на платформу с уровнем API ниже 11. Your donation helps keep the app running and improve features. Thank you for your generosity. Узнайте, как использовать индикаторы выполнения в Android с этим интерактивным уроком. Этот урок включает два раздела: один для горизонтального индикатора выполнения и один для кругового индикатора выполнения. В каждом разделе вы можете увидеть, как индикатор выполнения обновляется по мере загрузки. Вы также можете просмотреть код урока, нажав на плавающую кнопку действия в нижней части экрана. + Android — мобильная операционная система, разработанная компанией Google. Впервые она была выпущена в виде бета-версии 5 ноября 2007 года, а первая коммерческая версия, Android 1.0, была запущена 23 сентября 2008 года. Android основан на модифицированной версии ядра Linux и другом программном обеспечении с открытым исходным кодом.\n\nAndroid Studio — официальная интегрированная среда разработки (IDE) для создания приложений Android. Она была объявлена компанией Google на конференции I/O 15 мая 2013 года и выпущена для общего пользования 8 декабря 2014 года. Android Studio основана на IntelliJ IDEA, популярной IDE для Java. + Android — это не только операционная система, но и платформа, предлагающая множество функций и возможностей для пользователей и разработчиков. Некоторые из функций Android включают:\n\n- Near Field Communication (NFC), которая позволяет обмениваться данными, совершать платежи и подключаться к другим устройствам по беспроводной связи.\n- Альтернативные клавиатуры, которые позволяют настраивать метод ввода с различными раскладками, языками и темами.\n- ИК-передачу, которая позволяет использовать телефон в качестве пульта дистанционного управления для телевизоров, кондиционеров и других приборов.\n- Управление без касания, которое позволяет выполнять действия на телефоне, не прикасаясь к экрану, с помощью жестов или голосовых команд.\n- Автоматизацию, которая позволяет задавать сценарии и триггеры, чтобы телефон автоматически выполнял определённые задачи.\n- Беспроводные загрузки приложений, которые позволяют устанавливать приложения с компьютера или веб-браузера без подключения телефона кабелем.\n- Смену памяти и батареи, которая даёт возможность расширить память телефона или легко заменить аккумулятор.\n- Настраиваемый домашний экран, который позволяет персонализировать внешний вид телефона с помощью виджетов, обоев, значков и лаунчеров.\n\nAndroid Studio — лучший инструмент для разработки приложений Android. Она предлагает множество функций и преимуществ для разработчиков, таких как:\n\n- Редактор кода с подсветкой синтаксиса, автодополнением, рефакторингом, отладкой, тестированием и инструментами lint.\n- Редактор макетов с интерфейсом drag-and-drop для проектирования пользовательских интерфейсов.\n- Эмулятор с быстрым запуском и поддержкой снимков для тестирования приложений на различных устройствах и конфигурациях.\n- Интеграция с Firebase с такими сервисами, как аутентификация, база данных, хранилище, аналитика, отчёты о сбоях и многое другое.\n- Система сборки на базе Gradle с управлением зависимостями,\nкастомизацией,\nоптимизацией,\nподписыванием,\nвариантами распространения и многим другим.\n\nС помощью Android Studio\nвы можете создавать удивительные приложения для устройств Android на Java,\nмощном и широко используемом языке программирования. Хронометр — это специализированный виджет во фреймворке Android, который предоставляет функциональность, аналогичную таймеру, для отображения прошедшего времени. Его можно настроить для отсчета вверх или вниз, и он обычно используется в приложениях, требующих хронометража, таких как секундомеры или таймеры обратного отсчета. Класс Chronometer является производным от класса TextView, что означает, что он наследует все свойства и методы TextView. Это позволяет разработчикам легко настраивать внешний вид хронометра, включая размер текста, цвет и шрифт. Это всплывающая панель. нажато diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 0a66fbb7..eee9ffe8 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -257,6 +257,7 @@ 側邊選單 儀表板 程式碼字型 + 選擇用於程式碼片段的字體 語言 通知 通知設定 @@ -334,6 +335,7 @@ 下一個 後退 底部導航標籤 + 選擇底部導覽列中標籤的顯示方式 允許應用程式存取和修改裝置的通知政策,控制通知向使用者顯示的方式和時間,並提供自訂的通知管理功能。 允許應用程式建立和使用在前景執行的服務,使其優先於其他背景處理程序,並提高效能和可靠性。 設定應用程式語言。 @@ -391,6 +393,8 @@ Linear Layout Compat 是 Android 支援函式庫 appCompat v7 中的一個類別。它被加入以支援在較舊的 API 等級上新增於較新 API 等級的方法(如分隔線)。如果您比較 LinearLayout 和 LinearLayoutCompat 中的方法,您會發現 Compat 版面配置擁有 Linear Layout 的所有方法,且沒有任何 API 等級限制。例如,setShowDividers 方法是在 API 等級 11 中引入的。因此,在這種情況下,如果您針對 API 等級低於 11 的平台,則應使用 Linear Layout Compat 而非 LinearLayout 來調用 setShowDividers(及其參數)。 Your donation helps keep the app running and improve features. Thank you for your generosity. 透過這個互動式課程,學習如何在 Android 中使用進度列。本課程包括兩個部分:一個用於水平進度列,另一個用於圓形進度列。在每個部分,您都可以看到隨著下載進度,進度列如何更新。您還可以透過點擊螢幕底部的浮動操作按鈕來查看本課程的程式碼。 + Android 是由 Google 開發的行動作業系統。它於 2007 年 11 月 5 日首次以測試版發布,而第一個商業版本 Android 1.0 則於 2008 年 9 月 23 日推出。Android 基於修改過的 Linux 核心和其他開放原始碼軟體。\n\nAndroid Studio 是 Android 應用程式開發的官方整合開發環境 (IDE)。Google 在 2013 年 5 月 15 日的 I/O 大會上宣布了它,並於 2014 年 12 月 8 日正式對外發布。Android Studio 基於 IntelliJ IDEA,一款受歡迎的 Java IDE。 + Android 不僅是一個作業系統,也是為使用者和開發者提供各種功能與能力的平台。Android 的一些功能包括:\n\n- 近距離通訊 (NFC),可讓你無線分享資料、進行付款並與其他裝置連線。\n- 替代鍵盤,可讓你使用不同的配置、語言和主題自訂輸入方式。\n- 紅外線傳輸,可讓你把手機當作電視、冷氣等裝置的遙控器。\n- 免觸控操作,可讓你透過手勢或語音指令,在不碰螢幕的情況下操控手機。\n- 自動化,可讓你設定常式與觸發條件,讓手機自動執行特定工作。\n- 無線下載應用程式,可讓你從電腦或瀏覽器安裝應用程式,而不需要以線材連接手機。\n- 儲存空間與電池更換,可讓你擴充手機記憶體或輕鬆更換電池。\n- 自訂主畫面,可讓你使用小工具、桌布、圖示和啟動器來個人化手機外觀。\n\nAndroid Studio 是開發 Android 應用程式的最佳工具。它為開發者提供許多功能與優勢,例如:\n\n- 具備語法突顯、程式碼補全、重構、除錯、測試與 lint 工具的程式碼編輯器。\n- 具有拖放介面的版面配置編輯器,用於設計使用者介面。\n- 支援快速啟動與快照的模擬器,用於在不同裝置和組態上測試應用程式。\n- 與 Firebase 整合,提供驗證、資料庫、儲存空間、分析、當機報告等服務。\n- 基於 Gradle 的建置系統,具備相依管理,\n自訂化,\n最佳化,\n簽署,\n發佈選項等功能。\n\n使用 Android Studio,\n你可以使用 Java 這門強大且廣泛使用的程式語言,\n為 Android 裝置建立出色的應用程式。 Chronometer 是 Android 框架中的一個專用小工具,提供類似計時器的功能來顯示經過的時間。它可以被設定為向上或向下計數,通常用於需要計時的應用程式中,例如碼錶或倒數計時器應用程式。Chronometer 類別源自 TextView 類別,這意味著它繼承了 TextView 的所有屬性和方法。這使得開發人員可以輕鬆地自訂 Chronometer 的外觀,包括其文字大小、顏色和字型。 這是一個 Snack bar! 已點擊 From f02220cd636bb6235bc7d60a1506a9a38674658f Mon Sep 17 00:00:00 2001 From: D4rK7355608 Date: Sat, 13 Sep 2025 12:38:28 +0300 Subject: [PATCH 06/67] Fix XML formatting and remove unused namespace - Remove unused `tools` namespace from `activity_shortcuts_navigation_and_searching.xml`. - Add `tools` namespace to `strings.xml` for Russian and Traditional Chinese to address lint warnings. - Format `fragment_spring_interpolator.xml` for consistency. --- app/src/main/res/anim/fragment_spring_interpolator.xml | 2 +- .../res/layout/activity_shortcuts_navigation_and_searching.xml | 1 - app/src/main/res/values-ru-rRU/strings.xml | 2 +- app/src/main/res/values-zh-rTW/strings.xml | 2 +- 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/app/src/main/res/anim/fragment_spring_interpolator.xml b/app/src/main/res/anim/fragment_spring_interpolator.xml index e6087fb8..900ad8c9 100644 --- a/app/src/main/res/anim/fragment_spring_interpolator.xml +++ b/app/src/main/res/anim/fragment_spring_interpolator.xml @@ -1,4 +1,4 @@ + android:tension="1.0" /> diff --git a/app/src/main/res/layout/activity_shortcuts_navigation_and_searching.xml b/app/src/main/res/layout/activity_shortcuts_navigation_and_searching.xml index b1f70efc..b9ffb6e4 100644 --- a/app/src/main/res/layout/activity_shortcuts_navigation_and_searching.xml +++ b/app/src/main/res/layout/activity_shortcuts_navigation_and_searching.xml @@ -2,7 +2,6 @@ diff --git a/app/src/main/res/values-ru-rRU/strings.xml b/app/src/main/res/values-ru-rRU/strings.xml index 2d13de8e..c8959e1f 100644 --- a/app/src/main/res/values-ru-rRU/strings.xml +++ b/app/src/main/res/values-ru-rRU/strings.xml @@ -1,5 +1,5 @@ - + Предпросмотр изображения Узнайте, как создавать простые приложения в Android Studio. 📱 Доступно новое обновление. diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index eee9ffe8..165b0073 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -1,5 +1,5 @@ - + 圖片檢視預覽 學習如何在 Android Studio 中製作簡單的 Java 應用程式。 📱 有可用的新更新! From 7dac6326542bf583bdf7432c2a546615cdb2ff4b Mon Sep 17 00:00:00 2001 From: Mihai-Cristian Condrea Date: Sat, 13 Sep 2025 13:01:29 +0300 Subject: [PATCH 07/67] Add localized code and navigation strings --- app/src/main/res/values-ar-rEG/strings.xml | 2 ++ app/src/main/res/values-it-rIT/strings.xml | 2 ++ app/src/main/res/values-pl-rPL/strings.xml | 4 ++++ app/src/main/res/values-pt-rBR/strings.xml | 2 ++ app/src/main/res/values-ro-rRO/strings.xml | 2 ++ 5 files changed, 12 insertions(+) diff --git a/app/src/main/res/values-ar-rEG/strings.xml b/app/src/main/res/values-ar-rEG/strings.xml index 67fe7831..c6330425 100644 --- a/app/src/main/res/values-ar-rEG/strings.xml +++ b/app/src/main/res/values-ar-rEG/strings.xml @@ -257,6 +257,7 @@ قائمة التنقل الجانبية لوحة التحكم خط الكود + اختر الخط المستخدم لمقتطفات التعليمات البرمجية اللغة الإشعارات إعدادات الإشعارات @@ -334,6 +335,7 @@ التالي خلف تسميات الملاحة السفلية + اختر كيفية ظهور التسميات في شريط التنقل السفلي يسمح للتطبيق بالوصول إلى سياسة الإشعارات بالجهاز وتعديلها، والتحكم في كيفية ووقت عرض الإشعارات للمستخدم وتوفير ميزات إدارة إشعارات مخصصة. يسمح للتطبيق بإنشاء واستخدام خدمات تعمل في الواجهة، مما يمنحها الأولوية على العمليات الأخرى في الخلفية ويحسن الأداء والموثوقية. ضبط لغة التطبيق. diff --git a/app/src/main/res/values-it-rIT/strings.xml b/app/src/main/res/values-it-rIT/strings.xml index f4289358..2993ad20 100644 --- a/app/src/main/res/values-it-rIT/strings.xml +++ b/app/src/main/res/values-it-rIT/strings.xml @@ -304,6 +304,7 @@ Menu di navigazione Dashboard Font del codice + Scegli il font utilizzato per gli snippet di codice Lingua Notifiche Impostazioni delle notifiche @@ -381,6 +382,7 @@ Prossimo Indietro Etichette di navigazione inferiore + Scegli come visualizzare le etichette nella barra di navigazione in basso Consente all\'app di accedere e modificare la politica di notifiche del dispositivo, controllando come e quando le notifiche vengono mostrate all\'utente e fornendo funzionalità di gestione personalizzata delle notifiche. Consente all\'app di creare e utilizzare servizi che vengono eseguiti in primo piano, dando loro priorità rispetto ad altri processi in background e migliorando prestazioni e affidabilità. Imposta la lingua dell\'applicazione. diff --git a/app/src/main/res/values-pl-rPL/strings.xml b/app/src/main/res/values-pl-rPL/strings.xml index 579435c3..5c8c0414 100644 --- a/app/src/main/res/values-pl-rPL/strings.xml +++ b/app/src/main/res/values-pl-rPL/strings.xml @@ -304,6 +304,7 @@ Panel nawigacyjny Pulpit Czcionka kodu + Wybierz czcionkę używaną dla fragmentów kodu Język Powiadomienia Ustawienia powiadomień @@ -381,6 +382,7 @@ Następny Z powrotem Dolne etykiety nawigacyjne + Wybierz sposób wyświetlania etykiet na dolnym pasku nawigacji Umożliwia aplikacji dostęp i modyfikowanie zasad powiadomień urządzenia, kontrolując sposób i czas wyświetlania powiadomień oraz zapewniając funkcje niestandardowego zarządzania powiadomieniami. Umożliwia aplikacji tworzenie i używanie usług działających na pierwszym planie, nadając im priorytet przed innymi procesami w tle oraz poprawiając wydajność i niezawodność. Ustaw język aplikacji. @@ -439,6 +441,8 @@ Linear Layout Compat to klasa w bibliotece wsparcia Android appCompat v7. Dodano ją, aby obsługiwać metody wprowadzone w nowszych poziomach API na starszych (takie jak dzielniki). Jeśli porównasz metody w LinearLayout i LinearLayoutCompat, zobaczysz, że układ Compat ma wszystkie metody LinearLayout bez ograniczeń poziomu API. Na przykład metoda setShowDividers została wprowadzona na poziomie API 11. Zatem w tym przypadku setShowDividers (i jej parametry) należy wywoływać za pomocą Linear Layout Compat zamiast LinearLayout, jeśli celujesz w platformę z poziomem API poniżej 11. Twoja darowizna pomaga utrzymać aplikację i ulepszać jej funkcje. Dziękujemy za Twoją hojność. Dowiedz się, jak używać pasków postępu w Androidzie dzięki tej interaktywnej lekcji. Lekcja zawiera dwie sekcje: jedną dla poziomego paska postępu i jedną dla okrągłego paska postępu. W każdej sekcji możesz zobaczyć, jak pasek postępu aktualizuje się w miarę postępu pobierania. Możesz także zobaczyć kod lekcji, klikając przycisk akcji unoszący się na dole ekranu. + Android to mobilny system operacyjny opracowany przez Google. Po raz pierwszy została wydana jako wersja beta 5 listopada 2007 r., a pierwsza wersja komercyjna, Android 1.0, została wydana 23 września 2008 r. Android jest oparty na zmodyfikowanej wersji jądra Linuksa i innym oprogramowaniu typu open source.\n\nAndroid Studio jest oficjalnym zintegrowanym środowiskiem programistycznym (IDE) dla tworzenia aplikacji na Androida. Została ona ogłoszona przez Google na konferencji I/O w dniu 15 maja 2013 r. i udostępniona do użytku publicznego w dniu 8 grudnia 2014 r. Android Studio opiera się na IntelliJ IDEA, popularnym środowisku Java IDE. + Android to nie tylko system operacyjny, ale także platforma, która oferuje szereg funkcji i możliwości dla użytkowników i programistów. Niektóre z funkcji Androida to:\n- Near Field Communication (NFC), która umożliwia bezprzewodowe udostępnianie danych, dokonywanie płatności i łączenie się z innymi urządzeniami.\n- Alternatywne klawiatury, które pozwalają dostosować metodę wprowadzania danych do różnych układów, języków i motywów.\n- Transmisja podczerwieni, która umożliwia korzystanie z telefonu jako pilota do telewizorów, klimatyzatorów i innych urządzeń.\n- Sterowanie bezdotykowe, które pozwala wykonywać czynności na telefonie bez dotykania ekranu za pomocą gestów lub poleceń głosowych.\n- Automatyzacja, która pozwala skonfigurować procedury i wyzwalacze, aby telefon automatycznie wykonywał określone zadania.\n- Pobieranie aplikacji bezprzewodowych, które pozwala zainstalować aplikacje z komputera lub przeglądarki internetowej bez podłączania telefonu za pomocą kabla.\n- Pamięć masowa i wymiana baterii, które dają możliwość rozszerzenia pamięci telefonu lub łatwej wymiany baterii.\n- Niestandardowy ekran główny, który pozwala spersonalizować wygląd telefonu za pomocą widżetów, tapet, ikon i programów uruchamiających.\n\nAndroid Studio to najlepsze narzędzie do tworzenia aplikacji na Androida. Oferuje wiele funkcji i korzyści dla programistów, takich jak:\n- Edytor kodu z podświetlaniem składni, uzupełnianiem kodu, refaktoryzacją, debugowaniem, testowaniem i narzędziami do kładzenia kłaczków.\n- Edytor układów z interfejsem typu „przeciągnij i upuść” do projektowania interfejsów użytkownika.\n- Emulator z obsługą szybkiego uruchamiania i migawek do testowania aplikacji na różnych urządzeniach i konfiguracjach.\n- Integracja Firebase z usługami takimi jak uwierzytelnianie, baza danych, pamięć masowa, analityka, raportowanie awarii i nie tylko.\n- System budowania oparty na gradle z zarządzaniem zależnościami, dostosowywaniem, optymalizacją, podpisywaniem, opcjami dystrybucji i nie tylko.\n- Dzięki Android Studio możesz tworzyć niesamowite aplikacje na urządzenia z Androidem za pomocą Javy, potężnego i powszechnie używanego języka programowania. Chronometr to specjalizowany widget w frameworku Android, który zapewnia funkcjonalność podobną do timera do wyświetlania upływającego czasu. Można go skonfigurować do liczenia w górę lub w dół i jest powszechnie używany w aplikacjach wymagających pomiaru czasu, takich jak stopery lub liczniki czasu. Klasa Chronometer pochodzi od klasy TextView, co oznacza, że dziedziczy wszystkie właściwości i metody TextView. Dzięki temu deweloperzy mogą łatwo dostosować wygląd Chronometru, w tym rozmiar tekstu, kolor i czcionkę. To jest snackbar. kliknięto diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index c3be3546..ed59b165 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -257,6 +257,7 @@ Menu lateral Painel Fonte do código + Escolha a fonte usada para trechos de código Idioma Notificações Configurações de notificação @@ -334,6 +335,7 @@ Próximo Voltar Rótulos de navegação inferior + Escolha como as etiquetas aparecem na barra de navegação inferior Permite que o aplicativo acesse e modifique a política de notificação do dispositivo, controlando como e quando as notificações são exibidas para o usuário e fornecendo recursos de gerenciamento de notificação personalizados. Permite que o aplicativo crie e use serviços que são executados em primeiro plano, dando-lhes prioridade sobre outros processos em segundo plano e melhorando o desempenho e a confiabilidade. Definir o idioma do aplicativo. diff --git a/app/src/main/res/values-ro-rRO/strings.xml b/app/src/main/res/values-ro-rRO/strings.xml index 051f1ec1..e9f1cb9a 100644 --- a/app/src/main/res/values-ro-rRO/strings.xml +++ b/app/src/main/res/values-ro-rRO/strings.xml @@ -291,6 +291,7 @@ Meniu lateral Tablou de bord Font cod + Alegeți fontul utilizat pentru fragmentele de cod Limbă Notificări Setări notificări @@ -368,6 +369,7 @@ Următorul Spate Etichete de navigație de jos + Alegeți modul în care apar etichetele în bara de navigare din partea de jos Permite aplicației să acceseze și să modifice politica de notificări a dispozitivului, controlând modul și momentul în care notificările sunt afișate utilizatorului și oferind funcții personalizate de gestionare a notificărilor. Permite aplicației să creeze și să utilizeze servicii care rulează în prim plan, acordându-le prioritate față de alte procese din fundal și îmbunătățind performanța și fiabilitatea. Setați limba aplicației. From 72d9316c3eb057e20076da2a69a9448b7f0c2096 Mon Sep 17 00:00:00 2001 From: Mihai-Cristian Condrea Date: Sat, 13 Sep 2025 13:57:37 +0300 Subject: [PATCH 08/67] Translate new preference strings --- app/src/main/res/values-bg-rBG/strings.xml | 4 ++++ app/src/main/res/values-fi-rFI/strings.xml | 7 ++++++ app/src/main/res/values-hu-rHU/strings.xml | 25 ++++++++++++++++++++++ app/src/main/res/values-in-rID/strings.xml | 25 ++++++++++++++++++++++ app/src/main/res/values-lt-rLT/strings.xml | 7 ++++++ app/src/main/res/values-sv-rSE/strings.xml | 4 ++++ app/src/main/res/values-uk-rUA/strings.xml | 25 ++++++++++++++++++++++ 7 files changed, 97 insertions(+) create mode 100644 app/src/main/res/values-fi-rFI/strings.xml create mode 100644 app/src/main/res/values-lt-rLT/strings.xml diff --git a/app/src/main/res/values-bg-rBG/strings.xml b/app/src/main/res/values-bg-rBG/strings.xml index 29132d28..96e09f7f 100644 --- a/app/src/main/res/values-bg-rBG/strings.xml +++ b/app/src/main/res/values-bg-rBG/strings.xml @@ -283,6 +283,7 @@ Навигационно чекмедже Табло Шрифт на кода + Изберете шрифта, използван за фрагменти с код Език Известия Настройки на известията @@ -360,6 +361,7 @@ След това Назад Етикети на долната навигация + Изберете как да се показват етикетите в долната навигационна лента Позволява на приложението да осъществява достъп и да променя политиката за известия на устройството, контролирайки как и кога се показват известията на потребителя и предоставяйки персонализирани функции за управление на известията. Позволява на приложението да създава и използва услуги, които се изпълняват на преден план, давайки им приоритет пред други фонови процеси и подобрявайки производителността и надеждността. Задаване на езика на приложението. @@ -417,6 +419,8 @@ LinearLayoutCompat е клас в помощната библиотека на Android appCompat v7. Той е добавен, за да поддържа методи, които са добавени в по-нови нива на API, на стари (като разделители). Ако сравните методите в LinearLayout и LinearLayoutCompat, можете да видите, че Compat оформлението има всички методи на LinearLayout без никакво ограничение на нивото на API. Например, методът setShowDividers е въведен на API ниво 11. Така че, в този случай, setShowDividers (и неговите параметри) трябва да се извиква чрез LinearLayoutCompat вместо LinearLayout, ако разработвате за платформа с API ниво под 11. Your donation helps keep the app running and improve features. Thank you for your generosity. Научете как да използвате ленти за напредък в Android с този интерактивен урок. Този урок включва два раздела: един за хоризонтална лента за напредък и един за кръгова лента за напредък. Във всеки раздел можете да видите как лентата за напредък се актуализира с напредването на изтеглянето. Можете също да видите кода за урока, като кликнете върху плаващия бутон за действие в долната част на екрана. + Android е мобилна операционна система, разработена от Google. Тя беше пусната за първи път като бета версия на 5 ноември 2007 г., а първата комерсиална версия, Android 1.0, беше представена на 23 септември 2008 г. Android се базира на модифицирана версия на ядрото на Linux и друг софтуер с отворен код.\n\nAndroid Studio е официалната интегрирана среда за разработка (IDE) за разработване на Android приложения. Тя беше обявена от Google на конференцията I/O на 15 май 2013 г. и пусната за обществено ползване на 8 декември 2014 г. Android Studio се основава на IntelliJ IDEA, популярна Java IDE. + Android не е само операционна система, но и платформа, която предлага разнообразие от функции и възможности за потребители и разработчици. Някои от функциите на Android включват:\n\n- Комуникация в близко поле (NFC), която позволява споделяне на данни, извършване на плащания и безжично свързване с други устройства.\n- Алтернативни клавиатури, които ви позволяват да персонализирате метода на въвеждане с различни разположения, езици и теми.\n- IR предаване, което ви позволява да използвате телефона си като дистанционно управление за телевизори, климатици и други уреди.\n- Безконтролно управление, което ви позволява да извършвате действия на телефона си без да докосвате екрана, използвайки жестове или гласови команди.\n- Автоматизация, която ви позволява да задавате рутини и тригери, за да изпълнява телефонът ви определени задачи автоматично.\n- Безжични изтегляния на приложения, които ви позволяват да инсталирате приложения от компютъра или уеб браузъра си без кабел.\n- Смяна на памет и батерия, която ви дава възможност да разширите паметта на телефона или лесно да смените батерията.\n- Персонализиран начален екран, който ви позволява да персонализирате външния вид на телефона си с уиджети, тапети, икони и стартери.\n\nAndroid Studio е най-добрият инструмент за разработване на Android приложения. Той предлага много функции и предимства за разработчиците като:\n\n- Редактор на код със синтактично оцветяване, допълване на кода, рефакториране, дебъгване, тестване и lint инструменти.\n- Редактор на оформления с интерфейс тип влачи-и-пусни за проектиране на потребителски интерфейси.\n- Емулатор с бързо стартиране и поддръжка на снимки за тестване на приложения на различни устройства и конфигурации.\n- Интеграция с Firebase с услуги като удостоверяване, база данни, съхранение, анализи, докладване на сривове и други.\n- Система за изграждане, базирана на Gradle, с управление на зависимости, персонализиране, оптимизация, подписване, опции за разпространение и много други.\n\nС Android Studio можете да създавате удивителни приложения за Android устройства с помощта на Java, мощен и широко използван програмен език. Хронометърът е специализиран уиджет в рамката на Android, който предоставя функционалност, подобна на таймер, за показване на изминало време. Може да бъде конфигуриран да брои нагоре или надолу и често се използва в приложения, които изискват отчитане на време, като например приложения за хронометър или таймер за обратно броене. Класът Chronometer произлиза от класа TextView, което означава, че наследява всички свойства и методи на TextView. Това позволява на разработчиците лесно да персонализират външния вид на хронометъра, включително размера на текста, цвета и шрифта му. Това е кратко известие. кликнато diff --git a/app/src/main/res/values-fi-rFI/strings.xml b/app/src/main/res/values-fi-rFI/strings.xml new file mode 100644 index 00000000..bf0fadd4 --- /dev/null +++ b/app/src/main/res/values-fi-rFI/strings.xml @@ -0,0 +1,7 @@ + + + Valitse koodikatkelmien käyttämä fontti + Valitse, miten etiketit näkyvät alavalikon palkissa + Android on Googlen kehittämä mobiilikäyttöjärjestelmä. Se julkaistiin ensimmäisenä beetaversiona 5. marraskuuta 2007 ja ensimmäinen kaupallinen versio, Android 1.0, julkaistiin 23. syyskuuta 2008. Android perustuu muokattuun Linux-ytimen ja muun avoimen lähdekoodin ohjelmiston versioon.\n\nAndroid Studio on virallinen integroitu kehitysympäristö (IDE) Android-sovelluskehitykseen. Google ilmoitti siitä I/O-konferenssissaan 15. toukokuuta 2013 ja julkaisi sen yleiseen käyttöön 8. joulukuuta 2014. Android Studio perustuu IntelliJ IDEAan, suosittuun Java IDEen. + Android ei ole pelkästään käyttöjärjestelmä vaan myös alusta, joka tarjoaa monenlaisia ominaisuuksia ja mahdollisuuksia käyttäjille ja kehittäjille. Joitakin Androidin ominaisuuksia ovat:\n\n- Lähikenttäviestintä (NFC), jonka avulla voit jakaa dataa, maksaa ja yhdistää laitteita langattomasti.\n- Vaihtoehtoiset näppäimistöt, joiden avulla voit mukauttaa syöttötavan erilaisilla asetteluilla, kielillä ja teemoilla.\n- IR-lähetin, jonka avulla voit käyttää puhelintasi kaukosäätimenä televisioille, ilmastointilaitteille ja muille laitteille.\n- Kosketukseton ohjaus, jonka avulla voit suorittaa toimintoja ilman kosketusta näyttöön eleillä tai äänikomennoilla.\n- Automaatio, jonka avulla voit määrittää rutiineja ja laukaisimia, jotta puhelimesi suorittaa tehtäviä automaattisesti.\n- Langattomat sovellusten lataukset, joiden avulla voit asentaa sovelluksia tietokoneeltasi tai verkkoselaimestasi ilman kaapelia.\n- Tallennus- ja akkuvaihto, joka antaa mahdollisuuden laajentaa puhelimen muistia tai vaihtaa akun helposti.\n- Mukautettu aloitusnäyttö, jonka avulla voit personoida puhelimesi ulkoasun widgeteillä, taustakuvilla, kuvakkeilla ja käynnistysohjelmilla.\n\nAndroid Studio on paras työkalu Android-sovellusten kehittämiseen. Se tarjoaa monia ominaisuuksia ja etuja kehittäjille, kuten:\n\n- Koodieditori, jossa on syntaksin korostus, koodin täydennys, refaktorointi, virheenjäljitys, testaus ja lint-työkalut.\n- Asettelueditori vedä ja pudota -käyttöliittymällä käyttöliittymien suunnitteluun.\n- Emulaattori nopealla käynnistyksellä ja tilannevedolla sovellusten testaamiseen eri laitteilla ja kokoonpanoilla.\n- Firebase-integraatio palveluilla kuten autentikointi, tietokanta, tallennus, analytiikka, kaatumisraportointi ja paljon muuta.\n- Gradle-pohjainen rakennusjärjestelmä, jossa on riippuvuuksien hallinta, mukauttaminen, optimointi, allekirjoitus, jakeluvaihtoehdot ja paljon muuta.\n\nAndroid Studion avulla voit luoda mahtavia sovelluksia Android-laitteille käyttäen Javaa, tehokasta ja laajasti käytettyä ohjelmointikieltä. + diff --git a/app/src/main/res/values-hu-rHU/strings.xml b/app/src/main/res/values-hu-rHU/strings.xml index 0e644507..a2d8075d 100644 --- a/app/src/main/res/values-hu-rHU/strings.xml +++ b/app/src/main/res/values-hu-rHU/strings.xml @@ -304,6 +304,7 @@ Navigációs fiók Irányítópult Kódbetűtípus + Válassza ki a kódrészletekhez használt betűtípust Nyelv Értesítések Értesítési beállítások @@ -381,6 +382,7 @@ Következő Vissza Alsó navigációs címkék + Válassza ki, hogyan jelenjenek meg a címkék az alsó navigációs sávban Lehetővé teszi az alkalmazás számára, hogy hozzáférjen és módosítsa az eszköz értesítési szabályzatát, szabályozva, hogy mikor és hogyan jelenjenek meg az értesítések, valamint egyéni értesítéskezelési funkciókat nyújtson. Lehetővé teszi az alkalmazás számára, hogy előtérben futó szolgáltatásokat hozzon létre és használjon, prioritást adva nekik más háttérfolyamatokkal szemben, javítva a teljesítményt és a megbízhatóságot. Alkalmazás nyelvének beállítása. @@ -439,6 +441,29 @@ A Linear Layout Compat egy osztály az Android appCompat v7 támogatói könyvtárban. Azért adták hozzá, hogy a régebbi API-szinteken is támogassa az újabb API-szinteken bevezetett metódusokat (például az elválasztókat). Ha összehasonlítod a LinearLayout és a LinearLayoutCompat metódusait, láthatod, hogy a Compat elrendezés rendelkezik a LinearLayout összes metódusával API-szint korlátozás nélkül. Például a setShowDividers metódust az API 11-es szintjén vezették be. Ezért ebben az esetben a setShowDividers (és paraméterei) meghívásához a Linear Layout Compatet kell használni a LinearLayout helyett, ha 11 alatti API-szintű platformot célozol. Adományod segít az alkalmazás működtetésében és a funkciók fejlesztésében. Köszönjük a nagylelkűségedet. Ismerd meg, hogyan használhatsz folyamatjelző sávokat Androidon ezzel az interaktív leckével. A lecke két részt tartalmaz: egyet a vízszintes folyamatjelző sávhoz és egyet a kör alakú folyamatjelző sávhoz. Mindegyik részben láthatod, hogyan frissül a folyamatjelző sáv, ahogy a letöltés halad. A lecke kódját is megtekintheted a képernyő alján található lebegő műveletgombra kattintva. + Az Android egy mobil operációs rendszer, amelyet a Google fejlesztett. 2007. november 5-én jelent meg elsőként béta verzióként, az első kereskedelmi verzió, az Android 1.0 pedig 2008. szeptember 23-án indult. Az Android a Linux kernel módosított verzióján és más nyílt forráskódú szoftveren alapul. + +Az Android Studio a hivatalos integrált fejlesztőkörnyezet (IDE) az Android alkalmazások fejlesztéséhez. A Google a 2013. május 15-i I/O konferenciáján jelentette be, és 2014. december 8-án adta ki nyilvános használatra. Az Android Studio az IntelliJ IDEA-n alapul, egy népszerű Java IDE-n. + Az Android nemcsak egy operációs rendszer, hanem egy platform is, amely számos funkciót és lehetőséget kínál a felhasználóknak és a fejlesztőknek. Az Android néhány funkciója: + +- Near Field Communication (NFC), amely lehetővé teszi az adatok megosztását, a fizetéseket és más eszközökhöz való vezeték nélküli csatlakozást. +- Alternatív billentyűzetek, amelyekkel testre szabhatja a bevitel módját különböző elrendezésekkel, nyelvekkel és témákkal. +- IR-adás, amely lehetővé teszi, hogy telefonját távirányítóként használja TV-khez, légkondicionálókhoz és más készülékekhez. +- Érintés nélküli vezérlés, amely lehetővé teszi, hogy érintés nélkül hajtson végre műveleteket a telefonon gesztusokkal vagy hangparancsokkal. +- Automatizálás, amellyel beállíthat rutinszerű feladatokat és triggereket, hogy telefonja automatikusan végrehajtson bizonyos műveleteket. +- Vezeték nélküli alkalmazásletöltések, amelyek lehetővé teszik, hogy alkalmazásokat telepítsen számítógépéről vagy webböngészőjéből anélkül, hogy kábellel csatlakoztatná telefonját. +- Tárhely- és akkumulátorcsere, amely lehetőséget ad a telefon memóriájának bővítésére vagy az akkumulátor egyszerű cseréjére. +- Testreszabható kezdőképernyő, amellyel személyre szabhatja telefonja megjelenését widgetekkel, háttérképekkel, ikonokkal és indítókkal. + +Az Android Studio a legjobb eszköz az Android alkalmazások fejlesztéséhez. Számos funkciót és előnyt kínál a fejlesztők számára, például: + +- Kódszerkesztő szintaxis kiemeléssel, kódkiegészítéssel, refaktorálással, hibakereséssel, teszteléssel és lint eszközökkel. +- Elrendezésszerkesztő húzd-és-vedd felülettel a felhasználói felületek tervezéséhez. +- Emulátor gyors indítással és pillanatkép-támogatással az alkalmazások teszteléséhez különböző eszközökön és konfigurációkban. +- Firebase integráció olyan szolgáltatásokkal, mint hitelesítés, adatbázis, tárhely, analitika, összeomlásjelentés és még sok más. +- Gradle-alapú build rendszer függőségkezeléssel, testreszabással, optimalizálással, aláírással, terjesztési lehetőségekkel és egyebekkel. + +Az Android Studio segítségével csodálatos alkalmazásokat hozhat létre Android eszközökre Java segítségével, egy erőteljes és széles körben használt programozási nyelvvel. A Chronometer egy speciális widget az Android keretrendszerben, amely időzítő-szerű funkcionalitást biztosít az eltelt idő megjelenítéséhez. Beállítható felfelé vagy lefelé számolásra, és gyakran használják olyan alkalmazásokban, amelyek időmérést igényelnek, például stopper- vagy visszaszámláló alkalmazások. A Chronometer osztály a TextView osztályból származik, ami azt jelenti, hogy örökli a TextView összes tulajdonságát és metódusát. Ez lehetővé teszi a fejlesztők számára, hogy könnyen testre szabják a Chronometer megjelenését, beleértve a szöveg méretét, színét és betűtípusát. Ez egy snackbar. kattintva diff --git a/app/src/main/res/values-in-rID/strings.xml b/app/src/main/res/values-in-rID/strings.xml index af5cc87c..533c105d 100644 --- a/app/src/main/res/values-in-rID/strings.xml +++ b/app/src/main/res/values-in-rID/strings.xml @@ -283,6 +283,7 @@ Laci Navigasi Dasbor Font kode + Pilih font yang digunakan untuk cuplikan kode Bahasa Notifikasi Pengaturan notifikasi @@ -360,6 +361,7 @@ Berikutnya Kembali Label Navigasi Bawah + Pilih bagaimana label muncul di bilah navigasi bawah Memungkinkan aplikasi untuk mengakses dan memodifikasi kebijakan notifikasi perangkat, mengontrol bagaimana dan kapan notifikasi ditampilkan kepada pengguna dan menyediakan fitur manajemen notifikasi kustom. Memungkinkan aplikasi untuk membuat dan menggunakan layanan yang berjalan di latar depan, memberi mereka prioritas di atas proses latar belakang lainnya dan meningkatkan kinerja serta keandalan. Atur bahasa aplikasi. @@ -417,6 +419,29 @@ Linear Layout Compat adalah kelas dalam pustaka dukungan Android appCompat v7. Ini ditambahkan untuk mendukung metode yang ditambahkan di level API yang lebih baru pada yang lama (seperti pembagi). Jika Anda membandingkan metode di LinearLayout dan LinearLayoutCompat, Anda dapat melihat bahwa tata letak Compat memiliki semua metode dari Tata Letak Linear tanpa batasan level API. Misalnya, metode setShowDividers diperkenalkan pada level API 11. Jadi, dalam kasus ini, setShowDividers (dan parameternya) harus dipanggil menggunakan Linear Layout Compat alih-alih LinearLayout jika Anda menargetkan platform dengan level API di bawah 11. Your donation helps keep the app running and improve features. Thank you for your generosity. Pelajari cara menggunakan progress bar di Android dengan pelajaran interaktif ini. Pelajaran ini mencakup dua bagian: satu untuk progress bar horizontal dan satu untuk progress bar melingkar. Di setiap bagian, Anda dapat melihat bagaimana progress bar diperbarui saat unduhan berlangsung. Anda juga dapat melihat kode untuk pelajaran dengan mengeklik tombol aksi mengambang di bagian bawah layar. + Android adalah sistem operasi seluler yang dikembangkan oleh Google. Versi beta pertama dirilis pada 5 November 2007 dan versi komersial pertama, Android 1.0, diluncurkan pada 23 September 2008. Android didasarkan pada versi kernel Linux yang dimodifikasi dan perangkat lunak sumber terbuka lainnya. + +Android Studio adalah lingkungan pengembangan terintegrasi (IDE) resmi untuk pengembangan aplikasi Android. Ini diumumkan oleh Google pada konferensi I/O pada 15 Mei 2013 dan dirilis untuk penggunaan publik pada 8 Desember 2014. Android Studio didasarkan pada IntelliJ IDEA, IDE Java yang populer. + Android bukan hanya sistem operasi, tetapi juga platform yang menawarkan berbagai fitur dan kemampuan bagi pengguna dan pengembang. Beberapa fitur Android meliputi: + +- Near Field Communication (NFC) yang memungkinkan Anda berbagi data, melakukan pembayaran, dan terhubung dengan perangkat lain secara nirkabel. +- Papan ketik alternatif yang memungkinkan Anda menyesuaikan metode input dengan tata letak, bahasa, dan tema yang berbeda. +- Transmisi IR yang memungkinkan Anda menggunakan ponsel sebagai remote control untuk TV, AC, dan perangkat lainnya. +- Kontrol tanpa sentuhan yang memungkinkan Anda melakukan tindakan di ponsel tanpa menyentuh layar dengan menggunakan gerakan atau perintah suara. +- Otomatisasi yang memungkinkan Anda mengatur rutinitas dan pemicu agar ponsel melakukan tugas tertentu secara otomatis. +- Unduhan aplikasi nirkabel yang memungkinkan Anda memasang aplikasi dari komputer atau browser web tanpa menghubungkan ponsel dengan kabel. +- Penggantian penyimpanan dan baterai yang memberi Anda opsi untuk memperluas memori ponsel atau mengganti baterainya dengan mudah. +- Layar beranda kustom yang memungkinkan Anda mempersonalisasi tampilan ponsel dengan widget, wallpaper, ikon, dan peluncur. + +Android Studio adalah alat terbaik untuk mengembangkan aplikasi Android. Ini menawarkan banyak fitur dan manfaat bagi pengembang seperti: + +- Editor kode dengan penyorotan sintaks, pelengkapan kode, refactoring, debugging, pengujian, dan alat lint. +- Editor tata letak dengan antarmuka seret dan lepas untuk merancang antarmuka pengguna. +- Emulator dengan boot cepat dan dukungan snapshot untuk menguji aplikasi pada berbagai perangkat dan konfigurasi. +- Integrasi Firebase dengan layanan seperti autentikasi, database, penyimpanan, analitik, pelaporan kerusakan, dan lainnya. +- Sistem build berbasis Gradle dengan manajemen dependensi, kustomisasi, optimasi, penandatanganan, opsi distribusi, dan banyak lagi. + +Dengan Android Studio, Anda dapat membuat aplikasi luar biasa untuk perangkat Android menggunakan Java, bahasa pemrograman yang kuat dan banyak digunakan. Kronometer adalah widget khusus dalam kerangka kerja Android yang menyediakan fungsionalitas seperti timer untuk menampilkan waktu yang berlalu. Ini dapat dikonfigurasi untuk menghitung naik atau turun, dan umumnya digunakan dalam aplikasi yang memerlukan pencatatan waktu, seperti aplikasi stopwatch atau timer hitung mundur. Kelas Kronometer berasal dari kelas TextView, yang berarti mewarisi semua properti dan metode dari TextView. Ini memungkinkan pengembang untuk dengan mudah menyesuaikan tampilan Kronometer, termasuk ukuran teks, warna, dan fontnya. Ini adalah snack bar. diklik diff --git a/app/src/main/res/values-lt-rLT/strings.xml b/app/src/main/res/values-lt-rLT/strings.xml new file mode 100644 index 00000000..8027f36b --- /dev/null +++ b/app/src/main/res/values-lt-rLT/strings.xml @@ -0,0 +1,7 @@ + + + Pasirinkite šriftą, naudojamą kodo fragmentams + Pasirinkite, kaip apačios naršymo juostoje rodomos etiketės + Android yra Google sukurta mobili operacinė sistema. Ji pirmą kartą buvo išleista kaip beta versija 2007 m. lapkričio 5 d., o pirmoji komercinė versija, Android 1.0, pasirodė 2008 m. rugsėjo 23 d. Android yra pagrįsta modifikuota Linux branduolio ir kito atvirojo kodo programinės įrangos versija.\n\nAndroid Studio yra oficiali integruota kūrimo aplinka (IDE) Android programėlių kūrimui. Google ją pristatė I/O konferencijoje 2013 m. gegužės 15 d., o viešam naudojimui išleido 2014 m. gruodžio 8 d. Android Studio yra paremta IntelliJ IDEA, populiaria Java IDE. + Android yra ne tik operacinė sistema, bet ir platforma, siūlanti daugybę funkcijų ir galimybių naudotojams ir kūrėjams. Kai kurios Android funkcijos:\n\n- Near Field Communication (NFC), leidžianti dalytis duomenimis, atlikti mokėjimus ir belaidžiu būdu jungtis su kitais įrenginiais.\n- Alternatyvios klaviatūros, leidžiančios pritaikyti įvedimo metodą skirtingais išdėstymais, kalbomis ir temomis.\n- IR perdavimas, leidžiantis naudoti telefoną kaip nuotolinio valdymo pultą televizoriams, oro kondicionieriams ir kitiems prietaisams.\n- Valdymas neliečiant, leidžiantis atlikti veiksmus telefone neliečiant ekrano gestais ar balso komandomis.\n- Automatizavimas, leidžiantis nustatyti rutiną ir trigerius, kad telefonas automatiškai atliktų tam tikras užduotis.\n- Belaidžiai programų atsisiuntimai, leidžiantys įdiegti programas iš kompiuterio ar žiniatinklio naršyklės neprijungus telefono kabeliu.\n- Atminties ir baterijos keitimas, suteikiantis galimybę lengvai praplėsti telefono atmintį arba pakeisti bateriją.\n- Pritaikytas pagrindinis ekranas, leidžiantis suasmeninti telefono išvaizdą valdikliais, ekrano užsklandomis, piktogramomis ir paleidikliais.\n\nAndroid Studio yra geriausias įrankis Android programoms kurti. Jis siūlo daug funkcijų ir pranašumų kūrėjams, tokių kaip:\n\n- Kodo redaktorius su sintaksės paryškinimu, kodo užbaigimu, refaktorizacija, derinimu, testavimu ir lint įrankiais.\n- Išdėstymo redaktorius su „vilk ir paleisk“ sąsaja vartotojo sąsajoms kurti.\n- Emuliatorius su greitu paleidimu ir momentinių kopijų palaikymu programoms testuoti įvairiuose įrenginiuose ir konfigūracijose.\n- Firebase integracija su paslaugomis, tokiomis kaip autentifikavimas, duomenų bazė, saugykla, analizė, avarijų pranešimai ir kt.\n- Gradle pagrįsta kūrimo sistema su priklausomybių valdymu, pritaikymu, optimizavimu, pasirašymu, platinimo galimybėmis ir daugiau.\n\nNaudodami Android Studio galite kurti nuostabias programas Android įrenginiams naudodami Java, galingą ir plačiai naudojamą programavimo kalbą. + diff --git a/app/src/main/res/values-sv-rSE/strings.xml b/app/src/main/res/values-sv-rSE/strings.xml index fb259fe1..222d1e25 100644 --- a/app/src/main/res/values-sv-rSE/strings.xml +++ b/app/src/main/res/values-sv-rSE/strings.xml @@ -257,6 +257,7 @@ Navigeringsmeny Instrumentpanel Teckensnitt för kod + Välj teckensnitt för kodavsnitt Språk Notiser Notisinställningar @@ -334,6 +335,7 @@ Nästa Tillbaka Navigeringsetiketter + Välj hur etiketter visas i den nedre navigeringsfältet Tillåter appen att komma åt och ändra enhetens notispolicy, styra hur och när notiser visas för användaren och tillhandahålla anpassade funktioner för notishantering. Tillåter appen att skapa och använda tjänster som körs i förgrunden, vilket ger dem prioritet över andra bakgrundsprocesser och förbättrar prestanda och tillförlitlighet. Ställ in applikationsspråk. @@ -391,6 +393,8 @@ LinearLayoutCompat är en klass i Android-stöd-biblioteket appCompat v7. Den lades till för att stödja metoder som lades till i nyare API-nivåer på gamla (som avdelare). Om du jämför metoderna i LinearLayout och LinearLayoutCompat kan du se att Compat-layouten har alla metoder från LinearLayout utan någon API-nivåbegränsning. Till exempel introducerades metoden setShowDividers på API-nivå 11. Så i detta fall bör setShowDividers (och dess parametrar) anropas med LinearLayoutCompat istället för LinearLayout om du riktar dig mot en plattform med API-nivå under 11. Your donation helps keep the app running and improve features. Thank you for your generosity. Lär dig hur du använder förloppsindikatorer i Android med denna interaktiva lektion. Lektionen innehåller två avsnitt: ett för en horisontell förloppsindikator och ett för en cirkulär förloppsindikator. I varje avsnitt kan du se hur förloppsindikatorn uppdateras när en nedladdning fortskrider. Du kan också se koden för lektionen genom att klicka på den flytande åtgärdsknappen längst ner på skärmen. + Android är ett mobilt operativsystem utvecklat av Google. Det släpptes först som en betaversion den 5 november 2007 och den första kommersiella versionen, Android 1.0, lanserades den 23 september 2008. Android är baserat på en modifierad version av Linux-kärnan och annan öppen källkod.\n\nAndroid Studio är den officiella integrerade utvecklingsmiljön (IDE) för Android-apputveckling. Den tillkännagavs av Google på I/O-konferensen den 15 maj 2013 och släpptes för allmänt bruk den 8 december 2014. Android Studio är baserad på IntelliJ IDEA, en populär Java-IDE. + Android är inte bara ett operativsystem utan också en plattform som erbjuder en rad funktioner och möjligheter för användare och utvecklare. Några av Androids funktioner inkluderar:\n\n- Near Field Communication (NFC), som låter dig dela data, göra betalningar och ansluta till andra enheter trådlöst.\n- Alternativa tangentbord, som låter dig anpassa inmatningen med olika layouter, språk och teman.\n- IR-sändning, som gör att du kan använda telefonen som fjärrkontroll för TV-apparater, luftkonditioneringar och andra apparater.\n- Beröringsfri styrning, som låter dig utföra åtgärder på telefonen utan att röra skärmen genom gester eller röstkommandon.\n- Automatisering, som gör att du kan ställa in rutiner och utlösare så att telefonen automatiskt utför vissa uppgifter.\n- Trådlösa appnedladdningar, som låter dig installera appar från datorn eller webbläsaren utan att ansluta telefonen med kabel.\n- Lagrings- och batteribyte, som ger dig möjlighet att utöka telefonens minne eller enkelt byta batteri.\n- Anpassad startskärm, som låter dig personifiera telefonens utseende med widgets, bakgrunder, ikoner och launchers.\n\nAndroid Studio är det bästa verktyget för att utveckla Android-appar. Det erbjuder många funktioner och fördelar för utvecklare såsom:\n\n- Kodredigerare med syntaxmarkering, kodkomplettering, refaktorering, felsökning, testning och lint-verktyg.\n- Layoutediterare med dra-och-släpp-gränssnitt för att designa användargränssnitt.\n- Emulator med snabbstart och stöd för ögonblicksbilder för att testa appar på olika enheter och konfigurationer.\n- Firebase-integration med tjänster som autentisering, databas, lagring, analys, kraschrapportering och mer.\n- Gradle-baserat byggsystem med beroendehantering, anpassning, optimering, signering, distributionsalternativ och mer.\n\nMed Android Studio kan du skapa fantastiska appar för Android-enheter med Java, ett kraftfullt och allmänt använt programmeringsspråk. En kronometer är en specialiserad widget i Android-ramverket som tillhandahåller en timer-liknande funktionalitet för att visa förfluten tid. Den kan konfigureras för att räkna upp eller ner, och används vanligtvis i applikationer som kräver tidtagning, såsom stoppur eller nedräkningstimer-appar. Chronometer-klassen är härledd från TextView-klassen, vilket innebär att den ärver alla egenskaper och metoder från en TextView. Detta gör det möjligt för utvecklare att enkelt anpassa utseendet på kronometern, inklusive dess textstorlek, färg och teckensnitt. Detta är en snackbar. klickad diff --git a/app/src/main/res/values-uk-rUA/strings.xml b/app/src/main/res/values-uk-rUA/strings.xml index f0220946..cc9cdf56 100644 --- a/app/src/main/res/values-uk-rUA/strings.xml +++ b/app/src/main/res/values-uk-rUA/strings.xml @@ -257,6 +257,7 @@ Бічне меню Панель Шрифт коду + Виберіть шрифт, який використовується для фрагментів коду Мова Сповіщення Налаштування сповіщень @@ -334,6 +335,7 @@ Наступний Спинка Нижні навігаційні етикетки + Виберіть, як відображатимуться мітки в нижній панелі навігації Дозволяє додатку отримувати доступ до політики сповіщень пристрою та змінювати її, контролюючи, як і коли сповіщення відображаються користувачеві, та надаючи власні функції керування сповіщеннями. Дозволяє додатку створювати та використовувати служби, що працюють на передньому плані, надаючи їм пріоритет над іншими фоновими процесами та покращуючи продуктивність і надійність. Встановити мову програми. @@ -391,6 +393,29 @@ LinearLayoutCompat — це клас у бібліотеці підтримки Android appCompat v7. Він був доданий для підтримки методів, які були додані в нових рівнях API, на старих (наприклад, роздільники). Якщо ви порівняєте методи в LinearLayout та LinearLayoutCompat, ви побачите, що макет Compat має всі методи лінійного макета без будь-яких обмежень рівня API. Наприклад, метод setShowDividers був введений на рівні API 11. Отже, в цьому випадку setShowDividers (та його параметри) слід викликати за допомогою LinearLayoutCompat замість LinearLayout, якщо ви націлюєтесь на платформу з рівнем API нижче 11. Your donation helps keep the app running and improve features. Thank you for your generosity. Дізнайтеся, як використовувати індикатори прогресу в Android за допомогою цього інтерактивного уроку. Цей урок містить два розділи: один для горизонтального індикатора прогресу та один для кругового. У кожному розділі ви можете побачити, як індикатор прогресу оновлюється в міру завантаження. Ви також можете переглянути код уроку, натиснувши на плаваючу кнопку дії внизу екрана. + Android — це мобільна операційна система, розроблена компанією Google. Вперше вона була випущена як бета-версія 5 листопада 2007 року, а перша комерційна версія, Android 1.0, була запущена 23 вересня 2008 року. Android базується на модифікованій версії ядра Linux та іншому програмному забезпеченні з відкритим кодом. + +Android Studio — офіційне інтегроване середовище розробки (IDE) для створення Android-додатків. Воно було анонсоване Google на конференції I/O 15 травня 2013 року та випущене для загального користування 8 грудня 2014 року. Android Studio побудоване на IntelliJ IDEA, популярному Java IDE. + Android — це не лише операційна система, але й платформа, яка пропонує широкий спектр можливостей і функцій для користувачів і розробників. Деякі з функцій Android включають: + +- Бездротовий зв’язок ближнього поля (NFC), який дозволяє обмінюватися даними, здійснювати платежі та підключатися до інших пристроїв без дротів. +- Альтернативні клавіатури, що дозволяють налаштовувати метод введення з різними розкладками, мовами та темами. +- IR-передавач, що дозволяє використовувати телефон як пульт дистанційного керування для телевізорів, кондиціонерів та інших пристроїв. +- Керування без дотику, що дозволяє виконувати дії на телефоні без торкання екрана, використовуючи жести або голосові команди. +- Автоматизацію, яка дозволяє налаштовувати сценарії та тригери, щоб телефон автоматично виконував певні завдання. +- Бездротові завантаження додатків, що дозволяють встановлювати програми з комп’ютера або веб-браузера без підключення телефону кабелем. +- Заміна сховища та батареї, що дає змогу легко розширити пам’ять телефону або замінити батарею. +- Кастомізований домашній екран, що дозволяє персоналізувати зовнішній вигляд телефону за допомогою віджетів, шпалер, іконок та лаунчерів. + +Android Studio — найкращий інструмент для розробки Android-додатків. Він пропонує багато можливостей і переваг для розробників, таких як: + +- Редактор коду з підсвічуванням синтаксису, автодоповненням коду, рефакторингом, налагодженням, тестуванням і lint-інструментами. +- Редактор макетів із інтерфейсом перетягування для проектування користувацьких інтерфейсів. +- Емулятор із швидким запуском і підтримкою знімків для тестування програм на різних пристроях і конфігураціях. +- Інтеграція з Firebase з такими сервісами, як автентифікація, база даних, сховище, аналітика, звітність про збої тощо. +- Система збірки на основі Gradle з керуванням залежностями, налаштуванням, оптимізацією, підписом, варіантами розповсюдження та іншим. + +З Android Studio ви можете створювати неймовірні додатки для Android-пристроїв, використовуючи Java — потужну та широко використовувану мову програмування. Хронометр — це спеціалізований віджет у фреймворку Android, який надає функціональність, подібну до таймера, для відображення минулого часу. Його можна налаштувати на відлік часу вгору або вниз, і він зазвичай використовується в додатках, які вимагають обліку часу, таких як секундоміри або таймери зворотного відліку. Клас Chronometer походить від класу TextView, що означає, що він успадковує всі властивості та методи TextView. Це дозволяє розробникам легко налаштовувати зовнішній вигляд хронометра, включаючи розмір тексту, колір та шрифт. Це snackbar. натиснуто From ce32cf52184251c11c476bb171c4d76af752ca10 Mon Sep 17 00:00:00 2001 From: D4rK7355608 Date: Sat, 13 Sep 2025 14:12:55 +0300 Subject: [PATCH 09/67] Add tools namespace to localized strings.xml files --- app/src/main/res/values-bg-rBG/strings.xml | 2 +- app/src/main/res/values-hu-rHU/strings.xml | 2 +- app/src/main/res/values-in-rID/strings.xml | 2 +- app/src/main/res/values-sv-rSE/strings.xml | 2 +- app/src/main/res/values-uk-rUA/strings.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/src/main/res/values-bg-rBG/strings.xml b/app/src/main/res/values-bg-rBG/strings.xml index 96e09f7f..5b47108b 100644 --- a/app/src/main/res/values-bg-rBG/strings.xml +++ b/app/src/main/res/values-bg-rBG/strings.xml @@ -1,5 +1,5 @@ - + Преглед на изображението Научете как да създавате прости Java приложения в Android Studio. 📱 Налична е нова актуализация. diff --git a/app/src/main/res/values-hu-rHU/strings.xml b/app/src/main/res/values-hu-rHU/strings.xml index a2d8075d..bbb6d1c3 100644 --- a/app/src/main/res/values-hu-rHU/strings.xml +++ b/app/src/main/res/values-hu-rHU/strings.xml @@ -1,5 +1,5 @@ - + Képnézet előnézete Tanuld meg, hogyan készíts egyszerű Java alkalmazásokat az Android Studioban. 📱 Új frissítés érhető el. diff --git a/app/src/main/res/values-in-rID/strings.xml b/app/src/main/res/values-in-rID/strings.xml index 533c105d..4886d3a2 100644 --- a/app/src/main/res/values-in-rID/strings.xml +++ b/app/src/main/res/values-in-rID/strings.xml @@ -1,5 +1,5 @@ - + Pratinjau tampilan gambar Pelajari cara membuat aplikasi sederhana di Android Studio. 📱 Pembaruan baru tersedia. diff --git a/app/src/main/res/values-sv-rSE/strings.xml b/app/src/main/res/values-sv-rSE/strings.xml index 222d1e25..e907f28f 100644 --- a/app/src/main/res/values-sv-rSE/strings.xml +++ b/app/src/main/res/values-sv-rSE/strings.xml @@ -1,5 +1,5 @@ - + Förhandsgranskning av bildvy Lär dig hur man skapar enkla Java-appar i Android Studio. 📱 En ny uppdatering finns tillgänglig. diff --git a/app/src/main/res/values-uk-rUA/strings.xml b/app/src/main/res/values-uk-rUA/strings.xml index cc9cdf56..e78bd4d6 100644 --- a/app/src/main/res/values-uk-rUA/strings.xml +++ b/app/src/main/res/values-uk-rUA/strings.xml @@ -1,5 +1,5 @@ - + Попередній перегляд зображення Дізнайтеся, як створювати прості програми на Java в Android Studio. 📱 Доступне нове оновлення. From 3a2bc5714899e41f1f85c49b9e09068998372f5e Mon Sep 17 00:00:00 2001 From: Mihai-Cristian Condrea Date: Sat, 13 Sep 2025 14:17:18 +0300 Subject: [PATCH 10/67] Fix data binding ID collisions --- app/src/main/res/layout/fragment_onboarding_selection.xml | 8 ++++---- app/src/main/res/layout/item_onboarding_option.xml | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/src/main/res/layout/fragment_onboarding_selection.xml b/app/src/main/res/layout/fragment_onboarding_selection.xml index ab187775..24a1741b 100644 --- a/app/src/main/res/layout/fragment_onboarding_selection.xml +++ b/app/src/main/res/layout/fragment_onboarding_selection.xml @@ -55,7 +55,7 @@ android:padding="24dp"> + app:layout_constraintTop_toBottomOf="@id/title_text" /> + app:layout_constraintTop_toBottomOf="@id/description_text"> From 9c50de9adf140d1edb4f158aa7e025cfc1e90eac Mon Sep 17 00:00:00 2001 From: Mihai-Cristian Condrea Date: Sat, 13 Sep 2025 17:40:32 +0300 Subject: [PATCH 11/67] chore: remove tools namespace from localized strings --- .../java/ui/screens/main/MainActivity.java | 73 +++++++++++++++---- 1 file changed, 58 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/main/MainActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/main/MainActivity.java index 43bab4b9..37f7d153 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/main/MainActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/main/MainActivity.java @@ -30,8 +30,11 @@ import androidx.navigation.ui.NavigationUI; import androidx.preference.PreferenceManager; +import com.d4rk.androidtutorials.java.BuildConfig; import com.d4rk.androidtutorials.java.R; import com.d4rk.androidtutorials.java.databinding.ActivityMainBinding; +import com.d4rk.androidtutorials.java.notifications.managers.AppUpdateNotificationsManager; +import com.d4rk.androidtutorials.java.notifications.managers.AppUsageNotificationsManager; import com.d4rk.androidtutorials.java.ui.components.navigation.BottomSheetMenuFragment; import com.d4rk.androidtutorials.java.ui.screens.startup.StartupActivity; import com.d4rk.androidtutorials.java.ui.screens.startup.StartupViewModel; @@ -48,6 +51,7 @@ import com.google.android.play.core.install.InstallStateUpdatedListener; import com.google.android.play.core.install.model.AppUpdateType; import com.google.android.play.core.install.model.InstallStatus; +import com.google.android.play.core.install.model.UpdateAvailability; import com.google.android.ump.ConsentInformation; import com.google.android.ump.ConsentRequestParameters; import com.google.android.ump.UserMessagingPlatform; @@ -92,6 +96,7 @@ public void onResume(@NonNull LifecycleOwner owner) { private NavController navController; private int currentNavIndex; private AppUpdateManager appUpdateManager; + private AppUpdateNotificationsManager appUpdateNotificationsManager; private InstallStateUpdatedListener installStateUpdatedListener; private long backPressedTime; @@ -134,6 +139,7 @@ protected void onCreate(Bundle savedInstanceState) { } this.appUpdateManager = mainViewModel.getAppUpdateManager(); + setupUpdateNotifications(); registerInstallStateListener(); getLifecycle().addObserver(lifecycleObserver); @@ -238,17 +244,19 @@ private void observeViewModel() { .build(); if (useRail) { - NavigationUI.setupWithNavController(mBinding.navRail, navController); - mBinding.navRail.setOnItemSelectedListener(item -> { - if (item.getItemId() == navController.getCurrentDestination().getId()) { + if (mBinding.navRail != null) { + NavigationUI.setupWithNavController(mBinding.navRail, navController); + mBinding.navRail.setOnItemSelectedListener(item -> { + if (item.getItemId() == navController.getCurrentDestination().getId()) { + return true; + } + int newIndex = navOrder.get(item.getItemId()); + NavOptions options = newIndex > currentNavIndex ? forwardOptions : backwardOptions; + navController.navigate(item.getItemId(), null, options); + currentNavIndex = newIndex; return true; - } - int newIndex = navOrder.get(item.getItemId()); - NavOptions options = newIndex > currentNavIndex ? forwardOptions : backwardOptions; - navController.navigate(item.getItemId(), null, options); - currentNavIndex = newIndex; - return true; - }); + }); + } } else { NavigationUI.setupWithNavController(navBarView, navController); navBarView.setOnItemSelectedListener(item -> { @@ -299,12 +307,47 @@ public boolean onOptionsItemSelected(android.view.MenuItem item) { return super.onOptionsItemSelected(item); } + @Override + protected void onResume() { + super.onResume(); + AppUsageNotificationsManager appUsageNotificationsManager = new AppUsageNotificationsManager(this); + appUsageNotificationsManager.scheduleAppUsageCheck(); + appUpdateNotificationsManager.checkAndSendUpdateNotification(); + checkForFlexibleOrImmediateUpdate(); + } + + private void checkForFlexibleOrImmediateUpdate() { + appUpdateManager.getAppUpdateInfo().addOnSuccessListener(appUpdateInfo -> { + boolean updateAvailable = appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE; + if (updateAvailable) { + startImmediateUpdate(appUpdateInfo); + } + }) + .addOnFailureListener(e -> { + if (!BuildConfig.DEBUG) { + Snackbar.make( + findViewById(android.R.id.content), + getString(R.string.snack_general_error), + Snackbar.LENGTH_LONG + ).show(); + } + }); + } + private void startImmediateUpdate(AppUpdateInfo appUpdateInfo) { - appUpdateManager.startUpdateFlowForResult( - appUpdateInfo, - updateActivityResultLauncher, - AppUpdateOptions.newBuilder(AppUpdateType.IMMEDIATE).build() - ); + try { + appUpdateManager.startUpdateFlowForResult( + appUpdateInfo, + updateActivityResultLauncher, + AppUpdateOptions.newBuilder(AppUpdateType.IMMEDIATE).build() + ); + } catch (Exception e) { + Log.e("MainActivity", "Error starting in-app update", e); + } + } + + private void setupUpdateNotifications() { + appUpdateNotificationsManager = new AppUpdateNotificationsManager(this); } private void registerInstallStateListener() { From 4fd4d9a17f36e2a3b786bfcd8a21d5bea035b462 Mon Sep 17 00:00:00 2001 From: Mihai-Cristian Condrea Date: Sat, 13 Sep 2025 17:49:16 +0300 Subject: [PATCH 12/67] Guard update notifications on Oreo --- .../androidtutorials/java/ui/screens/main/MainActivity.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/main/MainActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/main/MainActivity.java index 37f7d153..6d16c9f4 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/main/MainActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/main/MainActivity.java @@ -312,7 +312,9 @@ protected void onResume() { super.onResume(); AppUsageNotificationsManager appUsageNotificationsManager = new AppUsageNotificationsManager(this); appUsageNotificationsManager.scheduleAppUsageCheck(); - appUpdateNotificationsManager.checkAndSendUpdateNotification(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + appUpdateNotificationsManager.checkAndSendUpdateNotification(); + } checkForFlexibleOrImmediateUpdate(); } From 135e1ad4ed1e80cfc52be703a458b24ffd069302 Mon Sep 17 00:00:00 2001 From: Mihai-Cristian Condrea Date: Sat, 13 Sep 2025 18:10:10 +0300 Subject: [PATCH 13/67] Add in-app review prompt after session threshold --- .../java/ui/screens/main/MainActivity.java | 20 +++++++- .../java/utils/ReviewHelper.java | 47 +++++++++++++++++++ app/src/main/res/values/keys.xml | 2 + 3 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/com/d4rk/androidtutorials/java/utils/ReviewHelper.java diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/main/MainActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/main/MainActivity.java index 6d16c9f4..f5722207 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/main/MainActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/main/MainActivity.java @@ -41,6 +41,7 @@ import com.d4rk.androidtutorials.java.ui.screens.support.SupportActivity; import com.d4rk.androidtutorials.java.utils.ConsentUtils; import com.d4rk.androidtutorials.java.utils.EdgeToEdgeDelegate; +import com.d4rk.androidtutorials.java.utils.ReviewHelper; import com.google.android.gms.ads.AdRequest; import com.google.android.gms.ads.MobileAds; import com.google.android.material.navigation.NavigationBarView; @@ -156,6 +157,8 @@ public void handleOnBackPressed() { } } }); + + checkInAppReview(); } private void setupActionBar() { @@ -333,7 +336,22 @@ private void checkForFlexibleOrImmediateUpdate() { Snackbar.LENGTH_LONG ).show(); } - }); + }); + } + + private void checkInAppReview() { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); + int sessionCount = prefs.getInt(getString(R.string.key_session_count), 0); + boolean hasPrompted = prefs.getBoolean(getString(R.string.key_has_prompted_review), false); + + ReviewHelper.launchInAppReviewIfEligible( + this, + sessionCount, + hasPrompted, + () -> prefs.edit().putBoolean(getString(R.string.key_has_prompted_review), true).apply() + ); + + prefs.edit().putInt(getString(R.string.key_session_count), sessionCount + 1).apply(); } private void startImmediateUpdate(AppUpdateInfo appUpdateInfo) { diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/utils/ReviewHelper.java b/app/src/main/java/com/d4rk/androidtutorials/java/utils/ReviewHelper.java new file mode 100644 index 00000000..53036953 --- /dev/null +++ b/app/src/main/java/com/d4rk/androidtutorials/java/utils/ReviewHelper.java @@ -0,0 +1,47 @@ +package com.d4rk.androidtutorials.java.utils; + +import android.app.Activity; + +import com.google.android.play.core.review.ReviewInfo; +import com.google.android.play.core.review.ReviewManager; +import com.google.android.play.core.review.ReviewManagerFactory; + +/** + * Utility class for launching Google Play in-app reviews. + */ +public final class ReviewHelper { + + private ReviewHelper() { + // Utility class + } + + public static void launchInAppReviewIfEligible(Activity activity, + int sessionCount, + boolean hasPromptedBefore, + Runnable onReviewLaunched) { + if (sessionCount < 3 || hasPromptedBefore) { + return; + } + launchReview(activity, onReviewLaunched); + } + + public static void forceLaunchInAppReview(Activity activity) { + launchReview(activity, null); + } + + private static void launchReview(Activity activity, Runnable onReviewLaunched) { + ReviewManager reviewManager = ReviewManagerFactory.create(activity); + reviewManager.requestReviewFlow() + .addOnCompleteListener(task -> { + if (task.isSuccessful()) { + ReviewInfo reviewInfo = task.getResult(); + reviewManager.launchReviewFlow(activity, reviewInfo) + .addOnCompleteListener(flow -> { + if (onReviewLaunched != null) { + onReviewLaunched.run(); + } + }); + } + }); + } +} diff --git a/app/src/main/res/values/keys.xml b/app/src/main/res/values/keys.xml index 2ad5e922..1612118c 100644 --- a/app/src/main/res/values/keys.xml +++ b/app/src/main/res/values/keys.xml @@ -21,4 +21,6 @@ consent_ad_user_data consent_ad_personalization onboarding_complete + session_count + has_prompted_review \ No newline at end of file From 33c792c4ff0d767862298e6fd9de51a81f835cdc Mon Sep 17 00:00:00 2001 From: Mihai-Cristian Condrea Date: Sat, 13 Sep 2025 20:49:23 +0300 Subject: [PATCH 14/67] Use static EdgeToEdgeDelegate helper --- .../java/ui/components/navigation/BaseActivity.java | 3 +-- .../java/ui/screens/android/CodeActivity.java | 3 +-- .../alerts/alertdialog/AlertDialogActivity.java | 3 +-- .../lessons/alerts/snackbar/SnackBarActivity.java | 3 +-- .../android/lessons/alerts/toast/ToastActivity.java | 3 +-- .../lessons/basics/history/AndroidHistory.java | 3 +-- .../permissions/PermissionsTutorialActivity.java | 3 +-- .../android/lessons/basics/sdk/AndroidSDK.java | 3 +-- .../lessons/basics/shortcuts/ShortcutsActivity.java | 3 +-- .../shortcuts/tabs/BuildShortcutsActivity.java | 3 +-- .../basics/shortcuts/tabs/CodeShortcutsActivity.java | 3 +-- .../shortcuts/tabs/DebuggingShortcutsActivity.java | 3 +-- .../shortcuts/tabs/GeneralShortcutsActivity.java | 3 +-- .../NavigationAndSearchingShortcutsActivity.java | 3 +-- .../shortcuts/tabs/RefactoringShortcutsActivity.java | 3 +-- .../tabs/VersionControlShortcutsActivity.java | 3 +-- .../viewbinding/ViewBindingTutorialActivity.java | 3 +-- .../lessons/buttons/buttons/ButtonsActivity.java | 3 +-- .../lessons/buttons/buttons/ButtonsCodeActivity.java | 3 +-- .../lessons/buttons/image/ImageButtonsActivity.java | 3 +-- .../lessons/buttons/radio/RadioButtonsActivity.java | 3 +-- .../lessons/buttons/switches/SwitchActivity.java | 3 +-- .../clocks/chronometer/ChronometerActivity.java | 3 +-- .../android/lessons/clocks/clock/ClockActivity.java | 3 +-- .../lessons/clocks/clock/ClockCodeActivity.java | 3 +-- .../clocks/datepicker/DatePickerActivity.java | 3 +-- .../clocks/timepicker/TimePickerActivity.java | 3 +-- .../android/lessons/data/room/RoomActivity.java | 3 +-- .../android/lessons/data/room/RoomCodeActivity.java | 3 +-- .../lessons/layouts/linear/LinearLayoutActivity.java | 3 +-- .../layouts/linear/LinearLayoutCodeActivity.java | 3 +-- .../layouts/relative/RelativeLayoutActivity.java | 3 +-- .../layouts/relative/RelativeLayoutCodeActivity.java | 3 +-- .../lessons/layouts/table/TableLayoutActivity.java | 3 +-- .../layouts/table/TableLayoutCodeActivity.java | 3 +-- .../bottomnavigation/BottomNavigationActivity.java | 3 +-- .../navigation/drawer/NavigationDrawerActivity.java | 3 +-- .../networking/retrofit/RetrofitActivity.java | 3 +-- .../networking/retrofit/RetrofitCodeActivity.java | 3 +-- .../progress/progressbar/ProgressBarActivity.java | 3 +-- .../progressbar/ProgressBarCodeActivity.java | 3 +-- .../lessons/start/AndroidStartProjectActivity.java | 3 +-- .../android/lessons/views/grid/GirdViewActivity.java | 3 +-- .../android/lessons/views/images/ImagesActivity.java | 3 +-- .../lessons/views/images/ImagesCodeActivity.java | 3 +-- .../android/lessons/views/web/WebViewActivity.java | 3 +-- .../java/ui/screens/main/MainActivity.java | 5 ++--- .../java/ui/screens/settings/SettingsActivity.java | 3 +-- .../screens/permissions/PermissionsActivity.java | 3 +-- .../java/utils/EdgeToEdgeDelegate.java | 12 +++++------- app/src/main/res/raw/text_retrofit_java.txt | 3 +-- 51 files changed, 56 insertions(+), 108 deletions(-) diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/components/navigation/BaseActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/components/navigation/BaseActivity.java index 3c5da3f9..019b54f2 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/components/navigation/BaseActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/components/navigation/BaseActivity.java @@ -17,8 +17,7 @@ protected void onPostCreate(@Nullable Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); View container = findViewById(R.id.container); if (container != null) { - EdgeToEdgeDelegate edgeToEdgeDelegate = new EdgeToEdgeDelegate(this); - edgeToEdgeDelegate.applyEdgeToEdge(container); + EdgeToEdgeDelegate.apply(this, container); } ActionBar actionBar = getSupportActionBar(); if (actionBar != null) { diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/CodeActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/CodeActivity.java index 29da8513..a0d36ed0 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/CodeActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/CodeActivity.java @@ -27,8 +27,7 @@ protected void onCreate(Bundle savedInstanceState) { binding = ActivityTabLayoutBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); - EdgeToEdgeDelegate edgeToEdgeDelegate = new EdgeToEdgeDelegate(this); - edgeToEdgeDelegate.applyEdgeToEdge(binding.tabLayout); + EdgeToEdgeDelegate.apply(this, binding.tabLayout); CodeViewModel viewModel = new ViewModelProvider(this).get(CodeViewModel.class); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/alerts/alertdialog/AlertDialogActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/alerts/alertdialog/AlertDialogActivity.java index e040f759..bd7c4ec2 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/alerts/alertdialog/AlertDialogActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/alerts/alertdialog/AlertDialogActivity.java @@ -22,8 +22,7 @@ protected void onCreate(Bundle savedInstanceState) { binding = ActivityAlertDialogBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); - EdgeToEdgeDelegate edgeToEdgeDelegate = new EdgeToEdgeDelegate(this); - edgeToEdgeDelegate.applyEdgeToEdge(binding.container); + EdgeToEdgeDelegate.apply(this, binding.container); MaterialAlertDialogBuilder alertDialog = createAlertDialog(); binding.button.setOnClickListener(v -> alertDialog.show()); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/alerts/snackbar/SnackBarActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/alerts/snackbar/SnackBarActivity.java index 087e8e70..b70f0ade 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/alerts/snackbar/SnackBarActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/alerts/snackbar/SnackBarActivity.java @@ -22,8 +22,7 @@ protected void onCreate(Bundle savedInstanceState) { binding = ActivitySnackBarBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); - EdgeToEdgeDelegate edgeToEdgeDelegate = new EdgeToEdgeDelegate(this); - edgeToEdgeDelegate.applyEdgeToEdge(binding.container); + EdgeToEdgeDelegate.apply(this, binding.container); binding.button.setOnClickListener(v -> { Snackbar snackbar = Snackbar.make(binding.getRoot(), R.string.snack_this_is_a_snackbar, Snackbar.LENGTH_INDEFINITE); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/alerts/toast/ToastActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/alerts/toast/ToastActivity.java index 2a0be19a..b556e426 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/alerts/toast/ToastActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/alerts/toast/ToastActivity.java @@ -22,8 +22,7 @@ protected void onCreate(Bundle savedInstanceState) { binding = ActivityToastBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); - EdgeToEdgeDelegate edgeToEdgeDelegate = new EdgeToEdgeDelegate(this); - edgeToEdgeDelegate.applyEdgeToEdge(binding.container); + EdgeToEdgeDelegate.apply(this, binding.container); binding.button.setOnClickListener(v -> Toast.makeText(this, R.string.toast_this_is_a_toast, Toast.LENGTH_SHORT).show() diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/history/AndroidHistory.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/history/AndroidHistory.java index bac5e923..e500df45 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/history/AndroidHistory.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/history/AndroidHistory.java @@ -18,8 +18,7 @@ protected void onCreate(Bundle savedInstanceState) { setContentView(binding.getRoot()); MobileAds.initialize(this); - EdgeToEdgeDelegate edgeToEdgeDelegate = new EdgeToEdgeDelegate(this); - edgeToEdgeDelegate.applyEdgeToEdge(binding.scrollView); + EdgeToEdgeDelegate.apply(this, binding.scrollView); binding.adView.loadAd(new AdRequest.Builder().build()); binding.adViewBottom.loadAd(new AdRequest.Builder().build()); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/permissions/PermissionsTutorialActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/permissions/PermissionsTutorialActivity.java index 565e835d..a46c33cd 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/permissions/PermissionsTutorialActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/permissions/PermissionsTutorialActivity.java @@ -20,8 +20,7 @@ protected void onCreate(Bundle savedInstanceState) { setContentView(binding.getRoot()); MobileAds.initialize(this); - EdgeToEdgeDelegate edgeToEdgeDelegate = new EdgeToEdgeDelegate(this); - edgeToEdgeDelegate.applyEdgeToEdge(binding.scrollView); + EdgeToEdgeDelegate.apply(this, binding.scrollView); binding.adViewBottom.loadAd(new AdRequest.Builder().build()); binding.adViewLarge.loadAd(new AdRequest.Builder().build()); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/sdk/AndroidSDK.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/sdk/AndroidSDK.java index 1c3f7fd0..58b1fd2e 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/sdk/AndroidSDK.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/sdk/AndroidSDK.java @@ -68,8 +68,7 @@ protected void onCreate(Bundle savedInstanceState) { setContentView(binding.getRoot()); MobileAds.initialize(this); - EdgeToEdgeDelegate edgeToEdgeDelegate = new EdgeToEdgeDelegate(this); - edgeToEdgeDelegate.applyEdgeToEdge(binding.scrollView); + EdgeToEdgeDelegate.apply(this, binding.scrollView); binding.adViewBottom.loadAd(new AdRequest.Builder().build()); binding.adView.loadAd(new AdRequest.Builder().build()); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/ShortcutsActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/ShortcutsActivity.java index 7c3bc3d3..f0840bf1 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/ShortcutsActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/ShortcutsActivity.java @@ -21,8 +21,7 @@ protected void onCreate(Bundle savedInstanceState) { ActivityShortcutsBinding binding = ActivityShortcutsBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); - EdgeToEdgeDelegate edgeToEdgeDelegate = new EdgeToEdgeDelegate(this); - edgeToEdgeDelegate.applyEdgeToEdge(binding.container); + EdgeToEdgeDelegate.apply(this, binding.container); MobileAds.initialize(this); binding.adViewBottom.loadAd(new AdRequest.Builder().build()); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/tabs/BuildShortcutsActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/tabs/BuildShortcutsActivity.java index 05fb19b8..3f66cf66 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/tabs/BuildShortcutsActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/tabs/BuildShortcutsActivity.java @@ -18,8 +18,7 @@ protected void onCreate(Bundle savedInstanceState) { setContentView(binding.getRoot()); MobileAds.initialize(this); - EdgeToEdgeDelegate edgeToEdgeDelegate = new EdgeToEdgeDelegate(this); - edgeToEdgeDelegate.applyEdgeToEdge(binding.scrollView); + EdgeToEdgeDelegate.apply(this, binding.scrollView); binding.adView.loadAd(new AdRequest.Builder().build()); new FastScrollerBuilder(binding.scrollView).useMd2Style().build(); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/tabs/CodeShortcutsActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/tabs/CodeShortcutsActivity.java index c8b4ec0f..f752325c 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/tabs/CodeShortcutsActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/tabs/CodeShortcutsActivity.java @@ -18,8 +18,7 @@ protected void onCreate(Bundle savedInstanceState) { setContentView(binding.getRoot()); MobileAds.initialize(this); - EdgeToEdgeDelegate edgeToEdgeDelegate = new EdgeToEdgeDelegate(this); - edgeToEdgeDelegate.applyEdgeToEdge(binding.scrollView); + EdgeToEdgeDelegate.apply(this, binding.scrollView); binding.adView.loadAd(new AdRequest.Builder().build()); new FastScrollerBuilder(binding.scrollView).useMd2Style().build(); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/tabs/DebuggingShortcutsActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/tabs/DebuggingShortcutsActivity.java index a80f0aa2..299e431e 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/tabs/DebuggingShortcutsActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/tabs/DebuggingShortcutsActivity.java @@ -18,8 +18,7 @@ protected void onCreate(Bundle savedInstanceState) { setContentView(binding.getRoot()); - EdgeToEdgeDelegate edgeToEdgeDelegate = new EdgeToEdgeDelegate(this); - edgeToEdgeDelegate.applyEdgeToEdge(binding.scrollView); + EdgeToEdgeDelegate.apply(this, binding.scrollView); MobileAds.initialize(this); binding.adView.loadAd(new AdRequest.Builder().build()); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/tabs/GeneralShortcutsActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/tabs/GeneralShortcutsActivity.java index 294cf17f..84a46d72 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/tabs/GeneralShortcutsActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/tabs/GeneralShortcutsActivity.java @@ -18,8 +18,7 @@ protected void onCreate(Bundle savedInstanceState) { setContentView(binding.getRoot()); MobileAds.initialize(this); - EdgeToEdgeDelegate edgeToEdgeDelegate = new EdgeToEdgeDelegate(this); - edgeToEdgeDelegate.applyEdgeToEdge(binding.scrollView); + EdgeToEdgeDelegate.apply(this, binding.scrollView); binding.adView.loadAd(new AdRequest.Builder().build()); new FastScrollerBuilder(binding.scrollView).useMd2Style().build(); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/tabs/NavigationAndSearchingShortcutsActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/tabs/NavigationAndSearchingShortcutsActivity.java index 7ae63be5..2df13c08 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/tabs/NavigationAndSearchingShortcutsActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/tabs/NavigationAndSearchingShortcutsActivity.java @@ -29,8 +29,7 @@ protected void onCreate(Bundle savedInstanceState) { setContentView(binding.getRoot()); MobileAds.initialize(this); - EdgeToEdgeDelegate edgeToEdgeDelegate = new EdgeToEdgeDelegate(this); - edgeToEdgeDelegate.applyEdgeToEdge(binding.shortcutList); + EdgeToEdgeDelegate.apply(this, binding.shortcutList); binding.adView.loadAd(new AdRequest.Builder().build()); new FastScrollerBuilder(binding.shortcutList).useMd2Style().build(); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/tabs/RefactoringShortcutsActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/tabs/RefactoringShortcutsActivity.java index ad62352b..c777d090 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/tabs/RefactoringShortcutsActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/tabs/RefactoringShortcutsActivity.java @@ -18,8 +18,7 @@ protected void onCreate(Bundle savedInstanceState) { setContentView(binding.getRoot()); MobileAds.initialize(this); - EdgeToEdgeDelegate edgeToEdgeDelegate = new EdgeToEdgeDelegate(this); - edgeToEdgeDelegate.applyEdgeToEdge(binding.scrollView); + EdgeToEdgeDelegate.apply(this, binding.scrollView); binding.adView.loadAd(new AdRequest.Builder().build()); new FastScrollerBuilder(binding.scrollView).useMd2Style().build(); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/tabs/VersionControlShortcutsActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/tabs/VersionControlShortcutsActivity.java index 00cfcc99..13fb6861 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/tabs/VersionControlShortcutsActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/tabs/VersionControlShortcutsActivity.java @@ -18,8 +18,7 @@ protected void onCreate(Bundle savedInstanceState) { setContentView(binding.getRoot()); MobileAds.initialize(this); - EdgeToEdgeDelegate edgeToEdgeDelegate = new EdgeToEdgeDelegate(this); - edgeToEdgeDelegate.applyEdgeToEdge(binding.scrollView); + EdgeToEdgeDelegate.apply(this, binding.scrollView); binding.adView.loadAd(new AdRequest.Builder().build()); new FastScrollerBuilder(binding.scrollView).useMd2Style().build(); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/viewbinding/ViewBindingTutorialActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/viewbinding/ViewBindingTutorialActivity.java index a45ae0bc..4df35e93 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/viewbinding/ViewBindingTutorialActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/viewbinding/ViewBindingTutorialActivity.java @@ -36,8 +36,7 @@ protected void onCreate(Bundle savedInstanceState) { new FastScrollerBuilder(binding.scrollView).useMd2Style().build(); - EdgeToEdgeDelegate edgeToEdgeDelegate = new EdgeToEdgeDelegate(this); - edgeToEdgeDelegate.applyEdgeToEdge(binding.scrollView); + EdgeToEdgeDelegate.apply(this, binding.scrollView); binding.adViewBottom.loadAd(new AdRequest.Builder().build()); binding.adView.loadAd(new AdRequest.Builder().build()); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/buttons/buttons/ButtonsActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/buttons/buttons/ButtonsActivity.java index fb1e635d..16f6fe21 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/buttons/buttons/ButtonsActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/buttons/buttons/ButtonsActivity.java @@ -23,8 +23,7 @@ protected void onCreate(Bundle savedInstanceState) { binding = ActivityButtonsBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); - EdgeToEdgeDelegate edgeToEdgeDelegate = new EdgeToEdgeDelegate(this); - edgeToEdgeDelegate.applyEdgeToEdge(binding.constraintLayout); + EdgeToEdgeDelegate.apply(this, binding.constraintLayout); new FastScrollerBuilder(binding.scrollView).useMd2Style().build(); binding.buttonNormal.setOnClickListener(view -> Snackbar.make(binding.getRoot(), getString(R.string.button_normal) + " " + getString(R.string.snack_bar_clicked), Snackbar.LENGTH_SHORT).show()); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/buttons/buttons/ButtonsCodeActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/buttons/buttons/ButtonsCodeActivity.java index 5baabe2f..13c7e4c7 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/buttons/buttons/ButtonsCodeActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/buttons/buttons/ButtonsCodeActivity.java @@ -27,8 +27,7 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { binding = ActivityTabLayoutBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); - EdgeToEdgeDelegate edgeToEdgeDelegate = new EdgeToEdgeDelegate(this); - edgeToEdgeDelegate.applyEdgeToEdge(binding.tabLayout); + EdgeToEdgeDelegate.apply(this, binding.tabLayout); setupViewPager(); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/buttons/image/ImageButtonsActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/buttons/image/ImageButtonsActivity.java index d0942e40..72b155e4 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/buttons/image/ImageButtonsActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/buttons/image/ImageButtonsActivity.java @@ -22,8 +22,7 @@ protected void onCreate(Bundle savedInstanceState) { binding = ActivityImageButtonsBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); - EdgeToEdgeDelegate edgeToEdgeDelegate = new EdgeToEdgeDelegate(this); - edgeToEdgeDelegate.applyEdgeToEdge(binding.container); + EdgeToEdgeDelegate.apply(this, binding.container); binding.imageButton.setOnClickListener(v -> Snackbar.make(binding.getRoot(), R.string.snack_image_button, Snackbar.LENGTH_SHORT).show() diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/buttons/radio/RadioButtonsActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/buttons/radio/RadioButtonsActivity.java index 296f940d..66cc8091 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/buttons/radio/RadioButtonsActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/buttons/radio/RadioButtonsActivity.java @@ -22,8 +22,7 @@ protected void onCreate(Bundle savedInstanceState) { binding = ActivityRadioButtonsBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); - EdgeToEdgeDelegate edgeToEdgeDelegate = new EdgeToEdgeDelegate(this); - edgeToEdgeDelegate.applyEdgeToEdge(binding.container); + EdgeToEdgeDelegate.apply(this, binding.container); binding.radioGroup.setOnCheckedChangeListener((group, checkedId) -> { RadioButton radioButton = findViewById(checkedId); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/buttons/switches/SwitchActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/buttons/switches/SwitchActivity.java index b9b7c7ef..c9fc4fb7 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/buttons/switches/SwitchActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/buttons/switches/SwitchActivity.java @@ -24,8 +24,7 @@ protected void onCreate(Bundle savedInstanceState) { binding = ActivitySwitchBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); - EdgeToEdgeDelegate edgeToEdgeDelegate = new EdgeToEdgeDelegate(this); - edgeToEdgeDelegate.applyEdgeToEdge(binding.container); + EdgeToEdgeDelegate.apply(this, binding.container); new FastScrollerBuilder(binding.scrollView).useMd2Style().build(); setOnClickListeners(); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/clocks/chronometer/ChronometerActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/clocks/chronometer/ChronometerActivity.java index 98b1108e..a3ab421d 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/clocks/chronometer/ChronometerActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/clocks/chronometer/ChronometerActivity.java @@ -24,8 +24,7 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { setContentView(binding.getRoot()); - EdgeToEdgeDelegate edgeToEdgeDelegate = new EdgeToEdgeDelegate(this); - edgeToEdgeDelegate.applyEdgeToEdge(binding.container); + EdgeToEdgeDelegate.apply(this, binding.container); binding.floatingButtonShowSyntax.setOnClickListener(v -> { Intent intent = new Intent(this, CodeActivity.class); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/clocks/clock/ClockActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/clocks/clock/ClockActivity.java index 279da4d1..d427652d 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/clocks/clock/ClockActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/clocks/clock/ClockActivity.java @@ -21,8 +21,7 @@ protected void onCreate(Bundle savedInstanceState) { binding = ActivityClockBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); - EdgeToEdgeDelegate edgeToEdgeDelegate = new EdgeToEdgeDelegate(this); - edgeToEdgeDelegate.applyEdgeToEdge(binding.container); + EdgeToEdgeDelegate.apply(this, binding.container); new FastScrollerBuilder(binding.scrollView).useMd2Style().build(); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/clocks/clock/ClockCodeActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/clocks/clock/ClockCodeActivity.java index 933acd2a..040829ea 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/clocks/clock/ClockCodeActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/clocks/clock/ClockCodeActivity.java @@ -27,8 +27,7 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { binding = ActivityTabLayoutBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); - EdgeToEdgeDelegate edgeToEdgeDelegate = new EdgeToEdgeDelegate(this); - edgeToEdgeDelegate.applyEdgeToEdge(binding.tabLayout); + EdgeToEdgeDelegate.apply(this, binding.tabLayout); setupViewPager(); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/clocks/datepicker/DatePickerActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/clocks/datepicker/DatePickerActivity.java index a3359c8b..09581ac8 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/clocks/datepicker/DatePickerActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/clocks/datepicker/DatePickerActivity.java @@ -27,8 +27,7 @@ protected void onCreate(Bundle savedInstanceState) { binding = ActivityDatePickerBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); - EdgeToEdgeDelegate edgeToEdgeDelegate = new EdgeToEdgeDelegate(this); - edgeToEdgeDelegate.applyEdgeToEdge(binding.container); + EdgeToEdgeDelegate.apply(this, binding.container); updateDateInView(); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/clocks/timepicker/TimePickerActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/clocks/timepicker/TimePickerActivity.java index 4b60e4f7..77ae7ae0 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/clocks/timepicker/TimePickerActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/clocks/timepicker/TimePickerActivity.java @@ -26,8 +26,7 @@ protected void onCreate(Bundle savedInstanceState) { binding = ActivityTimePickerBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); - EdgeToEdgeDelegate edgeToEdgeDelegate = new EdgeToEdgeDelegate(this); - edgeToEdgeDelegate.applyEdgeToEdge(binding.container); + EdgeToEdgeDelegate.apply(this, binding.container); updateTimeInView(); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/data/room/RoomActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/data/room/RoomActivity.java index 78b39541..3a25f658 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/data/room/RoomActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/data/room/RoomActivity.java @@ -40,8 +40,7 @@ protected void onCreate(Bundle savedInstanceState) { binding = ActivityRoomBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); - EdgeToEdgeDelegate edgeToEdgeDelegate = new EdgeToEdgeDelegate(this); - edgeToEdgeDelegate.applyEdgeToEdge(binding.constraintLayout); + EdgeToEdgeDelegate.apply(this, binding.constraintLayout); db = AppDatabase.getInstance(this); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/data/room/RoomCodeActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/data/room/RoomCodeActivity.java index 87d94bcb..c9376e66 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/data/room/RoomCodeActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/data/room/RoomCodeActivity.java @@ -29,8 +29,7 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { binding = ActivityTabLayoutBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); - EdgeToEdgeDelegate edgeToEdgeDelegate = new EdgeToEdgeDelegate(this); - edgeToEdgeDelegate.applyEdgeToEdge(binding.appBarLayout); + EdgeToEdgeDelegate.apply(this, binding.appBarLayout); setupViewPager(); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/layouts/linear/LinearLayoutActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/layouts/linear/LinearLayoutActivity.java index 7c6003d4..846714f0 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/layouts/linear/LinearLayoutActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/layouts/linear/LinearLayoutActivity.java @@ -21,8 +21,7 @@ protected void onCreate(Bundle savedInstanceState) { binding = ActivityLinearLayoutBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); - EdgeToEdgeDelegate edgeToEdgeDelegate = new EdgeToEdgeDelegate(this); - edgeToEdgeDelegate.applyEdgeToEdge(binding.container); + EdgeToEdgeDelegate.apply(this, binding.container); new FastScrollerBuilder(binding.scrollView).useMd2Style().build(); binding.floatingButtonShowSyntax.setOnClickListener(v -> startActivity(new Intent(LinearLayoutActivity.this, LinearLayoutCodeActivity.class))); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/layouts/linear/LinearLayoutCodeActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/layouts/linear/LinearLayoutCodeActivity.java index e1e5b43e..fd912809 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/layouts/linear/LinearLayoutCodeActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/layouts/linear/LinearLayoutCodeActivity.java @@ -29,8 +29,7 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { com.d4rk.androidtutorials.java.databinding.ActivityTabLayoutBinding binding = ActivityTabLayoutBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); - EdgeToEdgeDelegate edgeToEdgeDelegate = new EdgeToEdgeDelegate(this); - edgeToEdgeDelegate.applyEdgeToEdge(binding.appBarLayout); + EdgeToEdgeDelegate.apply(this, binding.appBarLayout); viewPager2 = binding.viewpager; diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/layouts/relative/RelativeLayoutActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/layouts/relative/RelativeLayoutActivity.java index 4eb2683a..2f804041 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/layouts/relative/RelativeLayoutActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/layouts/relative/RelativeLayoutActivity.java @@ -22,8 +22,7 @@ protected void onCreate(Bundle savedInstanceState) { setContentView(binding.getRoot()); - EdgeToEdgeDelegate edgeToEdgeDelegate = new EdgeToEdgeDelegate(this); - edgeToEdgeDelegate.applyEdgeToEdge(binding.container); + EdgeToEdgeDelegate.apply(this, binding.container); new FastScrollerBuilder(binding.scrollView).useMd2Style().build(); binding.floatingButtonShowSyntax.setOnClickListener(v -> startActivity(new Intent(RelativeLayoutActivity.this, RelativeLayoutCodeActivity.class))); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/layouts/relative/RelativeLayoutCodeActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/layouts/relative/RelativeLayoutCodeActivity.java index 576bc689..f79ca8eb 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/layouts/relative/RelativeLayoutCodeActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/layouts/relative/RelativeLayoutCodeActivity.java @@ -27,8 +27,7 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { binding = ActivityTabLayoutBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); - EdgeToEdgeDelegate edgeToEdgeDelegate = new EdgeToEdgeDelegate(this); - edgeToEdgeDelegate.applyEdgeToEdge(binding.tabLayout); + EdgeToEdgeDelegate.apply(this, binding.tabLayout); setupViewPager(); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/layouts/table/TableLayoutActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/layouts/table/TableLayoutActivity.java index 7ab319d0..669eb133 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/layouts/table/TableLayoutActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/layouts/table/TableLayoutActivity.java @@ -21,8 +21,7 @@ protected void onCreate(Bundle savedInstanceState) { binding = ActivityTableLayoutBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); - EdgeToEdgeDelegate edgeToEdgeDelegate = new EdgeToEdgeDelegate(this); - edgeToEdgeDelegate.applyEdgeToEdge(binding.container); + EdgeToEdgeDelegate.apply(this, binding.container); new FastScrollerBuilder(binding.scrollView).useMd2Style().build(); binding.floatingButtonShowSyntax.setOnClickListener(v -> startActivity(new Intent(TableLayoutActivity.this, TableLayoutCodeActivity.class))); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/layouts/table/TableLayoutCodeActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/layouts/table/TableLayoutCodeActivity.java index efc2a266..901235c3 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/layouts/table/TableLayoutCodeActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/layouts/table/TableLayoutCodeActivity.java @@ -27,8 +27,7 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { binding = ActivityTabLayoutBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); - EdgeToEdgeDelegate edgeToEdgeDelegate = new EdgeToEdgeDelegate(this); - edgeToEdgeDelegate.applyEdgeToEdge(binding.tabLayout); + EdgeToEdgeDelegate.apply(this, binding.tabLayout); setupViewPager(); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/navigation/bottomnavigation/BottomNavigationActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/navigation/bottomnavigation/BottomNavigationActivity.java index 30072d87..ea877e54 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/navigation/bottomnavigation/BottomNavigationActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/navigation/bottomnavigation/BottomNavigationActivity.java @@ -21,8 +21,7 @@ protected void onCreate(Bundle savedInstanceState) { binding = ActivityBottomNavigationBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); - EdgeToEdgeDelegate edgeToEdgeDelegate = new EdgeToEdgeDelegate(this); - edgeToEdgeDelegate.applyEdgeToEdgeBottomBar(binding.container, binding.bottomNav); + EdgeToEdgeDelegate.applyBottomBar(this, binding.container, binding.bottomNav); binding.bottomNav.setOnItemSelectedListener(item -> { binding.textView.setText(getString(R.string.selected, item.getTitle())); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/navigation/drawer/NavigationDrawerActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/navigation/drawer/NavigationDrawerActivity.java index 8a3a9a39..895b6fd4 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/navigation/drawer/NavigationDrawerActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/navigation/drawer/NavigationDrawerActivity.java @@ -23,8 +23,7 @@ protected void onCreate(Bundle savedInstanceState) { binding = ActivityNavigationDrawerBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); - EdgeToEdgeDelegate edgeToEdgeDelegate = new EdgeToEdgeDelegate(this); - edgeToEdgeDelegate.applyEdgeToEdge(binding.container); + EdgeToEdgeDelegate.apply(this, binding.container); binding.navigationView.setNavigationItemSelectedListener(item -> { binding.textView.setText(getString(R.string.selected, item.getTitle())); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/networking/retrofit/RetrofitActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/networking/retrofit/RetrofitActivity.java index 305754d3..bff6023f 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/networking/retrofit/RetrofitActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/networking/retrofit/RetrofitActivity.java @@ -34,8 +34,7 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { binding = ActivityRetrofitBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); - EdgeToEdgeDelegate edgeToEdgeDelegate = new EdgeToEdgeDelegate(this); - edgeToEdgeDelegate.applyEdgeToEdge(binding.constraintLayout); + EdgeToEdgeDelegate.apply(this, binding.constraintLayout); Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://jsonplaceholder.typicode.com/") diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/networking/retrofit/RetrofitCodeActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/networking/retrofit/RetrofitCodeActivity.java index 234ac2de..a5ef4323 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/networking/retrofit/RetrofitCodeActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/networking/retrofit/RetrofitCodeActivity.java @@ -29,8 +29,7 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { binding = ActivityTabLayoutBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); - EdgeToEdgeDelegate edgeToEdgeDelegate = new EdgeToEdgeDelegate(this); - edgeToEdgeDelegate.applyEdgeToEdge(binding.appBarLayout); + EdgeToEdgeDelegate.apply(this, binding.appBarLayout); setupViewPager(); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/progress/progressbar/ProgressBarActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/progress/progressbar/ProgressBarActivity.java index d7242912..fbcad8b1 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/progress/progressbar/ProgressBarActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/progress/progressbar/ProgressBarActivity.java @@ -21,8 +21,7 @@ protected void onCreate(Bundle savedInstanceState) { binding = ActivityProgressBarBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); - EdgeToEdgeDelegate edgeToEdgeDelegate = new EdgeToEdgeDelegate(this); - edgeToEdgeDelegate.applyEdgeToEdge(binding.container); + EdgeToEdgeDelegate.apply(this, binding.container); new FastScrollerBuilder(binding.scrollView).useMd2Style().build(); binding.progressBar.hide(); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/progress/progressbar/ProgressBarCodeActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/progress/progressbar/ProgressBarCodeActivity.java index 44e5fd19..1f33ef16 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/progress/progressbar/ProgressBarCodeActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/progress/progressbar/ProgressBarCodeActivity.java @@ -27,8 +27,7 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { binding = ActivityTabLayoutBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); - EdgeToEdgeDelegate edgeToEdgeDelegate = new EdgeToEdgeDelegate(this); - edgeToEdgeDelegate.applyEdgeToEdge(binding.tabLayout); + EdgeToEdgeDelegate.apply(this, binding.tabLayout); setupViewPager(); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/start/AndroidStartProjectActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/start/AndroidStartProjectActivity.java index e987e319..ca2aa52e 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/start/AndroidStartProjectActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/start/AndroidStartProjectActivity.java @@ -21,8 +21,7 @@ protected void onCreate(Bundle savedInstanceState) { ActivityAndroidStartProjectBinding binding = ActivityAndroidStartProjectBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); - EdgeToEdgeDelegate edgeToEdgeDelegate = new EdgeToEdgeDelegate(this); - edgeToEdgeDelegate.applyEdgeToEdge(binding.constraintLayout); + EdgeToEdgeDelegate.apply(this, binding.constraintLayout); setSupportActionBar(binding.topAppBar); binding.topAppBar.setNavigationOnClickListener(v -> getOnBackPressedDispatcher().onBackPressed()); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/views/grid/GirdViewActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/views/grid/GirdViewActivity.java index 2717472e..4d0e62b9 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/views/grid/GirdViewActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/views/grid/GirdViewActivity.java @@ -25,8 +25,7 @@ protected void onCreate(Bundle savedInstanceState) { binding = ActivityGridViewBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); - EdgeToEdgeDelegate edgeToEdgeDelegate = new EdgeToEdgeDelegate(this); - edgeToEdgeDelegate.applyEdgeToEdge(binding.container); + EdgeToEdgeDelegate.apply(this, binding.container); ArrayAdapter adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, numbers); binding.gridView.setAdapter(adapter); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/views/images/ImagesActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/views/images/ImagesActivity.java index 92c881aa..e581a4d4 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/views/images/ImagesActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/views/images/ImagesActivity.java @@ -21,8 +21,7 @@ protected void onCreate(Bundle savedInstanceState) { binding = ActivityImagesBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); - EdgeToEdgeDelegate edgeToEdgeDelegate = new EdgeToEdgeDelegate(this); - edgeToEdgeDelegate.applyEdgeToEdge(binding.container); + EdgeToEdgeDelegate.apply(this, binding.container); new FastScrollerBuilder(binding.scrollView).useMd2Style().build(); binding.floatingButtonShowSyntax.setOnClickListener(v -> startActivity(new Intent(ImagesActivity.this, ImagesCodeActivity.class))); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/views/images/ImagesCodeActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/views/images/ImagesCodeActivity.java index a35235d0..e32dda14 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/views/images/ImagesCodeActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/views/images/ImagesCodeActivity.java @@ -27,8 +27,7 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { binding = ActivityTabLayoutBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); - EdgeToEdgeDelegate edgeToEdgeDelegate = new EdgeToEdgeDelegate(this); - edgeToEdgeDelegate.applyEdgeToEdge(binding.tabLayout); + EdgeToEdgeDelegate.apply(this, binding.tabLayout); setupViewPager(); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/views/web/WebViewActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/views/web/WebViewActivity.java index 75edff7a..f4162d29 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/views/web/WebViewActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/views/web/WebViewActivity.java @@ -25,8 +25,7 @@ protected void onCreate(Bundle savedInstanceState) { binding = ActivityWebviewBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); - EdgeToEdgeDelegate edgeToEdgeDelegate = new EdgeToEdgeDelegate(this); - edgeToEdgeDelegate.applyEdgeToEdge(binding.container); + EdgeToEdgeDelegate.apply(this, binding.container); new FastScrollerBuilder(binding.webView).useMd2Style().build(); setupWebView(); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/main/MainActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/main/MainActivity.java index f5722207..2c02e929 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/main/MainActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/main/MainActivity.java @@ -187,7 +187,6 @@ private void observeViewModel() { } boolean useRail = shouldUseNavigationRail(); - EdgeToEdgeDelegate edgeToEdgeDelegate = new EdgeToEdgeDelegate(this); ActivityMainBinding binding = mBinding; if (binding == null) { return; @@ -196,11 +195,11 @@ private void observeViewModel() { if (useRail) { binding.navRail.setVisibility(View.VISIBLE); navBarView.setVisibility(View.GONE); - edgeToEdgeDelegate.applyEdgeToEdge(binding.container); + EdgeToEdgeDelegate.apply(this, binding.container); } else { binding.navRail.setVisibility(View.GONE); navBarView.setVisibility(View.VISIBLE); - edgeToEdgeDelegate.applyEdgeToEdgeBottomBar(binding.container, navBarView); + EdgeToEdgeDelegate.applyBottomBar(this, binding.container, navBarView); navBarView.setLabelVisibilityMode(uiState.bottomNavVisibility()); if (binding.adView != null) { diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/settings/SettingsActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/settings/SettingsActivity.java index 11b22c4c..94a6344b 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/settings/SettingsActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/settings/SettingsActivity.java @@ -28,8 +28,7 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { ActivitySettingsBinding binding = ActivitySettingsBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); - EdgeToEdgeDelegate edgeToEdgeDelegate = new EdgeToEdgeDelegate(this); - edgeToEdgeDelegate.applyEdgeToEdge(binding.container); + EdgeToEdgeDelegate.apply(this, binding.container); settingsViewModel = new ViewModelProvider(this).get(SettingsViewModel.class); settingsViewModel.applyConsent(); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/settings/screens/permissions/PermissionsActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/settings/screens/permissions/PermissionsActivity.java index eb40c99b..eaebe3ae 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/settings/screens/permissions/PermissionsActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/settings/screens/permissions/PermissionsActivity.java @@ -16,8 +16,7 @@ protected void onCreate(Bundle savedInstanceState) { ActivityPermissionsBinding binding = ActivityPermissionsBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); - EdgeToEdgeDelegate edgeToEdgeDelegate = new EdgeToEdgeDelegate(this); - edgeToEdgeDelegate.applyEdgeToEdge(binding.container); + EdgeToEdgeDelegate.apply(this, binding.container); getSupportFragmentManager().beginTransaction().replace(R.id.frame_layout_permissions, new SettingsFragment()).commit(); if (getSupportActionBar() != null) { diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/utils/EdgeToEdgeDelegate.java b/app/src/main/java/com/d4rk/androidtutorials/java/utils/EdgeToEdgeDelegate.java index b26d78bc..61466132 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/utils/EdgeToEdgeDelegate.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/utils/EdgeToEdgeDelegate.java @@ -8,15 +8,13 @@ import androidx.core.view.WindowCompat; import androidx.core.view.WindowInsetsCompat; -public class EdgeToEdgeDelegate { +public final class EdgeToEdgeDelegate { - private final Activity activity; - - public EdgeToEdgeDelegate(Activity activity) { - this.activity = activity; + private EdgeToEdgeDelegate() { + // Utility class } - public void applyEdgeToEdge(View container) { + public static void apply(Activity activity, View container) { WindowCompat.setDecorFitsSystemWindows(activity.getWindow(), false); ViewCompat.setOnApplyWindowInsetsListener(container, (v, insets) -> { @@ -26,7 +24,7 @@ public void applyEdgeToEdge(View container) { }); } - public void applyEdgeToEdgeBottomBar(View container, View bottomNavigationView) { + public static void applyBottomBar(Activity activity, View container, View bottomNavigationView) { WindowCompat.setDecorFitsSystemWindows(activity.getWindow(), false); ViewCompat.setOnApplyWindowInsetsListener(container, (v, insets) -> { diff --git a/app/src/main/res/raw/text_retrofit_java.txt b/app/src/main/res/raw/text_retrofit_java.txt index 6b2e70a8..55488755 100644 --- a/app/src/main/res/raw/text_retrofit_java.txt +++ b/app/src/main/res/raw/text_retrofit_java.txt @@ -40,8 +40,7 @@ public class RetrofitActivity extends UpNavigationActivity { binding = ActivityRetrofitBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); - EdgeToEdgeDelegate edgeToEdgeDelegate = new EdgeToEdgeDelegate(this); - edgeToEdgeDelegate.applyEdgeToEdge(binding.constraintLayout); + EdgeToEdgeDelegate.apply(this, binding.constraintLayout); Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://jsonplaceholder.typicode.com/") From 70fb1cabe513609d4fb43cd76dd8de2e75ae82f1 Mon Sep 17 00:00:00 2001 From: Mihai-Cristian Condrea Date: Sat, 13 Sep 2025 23:44:44 +0300 Subject: [PATCH 15/67] Add AdUtils for unified MobileAds init and banner loading --- .../androidtutorials/java/ads/AdUtils.java | 31 +++++++++++++++++++ .../java/ads/managers/AppOpenAd.java | 7 ++--- .../repository/DefaultSupportRepository.java | 4 +-- .../java/ui/screens/about/AboutFragment.java | 6 ++-- .../android/AndroidStudioFragment.java | 4 +-- .../basics/history/AndroidHistory.java | 9 ++---- .../PermissionsTutorialActivity.java | 9 ++---- .../lessons/basics/sdk/AndroidSDK.java | 12 +++---- .../basics/shortcuts/ShortcutsActivity.java | 8 ++--- .../tabs/BuildShortcutsActivity.java | 7 ++--- .../shortcuts/tabs/CodeShortcutsActivity.java | 7 ++--- .../tabs/DebuggingShortcutsActivity.java | 8 ++--- .../tabs/GeneralShortcutsActivity.java | 7 ++--- ...vigationAndSearchingShortcutsActivity.java | 7 ++--- .../tabs/RefactoringShortcutsActivity.java | 7 ++--- .../tabs/VersionControlShortcutsActivity.java | 7 ++--- .../ViewBindingTutorialActivity.java | 6 ++-- .../buttons/tabs/ButtonsTabCodeFragment.java | 4 +-- .../tabs/ButtonsTabLayoutFragment.java | 4 +-- .../clock/tabs/ClockTabCodeFragment.java | 6 ++-- .../clock/tabs/ClockTabLayoutFragment.java | 4 +-- .../data/room/tabs/RoomTabCodeFragment.java | 4 +-- .../data/room/tabs/RoomTabLayoutFragment.java | 4 +-- .../tabs/LinearLayoutTabCodeFragment.java | 6 ++-- .../tabs/LinearLayoutTabLayoutFragment.java | 4 +-- .../tabs/RelativeLayoutTabCodeFragment.java | 6 ++-- .../tabs/RelativeLayoutTabLayoutFragment.java | 4 +-- .../tabs/TableLayoutTabCodeFragment.java | 6 ++-- .../tabs/TableLayoutTabLayoutFragment.java | 4 +-- .../tabs/RetrofitTabCodeFragment.java | 4 +-- .../tabs/RetrofitTabLayoutFragment.java | 4 +-- .../tabs/ProgressBarTabCodeFragment.java | 4 +-- .../tabs/ProgressBarTabLayoutFragment.java | 4 +-- .../start/AndroidStartProjectActivity.java | 10 ++---- .../images/tabs/ImagesTabCodeFragment.java | 6 ++-- .../images/tabs/ImagesTabLayoutFragment.java | 4 +-- .../ui/screens/android/tabs/CodeFragment.java | 4 +-- .../screens/android/tabs/LayoutFragment.java | 4 +-- .../screens/android/tabs/NoCodeFragment.java | 6 ++-- .../java/ui/screens/home/HomeFragment.java | 9 ++---- .../java/ui/screens/main/MainActivity.java | 9 ++---- .../ui/screens/support/SupportActivity.java | 7 +++-- 42 files changed, 123 insertions(+), 154 deletions(-) create mode 100644 app/src/main/java/com/d4rk/androidtutorials/java/ads/AdUtils.java diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ads/AdUtils.java b/app/src/main/java/com/d4rk/androidtutorials/java/ads/AdUtils.java new file mode 100644 index 00000000..e07b0068 --- /dev/null +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ads/AdUtils.java @@ -0,0 +1,31 @@ +package com.d4rk.androidtutorials.java.ads; + +import android.content.Context; +import android.view.View; + +import com.google.android.gms.ads.AdRequest; +import com.google.android.gms.ads.AdView; +import com.google.android.gms.ads.MobileAds; + +public final class AdUtils { + private static boolean initialized = false; + + private AdUtils() { + // no-op + } + + public static synchronized void initialize(Context context) { + if (!initialized) { + MobileAds.initialize(context.getApplicationContext()); + initialized = true; + } + } + + public static void loadBanner(View adView) { + if (adView instanceof AdView) { + AdView view = (AdView) adView; + initialize(view.getContext()); + view.loadAd(new AdRequest.Builder().build()); + } + } +} diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ads/managers/AppOpenAd.java b/app/src/main/java/com/d4rk/androidtutorials/java/ads/managers/AppOpenAd.java index 1ba69e2f..53edf4ee 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ads/managers/AppOpenAd.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ads/managers/AppOpenAd.java @@ -18,7 +18,7 @@ import com.google.android.gms.ads.AdRequest; import com.google.android.gms.ads.FullScreenContentCallback; import com.google.android.gms.ads.LoadAdError; -import com.google.android.gms.ads.MobileAds; +import com.d4rk.androidtutorials.java.ads.AdUtils; import com.google.android.gms.ads.appopen.AppOpenAd.AppOpenAdLoadCallback; import java.util.Date; @@ -35,10 +35,7 @@ public class AppOpenAd extends Application implements ActivityLifecycleCallbacks public void onCreate() { super.onCreate(); registerActivityLifecycleCallbacks(this); - MobileAds.initialize( - this, - initializationStatus -> { - }); + AdUtils.initialize(this); CookieManager.getInstance(); ProcessLifecycleOwner.get().getLifecycle().addObserver(this); appOpenAdManager = new AppOpenAdManager(this); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/data/repository/DefaultSupportRepository.java b/app/src/main/java/com/d4rk/androidtutorials/java/data/repository/DefaultSupportRepository.java index 54225f15..fa92860a 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/data/repository/DefaultSupportRepository.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/data/repository/DefaultSupportRepository.java @@ -12,7 +12,7 @@ import com.android.billingclient.api.ProductDetails; import com.android.billingclient.api.QueryProductDetailsParams; import com.google.android.gms.ads.AdRequest; -import com.google.android.gms.ads.MobileAds; +import com.d4rk.androidtutorials.java.ads.AdUtils; import java.util.ArrayList; import java.util.Collections; @@ -150,7 +150,7 @@ public BillingFlowLauncher initiatePurchase(String productId) { * can be done here if needed for the support screen). */ public AdRequest initMobileAds() { - MobileAds.initialize(context); + AdUtils.initialize(context); return new AdRequest.Builder().build(); } diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/about/AboutFragment.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/about/AboutFragment.java index a9f45463..1b019b03 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/about/AboutFragment.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/about/AboutFragment.java @@ -18,8 +18,7 @@ import com.d4rk.androidtutorials.java.R; import com.d4rk.androidtutorials.java.databinding.FragmentAboutBinding; import com.d4rk.androidtutorials.java.utils.ConsentUtils; -import com.google.android.gms.ads.AdRequest; -import com.google.android.gms.ads.MobileAds; +import com.d4rk.androidtutorials.java.ads.AdUtils; import dagger.hilt.android.AndroidEntryPoint; import me.zhanghai.android.fastscroll.FastScrollerBuilder; @@ -42,9 +41,8 @@ public android.view.View onCreateView(@NonNull android.view.LayoutInflater infla new FastScrollerBuilder(binding.scrollView).useMd2Style().build(); if (ConsentUtils.canShowAds(requireContext())) { - MobileAds.initialize(requireContext()); binding.adView.setVisibility(android.view.View.VISIBLE); - binding.adView.loadAd(new AdRequest.Builder().build()); + AdUtils.loadBanner(binding.adView); } else { binding.adView.setVisibility(android.view.View.GONE); } diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/AndroidStudioFragment.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/AndroidStudioFragment.java index a8dfbb95..30284a96 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/AndroidStudioFragment.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/AndroidStudioFragment.java @@ -34,7 +34,7 @@ import com.d4rk.androidtutorials.java.databinding.ItemAndroidStudioLessonBinding; import com.google.android.gms.ads.AdListener; import com.google.android.gms.ads.LoadAdError; -import com.google.android.gms.ads.MobileAds; +import com.d4rk.androidtutorials.java.ads.AdUtils; import com.google.android.material.card.MaterialCardView; import com.google.android.material.textview.MaterialTextView; import com.google.android.material.shape.CornerFamily; @@ -138,7 +138,7 @@ public void onDestroyView() { private void ensureMobileAdsInitialized() { if (!mobileAdsInitialized) { - MobileAds.initialize(requireContext()); + AdUtils.initialize(requireContext()); mobileAdsInitialized = true; } } diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/history/AndroidHistory.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/history/AndroidHistory.java index e500df45..271762e5 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/history/AndroidHistory.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/history/AndroidHistory.java @@ -5,8 +5,7 @@ import com.d4rk.androidtutorials.java.databinding.ActivityAndroidHistoryBinding; import com.d4rk.androidtutorials.java.ui.components.navigation.UpNavigationActivity; import com.d4rk.androidtutorials.java.utils.EdgeToEdgeDelegate; -import com.google.android.gms.ads.AdRequest; -import com.google.android.gms.ads.MobileAds; +import com.d4rk.androidtutorials.java.ads.AdUtils; import me.zhanghai.android.fastscroll.FastScrollerBuilder; @@ -16,12 +15,10 @@ protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ActivityAndroidHistoryBinding binding = ActivityAndroidHistoryBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); - MobileAds.initialize(this); - EdgeToEdgeDelegate.apply(this, binding.scrollView); - binding.adView.loadAd(new AdRequest.Builder().build()); - binding.adViewBottom.loadAd(new AdRequest.Builder().build()); + AdUtils.loadBanner(binding.adView); + AdUtils.loadBanner(binding.adViewBottom); new FastScrollerBuilder(binding.scrollView).useMd2Style().build(); } } \ No newline at end of file diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/permissions/PermissionsTutorialActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/permissions/PermissionsTutorialActivity.java index a46c33cd..c2c4529b 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/permissions/PermissionsTutorialActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/permissions/PermissionsTutorialActivity.java @@ -7,8 +7,7 @@ import com.d4rk.androidtutorials.java.databinding.ActivityPermissionsTutorialBinding; import com.d4rk.androidtutorials.java.ui.components.navigation.UpNavigationActivity; import com.d4rk.androidtutorials.java.utils.EdgeToEdgeDelegate; -import com.google.android.gms.ads.AdRequest; -import com.google.android.gms.ads.MobileAds; +import com.d4rk.androidtutorials.java.ads.AdUtils; import me.zhanghai.android.fastscroll.FastScrollerBuilder; @@ -18,12 +17,10 @@ protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); com.d4rk.androidtutorials.java.databinding.ActivityPermissionsTutorialBinding binding = ActivityPermissionsTutorialBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); - MobileAds.initialize(this); - EdgeToEdgeDelegate.apply(this, binding.scrollView); - binding.adViewBottom.loadAd(new AdRequest.Builder().build()); - binding.adViewLarge.loadAd(new AdRequest.Builder().build()); + AdUtils.loadBanner(binding.adViewBottom); + AdUtils.loadBanner(binding.adViewLarge); new FastScrollerBuilder(binding.scrollView).useMd2Style().build(); binding.buttonMore.setOnClickListener(v -> startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://developer.android.com/guide/topics/permissions/overview")))); } diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/sdk/AndroidSDK.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/sdk/AndroidSDK.java index 58b1fd2e..0fb9e9f6 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/sdk/AndroidSDK.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/sdk/AndroidSDK.java @@ -12,8 +12,7 @@ import com.d4rk.androidtutorials.java.databinding.ActivityAndroidSdkBinding; import com.d4rk.androidtutorials.java.ui.components.navigation.UpNavigationActivity; import com.d4rk.androidtutorials.java.utils.EdgeToEdgeDelegate; -import com.google.android.gms.ads.AdRequest; -import com.google.android.gms.ads.MobileAds; +import com.d4rk.androidtutorials.java.ads.AdUtils; import java.util.Arrays; import java.util.List; @@ -65,13 +64,10 @@ public class AndroidSDK extends UpNavigationActivity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); binding = ActivityAndroidSdkBinding.inflate(getLayoutInflater()); - setContentView(binding.getRoot()); + setContentView(binding.getRoot()); EdgeToEdgeDelegate.apply(this, binding.scrollView); - MobileAds.initialize(this); - EdgeToEdgeDelegate.apply(this, binding.scrollView); - - binding.adViewBottom.loadAd(new AdRequest.Builder().build()); - binding.adView.loadAd(new AdRequest.Builder().build()); + AdUtils.loadBanner(binding.adViewBottom); + AdUtils.loadBanner(binding.adView); new FastScrollerBuilder(binding.scrollView).useMd2Style().build(); createDynamicTable(); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/ShortcutsActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/ShortcutsActivity.java index f0840bf1..084c3e54 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/ShortcutsActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/ShortcutsActivity.java @@ -11,8 +11,7 @@ import com.d4rk.androidtutorials.java.databinding.ActivityShortcutsBinding; import com.d4rk.androidtutorials.java.ui.components.navigation.UpNavigationActivity; import com.d4rk.androidtutorials.java.utils.EdgeToEdgeDelegate; -import com.google.android.gms.ads.AdRequest; -import com.google.android.gms.ads.MobileAds; +import com.d4rk.androidtutorials.java.ads.AdUtils; public class ShortcutsActivity extends UpNavigationActivity { @Override @@ -21,10 +20,7 @@ protected void onCreate(Bundle savedInstanceState) { ActivityShortcutsBinding binding = ActivityShortcutsBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); - EdgeToEdgeDelegate.apply(this, binding.container); - - MobileAds.initialize(this); - binding.adViewBottom.loadAd(new AdRequest.Builder().build()); + EdgeToEdgeDelegate.apply(this, binding.container); AdUtils.loadBanner(binding.adViewBottom); getSupportFragmentManager().beginTransaction().replace(R.id.frame_layout_shortcuts, new SettingsFragment()).commit(); ActionBar supportActionBar = getSupportActionBar(); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/tabs/BuildShortcutsActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/tabs/BuildShortcutsActivity.java index 3f66cf66..82c3c99e 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/tabs/BuildShortcutsActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/tabs/BuildShortcutsActivity.java @@ -5,8 +5,7 @@ import com.d4rk.androidtutorials.java.databinding.ActivityShortcutsBuildBinding; import com.d4rk.androidtutorials.java.ui.components.navigation.UpNavigationActivity; import com.d4rk.androidtutorials.java.utils.EdgeToEdgeDelegate; -import com.google.android.gms.ads.AdRequest; -import com.google.android.gms.ads.MobileAds; +import com.d4rk.androidtutorials.java.ads.AdUtils; import me.zhanghai.android.fastscroll.FastScrollerBuilder; @@ -16,11 +15,9 @@ protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); com.d4rk.androidtutorials.java.databinding.ActivityShortcutsBuildBinding binding = ActivityShortcutsBuildBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); - MobileAds.initialize(this); - EdgeToEdgeDelegate.apply(this, binding.scrollView); - binding.adView.loadAd(new AdRequest.Builder().build()); + AdUtils.loadBanner(binding.adView); new FastScrollerBuilder(binding.scrollView).useMd2Style().build(); } } \ No newline at end of file diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/tabs/CodeShortcutsActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/tabs/CodeShortcutsActivity.java index f752325c..b08e6535 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/tabs/CodeShortcutsActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/tabs/CodeShortcutsActivity.java @@ -5,8 +5,7 @@ import com.d4rk.androidtutorials.java.databinding.ActivityShortcutsCodeBinding; import com.d4rk.androidtutorials.java.ui.components.navigation.UpNavigationActivity; import com.d4rk.androidtutorials.java.utils.EdgeToEdgeDelegate; -import com.google.android.gms.ads.AdRequest; -import com.google.android.gms.ads.MobileAds; +import com.d4rk.androidtutorials.java.ads.AdUtils; import me.zhanghai.android.fastscroll.FastScrollerBuilder; @@ -16,11 +15,9 @@ protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ActivityShortcutsCodeBinding binding = ActivityShortcutsCodeBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); - MobileAds.initialize(this); - EdgeToEdgeDelegate.apply(this, binding.scrollView); - binding.adView.loadAd(new AdRequest.Builder().build()); + AdUtils.loadBanner(binding.adView); new FastScrollerBuilder(binding.scrollView).useMd2Style().build(); } } \ No newline at end of file diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/tabs/DebuggingShortcutsActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/tabs/DebuggingShortcutsActivity.java index 299e431e..007ea11e 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/tabs/DebuggingShortcutsActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/tabs/DebuggingShortcutsActivity.java @@ -5,8 +5,7 @@ import com.d4rk.androidtutorials.java.databinding.ActivityShortcutsDebuggingBinding; import com.d4rk.androidtutorials.java.ui.components.navigation.UpNavigationActivity; import com.d4rk.androidtutorials.java.utils.EdgeToEdgeDelegate; -import com.google.android.gms.ads.AdRequest; -import com.google.android.gms.ads.MobileAds; +import com.d4rk.androidtutorials.java.ads.AdUtils; import me.zhanghai.android.fastscroll.FastScrollerBuilder; @@ -18,10 +17,7 @@ protected void onCreate(Bundle savedInstanceState) { setContentView(binding.getRoot()); - EdgeToEdgeDelegate.apply(this, binding.scrollView); - - MobileAds.initialize(this); - binding.adView.loadAd(new AdRequest.Builder().build()); + EdgeToEdgeDelegate.apply(this, binding.scrollView); AdUtils.loadBanner(binding.adView); new FastScrollerBuilder(binding.scrollView).useMd2Style().build(); } } \ No newline at end of file diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/tabs/GeneralShortcutsActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/tabs/GeneralShortcutsActivity.java index 84a46d72..a968fbd7 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/tabs/GeneralShortcutsActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/tabs/GeneralShortcutsActivity.java @@ -5,8 +5,7 @@ import com.d4rk.androidtutorials.java.databinding.ActivityShortcutsGeneralBinding; import com.d4rk.androidtutorials.java.ui.components.navigation.UpNavigationActivity; import com.d4rk.androidtutorials.java.utils.EdgeToEdgeDelegate; -import com.google.android.gms.ads.AdRequest; -import com.google.android.gms.ads.MobileAds; +import com.d4rk.androidtutorials.java.ads.AdUtils; import me.zhanghai.android.fastscroll.FastScrollerBuilder; @@ -16,11 +15,9 @@ protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); com.d4rk.androidtutorials.java.databinding.ActivityShortcutsGeneralBinding binding = ActivityShortcutsGeneralBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); - MobileAds.initialize(this); - EdgeToEdgeDelegate.apply(this, binding.scrollView); - binding.adView.loadAd(new AdRequest.Builder().build()); + AdUtils.loadBanner(binding.adView); new FastScrollerBuilder(binding.scrollView).useMd2Style().build(); } } \ No newline at end of file diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/tabs/NavigationAndSearchingShortcutsActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/tabs/NavigationAndSearchingShortcutsActivity.java index 2df13c08..50ae218f 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/tabs/NavigationAndSearchingShortcutsActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/tabs/NavigationAndSearchingShortcutsActivity.java @@ -14,8 +14,7 @@ import com.d4rk.androidtutorials.java.databinding.ActivityShortcutsNavigationAndSearchingBinding; import com.d4rk.androidtutorials.java.ui.components.navigation.UpNavigationActivity; import com.d4rk.androidtutorials.java.utils.EdgeToEdgeDelegate; -import com.google.android.gms.ads.AdRequest; -import com.google.android.gms.ads.MobileAds; +import com.d4rk.androidtutorials.java.ads.AdUtils; import java.util.List; @@ -27,11 +26,9 @@ protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ActivityShortcutsNavigationAndSearchingBinding binding = ActivityShortcutsNavigationAndSearchingBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); - MobileAds.initialize(this); - EdgeToEdgeDelegate.apply(this, binding.shortcutList); - binding.adView.loadAd(new AdRequest.Builder().build()); + AdUtils.loadBanner(binding.adView); new FastScrollerBuilder(binding.shortcutList).useMd2Style().build(); List shortcuts = List.of( diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/tabs/RefactoringShortcutsActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/tabs/RefactoringShortcutsActivity.java index c777d090..43b68011 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/tabs/RefactoringShortcutsActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/tabs/RefactoringShortcutsActivity.java @@ -5,8 +5,7 @@ import com.d4rk.androidtutorials.java.databinding.ActivityShortcutsRefractoringBinding; import com.d4rk.androidtutorials.java.ui.components.navigation.UpNavigationActivity; import com.d4rk.androidtutorials.java.utils.EdgeToEdgeDelegate; -import com.google.android.gms.ads.AdRequest; -import com.google.android.gms.ads.MobileAds; +import com.d4rk.androidtutorials.java.ads.AdUtils; import me.zhanghai.android.fastscroll.FastScrollerBuilder; @@ -16,11 +15,9 @@ protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); com.d4rk.androidtutorials.java.databinding.ActivityShortcutsRefractoringBinding binding = ActivityShortcutsRefractoringBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); - MobileAds.initialize(this); - EdgeToEdgeDelegate.apply(this, binding.scrollView); - binding.adView.loadAd(new AdRequest.Builder().build()); + AdUtils.loadBanner(binding.adView); new FastScrollerBuilder(binding.scrollView).useMd2Style().build(); } } \ No newline at end of file diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/tabs/VersionControlShortcutsActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/tabs/VersionControlShortcutsActivity.java index 13fb6861..67b586df 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/tabs/VersionControlShortcutsActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/tabs/VersionControlShortcutsActivity.java @@ -5,8 +5,7 @@ import com.d4rk.androidtutorials.java.databinding.ActivityShortcutsVersionControlBinding; import com.d4rk.androidtutorials.java.ui.components.navigation.UpNavigationActivity; import com.d4rk.androidtutorials.java.utils.EdgeToEdgeDelegate; -import com.google.android.gms.ads.AdRequest; -import com.google.android.gms.ads.MobileAds; +import com.d4rk.androidtutorials.java.ads.AdUtils; import me.zhanghai.android.fastscroll.FastScrollerBuilder; @@ -16,11 +15,9 @@ protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); com.d4rk.androidtutorials.java.databinding.ActivityShortcutsVersionControlBinding binding = ActivityShortcutsVersionControlBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); - MobileAds.initialize(this); - EdgeToEdgeDelegate.apply(this, binding.scrollView); - binding.adView.loadAd(new AdRequest.Builder().build()); + AdUtils.loadBanner(binding.adView); new FastScrollerBuilder(binding.scrollView).useMd2Style().build(); } } \ No newline at end of file diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/viewbinding/ViewBindingTutorialActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/viewbinding/ViewBindingTutorialActivity.java index 4df35e93..ba3be064 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/viewbinding/ViewBindingTutorialActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/viewbinding/ViewBindingTutorialActivity.java @@ -16,7 +16,7 @@ import com.d4rk.androidtutorials.java.utils.CodeViewUtils; import com.d4rk.androidtutorials.java.utils.EdgeToEdgeDelegate; import com.d4rk.androidtutorials.java.utils.FontManager; -import com.google.android.gms.ads.AdRequest; +import com.d4rk.androidtutorials.java.ads.AdUtils; import java.io.BufferedReader; import java.io.IOException; @@ -38,8 +38,8 @@ protected void onCreate(Bundle savedInstanceState) { EdgeToEdgeDelegate.apply(this, binding.scrollView); - binding.adViewBottom.loadAd(new AdRequest.Builder().build()); - binding.adView.loadAd(new AdRequest.Builder().build()); + AdUtils.loadBanner(binding.adViewBottom); + AdUtils.loadBanner(binding.adView); binding.moreAboutViewBindingButton.setOnClickListener(v -> startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://developer.android.com/topic/libraries/view-binding#java")))); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/buttons/buttons/tabs/ButtonsTabCodeFragment.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/buttons/buttons/tabs/ButtonsTabCodeFragment.java index fb2e8fd1..d91cdadc 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/buttons/buttons/tabs/ButtonsTabCodeFragment.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/buttons/buttons/tabs/ButtonsTabCodeFragment.java @@ -17,7 +17,7 @@ import com.d4rk.androidtutorials.java.utils.CodeHighlighter; import com.d4rk.androidtutorials.java.utils.CodeViewUtils; import com.d4rk.androidtutorials.java.utils.FontManager; -import com.google.android.gms.ads.AdRequest; +import com.d4rk.androidtutorials.java.ads.AdUtils; import java.io.BufferedReader; import java.io.IOException; @@ -32,7 +32,7 @@ public class ButtonsTabCodeFragment extends Fragment { public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { FragmentSameCodeBinding binding = FragmentSameCodeBinding.inflate(inflater, container, false); new FastScrollerBuilder(binding.scrollView).useMd2Style().build(); - binding.adView.loadAd(new AdRequest.Builder().build()); + AdUtils.loadBanner(binding.adView); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(requireContext()); Typeface monospaceFont = FontManager.getMonospaceFont(requireContext(), prefs); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/buttons/buttons/tabs/ButtonsTabLayoutFragment.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/buttons/buttons/tabs/ButtonsTabLayoutFragment.java index cdc266da..8448aa51 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/buttons/buttons/tabs/ButtonsTabLayoutFragment.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/buttons/buttons/tabs/ButtonsTabLayoutFragment.java @@ -18,7 +18,7 @@ import com.d4rk.androidtutorials.java.utils.CodeHighlighter; import com.d4rk.androidtutorials.java.utils.CodeViewUtils; import com.d4rk.androidtutorials.java.utils.FontManager; -import com.google.android.gms.ads.AdRequest; +import com.d4rk.androidtutorials.java.ads.AdUtils; import java.io.BufferedReader; import java.io.IOException; @@ -37,7 +37,7 @@ public class ButtonsTabLayoutFragment extends Fragment { public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { FragmentButtonsLayoutBinding binding = FragmentButtonsLayoutBinding.inflate(inflater, container, false); new FastScrollerBuilder(binding.scrollView).useMd2Style().build(); - binding.adView.loadAd(new AdRequest.Builder().build()); + AdUtils.loadBanner(binding.adView); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(requireContext()); Typeface monospaceFont = FontManager.getMonospaceFont(requireContext(), prefs); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/clocks/clock/tabs/ClockTabCodeFragment.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/clocks/clock/tabs/ClockTabCodeFragment.java index 7b3f231c..bb47fa2e 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/clocks/clock/tabs/ClockTabCodeFragment.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/clocks/clock/tabs/ClockTabCodeFragment.java @@ -9,15 +9,13 @@ import androidx.fragment.app.Fragment; import com.d4rk.androidtutorials.java.databinding.FragmentNoCodeBinding; -import com.google.android.gms.ads.AdRequest; -import com.google.android.gms.ads.MobileAds; +import com.d4rk.androidtutorials.java.ads.AdUtils; public class ClockTabCodeFragment extends Fragment { @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { com.d4rk.androidtutorials.java.databinding.FragmentNoCodeBinding binding = FragmentNoCodeBinding.inflate(inflater, container, false); - MobileAds.initialize(requireContext()); - binding.adView.loadAd(new AdRequest.Builder().build()); + AdUtils.loadBanner(binding.adView); return binding.getRoot(); } } \ No newline at end of file diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/clocks/clock/tabs/ClockTabLayoutFragment.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/clocks/clock/tabs/ClockTabLayoutFragment.java index 18ba23ad..1a1120d0 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/clocks/clock/tabs/ClockTabLayoutFragment.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/clocks/clock/tabs/ClockTabLayoutFragment.java @@ -18,7 +18,7 @@ import com.d4rk.androidtutorials.java.utils.CodeHighlighter; import com.d4rk.androidtutorials.java.utils.CodeViewUtils; import com.d4rk.androidtutorials.java.utils.FontManager; -import com.google.android.gms.ads.AdRequest; +import com.d4rk.androidtutorials.java.ads.AdUtils; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -32,7 +32,7 @@ public class ClockTabLayoutFragment extends Fragment { public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { FragmentClockLayoutBinding binding = FragmentClockLayoutBinding.inflate(inflater, container, false); new FastScrollerBuilder(binding.scrollView).useMd2Style().build(); - binding.adView.loadAd(new AdRequest.Builder().build()); + AdUtils.loadBanner(binding.adView); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(requireContext()); Typeface monospaceFont = FontManager.getMonospaceFont(requireContext(), prefs); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/data/room/tabs/RoomTabCodeFragment.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/data/room/tabs/RoomTabCodeFragment.java index 83374bab..cd3c7eb6 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/data/room/tabs/RoomTabCodeFragment.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/data/room/tabs/RoomTabCodeFragment.java @@ -17,7 +17,7 @@ import com.d4rk.androidtutorials.java.utils.CodeHighlighter; import com.d4rk.androidtutorials.java.utils.CodeViewUtils; import com.d4rk.androidtutorials.java.utils.FontManager; -import com.google.android.gms.ads.AdRequest; +import com.d4rk.androidtutorials.java.ads.AdUtils; import java.io.BufferedReader; import java.io.IOException; @@ -35,7 +35,7 @@ public class RoomTabCodeFragment extends Fragment { public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { FragmentCodeBinding binding = FragmentCodeBinding.inflate(inflater, container, false); new FastScrollerBuilder(binding.scrollView).useMd2Style().build(); - binding.adView.loadAd(new AdRequest.Builder().build()); + AdUtils.loadBanner(binding.adView); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(requireContext()); Typeface monospaceFont = FontManager.getMonospaceFont(requireContext(), prefs); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/data/room/tabs/RoomTabLayoutFragment.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/data/room/tabs/RoomTabLayoutFragment.java index fe2e70ea..cee185c5 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/data/room/tabs/RoomTabLayoutFragment.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/data/room/tabs/RoomTabLayoutFragment.java @@ -17,7 +17,7 @@ import com.d4rk.androidtutorials.java.utils.CodeHighlighter; import com.d4rk.androidtutorials.java.utils.CodeViewUtils; import com.d4rk.androidtutorials.java.utils.FontManager; -import com.google.android.gms.ads.AdRequest; +import com.d4rk.androidtutorials.java.ads.AdUtils; import java.io.BufferedReader; import java.io.IOException; @@ -35,7 +35,7 @@ public class RoomTabLayoutFragment extends Fragment { public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { FragmentLayoutBinding binding = FragmentLayoutBinding.inflate(inflater, container, false); new FastScrollerBuilder(binding.scrollView).useMd2Style().build(); - binding.adView.loadAd(new AdRequest.Builder().build()); + AdUtils.loadBanner(binding.adView); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(requireContext()); Typeface monospaceFont = FontManager.getMonospaceFont(requireContext(), prefs); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/layouts/linear/tabs/LinearLayoutTabCodeFragment.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/layouts/linear/tabs/LinearLayoutTabCodeFragment.java index fd5a157e..d6102d9c 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/layouts/linear/tabs/LinearLayoutTabCodeFragment.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/layouts/linear/tabs/LinearLayoutTabCodeFragment.java @@ -9,15 +9,13 @@ import androidx.fragment.app.Fragment; import com.d4rk.androidtutorials.java.databinding.FragmentNoCodeBinding; -import com.google.android.gms.ads.AdRequest; -import com.google.android.gms.ads.MobileAds; +import com.d4rk.androidtutorials.java.ads.AdUtils; public class LinearLayoutTabCodeFragment extends Fragment { @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { com.d4rk.androidtutorials.java.databinding.FragmentNoCodeBinding binding = FragmentNoCodeBinding.inflate(inflater, container, false); - MobileAds.initialize(requireContext()); - binding.adView.loadAd(new AdRequest.Builder().build()); + AdUtils.loadBanner(binding.adView); return binding.getRoot(); } } \ No newline at end of file diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/layouts/linear/tabs/LinearLayoutTabLayoutFragment.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/layouts/linear/tabs/LinearLayoutTabLayoutFragment.java index 29e3e3d0..bfaa1577 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/layouts/linear/tabs/LinearLayoutTabLayoutFragment.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/layouts/linear/tabs/LinearLayoutTabLayoutFragment.java @@ -17,7 +17,7 @@ import com.d4rk.androidtutorials.java.utils.CodeHighlighter; import com.d4rk.androidtutorials.java.utils.CodeViewUtils; import com.d4rk.androidtutorials.java.utils.FontManager; -import com.google.android.gms.ads.AdRequest; +import com.d4rk.androidtutorials.java.ads.AdUtils; import java.io.BufferedReader; import java.io.IOException; @@ -31,7 +31,7 @@ public class LinearLayoutTabLayoutFragment extends Fragment { public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { FragmentLinearLayoutLayoutBinding binding = FragmentLinearLayoutLayoutBinding.inflate(inflater, container, false); new FastScrollerBuilder(binding.scrollView).useMd2Style().build(); - binding.adView.loadAd(new AdRequest.Builder().build()); + AdUtils.loadBanner(binding.adView); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(requireContext()); Typeface monospaceFont = FontManager.getMonospaceFont(requireContext(), prefs); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/layouts/relative/tabs/RelativeLayoutTabCodeFragment.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/layouts/relative/tabs/RelativeLayoutTabCodeFragment.java index d1aee623..0028fadb 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/layouts/relative/tabs/RelativeLayoutTabCodeFragment.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/layouts/relative/tabs/RelativeLayoutTabCodeFragment.java @@ -9,15 +9,13 @@ import androidx.fragment.app.Fragment; import com.d4rk.androidtutorials.java.databinding.FragmentNoCodeBinding; -import com.google.android.gms.ads.AdRequest; -import com.google.android.gms.ads.MobileAds; +import com.d4rk.androidtutorials.java.ads.AdUtils; public class RelativeLayoutTabCodeFragment extends Fragment { @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { com.d4rk.androidtutorials.java.databinding.FragmentNoCodeBinding binding = FragmentNoCodeBinding.inflate(inflater, container, false); - MobileAds.initialize(requireContext()); - binding.adView.loadAd(new AdRequest.Builder().build()); + AdUtils.loadBanner(binding.adView); return binding.getRoot(); } } \ No newline at end of file diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/layouts/relative/tabs/RelativeLayoutTabLayoutFragment.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/layouts/relative/tabs/RelativeLayoutTabLayoutFragment.java index 5ad17ceb..89ed6305 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/layouts/relative/tabs/RelativeLayoutTabLayoutFragment.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/layouts/relative/tabs/RelativeLayoutTabLayoutFragment.java @@ -17,7 +17,7 @@ import com.d4rk.androidtutorials.java.utils.CodeHighlighter; import com.d4rk.androidtutorials.java.utils.CodeViewUtils; import com.d4rk.androidtutorials.java.utils.FontManager; -import com.google.android.gms.ads.AdRequest; +import com.d4rk.androidtutorials.java.ads.AdUtils; import java.io.BufferedReader; import java.io.IOException; @@ -32,7 +32,7 @@ public class RelativeLayoutTabLayoutFragment extends Fragment { public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { FragmentLayoutBinding binding = FragmentLayoutBinding.inflate(inflater, container, false); new FastScrollerBuilder(binding.scrollView).useMd2Style().build(); - binding.adView.loadAd(new AdRequest.Builder().build()); + AdUtils.loadBanner(binding.adView); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(requireContext()); Typeface monospaceFont = FontManager.getMonospaceFont(requireContext(), prefs); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/layouts/table/tabs/TableLayoutTabCodeFragment.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/layouts/table/tabs/TableLayoutTabCodeFragment.java index 5462c45b..6c481695 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/layouts/table/tabs/TableLayoutTabCodeFragment.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/layouts/table/tabs/TableLayoutTabCodeFragment.java @@ -9,15 +9,13 @@ import androidx.fragment.app.Fragment; import com.d4rk.androidtutorials.java.databinding.FragmentNoCodeBinding; -import com.google.android.gms.ads.AdRequest; -import com.google.android.gms.ads.MobileAds; +import com.d4rk.androidtutorials.java.ads.AdUtils; public class TableLayoutTabCodeFragment extends Fragment { @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { com.d4rk.androidtutorials.java.databinding.FragmentNoCodeBinding binding = FragmentNoCodeBinding.inflate(inflater, container, false); - MobileAds.initialize(requireContext()); - binding.adView.loadAd(new AdRequest.Builder().build()); + AdUtils.loadBanner(binding.adView); return binding.getRoot(); } } \ No newline at end of file diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/layouts/table/tabs/TableLayoutTabLayoutFragment.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/layouts/table/tabs/TableLayoutTabLayoutFragment.java index 20c2e58b..9a219b8b 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/layouts/table/tabs/TableLayoutTabLayoutFragment.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/layouts/table/tabs/TableLayoutTabLayoutFragment.java @@ -17,7 +17,7 @@ import com.d4rk.androidtutorials.java.utils.CodeHighlighter; import com.d4rk.androidtutorials.java.utils.CodeViewUtils; import com.d4rk.androidtutorials.java.utils.FontManager; -import com.google.android.gms.ads.AdRequest; +import com.d4rk.androidtutorials.java.ads.AdUtils; import java.io.BufferedReader; import java.io.IOException; @@ -32,7 +32,7 @@ public class TableLayoutTabLayoutFragment extends Fragment { public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { FragmentLayoutBinding binding = FragmentLayoutBinding.inflate(inflater, container, false); new FastScrollerBuilder(binding.scrollView).useMd2Style().build(); - binding.adView.loadAd(new AdRequest.Builder().build()); + AdUtils.loadBanner(binding.adView); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(requireContext()); Typeface monospaceFont = FontManager.getMonospaceFont(requireContext(), prefs); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/networking/retrofit/tabs/RetrofitTabCodeFragment.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/networking/retrofit/tabs/RetrofitTabCodeFragment.java index fb034ed0..ed991d06 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/networking/retrofit/tabs/RetrofitTabCodeFragment.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/networking/retrofit/tabs/RetrofitTabCodeFragment.java @@ -17,7 +17,7 @@ import com.d4rk.androidtutorials.java.utils.CodeHighlighter; import com.d4rk.androidtutorials.java.utils.CodeViewUtils; import com.d4rk.androidtutorials.java.utils.FontManager; -import com.google.android.gms.ads.AdRequest; +import com.d4rk.androidtutorials.java.ads.AdUtils; import java.io.BufferedReader; import java.io.IOException; @@ -35,7 +35,7 @@ public class RetrofitTabCodeFragment extends Fragment { public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { FragmentCodeBinding binding = FragmentCodeBinding.inflate(inflater, container, false); new FastScrollerBuilder(binding.scrollView).useMd2Style().build(); - binding.adView.loadAd(new AdRequest.Builder().build()); + AdUtils.loadBanner(binding.adView); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(requireContext()); Typeface monospaceFont = FontManager.getMonospaceFont(requireContext(), prefs); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/networking/retrofit/tabs/RetrofitTabLayoutFragment.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/networking/retrofit/tabs/RetrofitTabLayoutFragment.java index a484472d..59d625db 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/networking/retrofit/tabs/RetrofitTabLayoutFragment.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/networking/retrofit/tabs/RetrofitTabLayoutFragment.java @@ -17,7 +17,7 @@ import com.d4rk.androidtutorials.java.utils.CodeHighlighter; import com.d4rk.androidtutorials.java.utils.CodeViewUtils; import com.d4rk.androidtutorials.java.utils.FontManager; -import com.google.android.gms.ads.AdRequest; +import com.d4rk.androidtutorials.java.ads.AdUtils; import java.io.BufferedReader; import java.io.IOException; @@ -35,7 +35,7 @@ public class RetrofitTabLayoutFragment extends Fragment { public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { FragmentLayoutBinding binding = FragmentLayoutBinding.inflate(inflater, container, false); new FastScrollerBuilder(binding.scrollView).useMd2Style().build(); - binding.adView.loadAd(new AdRequest.Builder().build()); + AdUtils.loadBanner(binding.adView); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(requireContext()); Typeface monospaceFont = FontManager.getMonospaceFont(requireContext(), prefs); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/progress/progressbar/tabs/ProgressBarTabCodeFragment.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/progress/progressbar/tabs/ProgressBarTabCodeFragment.java index e3960037..0497aca7 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/progress/progressbar/tabs/ProgressBarTabCodeFragment.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/progress/progressbar/tabs/ProgressBarTabCodeFragment.java @@ -17,7 +17,7 @@ import com.d4rk.androidtutorials.java.utils.CodeHighlighter; import com.d4rk.androidtutorials.java.utils.CodeViewUtils; import com.d4rk.androidtutorials.java.utils.FontManager; -import com.google.android.gms.ads.AdRequest; +import com.d4rk.androidtutorials.java.ads.AdUtils; import java.io.BufferedReader; import java.io.IOException; @@ -32,7 +32,7 @@ public class ProgressBarTabCodeFragment extends Fragment { public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { FragmentCodeBinding binding = FragmentCodeBinding.inflate(inflater, container, false); new FastScrollerBuilder(binding.scrollView).useMd2Style().build(); - binding.adView.loadAd(new AdRequest.Builder().build()); + AdUtils.loadBanner(binding.adView); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(requireContext()); Typeface monospaceFont = FontManager.getMonospaceFont(requireContext(), prefs); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/progress/progressbar/tabs/ProgressBarTabLayoutFragment.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/progress/progressbar/tabs/ProgressBarTabLayoutFragment.java index 825f6d2a..6c311f4d 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/progress/progressbar/tabs/ProgressBarTabLayoutFragment.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/progress/progressbar/tabs/ProgressBarTabLayoutFragment.java @@ -17,7 +17,7 @@ import com.d4rk.androidtutorials.java.utils.CodeHighlighter; import com.d4rk.androidtutorials.java.utils.CodeViewUtils; import com.d4rk.androidtutorials.java.utils.FontManager; -import com.google.android.gms.ads.AdRequest; +import com.d4rk.androidtutorials.java.ads.AdUtils; import java.io.BufferedReader; import java.io.IOException; @@ -31,7 +31,7 @@ public class ProgressBarTabLayoutFragment extends Fragment { public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { FragmentLinearLayoutLayoutBinding binding = FragmentLinearLayoutLayoutBinding.inflate(inflater, container, false); new FastScrollerBuilder(binding.scrollView).useMd2Style().build(); - binding.adView.loadAd(new AdRequest.Builder().build()); + AdUtils.loadBanner(binding.adView); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(requireContext()); Typeface monospaceFont = FontManager.getMonospaceFont(requireContext(), prefs); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/start/AndroidStartProjectActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/start/AndroidStartProjectActivity.java index ca2aa52e..0bb346f2 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/start/AndroidStartProjectActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/start/AndroidStartProjectActivity.java @@ -9,8 +9,7 @@ import com.d4rk.androidtutorials.java.databinding.ActivityAndroidStartProjectBinding; import com.d4rk.androidtutorials.java.ui.components.navigation.UpNavigationActivity; import com.d4rk.androidtutorials.java.utils.EdgeToEdgeDelegate; -import com.google.android.gms.ads.AdRequest; -import com.google.android.gms.ads.MobileAds; +import com.d4rk.androidtutorials.java.ads.AdUtils; import me.zhanghai.android.fastscroll.FastScrollerBuilder; @@ -36,11 +35,8 @@ protected void onCreate(Bundle savedInstanceState) { return true; } return false; - }); - - MobileAds.initialize(this); - binding.adViewBottom.loadAd(new AdRequest.Builder().build()); - binding.adView.loadAd(new AdRequest.Builder().build()); + }); AdUtils.loadBanner(binding.adViewBottom); + AdUtils.loadBanner(binding.adView); new FastScrollerBuilder(binding.scrollView).useMd2Style().build(); binding.textViewThirdStepSummary.setMovementMethod(LinkMovementMethod.getInstance()); } diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/views/images/tabs/ImagesTabCodeFragment.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/views/images/tabs/ImagesTabCodeFragment.java index 30dbcc32..bc4eebba 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/views/images/tabs/ImagesTabCodeFragment.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/views/images/tabs/ImagesTabCodeFragment.java @@ -9,15 +9,13 @@ import androidx.fragment.app.Fragment; import com.d4rk.androidtutorials.java.databinding.FragmentNoCodeBinding; -import com.google.android.gms.ads.AdRequest; -import com.google.android.gms.ads.MobileAds; +import com.d4rk.androidtutorials.java.ads.AdUtils; public class ImagesTabCodeFragment extends Fragment { @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { com.d4rk.androidtutorials.java.databinding.FragmentNoCodeBinding binding = FragmentNoCodeBinding.inflate(inflater, container, false); - MobileAds.initialize(requireContext()); - binding.adView.loadAd(new AdRequest.Builder().build()); + AdUtils.loadBanner(binding.adView); return binding.getRoot(); } } \ No newline at end of file diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/views/images/tabs/ImagesTabLayoutFragment.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/views/images/tabs/ImagesTabLayoutFragment.java index 01e66e7e..244e532c 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/views/images/tabs/ImagesTabLayoutFragment.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/views/images/tabs/ImagesTabLayoutFragment.java @@ -17,7 +17,7 @@ import com.d4rk.androidtutorials.java.utils.CodeHighlighter; import com.d4rk.androidtutorials.java.utils.CodeViewUtils; import com.d4rk.androidtutorials.java.utils.FontManager; -import com.google.android.gms.ads.AdRequest; +import com.d4rk.androidtutorials.java.ads.AdUtils; import java.io.BufferedReader; import java.io.IOException; @@ -32,7 +32,7 @@ public class ImagesTabLayoutFragment extends Fragment { public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { FragmentLayoutBinding binding = FragmentLayoutBinding.inflate(inflater, container, false); new FastScrollerBuilder(binding.scrollView).useMd2Style().build(); - binding.adView.loadAd(new AdRequest.Builder().build()); + AdUtils.loadBanner(binding.adView); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(requireContext()); Typeface monospaceFont = FontManager.getMonospaceFont(requireContext(), prefs); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/tabs/CodeFragment.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/tabs/CodeFragment.java index 2dcab1de..e75274ee 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/tabs/CodeFragment.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/tabs/CodeFragment.java @@ -18,7 +18,7 @@ import com.d4rk.androidtutorials.java.utils.CodeHighlighter; import com.d4rk.androidtutorials.java.utils.CodeViewUtils; import com.d4rk.androidtutorials.java.utils.FontManager; -import com.google.android.gms.ads.AdRequest; +import com.d4rk.androidtutorials.java.ads.AdUtils; import java.io.BufferedReader; import java.io.IOException; @@ -57,7 +57,7 @@ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, private void setupUI() { new FastScrollerBuilder(binding.scrollView).useMd2Style().build(); - binding.adView.loadAd(new AdRequest.Builder().build()); + AdUtils.loadBanner(binding.adView); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(requireContext()); Typeface monospaceFont = FontManager.getMonospaceFont(requireContext(), prefs); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/tabs/LayoutFragment.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/tabs/LayoutFragment.java index 04f0d557..a4f23665 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/tabs/LayoutFragment.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/tabs/LayoutFragment.java @@ -18,7 +18,7 @@ import com.d4rk.androidtutorials.java.utils.CodeHighlighter; import com.d4rk.androidtutorials.java.utils.CodeViewUtils; import com.d4rk.androidtutorials.java.utils.FontManager; -import com.google.android.gms.ads.AdRequest; +import com.d4rk.androidtutorials.java.ads.AdUtils; import java.io.BufferedReader; import java.io.IOException; @@ -57,7 +57,7 @@ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, private void setupUI() { new FastScrollerBuilder(binding.scrollView).useMd2Style().build(); - binding.adView.loadAd(new AdRequest.Builder().build()); + AdUtils.loadBanner(binding.adView); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(requireContext()); Typeface monospaceFont = FontManager.getMonospaceFont(requireContext(), prefs); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/tabs/NoCodeFragment.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/tabs/NoCodeFragment.java index be385d68..b8f71968 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/tabs/NoCodeFragment.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/tabs/NoCodeFragment.java @@ -11,8 +11,7 @@ import com.d4rk.androidtutorials.java.R; import com.d4rk.androidtutorials.java.databinding.FragmentNoCodeBinding; -import com.google.android.gms.ads.AdRequest; -import com.google.android.gms.ads.MobileAds; +import com.d4rk.androidtutorials.java.ads.AdUtils; public class NoCodeFragment extends Fragment { private static final String ARG_MESSAGE = "arg_message"; @@ -31,8 +30,7 @@ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { FragmentNoCodeBinding binding = FragmentNoCodeBinding.inflate(inflater, container, false); - MobileAds.initialize(requireContext()); - binding.adView.loadAd(new AdRequest.Builder().build()); + AdUtils.loadBanner(binding.adView); String message = requireArguments().getString(ARG_MESSAGE, String.valueOf(R.string.no_java_code_needed)); binding.textViewNoCodeMessage.setText(message); 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 c8627c20..09a7e671 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 @@ -14,8 +14,7 @@ import com.d4rk.androidtutorials.java.data.model.PromotedApp; import com.d4rk.androidtutorials.java.databinding.FragmentHomeBinding; import com.d4rk.androidtutorials.java.utils.ConsentUtils; -import com.google.android.gms.ads.AdRequest; -import com.google.android.gms.ads.MobileAds; +import com.d4rk.androidtutorials.java.ads.AdUtils; import dagger.hilt.android.AndroidEntryPoint; import me.zhanghai.android.fastscroll.FastScrollerBuilder; @@ -85,12 +84,10 @@ public void onDestroyView() { private void initializeAds() { if (ConsentUtils.canShowAds(requireContext())) { - MobileAds.initialize(requireContext()); binding.smallBannerAd.setVisibility(View.VISIBLE); binding.largeBannerAd.setVisibility(View.VISIBLE); - AdRequest request = new AdRequest.Builder().build(); - binding.smallBannerAd.loadAd(request); - binding.largeBannerAd.loadAd(request); + AdUtils.loadBanner(binding.smallBannerAd); + AdUtils.loadBanner(binding.largeBannerAd); } else { binding.smallBannerAd.setVisibility(View.GONE); binding.largeBannerAd.setVisibility(View.GONE); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/main/MainActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/main/MainActivity.java index 2c02e929..6ce41624 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/main/MainActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/main/MainActivity.java @@ -42,8 +42,7 @@ import com.d4rk.androidtutorials.java.utils.ConsentUtils; import com.d4rk.androidtutorials.java.utils.EdgeToEdgeDelegate; import com.d4rk.androidtutorials.java.utils.ReviewHelper; -import com.google.android.gms.ads.AdRequest; -import com.google.android.gms.ads.MobileAds; +import com.d4rk.androidtutorials.java.ads.AdUtils; import com.google.android.material.navigation.NavigationBarView; import com.google.android.material.snackbar.Snackbar; import com.google.android.play.core.appupdate.AppUpdateInfo; @@ -81,10 +80,9 @@ public void onResume(@NonNull LifecycleOwner owner) { if (mBinding != null && mBinding.adView != null) { if (ConsentUtils.canShowAds(MainActivity.this)) { if (mBinding.adView.getVisibility() != View.VISIBLE) { - MobileAds.initialize(MainActivity.this); mBinding.adPlaceholder.setVisibility(View.GONE); mBinding.adView.setVisibility(View.VISIBLE); - mBinding.adView.loadAd(new AdRequest.Builder().build()); + AdUtils.loadBanner(mBinding.adView); } } else { mBinding.adView.setVisibility(View.GONE); @@ -204,10 +202,9 @@ private void observeViewModel() { navBarView.setLabelVisibilityMode(uiState.bottomNavVisibility()); if (binding.adView != null) { if (ConsentUtils.canShowAds(this)) { - MobileAds.initialize(this); binding.adPlaceholder.setVisibility(View.GONE); binding.adView.setVisibility(View.VISIBLE); - binding.adView.loadAd(new AdRequest.Builder().build()); + AdUtils.loadBanner(binding.adView); } else { binding.adView.setVisibility(View.GONE); binding.adPlaceholder.setVisibility(View.VISIBLE); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/support/SupportActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/support/SupportActivity.java index 0de20f93..103b72a5 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/support/SupportActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/support/SupportActivity.java @@ -11,6 +11,7 @@ import com.d4rk.androidtutorials.java.databinding.ActivitySupportBinding; import com.d4rk.androidtutorials.java.ui.components.navigation.BaseActivity; import com.google.android.gms.ads.AdRequest; +import com.d4rk.androidtutorials.java.ads.AdUtils; import java.util.List; @@ -30,9 +31,9 @@ protected void onCreate(Bundle savedInstanceState) { supportViewModel = new ViewModelProvider(this).get(SupportViewModel.class); - AdRequest adRequest = supportViewModel.initMobileAds(); - binding.supportNativeAd.loadAd(adRequest); - binding.bannerAdView.loadAd(adRequest); + AdUtils.initialize(this); + binding.supportNativeAd.loadAd(new AdRequest.Builder().build()); + AdUtils.loadBanner(binding.bannerAdView); binding.buttonWebAd.setOnClickListener(v -> startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://bit.ly/3p8bpjj")))); From 4257f063f4b6b53d68a538bb4a9211f0ac842b37 Mon Sep 17 00:00:00 2001 From: Mihai-Cristian Condrea Date: Sun, 14 Sep 2025 00:03:09 +0300 Subject: [PATCH 16/67] Fix banner ads not loading --- .../java/com/d4rk/androidtutorials/java/ads/AdUtils.java | 7 +++++-- .../java/ui/screens/main/MainActivity.java | 8 +++----- .../java/ui/screens/support/SupportActivity.java | 4 +--- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ads/AdUtils.java b/app/src/main/java/com/d4rk/androidtutorials/java/ads/AdUtils.java index e07b0068..23acf6cc 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ads/AdUtils.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ads/AdUtils.java @@ -3,6 +3,7 @@ import android.content.Context; import android.view.View; +import com.d4rk.androidtutorials.java.ads.views.NativeAdBannerView; import com.google.android.gms.ads.AdRequest; import com.google.android.gms.ads.AdView; import com.google.android.gms.ads.MobileAds; @@ -22,10 +23,12 @@ public static synchronized void initialize(Context context) { } public static void loadBanner(View adView) { - if (adView instanceof AdView) { - AdView view = (AdView) adView; + if (adView instanceof AdView view) { initialize(view.getContext()); view.loadAd(new AdRequest.Builder().build()); + } else if (adView instanceof NativeAdBannerView nativeView) { + initialize(nativeView.getContext()); + nativeView.loadAd(); } } } diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/main/MainActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/main/MainActivity.java index 6ce41624..f7fb82a8 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/main/MainActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/main/MainActivity.java @@ -79,11 +79,9 @@ public void onResume(@NonNull LifecycleOwner owner) { ConsentUtils.applyStoredConsent(MainActivity.this); if (mBinding != null && mBinding.adView != null) { if (ConsentUtils.canShowAds(MainActivity.this)) { - if (mBinding.adView.getVisibility() != View.VISIBLE) { - mBinding.adPlaceholder.setVisibility(View.GONE); - mBinding.adView.setVisibility(View.VISIBLE); - AdUtils.loadBanner(mBinding.adView); - } + mBinding.adPlaceholder.setVisibility(View.GONE); + mBinding.adView.setVisibility(View.VISIBLE); + AdUtils.loadBanner(mBinding.adView); } else { mBinding.adView.setVisibility(View.GONE); mBinding.adPlaceholder.setVisibility(View.VISIBLE); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/support/SupportActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/support/SupportActivity.java index 103b72a5..d50d5ade 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/support/SupportActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/support/SupportActivity.java @@ -10,7 +10,6 @@ import com.d4rk.androidtutorials.java.data.repository.SupportRepository; import com.d4rk.androidtutorials.java.databinding.ActivitySupportBinding; import com.d4rk.androidtutorials.java.ui.components.navigation.BaseActivity; -import com.google.android.gms.ads.AdRequest; import com.d4rk.androidtutorials.java.ads.AdUtils; import java.util.List; @@ -31,8 +30,7 @@ protected void onCreate(Bundle savedInstanceState) { supportViewModel = new ViewModelProvider(this).get(SupportViewModel.class); - AdUtils.initialize(this); - binding.supportNativeAd.loadAd(new AdRequest.Builder().build()); + AdUtils.loadBanner(binding.supportNativeAd); AdUtils.loadBanner(binding.bannerAdView); binding.buttonWebAd.setOnClickListener(v -> From f63551bc1f276a5394dc6bf39e46ec0c029159f2 Mon Sep 17 00:00:00 2001 From: Mihai-Cristian Condrea Date: Sun, 14 Sep 2025 00:17:05 +0300 Subject: [PATCH 17/67] Refactor up navigation handling --- .../navigation/UpNavigationActivity.java | 16 +--------------- .../basics/shortcuts/ShortcutsActivity.java | 7 +------ .../java/ui/screens/main/MainActivity.java | 1 - .../ui/screens/settings/SettingsActivity.java | 11 ++--------- .../screens/permissions/PermissionsActivity.java | 6 ------ 5 files changed, 4 insertions(+), 37 deletions(-) diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/components/navigation/UpNavigationActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/components/navigation/UpNavigationActivity.java index 9407ad79..e7be318b 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/components/navigation/UpNavigationActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/components/navigation/UpNavigationActivity.java @@ -1,22 +1,8 @@ package com.d4rk.androidtutorials.java.ui.components.navigation; -import android.os.Bundle; - -import androidx.annotation.Nullable; -import androidx.appcompat.app.ActionBar; -import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.NavUtils; -public abstract class UpNavigationActivity extends AppCompatActivity { - @Override - protected void onPostCreate(@Nullable Bundle savedInstanceState) { - super.onPostCreate(savedInstanceState); - ActionBar actionBar = getSupportActionBar(); - if (actionBar != null) { - actionBar.setDisplayHomeAsUpEnabled(true); - } - } - +public abstract class UpNavigationActivity extends BaseActivity { @Override public boolean onSupportNavigateUp() { NavUtils.navigateUpFromSameTask(this); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/ShortcutsActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/ShortcutsActivity.java index 084c3e54..1e87bf07 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/ShortcutsActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/basics/shortcuts/ShortcutsActivity.java @@ -4,13 +4,11 @@ import android.net.Uri; import android.os.Bundle; -import androidx.appcompat.app.ActionBar; import androidx.preference.PreferenceFragmentCompat; import com.d4rk.androidtutorials.java.R; import com.d4rk.androidtutorials.java.databinding.ActivityShortcutsBinding; import com.d4rk.androidtutorials.java.ui.components.navigation.UpNavigationActivity; -import com.d4rk.androidtutorials.java.utils.EdgeToEdgeDelegate; import com.d4rk.androidtutorials.java.ads.AdUtils; public class ShortcutsActivity extends UpNavigationActivity { @@ -20,12 +18,9 @@ protected void onCreate(Bundle savedInstanceState) { ActivityShortcutsBinding binding = ActivityShortcutsBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); - EdgeToEdgeDelegate.apply(this, binding.container); AdUtils.loadBanner(binding.adViewBottom); + AdUtils.loadBanner(binding.adViewBottom); getSupportFragmentManager().beginTransaction().replace(R.id.frame_layout_shortcuts, new SettingsFragment()).commit(); - ActionBar supportActionBar = getSupportActionBar(); - if (supportActionBar != null) - supportActionBar.setDisplayHomeAsUpEnabled(true); binding.buttonMore.setOnClickListener(v -> startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://developer.android.com/studio/intro/keyboard-shortcuts")))); } diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/main/MainActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/main/MainActivity.java index f7fb82a8..62b7aaa7 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/main/MainActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/main/MainActivity.java @@ -160,7 +160,6 @@ public void handleOnBackPressed() { private void setupActionBar() { ActionBar actionBar = getSupportActionBar(); if (actionBar != null) { - actionBar.setDisplayHomeAsUpEnabled(true); new AppBarConfiguration.Builder( R.id.navigation_home, R.id.navigation_android_studio, diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/settings/SettingsActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/settings/SettingsActivity.java index 94a6344b..3002629d 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/settings/SettingsActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/settings/SettingsActivity.java @@ -4,19 +4,17 @@ import android.os.Bundle; import androidx.annotation.Nullable; -import androidx.appcompat.app.ActionBar; -import androidx.appcompat.app.AppCompatActivity; import androidx.lifecycle.ViewModelProvider; import androidx.preference.ListPreference; import com.d4rk.androidtutorials.java.R; import com.d4rk.androidtutorials.java.databinding.ActivitySettingsBinding; -import com.d4rk.androidtutorials.java.utils.EdgeToEdgeDelegate; +import com.d4rk.androidtutorials.java.ui.components.navigation.UpNavigationActivity; import dagger.hilt.android.AndroidEntryPoint; @AndroidEntryPoint -public class SettingsActivity extends AppCompatActivity +public class SettingsActivity extends UpNavigationActivity implements SharedPreferences.OnSharedPreferenceChangeListener, androidx.preference.Preference.SummaryProvider { @@ -28,7 +26,6 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { ActivitySettingsBinding binding = ActivitySettingsBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); - EdgeToEdgeDelegate.apply(this, binding.container); settingsViewModel = new ViewModelProvider(this).get(SettingsViewModel.class); settingsViewModel.applyConsent(); @@ -37,10 +34,6 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { .replace(R.id.settings, new SettingsFragment()) .commit(); - ActionBar supportActionBar = getSupportActionBar(); - if (supportActionBar != null) { - supportActionBar.setDisplayHomeAsUpEnabled(true); - } settingsViewModel.registerPreferenceChangeListener(this); } diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/settings/screens/permissions/PermissionsActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/settings/screens/permissions/PermissionsActivity.java index eaebe3ae..64ef2d7e 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/settings/screens/permissions/PermissionsActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/settings/screens/permissions/PermissionsActivity.java @@ -7,7 +7,6 @@ import com.d4rk.androidtutorials.java.R; import com.d4rk.androidtutorials.java.databinding.ActivityPermissionsBinding; import com.d4rk.androidtutorials.java.ui.components.navigation.UpNavigationActivity; -import com.d4rk.androidtutorials.java.utils.EdgeToEdgeDelegate; public class PermissionsActivity extends UpNavigationActivity { @Override @@ -16,12 +15,7 @@ protected void onCreate(Bundle savedInstanceState) { ActivityPermissionsBinding binding = ActivityPermissionsBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); - EdgeToEdgeDelegate.apply(this, binding.container); - getSupportFragmentManager().beginTransaction().replace(R.id.frame_layout_permissions, new SettingsFragment()).commit(); - if (getSupportActionBar() != null) { - getSupportActionBar().setDisplayHomeAsUpEnabled(true); - } } public static class SettingsFragment extends PreferenceFragmentCompat { From 74713c37b2bef3335b55b467cdac08a8a9eaf43a Mon Sep 17 00:00:00 2001 From: Mihai-Cristian Condrea Date: Sun, 14 Sep 2025 00:24:52 +0300 Subject: [PATCH 18/67] fix up navigation to respect back stack --- app/src/main/AndroidManifest.xml | 2 +- .../java/ui/components/navigation/UpNavigationActivity.java | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 3ddffffa..8dd3e47f 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -175,7 +175,7 @@ android:name=".ui.screens.settings.screens.permissions.PermissionsActivity" android:exported="false" android:label="@string/permissions" - android:parentActivityName=".ui.screens.main.MainActivity" /> + android:parentActivityName=".ui.screens.settings.SettingsActivity" /> Date: Sun, 14 Sep 2025 00:31:57 +0300 Subject: [PATCH 19/67] feat: introduce NoCodeAdFragment base --- .../java/ui/components/NoCodeAdFragment.java | 61 +++++++++++++++++++ .../buttons/tabs/ButtonsTabCodeFragment.java | 18 +++--- .../clock/tabs/ClockTabCodeFragment.java | 14 ++--- .../data/room/tabs/RoomTabCodeFragment.java | 17 +++--- .../tabs/LinearLayoutTabCodeFragment.java | 14 ++--- .../tabs/RelativeLayoutTabCodeFragment.java | 14 ++--- .../tabs/TableLayoutTabCodeFragment.java | 14 ++--- .../tabs/RetrofitTabCodeFragment.java | 17 +++--- .../tabs/ProgressBarTabCodeFragment.java | 18 +++--- .../images/tabs/ImagesTabCodeFragment.java | 14 ++--- 10 files changed, 122 insertions(+), 79 deletions(-) create mode 100644 app/src/main/java/com/d4rk/androidtutorials/java/ui/components/NoCodeAdFragment.java diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/components/NoCodeAdFragment.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/components/NoCodeAdFragment.java new file mode 100644 index 00000000..7ffe0e8b --- /dev/null +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/components/NoCodeAdFragment.java @@ -0,0 +1,61 @@ +package com.d4rk.androidtutorials.java.ui.components; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.viewbinding.ViewBinding; + +import com.d4rk.androidtutorials.java.R; +import com.d4rk.androidtutorials.java.ads.AdUtils; + +/** + * Generic fragment that inflates a provided ViewBinding and loads a banner ad. + * + * @param The type of ViewBinding associated with this fragment. + */ +public abstract class NoCodeAdFragment extends Fragment { + protected T binding; + + /** + * Inflate the ViewBinding for this fragment. + * + * @param inflater LayoutInflater to use. + * @param container Optional container. + * @return The inflated binding. + */ + @NonNull + protected abstract T inflateBinding(@NonNull LayoutInflater inflater, @Nullable ViewGroup container); + + /** + * Called after the binding has been created and the banner ad loaded. + * Subclasses can override to perform additional setup. + * + * @param binding The binding instance. + * @param savedInstanceState Saved instance state. + */ + protected void onBindingCreated(@NonNull T binding, @Nullable Bundle savedInstanceState) { + // default no-op + } + + @Override + @NonNull + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + binding = inflateBinding(inflater, container); + View adView = binding.getRoot().findViewById(R.id.adView); + AdUtils.loadBanner(adView); + onBindingCreated(binding, savedInstanceState); + return binding.getRoot(); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + binding = null; + } +} diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/buttons/buttons/tabs/ButtonsTabCodeFragment.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/buttons/buttons/tabs/ButtonsTabCodeFragment.java index d91cdadc..265756fc 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/buttons/buttons/tabs/ButtonsTabCodeFragment.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/buttons/buttons/tabs/ButtonsTabCodeFragment.java @@ -5,19 +5,17 @@ import android.os.Bundle; import android.util.Log; import android.view.LayoutInflater; -import android.view.View; import android.view.ViewGroup; import androidx.annotation.NonNull; -import androidx.fragment.app.Fragment; import androidx.preference.PreferenceManager; import com.d4rk.androidtutorials.java.R; import com.d4rk.androidtutorials.java.databinding.FragmentSameCodeBinding; +import com.d4rk.androidtutorials.java.ui.components.NoCodeAdFragment; import com.d4rk.androidtutorials.java.utils.CodeHighlighter; import com.d4rk.androidtutorials.java.utils.CodeViewUtils; import com.d4rk.androidtutorials.java.utils.FontManager; -import com.d4rk.androidtutorials.java.ads.AdUtils; import java.io.BufferedReader; import java.io.IOException; @@ -26,13 +24,17 @@ import me.zhanghai.android.fastscroll.FastScrollerBuilder; -public class ButtonsTabCodeFragment extends Fragment { +public class ButtonsTabCodeFragment extends NoCodeAdFragment { @Override - public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - FragmentSameCodeBinding binding = FragmentSameCodeBinding.inflate(inflater, container, false); + @NonNull + protected FragmentSameCodeBinding inflateBinding(@NonNull LayoutInflater inflater, ViewGroup container) { + return FragmentSameCodeBinding.inflate(inflater, container, false); + } + + @Override + protected void onBindingCreated(@NonNull FragmentSameCodeBinding binding, Bundle savedInstanceState) { new FastScrollerBuilder(binding.scrollView).useMd2Style().build(); - AdUtils.loadBanner(binding.adView); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(requireContext()); Typeface monospaceFont = FontManager.getMonospaceFont(requireContext(), prefs); @@ -51,7 +53,5 @@ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Log.e("ButtonsTabCode", "Error reading code", e); } binding.textViewWarning.setText(R.string.same_code_buttons); - return binding.getRoot(); } - } \ No newline at end of file diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/clocks/clock/tabs/ClockTabCodeFragment.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/clocks/clock/tabs/ClockTabCodeFragment.java index bb47fa2e..74c61e42 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/clocks/clock/tabs/ClockTabCodeFragment.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/clocks/clock/tabs/ClockTabCodeFragment.java @@ -1,21 +1,17 @@ package com.d4rk.androidtutorials.java.ui.screens.android.lessons.clocks.clock.tabs; -import android.os.Bundle; import android.view.LayoutInflater; -import android.view.View; import android.view.ViewGroup; import androidx.annotation.NonNull; -import androidx.fragment.app.Fragment; import com.d4rk.androidtutorials.java.databinding.FragmentNoCodeBinding; -import com.d4rk.androidtutorials.java.ads.AdUtils; +import com.d4rk.androidtutorials.java.ui.components.NoCodeAdFragment; -public class ClockTabCodeFragment extends Fragment { +public class ClockTabCodeFragment extends NoCodeAdFragment { @Override - public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - com.d4rk.androidtutorials.java.databinding.FragmentNoCodeBinding binding = FragmentNoCodeBinding.inflate(inflater, container, false); - AdUtils.loadBanner(binding.adView); - return binding.getRoot(); + @NonNull + protected FragmentNoCodeBinding inflateBinding(@NonNull LayoutInflater inflater, ViewGroup container) { + return FragmentNoCodeBinding.inflate(inflater, container, false); } } \ No newline at end of file diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/data/room/tabs/RoomTabCodeFragment.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/data/room/tabs/RoomTabCodeFragment.java index cd3c7eb6..5eb643b2 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/data/room/tabs/RoomTabCodeFragment.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/data/room/tabs/RoomTabCodeFragment.java @@ -5,19 +5,17 @@ import android.os.Bundle; import android.util.Log; import android.view.LayoutInflater; -import android.view.View; import android.view.ViewGroup; import androidx.annotation.NonNull; -import androidx.fragment.app.Fragment; import androidx.preference.PreferenceManager; import com.d4rk.androidtutorials.java.R; import com.d4rk.androidtutorials.java.databinding.FragmentCodeBinding; +import com.d4rk.androidtutorials.java.ui.components.NoCodeAdFragment; import com.d4rk.androidtutorials.java.utils.CodeHighlighter; import com.d4rk.androidtutorials.java.utils.CodeViewUtils; import com.d4rk.androidtutorials.java.utils.FontManager; -import com.d4rk.androidtutorials.java.ads.AdUtils; import java.io.BufferedReader; import java.io.IOException; @@ -29,13 +27,17 @@ /** * Shows the Java implementation for the Room example. */ -public class RoomTabCodeFragment extends Fragment { +public class RoomTabCodeFragment extends NoCodeAdFragment { @Override - public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - FragmentCodeBinding binding = FragmentCodeBinding.inflate(inflater, container, false); + @NonNull + protected FragmentCodeBinding inflateBinding(@NonNull LayoutInflater inflater, ViewGroup container) { + return FragmentCodeBinding.inflate(inflater, container, false); + } + + @Override + protected void onBindingCreated(@NonNull FragmentCodeBinding binding, Bundle savedInstanceState) { new FastScrollerBuilder(binding.scrollView).useMd2Style().build(); - AdUtils.loadBanner(binding.adView); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(requireContext()); Typeface monospaceFont = FontManager.getMonospaceFont(requireContext(), prefs); @@ -53,7 +55,6 @@ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, } catch (IOException e) { Log.e("RoomTabCode", "Error reading code", e); } - return binding.getRoot(); } } diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/layouts/linear/tabs/LinearLayoutTabCodeFragment.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/layouts/linear/tabs/LinearLayoutTabCodeFragment.java index d6102d9c..948ceeab 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/layouts/linear/tabs/LinearLayoutTabCodeFragment.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/layouts/linear/tabs/LinearLayoutTabCodeFragment.java @@ -1,21 +1,17 @@ package com.d4rk.androidtutorials.java.ui.screens.android.lessons.layouts.linear.tabs; -import android.os.Bundle; import android.view.LayoutInflater; -import android.view.View; import android.view.ViewGroup; import androidx.annotation.NonNull; -import androidx.fragment.app.Fragment; import com.d4rk.androidtutorials.java.databinding.FragmentNoCodeBinding; -import com.d4rk.androidtutorials.java.ads.AdUtils; +import com.d4rk.androidtutorials.java.ui.components.NoCodeAdFragment; -public class LinearLayoutTabCodeFragment extends Fragment { +public class LinearLayoutTabCodeFragment extends NoCodeAdFragment { @Override - public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - com.d4rk.androidtutorials.java.databinding.FragmentNoCodeBinding binding = FragmentNoCodeBinding.inflate(inflater, container, false); - AdUtils.loadBanner(binding.adView); - return binding.getRoot(); + @NonNull + protected FragmentNoCodeBinding inflateBinding(@NonNull LayoutInflater inflater, ViewGroup container) { + return FragmentNoCodeBinding.inflate(inflater, container, false); } } \ No newline at end of file diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/layouts/relative/tabs/RelativeLayoutTabCodeFragment.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/layouts/relative/tabs/RelativeLayoutTabCodeFragment.java index 0028fadb..8a7b61cd 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/layouts/relative/tabs/RelativeLayoutTabCodeFragment.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/layouts/relative/tabs/RelativeLayoutTabCodeFragment.java @@ -1,21 +1,17 @@ package com.d4rk.androidtutorials.java.ui.screens.android.lessons.layouts.relative.tabs; -import android.os.Bundle; import android.view.LayoutInflater; -import android.view.View; import android.view.ViewGroup; import androidx.annotation.NonNull; -import androidx.fragment.app.Fragment; import com.d4rk.androidtutorials.java.databinding.FragmentNoCodeBinding; -import com.d4rk.androidtutorials.java.ads.AdUtils; +import com.d4rk.androidtutorials.java.ui.components.NoCodeAdFragment; -public class RelativeLayoutTabCodeFragment extends Fragment { +public class RelativeLayoutTabCodeFragment extends NoCodeAdFragment { @Override - public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - com.d4rk.androidtutorials.java.databinding.FragmentNoCodeBinding binding = FragmentNoCodeBinding.inflate(inflater, container, false); - AdUtils.loadBanner(binding.adView); - return binding.getRoot(); + @NonNull + protected FragmentNoCodeBinding inflateBinding(@NonNull LayoutInflater inflater, ViewGroup container) { + return FragmentNoCodeBinding.inflate(inflater, container, false); } } \ No newline at end of file diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/layouts/table/tabs/TableLayoutTabCodeFragment.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/layouts/table/tabs/TableLayoutTabCodeFragment.java index 6c481695..75063288 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/layouts/table/tabs/TableLayoutTabCodeFragment.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/layouts/table/tabs/TableLayoutTabCodeFragment.java @@ -1,21 +1,17 @@ package com.d4rk.androidtutorials.java.ui.screens.android.lessons.layouts.table.tabs; -import android.os.Bundle; import android.view.LayoutInflater; -import android.view.View; import android.view.ViewGroup; import androidx.annotation.NonNull; -import androidx.fragment.app.Fragment; import com.d4rk.androidtutorials.java.databinding.FragmentNoCodeBinding; -import com.d4rk.androidtutorials.java.ads.AdUtils; +import com.d4rk.androidtutorials.java.ui.components.NoCodeAdFragment; -public class TableLayoutTabCodeFragment extends Fragment { +public class TableLayoutTabCodeFragment extends NoCodeAdFragment { @Override - public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - com.d4rk.androidtutorials.java.databinding.FragmentNoCodeBinding binding = FragmentNoCodeBinding.inflate(inflater, container, false); - AdUtils.loadBanner(binding.adView); - return binding.getRoot(); + @NonNull + protected FragmentNoCodeBinding inflateBinding(@NonNull LayoutInflater inflater, ViewGroup container) { + return FragmentNoCodeBinding.inflate(inflater, container, false); } } \ No newline at end of file diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/networking/retrofit/tabs/RetrofitTabCodeFragment.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/networking/retrofit/tabs/RetrofitTabCodeFragment.java index ed991d06..1116addf 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/networking/retrofit/tabs/RetrofitTabCodeFragment.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/networking/retrofit/tabs/RetrofitTabCodeFragment.java @@ -5,19 +5,17 @@ import android.os.Bundle; import android.util.Log; import android.view.LayoutInflater; -import android.view.View; import android.view.ViewGroup; import androidx.annotation.NonNull; -import androidx.fragment.app.Fragment; import androidx.preference.PreferenceManager; import com.d4rk.androidtutorials.java.R; import com.d4rk.androidtutorials.java.databinding.FragmentCodeBinding; +import com.d4rk.androidtutorials.java.ui.components.NoCodeAdFragment; import com.d4rk.androidtutorials.java.utils.CodeHighlighter; import com.d4rk.androidtutorials.java.utils.CodeViewUtils; import com.d4rk.androidtutorials.java.utils.FontManager; -import com.d4rk.androidtutorials.java.ads.AdUtils; import java.io.BufferedReader; import java.io.IOException; @@ -29,13 +27,17 @@ /** * Shows the Java implementation for the Retrofit example. */ -public class RetrofitTabCodeFragment extends Fragment { +public class RetrofitTabCodeFragment extends NoCodeAdFragment { @Override - public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - FragmentCodeBinding binding = FragmentCodeBinding.inflate(inflater, container, false); + @NonNull + protected FragmentCodeBinding inflateBinding(@NonNull LayoutInflater inflater, ViewGroup container) { + return FragmentCodeBinding.inflate(inflater, container, false); + } + + @Override + protected void onBindingCreated(@NonNull FragmentCodeBinding binding, Bundle savedInstanceState) { new FastScrollerBuilder(binding.scrollView).useMd2Style().build(); - AdUtils.loadBanner(binding.adView); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(requireContext()); Typeface monospaceFont = FontManager.getMonospaceFont(requireContext(), prefs); @@ -53,6 +55,5 @@ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, } catch (IOException e) { Log.e("RetrofitTabCode", "Error reading code", e); } - return binding.getRoot(); } } diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/progress/progressbar/tabs/ProgressBarTabCodeFragment.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/progress/progressbar/tabs/ProgressBarTabCodeFragment.java index 0497aca7..b9d20110 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/progress/progressbar/tabs/ProgressBarTabCodeFragment.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/progress/progressbar/tabs/ProgressBarTabCodeFragment.java @@ -5,19 +5,17 @@ import android.os.Bundle; import android.util.Log; import android.view.LayoutInflater; -import android.view.View; import android.view.ViewGroup; import androidx.annotation.NonNull; -import androidx.fragment.app.Fragment; import androidx.preference.PreferenceManager; import com.d4rk.androidtutorials.java.R; import com.d4rk.androidtutorials.java.databinding.FragmentCodeBinding; +import com.d4rk.androidtutorials.java.ui.components.NoCodeAdFragment; import com.d4rk.androidtutorials.java.utils.CodeHighlighter; import com.d4rk.androidtutorials.java.utils.CodeViewUtils; import com.d4rk.androidtutorials.java.utils.FontManager; -import com.d4rk.androidtutorials.java.ads.AdUtils; import java.io.BufferedReader; import java.io.IOException; @@ -26,13 +24,17 @@ import me.zhanghai.android.fastscroll.FastScrollerBuilder; -public class ProgressBarTabCodeFragment extends Fragment { +public class ProgressBarTabCodeFragment extends NoCodeAdFragment { @Override - public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - FragmentCodeBinding binding = FragmentCodeBinding.inflate(inflater, container, false); + @NonNull + protected FragmentCodeBinding inflateBinding(@NonNull LayoutInflater inflater, ViewGroup container) { + return FragmentCodeBinding.inflate(inflater, container, false); + } + + @Override + protected void onBindingCreated(@NonNull FragmentCodeBinding binding, Bundle savedInstanceState) { new FastScrollerBuilder(binding.scrollView).useMd2Style().build(); - AdUtils.loadBanner(binding.adView); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(requireContext()); Typeface monospaceFont = FontManager.getMonospaceFont(requireContext(), prefs); @@ -50,7 +52,5 @@ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, } catch (IOException e) { Log.e("ProgressBarTabCode", "Error reading code", e); } - return binding.getRoot(); } - } \ No newline at end of file diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/views/images/tabs/ImagesTabCodeFragment.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/views/images/tabs/ImagesTabCodeFragment.java index bc4eebba..5706d05b 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/views/images/tabs/ImagesTabCodeFragment.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/android/lessons/views/images/tabs/ImagesTabCodeFragment.java @@ -1,21 +1,17 @@ package com.d4rk.androidtutorials.java.ui.screens.android.lessons.views.images.tabs; -import android.os.Bundle; import android.view.LayoutInflater; -import android.view.View; import android.view.ViewGroup; import androidx.annotation.NonNull; -import androidx.fragment.app.Fragment; import com.d4rk.androidtutorials.java.databinding.FragmentNoCodeBinding; -import com.d4rk.androidtutorials.java.ads.AdUtils; +import com.d4rk.androidtutorials.java.ui.components.NoCodeAdFragment; -public class ImagesTabCodeFragment extends Fragment { +public class ImagesTabCodeFragment extends NoCodeAdFragment { @Override - public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - com.d4rk.androidtutorials.java.databinding.FragmentNoCodeBinding binding = FragmentNoCodeBinding.inflate(inflater, container, false); - AdUtils.loadBanner(binding.adView); - return binding.getRoot(); + @NonNull + protected FragmentNoCodeBinding inflateBinding(@NonNull LayoutInflater inflater, ViewGroup container) { + return FragmentNoCodeBinding.inflate(inflater, container, false); } } \ No newline at end of file From 1cf2e931edcf56cd75e3fd1ba48403d953d48d8a Mon Sep 17 00:00:00 2001 From: Mihai-Cristian Condrea Date: Sun, 14 Sep 2025 00:39:42 +0300 Subject: [PATCH 20/67] fix: correct ad view id in base fragment --- .../androidtutorials/java/ui/components/NoCodeAdFragment.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/components/NoCodeAdFragment.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/components/NoCodeAdFragment.java index 7ffe0e8b..67830a48 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/components/NoCodeAdFragment.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/components/NoCodeAdFragment.java @@ -47,7 +47,7 @@ protected void onBindingCreated(@NonNull T binding, @Nullable Bundle savedInstan public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { binding = inflateBinding(inflater, container); - View adView = binding.getRoot().findViewById(R.id.adView); + View adView = binding.getRoot().findViewById(R.id.ad_view); AdUtils.loadBanner(adView); onBindingCreated(binding, savedInstanceState); return binding.getRoot(); From 9946be68053427745b11f4bd2b8547dcff36ca7f Mon Sep 17 00:00:00 2001 From: Mihai-Cristian Condrea Date: Sun, 14 Sep 2025 00:50:28 +0300 Subject: [PATCH 21/67] Use MediaView in bottom bar native ad --- app/src/main/res/layout/ad_bottom_app_bar.xml | 100 ++++++++++-------- 1 file changed, 58 insertions(+), 42 deletions(-) diff --git a/app/src/main/res/layout/ad_bottom_app_bar.xml b/app/src/main/res/layout/ad_bottom_app_bar.xml index 52ed49df..eafdc6bb 100644 --- a/app/src/main/res/layout/ad_bottom_app_bar.xml +++ b/app/src/main/res/layout/ad_bottom_app_bar.xml @@ -1,65 +1,81 @@ - - - + app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.CardViewTopRounded"> + android:orientation="vertical" + android:padding="16dp"> - + + android:layout_marginTop="8dp" + android:gravity="center_vertical" + android:orientation="horizontal"> - + + + + + + + + android:layout_weight="1" + android:orientation="vertical"> - + + + + + + android:layout_marginStart="8dp" + android:minWidth="88dp" + android:minHeight="40dp" /> - - - + + From aa1826d2d3417a65ec1b28ffed27c894140946eb Mon Sep 17 00:00:00 2001 From: Mihai-Cristian Condrea Date: Sun, 14 Sep 2025 01:01:13 +0300 Subject: [PATCH 22/67] Safeguard app update flow --- .../java/ui/screens/main/MainActivity.java | 38 ++++++++++++------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/main/MainActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/main/MainActivity.java index 62b7aaa7..5d551606 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/main/MainActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/main/MainActivity.java @@ -20,6 +20,7 @@ import androidx.appcompat.app.AppCompatActivity; import androidx.core.splashscreen.SplashScreen; import androidx.lifecycle.DefaultLifecycleObserver; +import androidx.lifecycle.Lifecycle; import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.ViewModelProvider; import androidx.navigation.NavController; @@ -62,15 +63,7 @@ public class MainActivity extends AppCompatActivity { private static final long BACK_PRESS_INTERVAL = 2000; - private final ActivityResultLauncher updateActivityResultLauncher = - registerForActivityResult( - new ActivityResultContracts.StartIntentSenderForResult(), - result -> { - if (result.getResultCode() != Activity.RESULT_OK) { - Log.d("MainActivity", "In-app update flow failed! " + result.getResultCode()); - } - } - ); + private ActivityResultLauncher updateActivityResultLauncher; private final SparseIntArray navOrder = new SparseIntArray(); private ActivityMainBinding mBinding; private final DefaultLifecycleObserver lifecycleObserver = new DefaultLifecycleObserver() { @@ -101,6 +94,15 @@ public void onResume(@NonNull LifecycleOwner owner) { protected void onCreate(Bundle savedInstanceState) { SplashScreen.installSplashScreen(this); super.onCreate(savedInstanceState); + + updateActivityResultLauncher = registerForActivityResult( + new ActivityResultContracts.StartIntentSenderForResult(), + result -> { + if (result.getResultCode() != Activity.RESULT_OK) { + Log.d("MainActivity", "In-app update flow failed! " + result.getResultCode()); + } + } + ); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); if (!prefs.getBoolean(getString(R.string.key_onboarding_complete), false)) { startActivity(new Intent(this, StartupActivity.class)); @@ -315,13 +317,16 @@ protected void onResume() { } private void checkForFlexibleOrImmediateUpdate() { - appUpdateManager.getAppUpdateInfo().addOnSuccessListener(appUpdateInfo -> { - boolean updateAvailable = appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE; - if (updateAvailable) { + appUpdateManager + .getAppUpdateInfo() + .addOnSuccessListener(this, appUpdateInfo -> { + boolean updateAvailable = + appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE; + if (updateAvailable && getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED)) { startImmediateUpdate(appUpdateInfo); } }) - .addOnFailureListener(e -> { + .addOnFailureListener(this, e -> { if (!BuildConfig.DEBUG) { Snackbar.make( findViewById(android.R.id.content), @@ -329,7 +334,7 @@ private void checkForFlexibleOrImmediateUpdate() { Snackbar.LENGTH_LONG ).show(); } - }); + }); } private void checkInAppReview() { @@ -348,6 +353,11 @@ private void checkInAppReview() { } private void startImmediateUpdate(AppUpdateInfo appUpdateInfo) { + if (updateActivityResultLauncher == null + || !getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED)) { + Log.w("MainActivity", "Update launcher not ready"); + return; + } try { appUpdateManager.startUpdateFlowForResult( appUpdateInfo, From d85345c2845558d586f39715376a62307d02339c Mon Sep 17 00:00:00 2001 From: D4rK7355608 Date: Sun, 14 Sep 2025 01:09:57 +0300 Subject: [PATCH 23/67] Refactor: Organize documentation into subdirectories This commit moves existing documentation files into more specific subdirectories within the `docs/` folder to improve organization. It also updates the `AGENTS.md` and `README.md` files to reflect these new paths. Key changes: - Moved architecture recommendations to `docs/core/`. - Created new directories for screens, UI/UX, and general style guidance. - Added new documentation files for: - Core architecture and modules (`architecture.md`, `core-module.md`, `data-layer.md`, `solid-principles-android.md`) - Screen-specific details (`help.md`, `home.md`, `main.md`, `onboarding.md`, `settings.md`, `permissions.md`, `startup.md`, `support.md`) - UI components (`ui-components.md`) - UI/UX guidelines (`ui-ux-guidelines.md`) - General style guidance (`style-guidance.md`) --- AGENTS.md | 27 ++- README.md | 2 +- docs/android-architecture-recommendations.md | 158 -------------- .../android-architecture-recommendations.md | 169 +++++++++++++++ docs/core/architecture.md | 20 ++ docs/core/core-module.md | 52 +++++ docs/core/data-layer.md | 42 ++++ docs/core/solid-principles-android.md | 199 ++++++++++++++++++ docs/core/ui-components.md | 92 ++++++++ docs/general/style-guidance.md | 9 + docs/screens/help.md | 40 ++++ docs/screens/home.md | 27 +++ docs/screens/main.md | 48 +++++ docs/screens/onboarding.md | 37 ++++ docs/screens/settings/privacy/permissions.md | 17 ++ docs/screens/settings/settings.md | 15 ++ docs/screens/startup.md | 25 +++ docs/screens/support.md | 15 ++ docs/ui-ux/ui-ux-guidelines.md | 36 ++++ 19 files changed, 868 insertions(+), 162 deletions(-) delete mode 100644 docs/android-architecture-recommendations.md create mode 100644 docs/core/android-architecture-recommendations.md create mode 100644 docs/core/architecture.md create mode 100644 docs/core/core-module.md create mode 100644 docs/core/data-layer.md create mode 100644 docs/core/solid-principles-android.md create mode 100644 docs/core/ui-components.md create mode 100644 docs/general/style-guidance.md create mode 100644 docs/screens/help.md create mode 100644 docs/screens/home.md create mode 100644 docs/screens/main.md create mode 100644 docs/screens/onboarding.md create mode 100644 docs/screens/settings/privacy/permissions.md create mode 100644 docs/screens/settings/settings.md create mode 100644 docs/screens/startup.md create mode 100644 docs/screens/support.md create mode 100644 docs/ui-ux/ui-ux-guidelines.md diff --git a/AGENTS.md b/AGENTS.md index c90d9170..4e66f112 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -14,11 +14,32 @@ You are an experienced Android app developer. - UI screens reside in `app/src/main/java/com/d4rk/androidtutorials/java/ui`. - Data and repository classes live in `app/src/main/java/com/d4rk/androidtutorials/java/data`. -## Testing -- Run `./gradlew test` before committing changes. - ## Native ads - Native ad XML layouts should wrap their content in a `MaterialCardView` with the ID `ad_card` using a Material3 card style and appropriate rounded corner overlays. - Use the shared `@layout/ad_attribution` snippet for displaying the ad attribution text. - Include the attribution exactly as `` with no additional attributes such as padding or margins. - Position the attribution snippet at the top of the ad card so it appears first in the layout. + +## Architecture and principles +@./docs/core/ + +## UI/UX guidelines +@./docs/ui-ux/ + +## Coroutines and Flow +@./docs/coroutines-flow/ + +## Compose rules +@./docs/compose/ + +## Testing guidelines +@./docs/tests/ + +## General policies +@./docs/general/ + +# General app and libraries used documentation +@./docs/screens/ + +## Testing +- Run `./gradlew test` before committing changes. \ No newline at end of file diff --git a/README.md b/README.md index 252e87f9..b7f49869 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ operations through a small set of use cases. This pattern was inspired by the implemented in a simplified form to avoid adding heavy dependencies. The result keeps the UI simple and ensures a clear separation of concerns across the whole app. -For detailed guidance, see [Recommendations for Android architecture](docs/android-architecture-recommendations.md). +For detailed guidance, see [Recommendations for Android architecture](docs/core/android-architecture-recommendations.md). ## Feedback diff --git a/docs/android-architecture-recommendations.md b/docs/android-architecture-recommendations.md deleted file mode 100644 index 99a7b38a..00000000 --- a/docs/android-architecture-recommendations.md +++ /dev/null @@ -1,158 +0,0 @@ -# Recommendations for Android architecture (Java Projects) - -This page presents several Architecture best practices and recommendations. Adopt them to improve your app’s quality, robustness, and scalability. They also make it easier to maintain and test your app. - -**Note:** These recommendations are guidelines, not strict requirements. Adapt them to your app as needed. - -The best practices below are grouped by topic. Each has a priority that reflects how strongly the team recommends it. The list of priorities is as follows: - -* **Strongly recommended:** You should implement this practice unless it clashes fundamentally with your approach. -* **Recommended:** This practice is likely to improve your app. -* **Optional:** This practice can improve your app in certain circumstances. - -Before adopting these recommendations, you should be familiar with the Architecture guidance. - -## Layered architecture - -Our recommended layered architecture favors separation of concerns. It drives the UI from data models, complies with the single source of truth principle, and follows unidirectional data flow principles. Here are some best practices for layered architecture: - -| Recommendation | Description | -| --- | --- | -| **Use a clearly defined data layer.**
Strongly recommended | The data layer exposes application data to the rest of the app and contains the vast majority of business logic of your app.
You should create repositories even if they just contain a single data source.
In small apps, you can choose to place data layer types in a `data` package or module. | -| **Use a clearly defined UI layer.**
Strongly recommended | The UI layer displays the application data on the screen and serves as the primary point of user interaction.
In small apps, you can choose to place UI layer types in a `ui` package or module.
More UI layer best practices here. | -| **The data layer should expose application data using a repository.**
Strongly recommended | Components in the UI layer such as Activities, Fragments, or ViewModels shouldn't interact directly with a data source. Examples of data sources are databases, SharedPreferences, Firebase APIs, GPS, Bluetooth, or network status providers. | -| **Use a domain layer.**
Recommended in big apps | Use a domain layer (use cases) if you need to reuse business logic that interacts with the data layer across multiple ViewModels, or you want to simplify the business logic complexity of a particular ViewModel. | - -## UI layer - -The role of the UI layer is to display the application data on the screen and serve as the primary point of user interaction. Here are some best practices for the UI layer: - -| Recommendation | Description | -| --- | --- | -| **Follow Unidirectional Data Flow (UDF).**
Strongly recommended | Follow UDF principles, where ViewModels expose UI state using the observer pattern and receive actions from the UI through method calls. | -| **Use AAC ViewModels if their benefits apply to your app.**
Strongly recommended | Use AndroidX ViewModels to handle business logic and fetch application data to expose UI state to the UI. | -| **Use lifecycle-aware UI state collection.**
Strongly recommended | Collect UI state from the UI using lifecycle-aware APIs such as `repeatOnLifecycle`. | -| **Do not send events from the ViewModel to the UI.**
Strongly recommended | Process the event immediately in the ViewModel and cause a state update with the result of handling the event. | -| **Use a single-activity application.**
Recommended | Use Navigation Fragments to navigate between screens and deep link to your app if your app has more than one screen. | - -The following snippet outlines how to collect the UI state in a lifecycle-aware manner from a Java `Fragment`: - -```java -public class MyFragment extends Fragment { - - private MyViewModel viewModel; - - @Override - public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - viewModel = new ViewModelProvider(this).get(MyViewModel.class); - - viewModel.getUiState().observe(getViewLifecycleOwner(), value -> { - // Process item - }); - } -} -``` - -## ViewModel - -ViewModels are responsible for providing the UI state and access to the data layer. Here are some best practices for ViewModels: - -| Recommendation | Description | -| --- | --- | -| **ViewModels should be agnostic of the Android lifecycle.**
Strongly recommended | ViewModels shouldn't hold a reference to any lifecycle-related type. Don't pass `Activity`, `Fragment`, `Context`, or `Resources` as a dependency. If something needs a `Context` in the ViewModel, evaluate if it is in the right layer. | -| **Expose observable data.**
Strongly recommended | Use `LiveData` or other observable classes to expose application data from the ViewModel. | -| **Use ViewModels at screen level.**
Strongly recommended | Do not use ViewModels in reusable pieces of UI. Use ViewModels in screen-level Activities, Fragments, or destinations/graphs when using Jetpack Navigation. | -| **Use plain state holder classes in reusable UI components.**
Strongly recommended | Use plain state holder classes for handling complexity in reusable UI components. By doing this, the state can be hoisted and controlled externally. | -| **Do not use AndroidViewModel.**
Recommended | Use the `ViewModel` class, not `AndroidViewModel`. The `Application` class shouldn't be used in the ViewModel. Instead, move the dependency to the UI or the data layer. | -| **Expose a UI state.**
Recommended | ViewModels should expose data to the UI through a single `LiveData` or other observable property representing the UI state. | - -The following snippet outlines how to expose UI state from a ViewModel in Java: - -```java -@HiltViewModel -public class BookmarksViewModel extends ViewModel { - - private final MutableLiveData feedState = new MutableLiveData<>(); - - @Inject - public BookmarksViewModel(NewsRepository newsRepository) { - feedState.setValue(NewsFeedUiState.loading()); - // fetch data from repository and post updates - } - - public LiveData getFeedState() { - return feedState; - } -} -``` - -## Lifecycle - -The following are some best practices for working with the Android lifecycle: - -| Recommendation | Description | -| --- | --- | -| **Do not override lifecycle methods in Activities or Fragments.**
Strongly recommended | Do not override lifecycle methods such as `onResume` in Activities or Fragments. Use `LifecycleObserver` instead. If the app needs to perform work when the lifecycle reaches a certain state, use the `repeatOnLifecycle` API. | - -The following snippet outlines how to perform operations given a certain lifecycle state: - -```java -public class MyFragment extends Fragment { - @Override - public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - - getViewLifecycleOwner().getLifecycle().addObserver(new DefaultLifecycleObserver() { - @Override - public void onResume(@NonNull LifecycleOwner owner) { - // ... - } - - @Override - public void onPause(@NonNull LifecycleOwner owner) { - // ... - } - }); - } -} -``` - -## Handle dependencies - -There are several best practices you should observe when managing dependencies between components: - -| Recommendation | Description | -| --- | --- | -| **Use dependency injection.**
Strongly recommended | Use dependency injection best practices, mainly constructor injection when possible. | -| **Scope to a component when necessary.**
Strongly recommended | Scope to a dependency container when the type contains mutable data that needs to be shared or the type is expensive to initialize and is widely used in the app. | -| **Use Hilt.**
Recommended | Use Hilt or manual dependency injection in simple apps. Use Hilt if your project is complex enough. For example, if you have multiple screens with ViewModels, WorkManager usage, or advanced usage of Navigation such as ViewModels scoped to the nav graph. | - -## Testing - -The following are some best practices for testing: - -| Recommendation | Description | -| --- | --- | -| **Know what to test.**
Strongly recommended | Unless the project is roughly as simple as a hello world app, you should test it, at minimum with: unit test ViewModels (including observable data), unit test data layer entities (repositories and data sources), and UI navigation tests that are useful as regression tests in CI. | -| **Prefer fakes to mocks.**
Strongly recommended | Read more in the “Use test doubles in Android” documentation. | -| **Test LiveData.**
Strongly recommended | When testing `LiveData`, observe it and assert on emitted values. | - -## Models - -You should observe these best practices when developing models in your apps: - -| Recommendation | Description | -| --- | --- | -| **Create a model per layer in complex apps.**
Recommended | In complex apps, create new models in different layers or components when it makes sense. For example, a remote data source can map the model it receives through the network to a simpler class with just the data the app needs; repositories can map DAO models to simpler data classes with just the information the UI layer needs; ViewModel can include data layer models in `UiState` classes. | - -## Naming conventions - -When naming your codebase, you should be aware of the following best practices: - -| Recommendation | Description | -| --- | --- | -| **Naming methods.**
Optional | Methods should be a verb phrase. For example, `makePayment()`. | -| **Naming properties.**
Optional | Properties should be a noun phrase. For example, `inProgressTopicSelection`. | -| **Naming streams of data.**
Optional | When a class exposes a stream such as `LiveData`, the naming convention is `get{Model}Stream()`. For example, `getAuthorStream(): LiveData`. If the function returns a list of models the model name should be in the plural: `getAuthorsStream(): LiveData>`. | -| **Naming interfaces implementations.**
Optional | Names for the implementations of interfaces should be meaningful. Use `Default` as the prefix if a better name cannot be found. For example, for a `NewsRepository` interface, you could have an `OfflineFirstNewsRepository` or `InMemoryNewsRepository`. If you can find no good name, use `DefaultNewsRepository`. Fake implementations should be prefixed with `Fake`, as in `FakeAuthorsRepository`. | diff --git a/docs/core/android-architecture-recommendations.md b/docs/core/android-architecture-recommendations.md new file mode 100644 index 00000000..d1112927 --- /dev/null +++ b/docs/core/android-architecture-recommendations.md @@ -0,0 +1,169 @@ +# Recommendations for Android architecture (Java Projects) + +This page presents several Architecture best practices and recommendations. Adopt them to improve +your app’s quality, robustness, and scalability. They also make it easier to maintain and test your +app. + +**Note:** These recommendations are guidelines, not strict requirements. Adapt them to your app as +needed. + +The best practices below are grouped by topic. Each has a priority that reflects how strongly the +team recommends it. The list of priorities is as follows: + +* **Strongly recommended:** You should implement this practice unless it clashes fundamentally with + your approach. +* **Recommended:** This practice is likely to improve your app. +* **Optional:** This practice can improve your app in certain circumstances. + +Before adopting these recommendations, you should be familiar with the Architecture guidance. + +## Layered architecture + +Our recommended layered architecture favors separation of concerns. It drives the UI from data +models, complies with the single source of truth principle, and follows unidirectional data flow +principles. Here are some best practices for layered architecture: + +| Recommendation | Description | +|------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| **Use a clearly defined data layer.**
Strongly recommended | The data layer exposes application data to the rest of the app and contains the vast majority of business logic of your app.
You should create repositories even if they just contain a single data source.
In small apps, you can choose to place data layer types in a `data` package or module. | +| **Use a clearly defined UI layer.**
Strongly recommended | The UI layer displays the application data on the screen and serves as the primary point of user interaction.
In small apps, you can choose to place UI layer types in a `ui` package or module.
More UI layer best practices here. | +| **The data layer should expose application data using a repository.**
Strongly recommended | Components in the UI layer such as Activities, Fragments, or ViewModels shouldn't interact directly with a data source. Examples of data sources are databases, SharedPreferences, Firebase APIs, GPS, Bluetooth, or network status providers. | +| **Use a domain layer.**
Recommended in big apps | Use a domain layer (use cases) if you need to reuse business logic that interacts with the data layer across multiple ViewModels, or you want to simplify the business logic complexity of a particular ViewModel. | + +## UI layer + +The role of the UI layer is to display the application data on the screen and serve as the primary +point of user interaction. Here are some best practices for the UI layer: + +| Recommendation | Description | +|--------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------| +| **Follow Unidirectional Data Flow (UDF).**
Strongly recommended | Follow UDF principles, where ViewModels expose UI state using the observer pattern and receive actions from the UI through method calls. | +| **Use AAC ViewModels if their benefits apply to your app.**
Strongly recommended | Use AndroidX ViewModels to handle business logic and fetch application data to expose UI state to the UI. | +| **Use lifecycle-aware UI state collection.**
Strongly recommended | Collect UI state from the UI using lifecycle-aware APIs such as `repeatOnLifecycle`. | +| **Do not send events from the ViewModel to the UI.**
Strongly recommended | Process the event immediately in the ViewModel and cause a state update with the result of handling the event. | +| **Use a single-activity application.**
Recommended | Use Navigation Fragments to navigate between screens and deep link to your app if your app has more than one screen. | + +The following snippet outlines how to collect the UI state in a lifecycle-aware manner from a Java +`Fragment`: + +```java +public class MyFragment extends Fragment { + + private MyViewModel viewModel; + + @Override + public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + viewModel = new ViewModelProvider(this).get(MyViewModel.class); + + viewModel.getUiState().observe(getViewLifecycleOwner(), value -> { + // Process item + }); + } +} +``` + +## ViewModel + +ViewModels are responsible for providing the UI state and access to the data layer. Here are some +best practices for ViewModels: + +| Recommendation | Description | +|----------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| **ViewModels should be agnostic of the Android lifecycle.**
Strongly recommended | ViewModels shouldn't hold a reference to any lifecycle-related type. Don't pass `Activity`, `Fragment`, `Context`, or `Resources` as a dependency. If something needs a `Context` in the ViewModel, evaluate if it is in the right layer. | +| **Expose observable data.**
Strongly recommended | Use `LiveData` or other observable classes to expose application data from the ViewModel. | +| **Use ViewModels at screen level.**
Strongly recommended | Do not use ViewModels in reusable pieces of UI. Use ViewModels in screen-level Activities, Fragments, or destinations/graphs when using Jetpack Navigation. | +| **Use plain state holder classes in reusable UI components.**
Strongly recommended | Use plain state holder classes for handling complexity in reusable UI components. By doing this, the state can be hoisted and controlled externally. | +| **Do not use AndroidViewModel.**
Recommended | Use the `ViewModel` class, not `AndroidViewModel`. The `Application` class shouldn't be used in the ViewModel. Instead, move the dependency to the UI or the data layer. | +| **Expose a UI state.**
Recommended | ViewModels should expose data to the UI through a single `LiveData` or other observable property representing the UI state. | + +The following snippet outlines how to expose UI state from a ViewModel in Java: + +```java + +@HiltViewModel +public class BookmarksViewModel extends ViewModel { + + private final MutableLiveData feedState = new MutableLiveData<>(); + + @Inject + public BookmarksViewModel(NewsRepository newsRepository) { + feedState.setValue(NewsFeedUiState.loading()); + // fetch data from repository and post updates + } + + public LiveData getFeedState() { + return feedState; + } +} +``` + +## Lifecycle + +The following are some best practices for working with the Android lifecycle: + +| Recommendation | Description | +|--------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| **Do not override lifecycle methods in Activities or Fragments.**
Strongly recommended | Do not override lifecycle methods such as `onResume` in Activities or Fragments. Use `LifecycleObserver` instead. If the app needs to perform work when the lifecycle reaches a certain state, use the `repeatOnLifecycle` API. | + +The following snippet outlines how to perform operations given a certain lifecycle state: + +```java +public class MyFragment extends Fragment { + @Override + public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + getViewLifecycleOwner().getLifecycle().addObserver(new DefaultLifecycleObserver() { + @Override + public void onResume(@NonNull LifecycleOwner owner) { + // ... + } + + @Override + public void onPause(@NonNull LifecycleOwner owner) { + // ... + } + }); + } +} +``` + +## Handle dependencies + +There are several best practices you should observe when managing dependencies between components: + +| Recommendation | Description | +|-------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| **Use dependency injection.**
Strongly recommended | Use dependency injection best practices, mainly constructor injection when possible. | +| **Scope to a component when necessary.**
Strongly recommended | Scope to a dependency container when the type contains mutable data that needs to be shared or the type is expensive to initialize and is widely used in the app. | +| **Use Hilt.**
Recommended | Use Hilt or manual dependency injection in simple apps. Use Hilt if your project is complex enough. For example, if you have multiple screens with ViewModels, WorkManager usage, or advanced usage of Navigation such as ViewModels scoped to the nav graph. | + +## Testing + +The following are some best practices for testing: + +| Recommendation | Description | +|-----------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| **Know what to test.**
Strongly recommended | Unless the project is roughly as simple as a hello world app, you should test it, at minimum with: unit test ViewModels (including observable data), unit test data layer entities (repositories and data sources), and UI navigation tests that are useful as regression tests in CI. | +| **Prefer fakes to mocks.**
Strongly recommended | Read more in the “Use test doubles in Android” documentation. | +| **Test LiveData.**
Strongly recommended | When testing `LiveData`, observe it and assert on emitted values. | + +## Models + +You should observe these best practices when developing models in your apps: + +| Recommendation | Description | +|---------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| **Create a model per layer in complex apps.**
Recommended | In complex apps, create new models in different layers or components when it makes sense. For example, a remote data source can map the model it receives through the network to a simpler class with just the data the app needs; repositories can map DAO models to simpler data classes with just the information the UI layer needs; ViewModel can include data layer models in `UiState` classes. | + +## Naming conventions + +When naming your codebase, you should be aware of the following best practices: + +| Recommendation | Description | +|-----------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| **Naming methods.**
Optional | Methods should be a verb phrase. For example, `makePayment()`. | +| **Naming properties.**
Optional | Properties should be a noun phrase. For example, `inProgressTopicSelection`. | +| **Naming streams of data.**
Optional | When a class exposes a stream such as `LiveData`, the naming convention is `get{Model}Stream()`. For example, `getAuthorStream(): LiveData`. If the function returns a list of models the model name should be in the plural: `getAuthorsStream(): LiveData>`. | +| **Naming interfaces implementations.**
Optional | Names for the implementations of interfaces should be meaningful. Use `Default` as the prefix if a better name cannot be found. For example, for a `NewsRepository` interface, you could have an `OfflineFirstNewsRepository` or `InMemoryNewsRepository`. If you can find no good name, use `DefaultNewsRepository`. Fake implementations should be prefixed with `Fake`, as in `FakeAuthorsRepository`. | diff --git a/docs/core/architecture.md b/docs/core/architecture.md new file mode 100644 index 00000000..2c33b234 --- /dev/null +++ b/docs/core/architecture.md @@ -0,0 +1,20 @@ +# Architecture + +The project is split into three top-level modules, each with a clear responsibility. + +## app +- Android application layer. +- Hosts the UI and wires together features from the toolkit. + +## core +- Shared business logic and utilities. +- Defines interfaces consumed by the app and data modules. + +## data +- Handles persistence, network, and other data sources. +- Provides repository implementations for the core layer. + +### Data Flow & Dependencies +- **Dependencies:** `app → core → data` +- **Data flow:** `data → core → app` + diff --git a/docs/core/core-module.md b/docs/core/core-module.md new file mode 100644 index 00000000..6ab8dc1f --- /dev/null +++ b/docs/core/core-module.md @@ -0,0 +1,52 @@ +# Core Module + +The **core** package provides foundational building blocks shared across AppToolkit features. It offers base domain models, UI abstractions, utility helpers and dependency injection qualifiers used throughout the library. + +## Packages + +### domain +Defines reusable result wrappers and UI state models, plus base use case interfaces for repositories and operations. + +### ui +Hosts composable components and base classes like `ScreenViewModel` and `DefaultSnackbarHost` that standardize screen state handling and Snackbar presentation. + +### utils +Includes helpers, extensions, constants and `DispatcherProvider` to access standard `CoroutineDispatcher` instances. + +### di +Contains qualifiers such as `GithubToken` to assist dependency injection frameworks. + +## Usage examples + +### ScreenViewModel +```kotlin +class ExampleViewModel : ScreenViewModel( + initialState = UiStateScreen(data = UiScreen()) +) { + // handle events +} +``` + +### DefaultSnackbarHost +```kotlin +val snackbarHostState = remember { SnackbarHostState() } + +Scaffold( + snackbarHost = { DefaultSnackbarHost(snackbarState = snackbarHostState) } +) { /* screen content */ } +``` + +### DispatcherProvider +```kotlin +class ExampleRepository(private val dispatchers: DispatcherProvider) { + suspend fun load() = withContext(dispatchers.io) { + /* blocking work */ + } +} +``` + +## See also + +- [[Library]] – overview of all modules and features. +- [[Issue-Reporter-Module]] – demonstrates use of `ScreenViewModel` and networking helpers. +- [[Support-Module]] – integrates `DispatcherProvider` for billing and donation flows. diff --git a/docs/core/data-layer.md b/docs/core/data-layer.md new file mode 100644 index 00000000..14fff679 --- /dev/null +++ b/docs/core/data-layer.md @@ -0,0 +1,42 @@ +# Data Layer + +This page outlines the data-related building blocks provided by AppToolkit. + +## HTTP client + +`apptoolkit/data/client` exposes **KtorClient**, a factory for a preconfigured Ktor `HttpClient`. It installs JSON content negotiation, request timeouts, default headers and optional logging so callers only need to supply their own endpoints. + +### Setup + +```kotlin +val client = KtorClient().createClient(enableLogging = true) +``` + +## DataStore + +The `apptoolkit/data/datastore` package wraps Android DataStore in a singleton `CommonDataStore`. It centralizes preferences such as startup flags, theme options and user consents, exposing them as Kotlin `Flow`s with suspend functions to persist updates. + +### Usage + +```kotlin +val dataStore = CommonDataStore.getInstance(context) + +// Observe a value +val adsEnabled = dataStore.ads(default = true) + +// Save a value +scope.launch { dataStore.saveThemeMode("dark") } +``` + +## Ads + +Ads are configured through preferences in `CommonDataStore` via the `ads` flag and related consent entries. The `core/ads` package provides `AdsCoreManager`, which checks those preferences before initializing Google Mobile Ads and manages app-open ad loading and display. + +Use `AdsCoreManager` when the application should show an app-open ad on start or resume: + +```kotlin +val adsManager = AdsCoreManager(context, buildInfoProvider) +scope.launch { adsManager.initializeAds("ca-app-pub-xxxxxxxxxxxxxxxx/xxxxxxxxxx") } +adsManager.showAdIfAvailable(activity, scope) +``` + diff --git a/docs/core/solid-principles-android.md b/docs/core/solid-principles-android.md new file mode 100644 index 00000000..e0ad56c7 --- /dev/null +++ b/docs/core/solid-principles-android.md @@ -0,0 +1,199 @@ +# SOLID Principles in Android Development with Kotlin + +## Introduction +SOLID is an acronym for five design principles that help create maintainable and scalable codebases. Applying these ideas in Android projects keeps features isolated, encourages extension without breaking existing behavior and makes code easier to test. + +## Single Responsibility Principle +Each class should have one reason to change. Splitting responsibilities into distinct components improves clarity and testability. + +### Violation +```kotlin +class ItemManager(private val context: Context) { + private val items = mutableListOf() + + fun retrieveAndDisplayItems() { + val items = retrieveItemsFromServer() + val recyclerView = RecyclerView(context) + val adapter = ItemListAdapter(items) + recyclerView.adapter = adapter + } + + fun retrieveItemsFromServer(): List = emptyList() + + fun storeItemsLocally(items: List) { + // Save items to the local database + } +} +``` + +### Adherence +```kotlin +class ItemRepository { + fun fetchItems(): List { + // Fetch items from a server or database + } + + fun saveItems(items: List) { + // Persist items to a database + } +} +``` + +## Open-Closed Principle +Software entities should be open for extension and closed for modification. Favor abstraction so new behavior can be added without altering existing code. + +### Violation +```kotlin +class ItemService { + fun calculateTotalPrice(cart: List, discount: Double): Double { + var totalPrice = 0.0 + for (item in cart) { + totalPrice += item.price + } + totalPrice *= (1.0 - discount) + return totalPrice + } +} +``` + +### Adherence +```kotlin +interface PriceCalculator { + fun calculateTotalPrice(cart: List): Double +} + +class BasicPriceCalculator : PriceCalculator { + override fun calculateTotalPrice(cart: List): Double { + var totalPrice = 0.0 + for (product in cart) { + totalPrice += product.price + } + return totalPrice + } +} + +class DiscountedPriceCalculator(private val discount: Double) : PriceCalculator { + override fun calculateTotalPrice(cart: List): Double { + val basic = BasicPriceCalculator() + val total = basic.calculateTotalPrice(cart) + return total * (1.0 - discount) + } +} +``` + +## Liskov Substitution Principle +Subclasses must be replaceable for their base types without altering the correctness of the program. + +### Violation +```kotlin +open class Bird { + open fun fly() { + // Default flying behavior + } +} + +class Dog : Bird() { + override fun fly() { + throw UnsupportedOperationException("Dogs can't fly") + } +} +``` + +### Adherence +```kotlin +open class Bird { + open fun move() { + // Default movement behavior + } +} + +class Ostrich : Bird() { + override fun move() { + // Ostriches move by running + } +} +``` + +## Interface Segregation Principle +Clients should not be forced to implement methods they do not use. Split broad interfaces into focused ones. + +### Violation +```kotlin +interface Worker { + fun work() + fun eat() +} + +class SuperWorker : Worker { + override fun work() { + // Working behavior + } + + override fun eat() { + // Eating behavior + } +} +``` + +### Adherence +```kotlin +interface Workable { + fun work() +} + +interface Eatable { + fun eat() +} + +class SuperWorker : Workable, Eatable { + override fun work() { + // Working behavior + } + + override fun eat() { + // Eating behavior + } +} +``` + +## Dependency Inversion Principle +High-level modules should depend on abstractions rather than concrete implementations. + +### Violation +```kotlin +class LightBulb { + fun turnOn() { + // Turn on the light bulb + } +} + +class Switch { + private val bulb = LightBulb() + + fun control() { + bulb.turnOn() + } +} +``` + +### Adherence +```kotlin +interface Switchable { + fun turnOn() +} + +class LightBulb : Switchable { + override fun turnOn() { + // Turn on the light bulb + } +} + +class Switch(private val device: Switchable) { + fun control() { + device.turnOn() + } +} +``` + +## Conclusion +Applying the SOLID principles in Android projects encourages separation of concerns, extensibility and decoupling. These guidelines lead to code that is easier to understand, test and evolve over time. diff --git a/docs/core/ui-components.md b/docs/core/ui-components.md new file mode 100644 index 00000000..89ba9c75 --- /dev/null +++ b/docs/core/ui-components.md @@ -0,0 +1,92 @@ +# UI Components + +This page groups common Jetpack Compose components available in AppToolkit. + +## Buttons + +Use buttons to trigger actions. Compose offers `Button`, `OutlinedButton`, and `IconButton`. + +AppToolkit wraps `IconButton`, `FilledIconButton`, `FilledTonalIconButton`, and `OutlinedIconButton` with +Material 3's expressive shape morphing via `IconButtonDefaults.shapes()`, providing round-to-square +transitions in response to interaction states. + +```kotlin +Button(onClick = { /* handle action */ }) { + Text("Submit") +} +``` + +- **Theming:** Colors and typography derive from `MaterialTheme`. +- **State management:** Enable or disable via a `Boolean` state and update the `onClick` action. + +## Dialogs + +Dialogs display critical information or request decisions. + +```kotlin +var open by remember { mutableStateOf(true) } + +if (open) { + AlertDialog( + onDismissRequest = { open = false }, + confirmButton = { + TextButton(onClick = { open = false }) { Text("OK") } + }, + title = { Text("Title") }, + text = { Text("Message") } + ) +} +``` + +- **Theming:** Dialog shapes and colors follow `MaterialTheme` values. +- **State management:** Track visibility with a mutable state variable. + +## Form Fields + +Collect user input with fields like `TextField` or `OutlinedTextField`. + +```kotlin +var name by remember { mutableStateOf("") } + +TextField( + value = name, + onValueChange = { name = it }, + label = { Text("Name") } +) +``` + +- **Theming:** Uses `MaterialTheme` for colors, shapes, and typography. +- **State management:** Manage field values with `remember` and `mutableStateOf` or a view-model. + +## Layouts + +Arrange UI elements with layout composables such as `Column`, `Row`, and `Box`. + +```kotlin +Column( + modifier = Modifier.padding(16.dp), + verticalArrangement = Arrangement.spacedBy(8.dp) +) { + Text("Header", style = MaterialTheme.typography.titleLarge) + Button(onClick = { /* action */ }) { Text("Tap") } +} +``` + +- **Theming:** Spacing and typography should reference `MaterialTheme` dimensions and text styles. +- **State management:** Layouts themselves hold no state but position stateful children. + +## Feedback + +Provide feedback with components like `Snackbar` or `CircularProgressIndicator`. + +```kotlin +Scaffold { innerPadding -> + SnackbarHost(hostState = remember { SnackbarHostState() }) + // content +} +``` + +- **Theming:** Adapts to `MaterialTheme` for colors and elevation. +- **State management:** `SnackbarHostState` controls message queue. + +Return to [[Home]]. diff --git a/docs/general/style-guidance.md b/docs/general/style-guidance.md new file mode 100644 index 00000000..ee7e5b92 --- /dev/null +++ b/docs/general/style-guidance.md @@ -0,0 +1,9 @@ +# Style Guidance + +- Follow official Kotlin coding conventions. +- Prefer immutable `val` properties and keep functions small and focused. +- Name classes and files using `PascalCase`; use `camelCase` for functions and variables. +- Each file should end with a trailing newline. +- Compose UI uses Material 3 theming; reference `MaterialTheme` for colors, typography, and spacing. +- Use Kotlin Coroutines and Flow for asynchronous work and state streams. +- Inject dependencies with Koin; obtain ViewModels via Koin helpers. diff --git a/docs/screens/help.md b/docs/screens/help.md new file mode 100644 index 00000000..cc317175 --- /dev/null +++ b/docs/screens/help.md @@ -0,0 +1,40 @@ +# Help Screen + +## Overview +The Help screen provides users with access to frequently asked questions (FAQs), options to submit feedback, and links to important information like the app's privacy policy, terms of service, and open-source licenses. + +## Structure +The Help screen is implemented as an `Activity` that hosts different sections using `PreferenceFragmentCompat`: +- **`HelpActivity`**: The main activity for this screen. It manages the layout and hosts the fragments. +- **`FaqFragment`**: Displays a list of frequently asked questions, loaded from `R.xml.preferences_faq`. +- **`FeedbackFragment`**: Contains preferences related to user feedback, including an option to rate the app. This is loaded from `R.xml.preferences_feedback`. + +## Features +The Help screen offers the following functionalities, accessible primarily through an options menu: +- **View FAQs**: Users can browse a list of common questions and answers. +- **Provide Feedback**: Users can initiate a review flow or be directed to the Google Play Store to leave a review. +- **View in Google Play**: Opens the app's listing on the Google Play Store. +- **Version Info**: Displays a dialog with the app's name, version, and copyright information. +- **Beta Program**: Opens a link to join the app's beta testing program on Google Play. +- **Terms of Service**: Opens a web link to the app's Terms of Service. +- **Privacy Policy**: Opens a web link to the app's Privacy Policy. +- **Open Source Licenses**: Displays a screen with a list of open-source libraries used in the app and their licenses. + +## Ads +The Help page displays a single native ad between the FAQ list and the Contact Us card. +The banner is rendered by `HelpNativeAdBanner` which uses the shared `native_ad` configuration. +An **Ad** label is shown to comply with policy requirements and padding ensures the banner +does not interfere with surrounding content. +See [Ads](settings/privacy/ads.md) for more information on ad configuration. +*(Note: Presence of ads should be confirmed by checking layout files like `activity_help.xml`)* + +## Integration +To launch the Help screen, use the following Kotlin code: +```kotlin +startActivity(Intent(context, HelpActivity::class.java)) +``` +Or in Java: +```java +Intent intent = new Intent(context, HelpActivity.class); +startActivity(intent); +``` \ No newline at end of file diff --git a/docs/screens/home.md b/docs/screens/home.md new file mode 100644 index 00000000..9acce7b2 --- /dev/null +++ b/docs/screens/home.md @@ -0,0 +1,27 @@ +# Android Studio Tutorials Java Edition Wiki + +Welcome to the **Android Studio Tutorials Java Edition** wiki! This section provides extended documentation beyond the [README](../README.md). + +## Available Pages + +- [[Architecture]] +- [[Installation]] +- [[Contributing]] +- [[Wiki-Guide]] +- [[Library]] +- [[UI-Components]] + +## Features +- [[Home]] +- [[Android Tutorials]] +- [[About]] +- [[Support]] +- [[Startup]] +- [[Ads]] +- [[App Updates]] +- [[In-App Reviews]] +- [[Themes]] +- [[Notifications]] +- [[Consent Management]] + +Use the sidebar to navigate between pages. diff --git a/docs/screens/main.md b/docs/screens/main.md new file mode 100644 index 00000000..3ef32d5b --- /dev/null +++ b/docs/screens/main.md @@ -0,0 +1,48 @@ +# Main Activity + +## Overview +`MainActivity` serves as the primary entry point of the application after the initial startup and onboarding process. It hosts the main navigation structure and manages core UI components. + +## Key Components +- **`ActivityMainBinding`**: View binding class for `activity_main.xml`. +- **`MainViewModel`**: ViewModel responsible for UI state and business logic related to the main screen. +- **Navigation**: + - `NavHostFragment`: Hosts the navigation graph (`R.navigation.mobile_navigation`). + - `NavController`: Manages app navigation. + - `BottomNavigationView` (`nav_view`): Primary navigation for phones. + - `NavigationRailView` (`nav_rail`): Primary navigation for tablets/larger screens. + - `AppBarConfiguration`: Configures the Toolbar/Action Bar with the navigation graph. +- **UI Elements**: + - `Toolbar`: The main app bar. + - `AdView`: Displays advertisements. +- **Lifecycle Management**: + - Handles consent information for ads. + - Manages in-app updates using `AppUpdateManager`. + - Prompts for in-app reviews using `ReviewHelper`. + - Manages notifications for app updates and usage. + +## Core Functionalities +- **Initialization**: + - Sets up the splash screen. + - Redirects to `StartupActivity` if onboarding is not complete. + - Initializes ViewBinding and ViewModel. + - Requests notification permissions on Android Tiramisu and above. +- **UI Setup**: + - Configures the Action Bar. + - Observes `MainViewModel` for UI state changes (e.g., theme, bottom navigation visibility, default tab). + - Dynamically switches between `BottomNavigationView` and `NavigationRailView` based on screen width. + - Handles navigation item selections and custom animations. +- **In-app Updates**: + - Checks for and manages immediate in-app updates. + - Shows notifications for downloaded updates. +- **In-app Reviews**: + - Prompts users for an in-app review based on session count and prior prompts. +- **Ad Handling**: + - Loads banner ads if consent is granted. +- **Back Press Handling**: + - Implements a "press back again to exit" functionality. + +## Interactions +- **`StartupActivity`**: Handles initial app setup and onboarding. If onboarding is not complete, `MainActivity` redirects to it. +- **`SupportActivity`**: Launched when the "Support" menu item is selected. +- **`BottomSheetMenuFragment`**: Shown when the navigation icon in the Toolbar is clicked, providing additional menu options. \ No newline at end of file diff --git a/docs/screens/onboarding.md b/docs/screens/onboarding.md new file mode 100644 index 00000000..006cf6e0 --- /dev/null +++ b/docs/screens/onboarding.md @@ -0,0 +1,37 @@ +# Onboarding + +## Overview +The `OnboardingActivity` guides users through the initial setup and configuration of the application. It utilizes a `ViewPager2` to present a series of steps as fragments. User preferences selected during onboarding are saved via `OnboardingViewModel`. + +## Key Components +- **`OnboardingActivity.java`**: The main activity orchestrating the onboarding flow. +- **`ActivityOnboardingBinding.java`**: View binding class for `activity_onboarding.xml`. +- **`OnboardingViewModel.java`**: ViewModel responsible for managing onboarding state, such as the current page and marking onboarding as complete. +- **`OnboardingPagerAdapter`**: A `FragmentStateAdapter` that provides the fragments for each step of the onboarding process. +- **Fragments**: + - `ThemeFragment`: Allows the user to select app theme preferences. + - `StartPageFragment`: Allows the user to configure their preferred start page. + - `FontFragment`: Allows the user to customize font settings. + - `BottomLabelsFragment`: Allows the user to configure bottom navigation label visibility. + - `DataFragment`: Handles data-related preferences or information. + - `DoneFragment`: The final screen indicating the completion of the onboarding process. +- **Layout**: `activity_onboarding.xml` (implicitly, based on `ActivityOnboardingBinding`) which includes: + - A `ViewPager2` for swiping through onboarding steps. + - A `TabLayout` (likely styled as dot indicators) to show progress. + - Navigation buttons: "Back", "Skip", and "Next". + +## Flow +1. The activity initializes the `ViewPager2` with `OnboardingPagerAdapter`. +2. It checks for and requests UMP consent if required. +3. Users navigate through the fragments using swipe gestures or the "Next" and "Back" buttons. +4. The "Skip" button allows users to bypass the onboarding process. +5. Selections made in each fragment (e.g., Theme, Start Page) are saved, often when navigating away from the fragment or by explicitly pressing "Next". +6. The `OnboardingViewModel` tracks the current page and saves the onboarding completion status. +7. Upon completion (either by finishing the last step or skipping), the user is navigated to `MainActivity`. + +## Integration +To start the onboarding flow: +```java +Intent intent = new Intent(context, OnboardingActivity.class); +startActivity(intent); +``` diff --git a/docs/screens/settings/privacy/permissions.md b/docs/screens/settings/privacy/permissions.md new file mode 100644 index 00000000..b3e34452 --- /dev/null +++ b/docs/screens/settings/privacy/permissions.md @@ -0,0 +1,17 @@ +# Permissions + +## Overview +The `PermissionsActivity` screen displays and manages app-specific permissions or settings that are not covered by Android's standard runtime permission system. These are typically presented as preferences within the app. + +## Key Components +- **`PermissionsActivity.java`**: The main activity for this screen. It hosts a `PreferenceFragmentCompat` to display the settings. +- **`PermissionsActivity.SettingsFragment`**: An inner class within `PermissionsActivity`. This fragment is responsible for loading and displaying preferences from an XML resource file (`R.xml.preferences_permissions`). + +## UI Layout +The preferences UI is defined in `res/xml/preferences_permissions.xml`. This file contains the different preference items that the user can interact with. + +## Integration +To launch the permissions settings screen: +```java +startActivity(new Intent(context, PermissionsActivity.class)); +``` diff --git a/docs/screens/settings/settings.md b/docs/screens/settings/settings.md new file mode 100644 index 00000000..77b054ba --- /dev/null +++ b/docs/screens/settings/settings.md @@ -0,0 +1,15 @@ +# Settings + +## Overview +The settings screen allows users to configure application preferences. The main entry point is `SettingsActivity.java`, which hosts `SettingsFragment.java` for displaying the preference items. A `SettingsViewModel.java` handles the business logic and data persistence for the settings. + +## Key Components +- **`SettingsActivity.java`**: The main activity for settings. It sets up the fragment and handles theme changes based on preferences. +- **`SettingsFragment.java`**: Displays the actual settings UI using AndroidX Preference components. +- **`SettingsViewModel.java`**: Manages settings data, listens for preference changes, and applies them (e.g., dark mode). + +## Integration +To launch the settings screen: +```kotlin +startActivity(Intent(context, SettingsActivity::class.java)) +``` diff --git a/docs/screens/startup.md b/docs/screens/startup.md new file mode 100644 index 00000000..d193d1bb --- /dev/null +++ b/docs/screens/startup.md @@ -0,0 +1,25 @@ +# Startup + +## Layers +- **ViewModel**: `StartupViewModel` handles the business logic for the startup screen, including consent information management. +- **UI**: `StartupActivity` displays the startup screen, handles user interactions, and presents options for privacy policy and proceeding to the app. It utilizes `ActivityStartupBinding` for view binding. +- **Utils**: + - `ConsentRequestParameters`: Used to configure and request user consent. + - `FastScrollerBuilder`: Enhances scrolling in the layout. + +## Primary UI Components +- `ActivityStartupBinding`: Manages the views in `StartupActivity`. +- `buttonBrowsePrivacyPolicyAndTermsOfService`: Allows users to view the privacy policy and terms of service. +- `floatingButtonAgree`: Allows users to agree and navigate to `OnboardingActivity`. + +## Integration & Navigation +- The `StartupActivity` is the initial activity launched. +- Upon user agreement, it navigates to `OnboardingActivity`. +```kotlin +// To start StartupActivity (example) +// Intent(context, StartupActivity::class.java) + +// Inside StartupActivity, on agreement: +// startActivity(new Intent(this, OnboardingActivity.class)); +// finish(); +``` \ No newline at end of file diff --git a/docs/screens/support.md b/docs/screens/support.md new file mode 100644 index 00000000..65365302 --- /dev/null +++ b/docs/screens/support.md @@ -0,0 +1,15 @@ +# Support + +## Layers +- **Domain**: `SupportRepository` for managing product details and purchases. Handles donation logic. +- **UI**: `SupportActivity` (using `ActivitySupportBinding` and `SupportViewModel`) displays donation options, advertisements, and external links. +- **Billing**: Integrates `com.android.billingclient.api.BillingClient` for in-app purchases. +- **Ads**: `AdUtils` for loading banner advertisements. + +## Primary Screens +- `SupportActivity` – allows users to make donations at different tiers, view advertisements, and visit an external support website. + +## Integration +```kotlin +startActivity(Intent(context, SupportActivity::class.java)) +``` \ No newline at end of file diff --git a/docs/ui-ux/ui-ux-guidelines.md b/docs/ui-ux/ui-ux-guidelines.md new file mode 100644 index 00000000..dcbf855e --- /dev/null +++ b/docs/ui-ux/ui-ux-guidelines.md @@ -0,0 +1,36 @@ +# UI/UX Guidelines + +This project follows modern Android design practices to create consistent and accessible experiences across devices. Use these principles when designing new features. + +## Adopt Material Design 3 +- Implement layouts and components using Material Design 3. +- Leverage dynamic color, expressive shapes, and typography to convey hierarchy and personality. + +## Provide a meaningful landing screen +- Present the app name and primary actions immediately. +- Give users a clear starting point to explore functionality. + +## Delay sign-in prompts +- Offer as much functionality as possible before requiring authentication. +- Only prompt users to sign in when a feature truly requires it. + +## Preserve user state +- Persist session data so users can seamlessly continue after navigating away or installing updates. + +## Avoid redundant splash screens +- The platform already handles app launch visuals; additional splash screens slow users down. + +## Use proper navigation +- The Up button moves within the app hierarchy and should never exit the app. +- The system Back button follows the user's history across screens. + +## Keep UI consistent +- Maintain visual and behavioral consistency across screens and modules. +- Reuse components and patterns instead of creating divergent UIs. + +## Enable sharing +- Provide explicit actions to share content or app links with other apps. + +## Secure external links +- Open third-party content in a browser or WebView over HTTPS. + From 1256096e94f77fa37ffe1802cde6ac47ef5bfd19 Mon Sep 17 00:00:00 2001 From: D4rK7355608 Date: Sun, 14 Sep 2025 01:15:48 +0300 Subject: [PATCH 24/67] Update `versionCode` to 45 --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index c8a44d68..2b07429b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -13,7 +13,7 @@ android { applicationId 'com.d4rk.androidtutorials.java' minSdk 23 targetSdk 36 - versionCode 44 + versionCode 45 versionName '5.0.2' vectorDrawables.useSupportLibrary = true testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' From dd2e8e26c86fcbd89d6a622936086f51e8be0366 Mon Sep 17 00:00:00 2001 From: Mihai-Cristian Condrea Date: Sun, 14 Sep 2025 01:28:46 +0300 Subject: [PATCH 25/67] Remove ScreenViewModel references and align docs --- docs/core/core-module.md | 57 ++++----- docs/core/data-layer.md | 86 +++++++++---- docs/core/solid-principles-android.md | 168 +++++++++++++++----------- docs/core/ui-components.md | 120 +++++++++--------- docs/general/style-guidance.md | 12 +- docs/screens/help.md | 8 +- docs/screens/settings/settings.md | 6 +- docs/screens/startup.md | 13 +- docs/screens/support.md | 8 +- 9 files changed, 275 insertions(+), 203 deletions(-) diff --git a/docs/core/core-module.md b/docs/core/core-module.md index 6ab8dc1f..aa1db163 100644 --- a/docs/core/core-module.md +++ b/docs/core/core-module.md @@ -1,52 +1,53 @@ # Core Module -The **core** package provides foundational building blocks shared across AppToolkit features. It offers base domain models, UI abstractions, utility helpers and dependency injection qualifiers used throughout the library. +The **core** package provides building blocks shared across the app. It offers domain models, ViewModel classes, utility helpers and dependency injection qualifiers used throughout the project. ## Packages ### domain -Defines reusable result wrappers and UI state models, plus base use case interfaces for repositories and operations. +Houses use case classes and other business logic that operate on repositories. ### ui -Hosts composable components and base classes like `ScreenViewModel` and `DefaultSnackbarHost` that standardize screen state handling and Snackbar presentation. +Contains Activities, Fragments and ViewModels such as `MainViewModel`. ### utils -Includes helpers, extensions, constants and `DispatcherProvider` to access standard `CoroutineDispatcher` instances. +Provides helpers like `OpenSourceLicensesUtils`, `ReviewHelper` and `EdgeToEdgeDelegate`. ### di -Contains qualifiers such as `GithubToken` to assist dependency injection frameworks. +Contains Hilt modules and qualifiers for dependency injection. ## Usage examples -### ScreenViewModel -```kotlin -class ExampleViewModel : ScreenViewModel( - initialState = UiStateScreen(data = UiScreen()) -) { - // handle events +### ViewModel +```java +public class MainViewModel extends ViewModel { + private final ShouldShowStartupScreenUseCase shouldShowStartupScreenUseCase; + + public MainViewModel(ShouldShowStartupScreenUseCase shouldShowStartupScreenUseCase) { + this.shouldShowStartupScreenUseCase = shouldShowStartupScreenUseCase; + } + + public boolean shouldShowStartupScreen() { + return shouldShowStartupScreenUseCase.invoke(); + } } ``` -### DefaultSnackbarHost -```kotlin -val snackbarHostState = remember { SnackbarHostState() } - -Scaffold( - snackbarHost = { DefaultSnackbarHost(snackbarState = snackbarHostState) } -) { /* screen content */ } +### Snackbar helper +```java +Snackbar.make(view, "Message", Snackbar.LENGTH_SHORT).show(); ``` -### DispatcherProvider -```kotlin -class ExampleRepository(private val dispatchers: DispatcherProvider) { - suspend fun load() = withContext(dispatchers.io) { - /* blocking work */ - } -} +### ReviewHelper +```java +ReviewHelper.launchInAppReviewIfEligible(activity, sessionCount, hasPromptedBefore, () -> { + // callback when review flow finishes +}); ``` ## See also -- [[Library]] – overview of all modules and features. -- [[Issue-Reporter-Module]] – demonstrates use of `ScreenViewModel` and networking helpers. -- [[Support-Module]] – integrates `DispatcherProvider` for billing and donation flows. +- [[Architecture]] – overview of app layers. +- [[Data Layer]] – repository and data source details. +- [[UI Components]] – common reusable views. + diff --git a/docs/core/data-layer.md b/docs/core/data-layer.md index 14fff679..e62f6aa3 100644 --- a/docs/core/data-layer.md +++ b/docs/core/data-layer.md @@ -1,42 +1,80 @@ # Data Layer -This page outlines the data-related building blocks provided by AppToolkit. +This page outlines how the app manages and persists data. -## HTTP client +## Repositories -`apptoolkit/data/client` exposes **KtorClient**, a factory for a preconfigured Ktor `HttpClient`. It installs JSON content negotiation, request timeouts, default headers and optional logging so callers only need to supply their own endpoints. +Repositories expose data to the rest of the app and hide the underlying storage. -### Setup - -```kotlin -val client = KtorClient().createClient(enableLogging = true) +```java +public interface MainRepository { + boolean shouldShowStartupScreen(); + void markStartupScreenShown(); +} ``` -## DataStore - -The `apptoolkit/data/datastore` package wraps Android DataStore in a singleton `CommonDataStore`. It centralizes preferences such as startup flags, theme options and user consents, exposing them as Kotlin `Flow`s with suspend functions to persist updates. +`DefaultMainRepository` implements these methods using `SharedPreferences`: -### Usage +```java +public class DefaultMainRepository implements MainRepository { + private final Context context; -```kotlin -val dataStore = CommonDataStore.getInstance(context) + public DefaultMainRepository(Context context) { + this.context = context.getApplicationContext(); + } -// Observe a value -val adsEnabled = dataStore.ads(default = true) + @Override + public boolean shouldShowStartupScreen() { + SharedPreferences startup = context.getSharedPreferences("startup", Context.MODE_PRIVATE); + return startup.getBoolean("value", true); + } -// Save a value -scope.launch { dataStore.saveThemeMode("dark") } + @Override + public void markStartupScreenShown() { + SharedPreferences startup = context.getSharedPreferences("startup", Context.MODE_PRIVATE); + startup.edit().putBoolean("value", false).apply(); + } +} ``` -## Ads +## Data sources + +Remote and local sources supply the repositories with data. For example, `DefaultHomeRemoteDataSource` uses Volley to fetch promoted apps: + +```java +public class DefaultHomeRemoteDataSource implements HomeRemoteDataSource { + private final RequestQueue requestQueue; + private final String apiUrl; + + public DefaultHomeRemoteDataSource(RequestQueue requestQueue, String apiUrl) { + this.requestQueue = requestQueue; + this.apiUrl = apiUrl; + } + + @Override + public void fetchPromotedApps(PromotedAppsCallback callback) { + JsonObjectRequest request = new JsonObjectRequest( + Request.Method.GET, + apiUrl, + null, + response -> { /* parse and callback */ }, + error -> { /* handle error */ } + ); + requestQueue.add(request); + } +} +``` -Ads are configured through preferences in `CommonDataStore` via the `ads` flag and related consent entries. The `core/ads` package provides `AdsCoreManager`, which checks those preferences before initializing Google Mobile Ads and manages app-open ad loading and display. +## Models -Use `AdsCoreManager` when the application should show an app-open ad on start or resume: +Model classes like `PromotedApp` encapsulate the data returned by the layer: -```kotlin -val adsManager = AdsCoreManager(context, buildInfoProvider) -scope.launch { adsManager.initializeAds("ca-app-pub-xxxxxxxxxxxxxxxx/xxxxxxxxxx") } -adsManager.showAdIfAvailable(activity, scope) +```java +public record PromotedApp(String name, String packageName, String iconUrl) {} ``` +## See also + +- [[Architecture]] – overview of app layers. +- [[Core Module]] – shared utilities and components. + diff --git a/docs/core/solid-principles-android.md b/docs/core/solid-principles-android.md index e0ad56c7..489a7a3e 100644 --- a/docs/core/solid-principles-android.md +++ b/docs/core/solid-principles-android.md @@ -1,4 +1,4 @@ -# SOLID Principles in Android Development with Kotlin +# SOLID Principles in Android Development with Java ## Introduction SOLID is an acronym for five design principles that help create maintainable and scalable codebases. Applying these ideas in Android projects keeps features isolated, encourages extension without breaking existing behavior and makes code easier to test. @@ -7,33 +7,41 @@ SOLID is an acronym for five design principles that help create maintainable and Each class should have one reason to change. Splitting responsibilities into distinct components improves clarity and testability. ### Violation -```kotlin -class ItemManager(private val context: Context) { - private val items = mutableListOf() +```java +class ItemManager { + private final Context context; + private final List items = new ArrayList<>(); - fun retrieveAndDisplayItems() { - val items = retrieveItemsFromServer() - val recyclerView = RecyclerView(context) - val adapter = ItemListAdapter(items) - recyclerView.adapter = adapter + ItemManager(Context context) { + this.context = context; } - fun retrieveItemsFromServer(): List = emptyList() + void retrieveAndDisplayItems() { + List items = retrieveItemsFromServer(); + RecyclerView recyclerView = new RecyclerView(context); + ItemListAdapter adapter = new ItemListAdapter(items); + recyclerView.setAdapter(adapter); + } + + List retrieveItemsFromServer() { + return Collections.emptyList(); + } - fun storeItemsLocally(items: List) { + void storeItemsLocally(List items) { // Save items to the local database } } ``` ### Adherence -```kotlin +```java class ItemRepository { - fun fetchItems(): List { + List fetchItems() { // Fetch items from a server or database + return Collections.emptyList(); } - fun saveItems(items: List) { + void saveItems(List items) { // Persist items to a database } } @@ -43,40 +51,48 @@ class ItemRepository { Software entities should be open for extension and closed for modification. Favor abstraction so new behavior can be added without altering existing code. ### Violation -```kotlin +```java class ItemService { - fun calculateTotalPrice(cart: List, discount: Double): Double { - var totalPrice = 0.0 - for (item in cart) { - totalPrice += item.price + double calculateTotalPrice(List cart, double discount) { + double totalPrice = 0.0; + for (Item item : cart) { + totalPrice += item.getPrice(); } - totalPrice *= (1.0 - discount) - return totalPrice + totalPrice *= (1.0 - discount); + return totalPrice; } } ``` ### Adherence -```kotlin +```java interface PriceCalculator { - fun calculateTotalPrice(cart: List): Double + double calculateTotalPrice(List cart); } -class BasicPriceCalculator : PriceCalculator { - override fun calculateTotalPrice(cart: List): Double { - var totalPrice = 0.0 - for (product in cart) { - totalPrice += product.price +class BasicPriceCalculator implements PriceCalculator { + @Override + public double calculateTotalPrice(List cart) { + double totalPrice = 0.0; + for (Product product : cart) { + totalPrice += product.getPrice(); } - return totalPrice + return totalPrice; } } -class DiscountedPriceCalculator(private val discount: Double) : PriceCalculator { - override fun calculateTotalPrice(cart: List): Double { - val basic = BasicPriceCalculator() - val total = basic.calculateTotalPrice(cart) - return total * (1.0 - discount) +class DiscountedPriceCalculator implements PriceCalculator { + private final double discount; + + DiscountedPriceCalculator(double discount) { + this.discount = discount; + } + + @Override + public double calculateTotalPrice(List cart) { + PriceCalculator basic = new BasicPriceCalculator(); + double total = basic.calculateTotalPrice(cart); + return total * (1.0 - discount); } } ``` @@ -85,30 +101,32 @@ class DiscountedPriceCalculator(private val discount: Double) : PriceCalculator Subclasses must be replaceable for their base types without altering the correctness of the program. ### Violation -```kotlin -open class Bird { - open fun fly() { +```java +class Bird { + void fly() { // Default flying behavior } } -class Dog : Bird() { - override fun fly() { - throw UnsupportedOperationException("Dogs can't fly") +class Dog extends Bird { + @Override + void fly() { + throw new UnsupportedOperationException("Dogs can't fly"); } } ``` ### Adherence -```kotlin -open class Bird { - open fun move() { +```java +class Bird { + void move() { // Default movement behavior } } -class Ostrich : Bird() { - override fun move() { +class Ostrich extends Bird { + @Override + void move() { // Ostriches move by running } } @@ -118,39 +136,43 @@ class Ostrich : Bird() { Clients should not be forced to implement methods they do not use. Split broad interfaces into focused ones. ### Violation -```kotlin +```java interface Worker { - fun work() - fun eat() + void work(); + void eat(); } -class SuperWorker : Worker { - override fun work() { +class SuperWorker implements Worker { + @Override + public void work() { // Working behavior } - override fun eat() { + @Override + public void eat() { // Eating behavior } } ``` ### Adherence -```kotlin +```java interface Workable { - fun work() + void work(); } interface Eatable { - fun eat() + void eat(); } -class SuperWorker : Workable, Eatable { - override fun work() { +class SuperWorker implements Workable, Eatable { + @Override + public void work() { // Working behavior } - override fun eat() { + @Override + public void eat() { // Eating behavior } } @@ -160,40 +182,48 @@ class SuperWorker : Workable, Eatable { High-level modules should depend on abstractions rather than concrete implementations. ### Violation -```kotlin +```java class LightBulb { - fun turnOn() { + void turnOn() { // Turn on the light bulb } } class Switch { - private val bulb = LightBulb() + private final LightBulb bulb = new LightBulb(); - fun control() { - bulb.turnOn() + void control() { + bulb.turnOn(); } } ``` ### Adherence -```kotlin +```java interface Switchable { - fun turnOn() + void turnOn(); } -class LightBulb : Switchable { - override fun turnOn() { +class LightBulb implements Switchable { + @Override + public void turnOn() { // Turn on the light bulb } } -class Switch(private val device: Switchable) { - fun control() { - device.turnOn() +class Switch { + private final Switchable device; + + Switch(Switchable device) { + this.device = device; + } + + void control() { + device.turnOn(); } } ``` ## Conclusion Applying the SOLID principles in Android projects encourages separation of concerns, extensibility and decoupling. These guidelines lead to code that is easier to understand, test and evolve over time. + diff --git a/docs/core/ui-components.md b/docs/core/ui-components.md index 89ba9c75..58e9f626 100644 --- a/docs/core/ui-components.md +++ b/docs/core/ui-components.md @@ -1,92 +1,94 @@ # UI Components -This page groups common Jetpack Compose components available in AppToolkit. +This page groups common Android View components used in AppToolkit. ## Buttons -Use buttons to trigger actions. Compose offers `Button`, `OutlinedButton`, and `IconButton`. +Use buttons to trigger actions. -AppToolkit wraps `IconButton`, `FilledIconButton`, `FilledTonalIconButton`, and `OutlinedIconButton` with -Material 3's expressive shape morphing via `IconButtonDefaults.shapes()`, providing round-to-square -transitions in response to interaction states. +**XML** -```kotlin -Button(onClick = { /* handle action */ }) { - Text("Submit") -} +```xml +