Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
82 commits
Select commit Hold shift + click to select a range
789292d
Update color palette for module cards and dynamic themes
Dhangofa Aug 15, 2026
8ba2e1a
Update night color palette for module cards and pure black themes
Dhangofa Aug 15, 2026
2856ae1
Update night theme status bar color for improved layout blending
Dhangofa Aug 15, 2026
b409de4
Update light theme status bar color for improved layout blending
Dhangofa Aug 15, 2026
00aa064
Add state list text colors for custom themed segmented text exec
Dhangofa Aug 15, 2026
ea9fa5a
Add state list text colors for custom themed segmented text cycle
Dhangofa Aug 15, 2026
9c0cfdd
Add general app icon background shape
Dhangofa Aug 15, 2026
4071b31
Add adaptive icon foreground layer
Dhangofa Aug 15, 2026
4b3f696
Add Execution Mode card background
Dhangofa Aug 15, 2026
971e5fb
Add general app icon background shape
Dhangofa Aug 15, 2026
ef42907
Add Quick Tile Cycle card background
Dhangofa Aug 15, 2026
bdfa024
Add Developer Profile floating footer background
Dhangofa Aug 15, 2026
49818a7
Add Execution Mode segmented tab selector
Dhangofa Aug 15, 2026
1c9650e
Add Quick Tile Cycle segmented tab selector
Dhangofa Aug 15, 2026
1ecfe2e
Add rectangular segmented tab selector
Dhangofa Aug 15, 2026
f565c08
Add rectangular segmented container shape
Dhangofa Aug 15, 2026
6930b6e
Add small horizontal divider shape
Dhangofa Aug 15, 2026
fd182c5
Add cycle order text badge background
Dhangofa Aug 15, 2026
3f16524
Delete app/src/main/res/drawable/shape_radio_item_bg.xml
Dhangofa Aug 15, 2026
cde589a
Modernize section outline styling with softer curves
Dhangofa Aug 15, 2026
2493bb6
Update segmented tab selector to fully rounded pills
Dhangofa Aug 15, 2026
d6c138a
Soften Quick Settings hint banner styling
Dhangofa Aug 15, 2026
59f4c9b
Update segmented container to fully rounded pill shape
Dhangofa Aug 15, 2026
405268e
Update social icon background from oval to rounded squircle
Dhangofa Aug 15, 2026
882b457
Refactor portrait layout and upgrade header styling
Dhangofa Aug 15, 2026
7aae958
Redesign Quick Tile Cycle card into 2x3 segmented grids
Dhangofa Aug 15, 2026
7b32ccc
Create modular Execution Mode and Target SIM card layout
Dhangofa Aug 15, 2026
55d3c0b
Add & Refactor landscape layout for modular side-by-side cards
Dhangofa Aug 15, 2026
7885a3e
Upgrade TileCycleUiController for 2x3 grid and dynamic separators
Dhangofa Aug 15, 2026
2c65e44
Upgrade MainActivity for dynamic segmented separators and UI tweaks
Dhangofa Aug 15, 2026
392533a
Add support for 3G Preferred and 2G Only modes
Dhangofa Aug 15, 2026
4b4ebd2
Add 3G Preferred and 2G Only icon caching
Dhangofa Aug 15, 2026
4644d36
Correct Pref 3G/4G binary masks to Mode 21 and 22
Dhangofa Aug 15, 2026
2c182b4
Implement dynamic network capability caching
Dhangofa Aug 15, 2026
222584a
Created a brand-new registry class to manage Stage 4 of our resolutio…
Dhangofa Aug 16, 2026
e8bd84e
Upgrade shell parser to support key-based extraction
Dhangofa Aug 16, 2026
d16621c
Implement 4-stage robust network capability resolution
Dhangofa Aug 16, 2026
3966069
Add READ_PHONE_STATE permission
Dhangofa Aug 16, 2026
c256dce
Add diagnostic tracking, capability cache and explicit tile error states
Dhangofa Aug 16, 2026
9102fbb
Implement dynamic capability syncing and diagnostic UI
Dhangofa Aug 16, 2026
409991f
Implement passive Shizuku checks and error state UI
Dhangofa Aug 16, 2026
ab02a8d
Implement native Settings API bypass for network reads
Dhangofa Aug 16, 2026
274df9b
Leverage native SubscriptionManager for SIM resolution
Dhangofa Aug 16, 2026
8f96520
Enhance slot capability caching behavior
Dhangofa Aug 16, 2026
95e7230
Add dynamic UI capability filtering to cycle checkboxes
Dhangofa Aug 16, 2026
3fb6ce3
Create DiagnosticError data model
Dhangofa Aug 16, 2026
b1f0296
Implement shell error formatting utility
Dhangofa Aug 16, 2026
8dcd096
Add warning icon drawable
Dhangofa Aug 16, 2026
d890b04
Add speed/performance icon drawable
Dhangofa Aug 16, 2026
d362a7d
Add directional arrow drawable
Dhangofa Aug 16, 2026
6b689e8
Add rounded dialog background shape
Dhangofa Aug 16, 2026
ad8a09c
Add layout for the main screen error alert banner
Dhangofa Aug 16, 2026
13fed81
Add layout for READ_PHONE_STATE permission prompt
Dhangofa Aug 16, 2026
664c6ff
Add layout for shell error reporting dialog
Dhangofa Aug 16, 2026
4a69928
Inject diagnostic error banner layout
Dhangofa Aug 16, 2026
644e825
Integrate network capability filtering into tile cycle UI
Dhangofa Aug 16, 2026
ecaa1dc
Remove action bar styling constraint
Dhangofa Aug 16, 2026
dbd61c3
Inject diagnostic error banner layout in landscape mode
Dhangofa Aug 16, 2026
1b8adb0
Add android:id="@+id/targetSimHeaderContainer" attribute
Dhangofa Aug 16, 2026
2866cfd
Added button shape
Dhangofa Aug 16, 2026
29b8fdb
Expand LTEandAbove carrier registry with global 2G/3G sunset data
Dhangofa Aug 16, 2026
29187a0
Updated version code and name
Dhangofa Aug 16, 2026
fbb6326
Add carousel page colors
Dhangofa Aug 17, 2026
850ae8c
Add carousel page colors for dark mode
Dhangofa Aug 17, 2026
99721c7
Update dialog background radius and color
Dhangofa Aug 17, 2026
84d1614
Add carousel graphics and new vector icons
Dhangofa Aug 17, 2026
a02bc6d
Add shape_code_block for formatting raw stdout/stderr text in the dia…
Dhangofa Aug 17, 2026
a0d0c3d
Add Carousel viewholder
Dhangofa Aug 17, 2026
9e56ace
Add carousel view adapter
Dhangofa Aug 17, 2026
051d354
Polish diagnostic dialog padding and constraints
Dhangofa Aug 17, 2026
01d3b81
Fix constraint alignment on execution card title
Dhangofa Aug 17, 2026
9bf8110
Protrait mode: Replace static QS hint with Carousel container
Dhangofa Aug 17, 2026
ea14393
Lanscape mode - Replace static QS hint with Carousel container
Dhangofa Aug 17, 2026
367d2d7
Overhaul diagnostic report generation formatting
Dhangofa Aug 17, 2026
ff93539
Expand DiagnosticError schema
Dhangofa Aug 17, 2026
a58a0f0
Expand error state serialization for deep telemetry
Dhangofa Aug 17, 2026
406f90c
Integrate interactive setup carousel and FAQ action
Dhangofa Aug 17, 2026
62ede23
Capture detailed execution context on failure
Dhangofa Aug 17, 2026
f297e5e
Merge branch 'main' into exp
Dhangofa Aug 17, 2026
16fb9e2
Add missing dependencies for carousel
Dhangofa Aug 17, 2026
c4cf1fa
Add missing dependency for viewpager2
Dhangofa Aug 17, 2026
25cfacd
Removed unncessary dependencies
Dhangofa Aug 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ android {
}

