Skip to content

Commit 37dee89

Browse files
Merge pull request #94 from MihaiCristianCondrea/codex/improve-ui-ux-design
feat: improve main screen accessibility and UX
2 parents a8307a6 + 342a96a commit 37dee89

File tree

32 files changed

+157
-55
lines changed

32 files changed

+157
-55
lines changed
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package com.d4rk.androidtutorials.java.ui.components.navigation;
2+
3+
import android.os.Bundle;
4+
import android.view.View;
5+
6+
import androidx.annotation.Nullable;
7+
import androidx.appcompat.app.ActionBar;
8+
import androidx.appcompat.app.AppCompatActivity;
9+
10+
import com.d4rk.androidtutorials.java.R;
11+
import com.d4rk.androidtutorials.java.utils.EdgeToEdgeDelegate;
12+
13+
public abstract class BaseActivity extends AppCompatActivity {
14+
15+
@Override
16+
protected void onPostCreate(@Nullable Bundle savedInstanceState) {
17+
super.onPostCreate(savedInstanceState);
18+
View container = findViewById(R.id.container);
19+
if (container != null) {
20+
EdgeToEdgeDelegate edgeToEdgeDelegate = new EdgeToEdgeDelegate(this);
21+
edgeToEdgeDelegate.applyEdgeToEdge(container);
22+
}
23+
ActionBar actionBar = getSupportActionBar();
24+
if (actionBar != null) {
25+
actionBar.setDisplayHomeAsUpEnabled(true);
26+
}
27+
}
28+
29+
@Override
30+
public boolean onSupportNavigateUp() {
31+
finish();
32+
return true;
33+
}
34+
}

app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/help/HelpActivity.java

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,8 @@
99
import android.view.MenuItem;
1010

1111
import androidx.annotation.NonNull;
12-
import androidx.appcompat.app.ActionBar;
1312
import androidx.appcompat.app.AlertDialog;
14-
import com.d4rk.androidtutorials.java.ui.components.navigation.UpNavigationActivity;
13+
import com.d4rk.androidtutorials.java.ui.components.navigation.BaseActivity;
1514
import androidx.lifecycle.ViewModelProvider;
1615
import androidx.preference.Preference;
1716
import androidx.preference.PreferenceFragmentCompat;
@@ -21,7 +20,6 @@
2120
import com.d4rk.androidtutorials.java.databinding.ActivityHelpBinding;
2221
import com.d4rk.androidtutorials.java.databinding.DialogVersionInfoBinding;
2322
import com.d4rk.androidtutorials.java.ui.screens.help.repository.HelpRepository;
24-
import com.d4rk.androidtutorials.java.utils.EdgeToEdgeDelegate;
2523
import com.d4rk.androidtutorials.java.utils.OpenSourceLicensesUtils;
2624
import com.google.android.material.snackbar.Snackbar;
2725
import com.google.android.play.core.review.ReviewInfo;
@@ -30,7 +28,7 @@
3028
import dagger.hilt.android.AndroidEntryPoint;
3129

