Skip to content

Commit fe58db2

Browse files
authored
UI Polish, Adaptive Icons, Edge-to-Edge Layout Fixes, and Carousel Simplification (#22)
* Implement adaptive icon background with gradient depth - Created a dedicated background layer XML for the adaptive icon implementation. - Implemented a rich linear gradient transitioning from vibrant light blue to a deep navy edge. - Eliminated the flat, single-color background to give the launcher icon a premium, polished aesthetic. * Add unscaled logo asset for in-app action bar - Created an exact duplicate of the cell-tower vector graphic specifically for in-app layouts. - Removed the 75% scaling group from this version so it renders at full 100% size inside the app's UI. - Decoupled the in-app branding from the launcher icon, preventing the action bar logo from appearing artificially small. * Scale and add 3D drop shadow to launcher foreground - Wrapped the core cell-tower vector paths in a scaling group (scaleX="0.75", scaleY="0.75") to add generous padding and negative space around the edges. - Duplicated the main vector path and applied a semi-transparent dark tint (#40001030) with an X/Y offset. - Created a subtle optical drop shadow, giving the white icon a floating, three-dimensional depth over the background. * Add adaptive icon wrapper for Android 8.0+ - Created the API 26+ <adaptive-icon> configuration file to support modern Android launcher shapes. - Linked the newly split foreground (ic_launcher_foreground) and background (ic_launcher_background) layers. - Resolved the visual bug on AOSP/Pixel ROMs where the legacy icon was forcibly shrunk inside a white circular background. * Resolve portrait edge-to-edge layout clipping and Add new icon logo - Added android:fitsSystemWindows="true" to the root LinearLayout to instruct AOSP Window Managers to respect safe insets. - Fixed the bug where the top header clipped into the status bar and the footer hid behind the 3-button navigation bar on stock Android. - Replaced the action bar's ic_launcher_foreground reference with the new unscaled ic_logo_ui drawable. * Resolve landscape edge-to-edge clipping and icon scaling - Applied android:fitsSystemWindows="true" to the root landscape layout to ensure safe padding across notches and navigation bars when the device is rotated. - Swapped the action bar logo reference to use the unscaled ic_logo_ui asset. - Restored the landscape ImageView dimensions (39dp) to ensure the branding matches the proportions of the portrait layout perfectly. * Fix landscape static cards cut-off spacing by making them responsive - Replaced the hardcoded HorizontalScrollView wrapper around the 3 static guide cards with a fluid LinearLayout configured as match_parent. - Changed the static width (android:layout_width="260dp") of all three individual cards (staticCard1, staticCard2, staticCard3) to 0dp. - Added android:layout_weight="1" to all three cards. - This ensures that instead of cutting off or requiring side-scrolling on large screens, all three cards stretch and evenly distribute themselves to seamlessly fill 100% of the available horizontal space regardless of the device's aspect ratio. * Remove all carousel auto-cycling and timer logic - Stripped the authorizedCarouselRunnable entirely from the codebase, stopping the 7-second automatic swipe timer. - Removed the carouselHandler instantiation entirely since background threads and timers are no longer needed for the UI. - Cleaned up the updateCarouselContext method: it now statically sets currentCarouselIndex = 0 (Page 1) if unauthorized, and statically sets currentCarouselIndex = 1 (Page 2) if authorized. - Updated the onResume lifecycle hook to instantly snap to the 2nd page if isUIAuthorized is true, without trying to attach a timer runnable. - Cleaned up the onPause lifecycle hook to completely empty (removed all handler callbacks and preference savings). - Removed all SharedPreferences logic related to carousel memory state to prevent ghosting or saving incorrect indices across sessions. - Cleaned up the manual touch listener to remove any removeCallbacks or postDelayed timer resets when the user manually swipes left or right. * Upgrade ic_launcher with depth and gradient background - Added Background Gradient - Added Shadow Effect in main Antenna Icon
1 parent 29e763e commit fe58db2

8 files changed

Lines changed: 378 additions & 163 deletions

File tree

app/src/main/java/com/dhangofa/networktoggle/MainActivity.java

Lines changed: 43 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@ protected void onCreate(Bundle savedInstanceState) {
192192
bindSelectionListeners();
193193
updateSeparatorVisibility();
194194
setupCarousel();
195+
setupStaticLandscapeCards();
195196
}
196197

197198
private void configureStatusBar() {
@@ -247,6 +248,21 @@ private void bindLinks() {
247248
}
248249

249250

251+
private void setupStaticLandscapeCards() {
252+
View btn1 = findViewById(R.id.staticCardButton1);
253+
if (btn1 != null) {
254+
btn1.setOnClickListener(v -> openUrl("https://github.com/Dhangofa/NetToggle/wiki/1.-Execution-Mode-Configuration"));
255+
}
256+
View btn2 = findViewById(R.id.staticCardButton2);
257+
if (btn2 != null) {
258+
btn2.setOnClickListener(v -> openUrl("https://github.com/Dhangofa/NetToggle/wiki/2.-Target-SIM-Setup-&-Quick-Tile-Cycle-Guide"));
259+
}
260+
View btn3 = findViewById(R.id.staticCardButton3);
261+
if (btn3 != null) {
262+
btn3.setOnClickListener(v -> openUrl("https://github.com/Dhangofa/NetToggle/wiki/3.-Adding-the-Tile-to-Quick-Settings"));
263+
}
264+
}
265+
250266
private void setupCarousel() {
251267
if (carouselBackground == null) return;
252268
carouselItems = new CarouselItem[]{
@@ -270,16 +286,17 @@ private void setupCarousel() {
270286
case android.view.MotionEvent.ACTION_CANCEL:
271287
float deltaX = event.getX() - touchStartX;
272288
if (Math.abs(deltaX) > 100) {
273-
carouselHandler.removeCallbacks(carouselAutoRunnable);
274-
if (deltaX > 0 && currentCarouselIndex > 0) {
275-
currentCarouselIndex--;
289+
if (deltaX > 0) {
290+
// Swipe right (previous page)
291+
currentCarouselIndex = (currentCarouselIndex - 1 + carouselItems.length) % carouselItems.length;
276292
renderCarouselPage(currentCarouselIndex, true, -1);
277-
} else if (deltaX < 0 && currentCarouselIndex < carouselItems.length - 1) {
278-
currentCarouselIndex++;
293+
} else if (deltaX < 0) {
294+
// Swipe left (next page)
295+
currentCarouselIndex = (currentCarouselIndex + 1) % carouselItems.length;
279296
renderCarouselPage(currentCarouselIndex, true, 1);
280297
}
281-
// Resume after 10 seconds of inactivity
282-
carouselHandler.postDelayed(carouselAutoRunnable, 10000);
298+
299+
283300
} else if (event.getAction() == android.view.MotionEvent.ACTION_UP) {
284301
v.performClick();
285302
}
@@ -343,7 +360,7 @@ private void renderCarouselPage(int index, boolean animate, int direction) {
343360

344361
// Prepare for animate in
345362
view.setTranslationX(moveInX);
346-
view.animate().translationX(0f).alpha(1f).setDuration(duration)
363+
view.animate().translationX(0f).alpha(1f).setDuration(duration / 2)
347364
.setInterpolator(new android.view.animation.DecelerateInterpolator())
348365
.start();
349366
}).start();
@@ -488,22 +505,17 @@ private void bindSelectionListeners() {
488505
@Override
489506
protected void onResume() {
490507
super.onResume();
508+
491509
if (appPreferences != null) updateAutoSimWarning();
492510
checkAndRequestPermission();
493511
updateErrorBanner();
494512
updateCapabilities();
495-
if (carouselHandler != null && carouselAutoRunnable != null) {
496-
carouselHandler.removeCallbacks(carouselAutoRunnable);
497-
carouselHandler.postDelayed(carouselAutoRunnable, 5000);
498-
}
513+
499514
}
500515

501516
@Override
502517
protected void onPause() {
503518
super.onPause();
504-
if (carouselHandler != null && carouselAutoRunnable != null) {
505-
carouselHandler.removeCallbacks(carouselAutoRunnable);
506-
}
507519
}
508520

509521
private void checkAndRequestPermission() {
@@ -733,56 +745,40 @@ private void setStatus(String text, int color) {
733745
}
734746

735747

736-
private android.os.Handler carouselHandler = new android.os.Handler(android.os.Looper.getMainLooper());
737748

738-
private Runnable carouselAutoRunnable = new Runnable() {
739-
@Override
740-
public void run() {
741-
if (carouselItems == null) return;
742-
743-
int nextIndex;
744-
if (!isUIAuthorized) {
745-
// If unauthorized but user manually swiped away, bring them back to 0 eventually
746-
nextIndex = 0;
747-
} else {
748-
// If authorized, cycle through logically
749-
nextIndex = (currentCarouselIndex == 2) ? 1 : (currentCarouselIndex + 1);
750-
}
751-
752-
if (nextIndex != currentCarouselIndex) {
753-
int direction = (nextIndex > currentCarouselIndex) ? 1 : -1;
754-
currentCarouselIndex = nextIndex;
755-
renderCarouselPage(currentCarouselIndex, true, direction);
756-
}
757-
carouselHandler.postDelayed(this, 5000);
758-
}
759-
};
749+
750+
751+
752+
760753

761754
private void updateCarouselContext(boolean authorized) {
762755
if (carouselBackground == null || carouselItems == null) return;
763-
carouselHandler.removeCallbacks(carouselAutoRunnable);
756+
757+
boolean animate = !isFirstCarouselRender;
758+
isFirstCarouselRender = false;
764759

765760
if (!authorized) {
766-
if (currentCarouselIndex != 0) {
761+
if (currentCarouselIndex != 0 || !animate) {
767762
currentCarouselIndex = 0;
768-
renderCarouselPage(0, true, -1);
763+
renderCarouselPage(0, animate, -1);
769764
}
770-
// Will auto-correct to 0 every 5 seconds if user swipes away while unauthorized
771-
carouselHandler.postDelayed(carouselAutoRunnable, 5000);
772765
} else {
773-
if (currentCarouselIndex == 0) {
766+
// Always start at 2nd page (index 1) on auth transition
767+
if (currentCarouselIndex != 1 || !animate) {
774768
currentCarouselIndex = 1;
775-
renderCarouselPage(1, true, 1);
769+
renderCarouselPage(1, animate, 1);
776770
}
777-
carouselHandler.postDelayed(carouselAutoRunnable, 5000);
778771
}
779772
}
780773

781774
private boolean isUIAuthorized = false;
775+
private boolean isFirstCarouselRender = true;
776+
777+
782778
private void updateAuthorizationUI(boolean authorized) {
783-
updateCarouselContext(authorized);
784779
if (isUIAuthorized == authorized) return;
785780
isUIAuthorized = authorized;
781+
updateCarouselContext(authorized);
786782

787783
float alpha = authorized ? 1.0f : 0.4f;
788784
View targetSimCard = findViewById(R.id.targetSimRadioGroup);
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
3+
<background android:drawable="@drawable/ic_launcher_background" />
4+
<foreground android:drawable="@drawable/ic_launcher_foreground" />
5+
</adaptive-icon>
Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,48 @@
1-
<?xml version="1.0" encoding="utf-8"?>
21
<vector xmlns:android="http://schemas.android.com/apk/res/android"
2+
xmlns:aapt="http://schemas.android.com/aapt"
33
android:width="108dp"
44
android:height="108dp"
55
android:viewportWidth="108"
66
android:viewportHeight="108">
77

8-
<!-- Background Circle -->
8+
<!-- Background Circle for legacy icon (Flat blue fallback) -->
99
<path
10-
android:fillColor="#0066FF"
10+
android:fillColor="#0055FF"
1111
android:pathData="M54,54m-50,0a50,50 0,1 1,100 0a50,50 0,1 1,-100 0" />
12+
13+
<!-- Background Circle with Gradient -->
14+
<path android:pathData="M54,54m-50,0a50,50 0,1 1,100 0a50,50 0,1 1,-100 0">
15+
<aapt:attr name="android:fillColor">
16+
<gradient
17+
android:startX="4"
18+
android:startY="4"
19+
android:endX="104"
20+
android:endY="104"
21+
android:type="linear">
22+
<item android:offset="0.0" android:color="#2979FF" /> <!-- Vibrant light blue -->
23+
<item android:offset="0.6" android:color="#0055FF" /> <!-- Standard solid blue -->
24+
<item android:offset="1.0" android:color="#002277" /> <!-- Deep navy edge -->
25+
</gradient>
26+
</aapt:attr>
27+
</path>
28+
29+
<!-- Foreground Group -->
30+
<group
31+
android:translateX="0.0"
32+
android:translateY="0.0">
1233

13-
<!-- Stylized Cell Tower & Signal Waves -->
14-
<path
15-
android:fillColor="#FFFFFF"
16-
android:pathData="M54,28 A2,2 0 0,0 52,30 L52,42 L42,42 A2,2 0 0,0 40,44 L40,78 A2,2 0 0,0 42,80 L66,80 A2,2 0 0,0 68,78 L68,44 A2,2 0 0,0 66,42 L56,42 L56,30 A2,2 0 0,0 54,28 Z M45,47 L63,47 L63,75 L45,75 Z M32,46 A2,2 0 0,0 30,48 C30,58 34,68 40,75 A2,2 0 0,0 43,72 C37,66 34,58 34,48 A2,2 0 0,0 32,46 Z M76,46 A2,2 0 0,0 74,48 C74,58 71,66 65,72 A2,2 0 0,0 68,75 C74,68 78,58 78,48 A2,2 0 0,0 76,46 Z M23,36 A2,2 0 0,0 21,38 C21,52 26,65 35,76 A2,2 0 0,0 38,73 C29,63 25,51 25,38 A2,2 0 0,0 23,36 Z M85,36 A2,2 0 0,0 83,38 C83,51 79,63 70,73 A2,2 0 0,0 73,76 C82,65 87,52 87,38 A2,2 0 0,0 85,36 Z" />
34+
<!-- Subtle Drop Shadow to create floating depth -->
35+
<group android:translateX="2.5" android:translateY="4.5">
36+
<path
37+
android:fillColor="#40001030"
38+
android:fillType="nonZero"
39+
android:pathData="M54,28 A2,2 0 0,0 52,30 L52,42 L42,42 A2,2 0 0,0 40,44 L40,78 A2,2 0 0,0 42,80 L66,80 A2,2 0 0,0 68,78 L68,44 A2,2 0 0,0 66,42 L56,42 L56,30 A2,2 0 0,0 54,28 Z M45,47 L63,47 L63,75 L45,75 Z M32,46 A2,2 0 0,0 30,48 C30,58 34,68 40,75 A2,2 0 0,0 43,72 C37,66 34,58 34,48 A2,2 0 0,0 32,46 Z M76,46 A2,2 0 0,0 74,48 C74,58 71,66 65,72 A2,2 0 0,0 68,75 C74,68 78,58 78,48 A2,2 0 0,0 76,46 Z M23,36 A2,2 0 0,0 21,38 C21,52 26,65 35,76 A2,2 0 0,0 38,73 C29,63 25,51 25,38 A2,2 0 0,0 23,36 Z M85,36 A2,2 0 0,0 83,38 C83,51 79,63 70,73 A2,2 0 0,0 73,76 C82,65 87,52 87,38 A2,2 0 0,0 85,36 Z" />
40+
</group>
41+
42+
<!-- Main Crisp White Icon -->
43+
<path
44+
android:fillColor="#FFFFFF"
45+
android:fillType="nonZero"
46+
android:pathData="M54,28 A2,2 0 0,0 52,30 L52,42 L42,42 A2,2 0 0,0 40,44 L40,78 A2,2 0 0,0 42,80 L66,80 A2,2 0 0,0 68,78 L68,44 A2,2 0 0,0 66,42 L56,42 L56,30 A2,2 0 0,0 54,28 Z M45,47 L63,47 L63,75 L45,75 Z M32,46 A2,2 0 0,0 30,48 C30,58 34,68 40,75 A2,2 0 0,0 43,72 C37,66 34,58 34,48 A2,2 0 0,0 32,46 Z M76,46 A2,2 0 0,0 74,48 C74,58 71,66 65,72 A2,2 0 0,0 68,75 C74,68 78,58 78,48 A2,2 0 0,0 76,46 Z M23,36 A2,2 0 0,0 21,38 C21,52 26,65 35,76 A2,2 0 0,0 38,73 C29,63 25,51 25,38 A2,2 0 0,0 23,36 Z M85,36 A2,2 0 0,0 83,38 C83,51 79,63 70,73 A2,2 0 0,0 73,76 C82,65 87,52 87,38 A2,2 0 0,0 85,36 Z" />
47+
</group>
1748
</vector>
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
3+
xmlns:aapt="http://schemas.android.com/aapt"
4+
android:width="108dp"
5+
android:height="108dp"
6+
android:viewportWidth="108"
7+
android:viewportHeight="108">
8+
<path android:pathData="M0,0h108v108h-108z">
9+
<aapt:attr name="android:fillColor">
10+
<gradient
11+
android:startX="0"
12+
android:startY="0"
13+
android:endX="108"
14+
android:endY="108"
15+
android:type="linear">
16+
<item android:offset="0.0" android:color="#2979FF" /> <!-- Vibrant light blue -->
17+
<item android:offset="0.6" android:color="#0055FF" /> <!-- Standard solid blue -->
18+
<item android:offset="1.0" android:color="#002277" /> <!-- Deep navy edge -->
19+
</gradient>
20+
</aapt:attr>
21+
</path>
22+
</vector>

app/src/main/res/drawable/ic_launcher_foreground.xml

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,25 @@
44
android:height="108dp"
55
android:viewportWidth="108"
66
android:viewportHeight="108">
7-
<path
8-
android:fillColor="#FFFFFF"
9-
android:fillType="nonZero"
10-
android:pathData="M54,28 A2,2 0 0,0 52,30 L52,42 L42,42 A2,2 0 0,0 40,44 L40,78 A2,2 0 0,0 42,80 L66,80 A2,2 0 0,0 68,78 L68,44 A2,2 0 0,0 66,42 L56,42 L56,30 A2,2 0 0,0 54,28 Z M45,47 L63,47 L63,75 L45,75 Z M32,46 A2,2 0 0,0 30,48 C30,58 34,68 40,75 A2,2 0 0,0 43,72 C37,66 34,58 34,48 A2,2 0 0,0 32,46 Z M76,46 A2,2 0 0,0 74,48 C74,58 71,66 65,72 A2,2 0 0,0 68,75 C74,68 78,58 78,48 A2,2 0 0,0 76,46 Z M23,36 A2,2 0 0,0 21,38 C21,52 26,65 35,76 A2,2 0 0,0 38,73 C29,63 25,51 25,38 A2,2 0 0,0 23,36 Z M85,36 A2,2 0 0,0 83,38 C83,51 79,63 70,73 A2,2 0 0,0 73,76 C82,65 87,52 87,38 A2,2 0 0,0 85,36 Z"
11-
android:strokeWidth="1"
12-
android:strokeColor="#00000000" />
7+
8+
<group
9+
android:pivotX="54"
10+
android:pivotY="54"
11+
android:scaleX="0.75"
12+
android:scaleY="0.75">
13+
14+
<!-- Subtle Drop Shadow to create floating depth -->
15+
<group android:translateX="2.5" android:translateY="4.5">
16+
<path
17+
android:fillColor="#40001030"
18+
android:fillType="nonZero"
19+
android:pathData="M54,28 A2,2 0 0,0 52,30 L52,42 L42,42 A2,2 0 0,0 40,44 L40,78 A2,2 0 0,0 42,80 L66,80 A2,2 0 0,0 68,78 L68,44 A2,2 0 0,0 66,42 L56,42 L56,30 A2,2 0 0,0 54,28 Z M45,47 L63,47 L63,75 L45,75 Z M32,46 A2,2 0 0,0 30,48 C30,58 34,68 40,75 A2,2 0 0,0 43,72 C37,66 34,58 34,48 A2,2 0 0,0 32,46 Z M76,46 A2,2 0 0,0 74,48 C74,58 71,66 65,72 A2,2 0 0,0 68,75 C74,68 78,58 78,48 A2,2 0 0,0 76,46 Z M23,36 A2,2 0 0,0 21,38 C21,52 26,65 35,76 A2,2 0 0,0 38,73 C29,63 25,51 25,38 A2,2 0 0,0 23,36 Z M85,36 A2,2 0 0,0 83,38 C83,51 79,63 70,73 A2,2 0 0,0 73,76 C82,65 87,52 87,38 A2,2 0 0,0 85,36 Z" />
20+
</group>
21+
22+
<!-- Main Crisp White Icon -->
23+
<path
24+
android:fillColor="#FFFFFF"
25+
android:fillType="nonZero"
26+
android:pathData="M54,28 A2,2 0 0,0 52,30 L52,42 L42,42 A2,2 0 0,0 40,44 L40,78 A2,2 0 0,0 42,80 L66,80 A2,2 0 0,0 68,78 L68,44 A2,2 0 0,0 66,42 L56,42 L56,30 A2,2 0 0,0 54,28 Z M45,47 L63,47 L63,75 L45,75 Z M32,46 A2,2 0 0,0 30,48 C30,58 34,68 40,75 A2,2 0 0,0 43,72 C37,66 34,58 34,48 A2,2 0 0,0 32,46 Z M76,46 A2,2 0 0,0 74,48 C74,58 71,66 65,72 A2,2 0 0,0 68,75 C74,68 78,58 78,48 A2,2 0 0,0 76,46 Z M23,36 A2,2 0 0,0 21,38 C21,52 26,65 35,76 A2,2 0 0,0 38,73 C29,63 25,51 25,38 A2,2 0 0,0 23,36 Z M85,36 A2,2 0 0,0 83,38 C83,51 79,63 70,73 A2,2 0 0,0 73,76 C82,65 87,52 87,38 A2,2 0 0,0 85,36 Z" />
27+
</group>
1328
</vector>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
2+
xmlns:aapt="http://schemas.android.com/aapt"
3+
android:width="108dp"
4+
android:height="108dp"
5+
android:viewportWidth="108"
6+
android:viewportHeight="108">
7+
<path
8+
android:fillColor="#FFFFFF"
9+
android:fillType="nonZero"
10+
android:pathData="M54,28 A2,2 0 0,0 52,30 L52,42 L42,42 A2,2 0 0,0 40,44 L40,78 A2,2 0 0,0 42,80 L66,80 A2,2 0 0,0 68,78 L68,44 A2,2 0 0,0 66,42 L56,42 L56,30 A2,2 0 0,0 54,28 Z M45,47 L63,47 L63,75 L45,75 Z M32,46 A2,2 0 0,0 30,48 C30,58 34,68 40,75 A2,2 0 0,0 43,72 C37,66 34,58 34,48 A2,2 0 0,0 32,46 Z M76,46 A2,2 0 0,0 74,48 C74,58 71,66 65,72 A2,2 0 0,0 68,75 C74,68 78,58 78,48 A2,2 0 0,0 76,46 Z M23,36 A2,2 0 0,0 21,38 C21,52 26,65 35,76 A2,2 0 0,0 38,73 C29,63 25,51 25,38 A2,2 0 0,0 23,36 Z M85,36 A2,2 0 0,0 83,38 C83,51 79,63 70,73 A2,2 0 0,0 73,76 C82,65 87,52 87,38 A2,2 0 0,0 85,36 Z"
11+
android:strokeWidth="1"
12+
android:strokeColor="#00000000" />
13+
</vector>

0 commit comments

Comments
 (0)