dependencies {
implementation("androidx.viewpager2:viewpager2:1.0.0")

// Shizuku API
implementation("dev.rikka.shizuku:api:13.1.5")
implementation("dev.rikka.shizuku:provider:13.1.5")
Expand Down
104 changes: 104 additions & 0 deletions app/src/main/java/com/dhangofa/networktoggle/MainActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,16 @@
import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.TextView;
import androidx.viewpager2.widget.ViewPager2;
import com.dhangofa.networktoggle.ui.SetupCarouselAdapter;
import java.util.ArrayList;
import java.util.List;
import android.widget.ImageView;
import android.widget.LinearLayout;

import com.dhangofa.networktoggle.config.AppPreferences;
import com.dhangofa.networktoggle.model.ExecutionMode;
Expand Down Expand Up @@ -72,6 +79,10 @@ public class MainActivity extends Activity implements android.content.SharedPref
private RadioButton radioSim1;
private RadioButton radioSim2;
private TextView autoSimWarningText;
private ViewPager2 setupCarouselPager;
private android.widget.LinearLayout setupCarouselContainer;
private LinearLayout carouselIndicators;
private ImageView faqLink;

private View separatorRootShizuku;
private View separatorAutoSim1;
Expand Down Expand Up @@ -166,6 +177,84 @@ protected void onCreate(Bundle savedInstanceState) {
updateSeparatorVisibility();
}

private void setupCarousel() {
List<SetupCarouselAdapter.SetupItem> carouselItems = new ArrayList<>();
carouselItems.add(new SetupCarouselAdapter.SetupItem("New to NetToggle?", "Learn how to set up Execution Modes", "Read Guide", "https://github.com/Dhangofa/NetToggle/wiki/1.-Execution-Mode-Configuration", R.drawable.ic_terminal, R.color.first_pg_bg, R.color.exec_accent, R.color.view_guide_button_bg));
carouselItems.add(new SetupCarouselAdapter.SetupItem("Target SIM & Cycle", "Learn how to configure your modes", "Read Guide", "https://github.com/Dhangofa/NetToggle/wiki/2.-Target-SIM-Setup-&-Quick-Tile-Cycle-Guide", R.drawable.ic_sim_card, R.color.second_pg_bg, R.color.accent_orange, R.color.view_guide_button_bg));
carouselItems.add(new SetupCarouselAdapter.SetupItem("Quick Settings Ready", "Add the tile to your Control Center", "View Guide", "https://github.com/Dhangofa/NetToggle/wiki/3.-Adding-the-Tile-to-Quick-Settings", R.drawable.ic_network_bars, R.color.third_pg_bg, R.color.accent_pink, R.color.view_guide_button_bg));

if (setupCarouselContainer != null) {
setupCarouselContainer.removeAllViews();
for (int i = 0; i < carouselItems.size(); i++) {
SetupCarouselAdapter.SetupItem item = carouselItems.get(i);
View view = getLayoutInflater().inflate(R.layout.item_setup_carousel, setupCarouselContainer, false);
android.widget.LinearLayout.LayoutParams params = new android.widget.LinearLayout.LayoutParams(0, android.widget.LinearLayout.LayoutParams.WRAP_CONTENT, 1.0f);
if (i > 0) params.setMarginStart(12);
if (i < carouselItems.size() - 1) params.setMarginEnd(12);
view.setLayoutParams(params);

((TextView) view.findViewById(R.id.carouselTitle)).setText(item.title);
((TextView) view.findViewById(R.id.carouselDesc)).setText(item.description);
((TextView) view.findViewById(R.id.carouselButtonText)).setText(item.buttonText);
((TextView) view.findViewById(R.id.carouselButtonText)).setTextColor(getColor(item.accentColorRes));

ImageView icon = view.findViewById(R.id.carouselIcon);
icon.setImageResource(item.iconRes);
icon.setColorFilter(getColor(item.accentColorRes));

ImageView btnIcon = view.findViewById(R.id.carouselButtonIcon);
btnIcon.setColorFilter(getColor(item.accentColorRes));

view.findViewById(R.id.carouselBackground).setBackgroundTintList(android.content.res.ColorStateList.valueOf(getColor(item.bgColorRes)));
view.findViewById(R.id.carouselButton).setBackgroundTintList(android.content.res.ColorStateList.valueOf(getColor(item.btnBgColorRes)));
view.findViewById(R.id.carouselIconContainer).setBackgroundTintList(android.content.res.ColorStateList.valueOf(getColor(item.btnBgColorRes)));

view.findViewById(R.id.carouselButton).setOnClickListener(v -> {
try { startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(item.url))); } catch (Exception e) {}
});

setupCarouselContainer.addView(view);
}
} else if (setupCarouselPager != null) {
SetupCarouselAdapter adapter = new SetupCarouselAdapter(this, carouselItems);
setupCarouselPager.setAdapter(adapter);
setupCarouselIndicators(carouselItems.size());
setupCarouselPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
@Override
public void onPageSelected(int position) {
super.onPageSelected(position);
updateCarouselIndicators(position);
}
});
}
}