3230
@AndroidEntryPoint
33-
public class HelpActivity extends UpNavigationActivity {
31+
public class HelpActivity extends BaseActivity {
3432

3533
private HelpViewModel helpViewModel;
3634

@@ -40,16 +38,8 @@ protected void onCreate(Bundle savedInstanceState) {
4038
ActivityHelpBinding binding = ActivityHelpBinding.inflate(getLayoutInflater());
4139
setContentView(binding.getRoot());
4240

43-
EdgeToEdgeDelegate edgeToEdgeDelegate = new EdgeToEdgeDelegate(this);
44-
edgeToEdgeDelegate.applyEdgeToEdge(binding.container);
45-
4641
helpViewModel = new ViewModelProvider(this).get(HelpViewModel.class);
4742

48-
ActionBar actionBar = getSupportActionBar();
49-
if (actionBar != null) {
50-
actionBar.setDisplayHomeAsUpEnabled(true);
51-
}
52-
5343
getSupportFragmentManager().beginTransaction()
5444
.replace(R.id.frame_layout_faq, new FaqFragment())
5545
.commit();
@@ -73,7 +63,10 @@ public boolean onCreateOptionsMenu(Menu menu) {
7363
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
7464
int itemId = item.getItemId();
7565

76-
if (itemId == R.id.view_in_google_play) {
66+
if (itemId == android.R.id.home) {
67+
finish();
68+
return true;
69+
} else if (itemId == R.id.view_in_google_play) {
7770
openGooglePlayListing();
7871
return true;
7972
} else if (itemId == R.id.version_info) {

app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/main/MainActivity.java

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import android.util.Log;
99
import android.util.SparseIntArray;
1010
import android.view.View;
11+
import android.widget.Toast;
1112

1213
import androidx.activity.OnBackPressedCallback;
1314
import androidx.activity.result.ActivityResultLauncher;
@@ -42,7 +43,6 @@
4243
import com.d4rk.androidtutorials.java.utils.EdgeToEdgeDelegate;
4344
import com.google.android.gms.ads.AdRequest;
4445
import com.google.android.gms.ads.MobileAds;
45-
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
4646
import com.google.android.material.navigation.NavigationBarView;
4747
import com.google.android.material.navigationrail.NavigationRailView;
4848
import com.google.android.material.snackbar.Snackbar;
@@ -80,6 +80,8 @@ public class MainActivity extends AppCompatActivity {
8080
private AppUpdateNotificationsManager appUpdateNotificationsManager;
8181
private AppUpdateManager appUpdateManager;
8282
private InstallStateUpdatedListener installStateUpdatedListener;
83+
private long backPressedTime;
84+
private static final long BACK_PRESS_INTERVAL = 2000;
8385
private final DefaultLifecycleObserver lifecycleObserver = new DefaultLifecycleObserver() {
8486
@Override
8587
public void onResume(@NonNull LifecycleOwner owner) {
@@ -88,11 +90,13 @@ public void onResume(@NonNull LifecycleOwner owner) {
8890
if (ConsentUtils.canShowAds(MainActivity.this)) {
8991
if (mBinding.adView.getVisibility() != View.VISIBLE) {
9092
MobileAds.initialize(MainActivity.this);
93+
mBinding.adPlaceholder.setVisibility(View.GONE);
9194
mBinding.adView.setVisibility(View.VISIBLE);
9295
mBinding.adView.loadAd(new AdRequest.Builder().build());
9396
}
9497
} else {
9598
mBinding.adView.setVisibility(View.GONE);
99+
mBinding.adPlaceholder.setVisibility(View.VISIBLE);
96100
}
97101
}
98102
}
@@ -149,15 +153,15 @@ protected void onCreate(Bundle savedInstanceState) {
149153
getOnBackPressedDispatcher().addCallback(this, new OnBackPressedCallback(true) {
150154
@Override
151155
public void handleOnBackPressed() {
152-
new MaterialAlertDialogBuilder(MainActivity.this)
153-
.setTitle(R.string.alert_dialog_close)
154-
.setMessage(R.string.summary_alert_dialog_close)
155-
.setPositiveButton(android.R.string.yes, (dialog, which) -> {
156-
finish();
157-
moveTaskToBack(true);
158-
})
159-
.setNegativeButton(android.R.string.no, null)
160-
.show(); }
156+
long currentTime = System.currentTimeMillis();
157+
if (currentTime - backPressedTime < BACK_PRESS_INTERVAL) {
158+
finish();
159+
moveTaskToBack(true);
160+
} else {
161+
backPressedTime = currentTime;
162+
Toast.makeText(MainActivity.this, R.string.press_back_again_to_exit, Toast.LENGTH_SHORT).show();
163+
}
164+
}
161165
});
162166
}
163167

@@ -214,10 +218,12 @@ private void observeViewModel() {
214218
if (mBinding.adView != null) {
215219
if (ConsentUtils.canShowAds(this)) {
216220
MobileAds.initialize(this);
221+
mBinding.adPlaceholder.setVisibility(View.GONE);
217222
mBinding.adView.setVisibility(View.VISIBLE);
218223
mBinding.adView.loadAd(new AdRequest.Builder().build());
219224
} else {
220225
mBinding.adView.setVisibility(View.GONE);
226+
mBinding.adPlaceholder.setVisibility(View.VISIBLE);
221227
}
222228
}
223229
}
@@ -287,6 +293,9 @@ private void observeViewModel() {
287293
recreate();
288294
}
289295
});
296+
297+
mainViewModel.getLoadingState().observe(this, isLoading ->
298+
mBinding.progressBar.setVisibility(Boolean.TRUE.equals(isLoading) ? View.VISIBLE : View.GONE));
290299
}
291300

292301
private void setupUpdateNotifications() {

app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/main/MainViewModel.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ public class MainViewModel extends ViewModel {
3939
private final BuildShortcutIntentUseCase buildShortcutIntentUseCase;
4040
private final GetAppUpdateManagerUseCase getAppUpdateManagerUseCase;
4141
private final MutableLiveData<MainUiState> uiState = new MutableLiveData<>();
42+
private final MutableLiveData<Boolean> isLoading = new MutableLiveData<>(false);
4243

4344
@Inject
4445
public MainViewModel(ApplyThemeSettingsUseCase applyThemeSettingsUseCase,
@@ -80,6 +81,7 @@ public MainViewModel(ApplyThemeSettingsUseCase applyThemeSettingsUseCase,
8081
public void applySettings(String[] themeValues,
8182
String[] bottomNavBarLabelsValues,
8283
String[] defaultTabValues) {
84+
isLoading.setValue(true);
8385
boolean changedTheme = applyThemeSettingsUseCase.invoke(themeValues);
8486

8587
String labelVisibilityStr = getBottomNavLabelVisibilityUseCase.invoke();
@@ -99,6 +101,7 @@ public void applySettings(String[] themeValues,
99101

100102
uiState.setValue(new MainUiState(visibilityMode, startFragmentId, changedTheme));
101103
applyLanguageSettingsUseCase.invoke();
104+
isLoading.setValue(false);
102105
}
103106

104107
/**
@@ -136,6 +139,13 @@ public LiveData<MainUiState> getUiState() {
136139
return uiState;
137140
}
138141

142+
/**
143+
* Expose loading state to toggle progress indicators.
144+
*/
145+
public LiveData<Boolean> getLoadingState() {
146+
return isLoading;
147+
}
148+
139149
/**
140150
* Expose the AppUpdateManager if the Activity wants to directly check for in-app updates.
141151
*/

app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/support/SupportActivity.java

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,20 @@
33
import android.content.Intent;
44
import android.net.Uri;
55
import android.os.Bundle;
6-
import android.view.MenuItem;
7-
8-
import androidx.annotation.NonNull;
9-
import androidx.appcompat.app.ActionBar;
10-
import androidx.appcompat.app.AppCompatActivity;
6+
import com.d4rk.androidtutorials.java.ui.components.navigation.BaseActivity;
117
import androidx.lifecycle.ViewModelProvider;
128

139
import com.android.billingclient.api.ProductDetails;
1410
import com.d4rk.androidtutorials.java.data.repository.SupportRepository;
1511
import com.d4rk.androidtutorials.java.databinding.ActivitySupportBinding;
16-
import com.d4rk.androidtutorials.java.utils.EdgeToEdgeDelegate;
1712
import com.google.android.gms.ads.AdRequest;
1813

1914
import java.util.List;
2015

2116
import dagger.hilt.android.AndroidEntryPoint;
2217

2318
@AndroidEntryPoint
24-
public class SupportActivity extends AppCompatActivity {
19+
public class SupportActivity extends BaseActivity {
2520

2621
private ActivitySupportBinding binding;
2722
private SupportViewModel supportViewModel;
@@ -32,15 +27,6 @@ protected void onCreate(Bundle savedInstanceState) {
3227
binding = ActivitySupportBinding.inflate(getLayoutInflater());
3328
setContentView(binding.getRoot());
3429

35-
36-
EdgeToEdgeDelegate edgeToEdgeDelegate = new EdgeToEdgeDelegate(this);
37-
edgeToEdgeDelegate.applyEdgeToEdge(binding.container);
38-
39-
ActionBar actionBar = getSupportActionBar();
40-
if (actionBar != null) {
41-
actionBar.setDisplayHomeAsUpEnabled(true);
42-
}
43-
4430
supportViewModel = new ViewModelProvider(this).get(SupportViewModel.class);
4531

4632
AdRequest adRequest = supportViewModel.initMobileAds();
@@ -81,12 +67,5 @@ private void initiatePurchase(String productId) {
8167
}
8268
}
8369

84-
@Override
85-
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
86-
if (item.getItemId() == android.R.id.home) {
87-
finish();
88-
return true;
89-
}
90-
return super.onOptionsItemSelected(item);
91-
}
70+
// Up navigation handled by BaseActivity
9271
}

app/src/main/res/layout/activity_main.xml

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
android:layout_width="match_parent"
1616
android:layout_height="wrap_content"
1717
app:navigationIcon="@drawable/ic_menu"
18+
app:navigationContentDescription="@string/menu"
1819
app:shapeAppearanceOverlay="@style/ShapeTokens.Clover"
1920
app:title="@string/app_name" />
2021
</com.google.android.material.appbar.AppBarLayout>
@@ -26,7 +27,7 @@
2627
android:visibility="gone"
2728
app:layout_constraintStart_toStartOf="parent"
2829
app:layout_constraintTop_toBottomOf="@id/app_bar_layout"
29-
app:layout_constraintBottom_toTopOf="@+id/ad_view"
30+
app:layout_constraintBottom_toTopOf="@+id/ad_container"
3031
app:menu="@menu/bottom_nav_menu" />
3132

3233
<androidx.fragment.app.FragmentContainerView
@@ -37,17 +38,30 @@
3738
app:defaultNavHost="true"
3839
app:layout_constraintStart_toEndOf="@id/nav_rail"
3940
app:layout_constraintEnd_toEndOf="parent"
40-
app:layout_constraintBottom_toTopOf="@+id/ad_view"
41+
app:layout_constraintBottom_toTopOf="@+id/ad_container"
4142
app:layout_constraintTop_toBottomOf="@id/app_bar_layout"
4243
app:navGraph="@navigation/mobile_navigation" />
4344

44-
<com.google.android.gms.ads.AdView
45-
android:id="@+id/ad_view"
45+
<FrameLayout
46+
android:id="@+id/ad_container"
4647
android:layout_width="match_parent"
47-
android:layout_height="wrap_content"
48-
app:adSize="FULL_BANNER"
49-
app:adUnitId="@string/ad_banner_unit_id"
50-
app:layout_constraintBottom_toTopOf="@+id/nav_view" />
48+
android:layout_height="50dp"
49+
app:layout_constraintBottom_toTopOf="@+id/nav_view">
50+
51+
<View
52+
android:id="@+id/ad_placeholder"
53+
android:layout_width="match_parent"
54+
android:layout_height="match_parent"
55+
android:background="@android:color/darker_gray" />
56+
57+
<com.google.android.gms.ads.AdView
58+
android:id="@+id/ad_view"
59+
android:layout_width="match_parent"
60+
android:layout_height="match_parent"
61+
android:visibility="gone"
62+
app:adSize="FULL_BANNER"
63+
app:adUnitId="@string/ad_banner_unit_id" />
64+
</FrameLayout>
5165

5266
<com.google.android.material.bottomnavigation.BottomNavigationView
5367
android:id="@+id/nav_view"
@@ -56,4 +70,14 @@
5670
app:labelVisibilityMode="labeled"
5771
app:layout_constraintBottom_toBottomOf="parent"
5872
app:menu="@menu/bottom_nav_menu" />
73+
74+
<ProgressBar
75+
android:id="@+id/progress_bar"
76+
android:layout_width="wrap_content"
77+
android:layout_height="wrap_content"
78+
android:visibility="gone"
79+
app:layout_constraintTop_toTopOf="parent"
80+
app:layout_constraintBottom_toBottomOf="parent"
81+
app:layout_constraintStart_toStartOf="parent"
82+
app:layout_constraintEnd_toEndOf="parent" />
5983
</androidx.constraintlayout.widget.ConstraintLayout>

app/src/main/res/values-ar-rEG/strings.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
<string name="summary_notification_update">إصدار جديد من التطبيق متاح. اضغط للتحديث.</string>
66
<string name="notification_last_time_used_title">بقالك فترة مدخلتش.</string>
77
<string name="summary_notification_last_time_used">It\'s been a while—learn something new about Android.</string>
8+
<string name="menu">القائمة</string>
9+
<string name="press_back_again_to_exit">اضغط مرة أخرى للخروج</string>
810
<string name="welcome">أهلاً بك</string>
911
<string name="summary_browse_terms_of_service_and_privacy_policy">Read and agree to the Terms of Service and Privacy Policy to continue</string>
1012
<string name="agree">موافق</string>

app/src/main/res/values-bg-rBG/strings.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
<string name="summary_notification_update">Налична е нова версия на приложението. Кликнете, за да актуализирате.</string>
66
<string name="notification_last_time_used_title">Отдавна не сме се виждали.</string>
77
<string name="summary_notification_last_time_used">It\'s been a while—learn something new about Android.</string>
8+
<string name="menu">Меню</string>
9+
<string name="press_back_again_to_exit">Натиснете отново назад, за да излезете</string>
810
<string name="welcome">Добре дошли</string>
911
<string name="summary_browse_terms_of_service_and_privacy_policy">Read and agree to the Terms of Service and Privacy Policy to continue</string>
1012
<string name="agree">Съгласен съм</string>

app/src/main/res/values-bn-rBD/strings.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
<string name="summary_notification_update">অ্যাপটির একটি নতুন সংস্করণ উপলব্ধ। আপডেট করতে ক্লিক করুন.</string>
66
<string name="notification_last_time_used_title">অনেক দিন হয়ে গেছে.</string>
77
<string name="summary_notification_last_time_used">It\'s been a while—learn something new about Android.</string>
8+
<string name="menu">মেনু</string>
9+
<string name="press_back_again_to_exit">প্রস্থান করতে আবার ফিরে টিপুন</string>
810
<string name="welcome">স্বাগতম</string>
911
<string name="summary_browse_terms_of_service_and_privacy_policy">Read and agree to the Terms of Service and Privacy Policy to continue</string>
1012
<string name="agree">সম্মত</string>

app/src/main/res/values-de-rDE/strings.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
<string name="summary_notification_update">Eine neue Version der App ist verfügbar. Zum Aktualisieren klicken.</string>
66
<string name="notification_last_time_used_title">Schon eine Weile her.</string>
77
<string name="summary_notification_last_time_used">It\'s been a while—learn something new about Android.</string>
8+
<string name="menu">Menü</string>
9+
<string name="press_back_again_to_exit">Drücken Sie erneut zurück, um zu verlassen</string>
810
<string name="welcome">Willkommen</string>
911
<string name="summary_browse_terms_of_service_and_privacy_policy">Read and agree to the Terms of Service and Privacy Policy to continue</string>
1012
<string name="agree">Zustimmen</string>

0 commit comments

Comments
 (0)