private void setupCarouselIndicators(int count) {
if (carouselIndicators == null) return;
carouselIndicators.removeAllViews();
for (int i = 0; i < count; i++) {
android.widget.ImageView dot = new android.widget.ImageView(this);
dot.setImageDrawable(getDrawable(R.drawable.shape_dot_inactive));
android.widget.LinearLayout.LayoutParams params = new android.widget.LinearLayout.LayoutParams(16, 16);
params.setMargins(8, 0, 8, 0);
dot.setLayoutParams(params);
carouselIndicators.addView(dot);
}
}

private void updateCarouselIndicators(int position) {
if (carouselIndicators == null) return;
for (int i = 0; i < carouselIndicators.getChildCount(); i++) {
android.widget.ImageView dot = (android.widget.ImageView) carouselIndicators.getChildAt(i);
if (i == position) {
dot.setImageDrawable(getDrawable(R.drawable.shape_dot_active));
} else {
dot.setImageDrawable(getDrawable(R.drawable.shape_dot_inactive));
}
}
}

private void configureStatusBar() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return;

Expand All @@ -177,6 +266,11 @@ private void configureStatusBar() {
}

private void bindViews() {
setupCarouselPager = findViewById(R.id.setupCarouselPager);
setupCarouselContainer = findViewById(R.id.setupCarouselContainer);
carouselIndicators = findViewById(R.id.carouselIndicators);
faqLink = findViewById(R.id.faqLink);
setupCarousel();
radioGroup = findViewById(R.id.modeRadioGroup);
radioRoot = findViewById(R.id.radioRoot);
radioShizuku = findViewById(R.id.radioShizuku);
Expand All @@ -202,6 +296,12 @@ private void bindViews() {
}

private void bindLinks() {
if (faqLink != null) {
faqLink.setOnClickListener(v -> {
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/Dhangofa/NetToggle/wiki/Frequently-Asked-Questions-(FAQ)"));
try { startActivity(browserIntent); } catch (Exception e) { android.widget.Toast.makeText(MainActivity.this, "No web browser installed to open this link.", android.widget.Toast.LENGTH_SHORT).show(); }
});
}
githubLink.setOnClickListener(v -> openUrl("https://github.com/Dhangofa/NetToggle"));
telegramLink.setOnClickListener(v -> openUrl("https://t.me/dhangofas_projects_chat"));
}
Expand Down Expand Up @@ -551,6 +651,9 @@ private void setStatus(String text, int color) {

private boolean isUIAuthorized = true;
private void updateAuthorizationUI(boolean authorized) {
if (setupCarouselPager != null) {
setupCarouselPager.setCurrentItem(authorized ? 2 : 0, true);
}
if (isUIAuthorized == authorized) return;
isUIAuthorized = authorized;

Expand Down Expand Up @@ -587,6 +690,7 @@ private void openUrl(String url) {
try {
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
} catch (Exception ignored) {
android.widget.Toast.makeText(MainActivity.this, "No web browser installed to open this link.", android.widget.Toast.LENGTH_SHORT).show();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ public void onClick() {

if (!isAuthError) {
appPreferences.setTileErrorState(AppPreferences.TILE_ERROR_CMD);
appPreferences.setLastError(result.getCommand(), result.getStderr());
appPreferences.setLastError(result.getCommand(), result.getExitCode(), result.getStdout(), result.getStderr(), result.getExceptionMessage());
}

mainHandler.post(() -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,21 +87,29 @@ public void setAutoSimError(boolean hasError) {
preferences.edit().putBoolean(KEY_AUTO_SIM_ERROR, hasError).apply();
}

public void setLastError(String command, String stderr) {
public void setLastError(String command, int exitCode, String stdout, String stderr, String exceptionMsg) {
preferences.edit()
.putString(KEY_LAST_ERROR_CMD, command)
.putInt("last_error_exit_code", exitCode)
.putString("last_error_stdout", stdout)

.putString(KEY_LAST_ERROR_STDERR, stderr)
.putString("last_error_exception", exceptionMsg)

.putLong(KEY_LAST_ERROR_TIMESTAMP, System.currentTimeMillis())
.apply();
}

public com.dhangofa.networktoggle.model.DiagnosticError getLastError() {
String cmd = preferences.getString(KEY_LAST_ERROR_CMD, null);
String stderr = preferences.getString(KEY_LAST_ERROR_STDERR, null);
int exitCode = preferences.getInt("last_error_exit_code", -1);
String stdout = preferences.getString("last_error_stdout", "");
String exceptionMsg = preferences.getString("last_error_exception", "");
long time = preferences.getLong(KEY_LAST_ERROR_TIMESTAMP, 0);

if (cmd == null && stderr == null) return null;
return new com.dhangofa.networktoggle.model.DiagnosticError(cmd, stderr, time);
return new com.dhangofa.networktoggle.model.DiagnosticError(cmd, exitCode, stdout, stderr, exceptionMsg, time);
}

public void clearLastError() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,18 @@

public class DiagnosticError {
public final String command;
public final int exitCode;
public final String stdout;
public final String stderr;
public final String exceptionMessage;
public final long timestamp;

public DiagnosticError(String command, String stderr, long timestamp) {
public DiagnosticError(String command, int exitCode, String stdout, String stderr, String exceptionMessage, long timestamp) {
this.command = command;
this.exitCode = exitCode;
this.stdout = stdout;
this.stderr = stderr;
this.exceptionMessage = exceptionMessage;
this.timestamp = timestamp;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package com.dhangofa.networktoggle.ui;

import android.content.Context;
import android.content.Intent;
import android.content.res.ColorStateList;
import android.net.Uri;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import androidx.core.content.ContextCompat;

import com.dhangofa.networktoggle.R;

import java.util.List;

public class SetupCarouselAdapter extends RecyclerView.Adapter<SetupCarouselAdapter.ViewHolder> {

public static class SetupItem {
public String title;
public String description;
public String buttonText;
public String url;
public int iconRes;
public int bgColorRes;
public int accentColorRes;
public int btnBgColorRes;

public SetupItem(String title, String description, String buttonText, String url, int iconRes, int bgColorRes, int accentColorRes, int btnBgColorRes) {
this.title = title;
this.description = description;
this.buttonText = buttonText;
this.url = url;
this.iconRes = iconRes;
this.bgColorRes = bgColorRes;
this.accentColorRes = accentColorRes;
this.btnBgColorRes = btnBgColorRes;
}
}

private List<SetupItem> items;
private Context context;

public SetupCarouselAdapter(Context context, List<SetupItem> items) {
this.context = context;
this.items = items;
}

@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.item_setup_carousel, parent, false);
// Important: Ensure the item fills the ViewPager2 width
view.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
return new ViewHolder(view);
}

@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
SetupItem item = items.get(position);
holder.title.setText(item.title);
holder.desc.setText(item.description);
holder.buttonText.setText(item.buttonText);
holder.icon.setImageResource(item.iconRes);

int bgColor = ContextCompat.getColor(context, item.bgColorRes);
int accentColor = ContextCompat.getColor(context, item.accentColorRes);

holder.background.setBackgroundTintList(ColorStateList.valueOf(bgColor));

holder.buttonText.setTextColor(accentColor);
holder.buttonIcon.setColorFilter(accentColor);
holder.icon.setColorFilter(accentColor);
int btnBgColor = ContextCompat.getColor(context, item.btnBgColorRes);
holder.button.setBackgroundTintList(ColorStateList.valueOf(btnBgColor));
holder.iconContainer.setBackgroundTintList(ColorStateList.valueOf(btnBgColor));

holder.button.setOnClickListener(v -> {
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(item.url));
try { context.startActivity(browserIntent); } catch (Exception e) { android.widget.Toast.makeText(context, "No web browser installed to open this link.", android.widget.Toast.LENGTH_SHORT).show(); }
});
}

@Override
public int getItemCount() {
return items.size();
}

static class ViewHolder extends RecyclerView.ViewHolder {
View background;
TextView title, desc, buttonText;
ImageView icon, buttonIcon;
LinearLayout button;
View iconContainer;

public ViewHolder(@NonNull View itemView) {
super(itemView);
background = itemView.findViewById(R.id.carouselBackground);
title = itemView.findViewById(R.id.carouselTitle);
desc = itemView.findViewById(R.id.carouselDesc);
buttonText = itemView.findViewById(R.id.carouselButtonText);
icon = itemView.findViewById(R.id.carouselIcon);
buttonIcon = itemView.findViewById(R.id.carouselButtonIcon);
button = itemView.findViewById(R.id.carouselButton);
iconContainer = itemView.findViewById(R.id.carouselIconContainer);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import java.util.Locale;

public class DiagnosticReporter {

public static String generateReport(AppPreferences prefs, SimResolver simResolver) {
DiagnosticError error = prefs.getLastError();
if (error == null) {
Expand All @@ -33,16 +32,26 @@ public static String generateReport(AppPreferences prefs, SimResolver simResolve
sb.append("[APP STATE]\n");
sb.append("Execution Mode: ").append(prefs.getExecutionMode().name()).append("\n");
sb.append("Target SIM Setting: ").append(prefs.getTargetSim().name()).append("\n");

int slotIndex = simResolver.resolveTargetSlotIndex(prefs.getExecutionMode());
sb.append("Resolved Slot Index: ").append(slotIndex).append("\n\n");

sb.append("[ERROR DETAILS]\n");
sb.append("Command Attempted:\n").append(error.command).append("\n\n");
sb.append("Standard Error (stderr):\n").append(error.stderr).append("\n\n");

sb.append("--- END OF REPORT ---");

sb.append("Exit Code: ").append(error.exitCode).append("\n\n");

if (error.exceptionMessage != null && !error.exceptionMessage.isEmpty()) {
sb.append("Exception Message:\n").append(error.exceptionMessage).append("\n\n");
}

if (error.stdout != null && !error.stdout.isEmpty()) {
sb.append("Standard Output (stdout):\n").append(error.stdout).append("\n\n");
}

sb.append("Standard Error (stderr):\n").append(error.stderr != null && !error.stderr.isEmpty() ? error.stderr : "(empty)").append("\n\n");

sb.append("--- END OF REPORT ---");
return sb.toString();
}
}
10 changes: 10 additions & 0 deletions app/src/main/res/drawable/ic_help_outline.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M11,18h2v-2h-2v2zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM12,6c-2.21,0 -4,1.79 -4,4h2c0,-1.1 0.9,-2 2,-2s2,0.9 2,2c0,2 -3,1.75 -3,5h2c0,-2.25 3,-2.5 3,-5 0,-2.21 -1.79,-4 -4,-4z" />
</vector>
Loading