Merge pull request #175 from vernu/android-app-ui-improvement

enhance app ui
This commit is contained in:
Israel Abebe
2026-02-06 10:18:31 +03:00
committed by GitHub
6 changed files with 200 additions and 115 deletions

View File

@@ -35,6 +35,9 @@ import com.vernu.sms.helpers.SharedPreferenceHelper;
import com.vernu.sms.helpers.VersionTracker;
import com.vernu.sms.helpers.HeartbeatManager;
import com.google.firebase.crashlytics.FirebaseCrashlytics;
import com.google.gson.Gson;
import okhttp3.ResponseBody;
import java.io.IOException;
import java.util.Arrays;
import java.util.Objects;
import retrofit2.Call;
@@ -159,7 +162,7 @@ public class MainActivity extends AppCompatActivity {
public void onResponse(Call<RegisterDeviceResponseDTO> call, Response<RegisterDeviceResponseDTO> response) {
Log.d(TAG, response.toString());
if (!response.isSuccessful()) {
Snackbar.make(view, response.message().isEmpty() ? "An error occurred :( "+ response.code() : response.message(), Snackbar.LENGTH_LONG).show();
Snackbar.make(view, extractErrorMessage(response), Snackbar.LENGTH_LONG).show();
compoundButton.setEnabled(true);
return;
}
@@ -257,7 +260,8 @@ public class MainActivity extends AppCompatActivity {
// Create radio buttons for each SIM with proper styling
TextBeeUtils.getAvailableSimSlots(mContext).forEach(subscriptionInfo -> {
String simInfo = "SIM " + (subscriptionInfo.getSimSlotIndex() + 1) + " (" + subscriptionInfo.getDisplayName() + ")";
String displayName = subscriptionInfo.getDisplayName() != null ? subscriptionInfo.getDisplayName().toString() : "Unknown";
String simInfo = displayName + " (Subscription ID: " + subscriptionInfo.getSubscriptionId() + ")";
RadioButton radioButton = new RadioButton(mContext);
radioButton.setText(simInfo);
radioButton.setId(subscriptionInfo.getSubscriptionId());
@@ -292,6 +296,44 @@ public class MainActivity extends AppCompatActivity {
}
}
/**
* Extracts error message from API response, trying multiple sources:
* 1. Error message from response body (error field)
* 2. Response message from HTTP headers
* 3. Generic error with status code as fallback
*/
private String extractErrorMessage(Response<?> response) {
// Try to parse error from response body
try {
ResponseBody errorBody = response.errorBody();
if (errorBody != null) {
String errorBodyString = errorBody.string();
if (errorBodyString != null && !errorBodyString.isEmpty()) {
try {
Gson gson = new Gson();
RegisterDeviceResponseDTO errorResponse = gson.fromJson(errorBodyString, RegisterDeviceResponseDTO.class);
if (errorResponse != null && errorResponse.error != null && !errorResponse.error.isEmpty()) {
return errorResponse.error;
}
} catch (Exception e) {
// If JSON parsing fails, try to extract message from raw string
Log.d(TAG, "Could not parse error response as JSON: " + errorBodyString);
}
}
}
} catch (IOException e) {
Log.d(TAG, "Could not read error body: " + e.getMessage());
}
// Fall back to response message
if (response.message() != null && !response.message().isEmpty()) {
return response.message();
}
// Final fallback to generic error with status code
return "An error occurred :( " + response.code();
}
/**
* Apply the custom radio button style to a programmatically created radio button
*/
@@ -391,7 +433,7 @@ public class MainActivity extends AppCompatActivity {
public void onResponse(Call<RegisterDeviceResponseDTO> call, Response<RegisterDeviceResponseDTO> response) {
Log.d(TAG, response.toString());
if (!response.isSuccessful()) {
Snackbar.make(view, response.message().isEmpty() ? "An error occurred :( "+ response.code() : response.message(), Snackbar.LENGTH_LONG).show();
Snackbar.make(view, extractErrorMessage(response), Snackbar.LENGTH_LONG).show();
registerDeviceBtn.setEnabled(true);
registerDeviceBtn.setText("Update");
return;
@@ -445,18 +487,18 @@ public class MainActivity extends AppCompatActivity {
}
Call<RegisterDeviceResponseDTO> apiCall = ApiManager.getApiService().registerDevice(newKey, registerDeviceInput);
apiCall.enqueue(new Callback<RegisterDeviceResponseDTO>() {
@Override
public void onResponse(Call<RegisterDeviceResponseDTO> call, Response<RegisterDeviceResponseDTO> response) {
Log.d(TAG, response.toString());
if (!response.isSuccessful()) {
Snackbar.make(view, response.message().isEmpty() ? "An error occurred :( "+ response.code() : response.message(), Snackbar.LENGTH_LONG).show();
registerDeviceBtn.setEnabled(true);
registerDeviceBtn.setText("Update");
return;
}
SharedPreferenceHelper.setSharedPreferenceString(mContext, AppConstants.SHARED_PREFS_API_KEY_KEY, newKey);
Snackbar.make(view, "Device Registration Successful :)", Snackbar.LENGTH_LONG).show();
apiCall.enqueue(new Callback<RegisterDeviceResponseDTO>() {
@Override
public void onResponse(Call<RegisterDeviceResponseDTO> call, Response<RegisterDeviceResponseDTO> response) {
Log.d(TAG, response.toString());
if (!response.isSuccessful()) {
Snackbar.make(view, extractErrorMessage(response), Snackbar.LENGTH_LONG).show();
registerDeviceBtn.setEnabled(true);
registerDeviceBtn.setText("Update");
return;
}
SharedPreferenceHelper.setSharedPreferenceString(mContext, AppConstants.SHARED_PREFS_API_KEY_KEY, newKey);
Snackbar.make(view, "Device Registration Successful :)", Snackbar.LENGTH_LONG).show();
if (response.body() != null && response.body().data != null && response.body().data.get("_id") != null) {
deviceId = response.body().data.get("_id").toString();
@@ -544,7 +586,7 @@ public class MainActivity extends AppCompatActivity {
public void onResponse(Call<RegisterDeviceResponseDTO> call, Response<RegisterDeviceResponseDTO> response) {
Log.d(TAG, response.toString());
if (!response.isSuccessful()) {
Snackbar.make(view, response.message().isEmpty() ? "An error occurred :( "+ response.code() : response.message(), Snackbar.LENGTH_LONG).show();
Snackbar.make(view, extractErrorMessage(response), Snackbar.LENGTH_LONG).show();
registerDeviceBtn.setEnabled(true);
registerDeviceBtn.setText("Update");
return;

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="?attr/colorPrimary" android:state_enabled="true"/>
<item android:color="?attr/colorPrimary" android:alpha="0.12"/>
</selector>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/white" android:state_enabled="true"/>
<item android:color="@color/white" android:alpha="0.38"/>
</selector>

View File

@@ -20,7 +20,7 @@
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
android:orientation="vertical"
android:padding="24dp">
android:padding="28dp">
<TextView
android:layout_width="match_parent"
@@ -28,18 +28,25 @@
android:text="textbee.dev - sms gateway"
android:textAlignment="center"
android:textColor="@color/white"
android:textSize="22sp"
android:textStyle="bold" />
android:textSize="24sp"
android:textStyle="bold"
android:letterSpacing="0.02"
android:shadowColor="#80000000"
android:shadowDx="0"
android:shadowDy="2"
android:shadowRadius="4" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:text="Your ultimate solution for seamless SMS communication"
android:textAlignment="center"
android:textColor="@color/white"
android:textSize="12sp" />
android:textSize="13sp"
android:letterSpacing="0.01"
android:alpha="0.95" />
</LinearLayout>
<!-- Device Info Card -->
@@ -50,21 +57,21 @@
android:layout_marginTop="-24dp"
android:layout_marginBottom="16dp"
app:cardBackgroundColor="@color/background_secondary"
app:cardCornerRadius="8dp"
app:cardElevation="4dp">
app:cardCornerRadius="12dp"
app:cardElevation="6dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal"
android:padding="6dp">
android:padding="12dp">
<ImageView
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_width="52dp"
android:layout_height="52dp"
android:layout_gravity="center"
android:padding="3dp"
android:padding="6dp"
android:src="@drawable/ic_baseline_phone_android_24"
android:tint="?attr/colorPrimary" />
@@ -72,7 +79,7 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginStart="3dp"
android:layout_marginStart="8dp"
android:layout_weight="1"
android:orientation="vertical">
@@ -109,11 +116,11 @@
<ImageButton
android:id="@+id/copyDeviceIdImgBtn"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginStart="2dp"
android:layout_width="26dp"
android:layout_height="26dp"
android:layout_marginStart="4dp"
android:background="?attr/selectableItemBackgroundBorderless"
android:padding="2dp"
android:padding="4dp"
android:src="@drawable/ic_baseline_content_copy_24"
android:tint="?attr/colorPrimary" />
</LinearLayout>
@@ -163,23 +170,24 @@
android:layout_marginHorizontal="16dp"
android:layout_marginBottom="16dp"
app:cardBackgroundColor="@color/background_secondary"
app:cardCornerRadius="8dp"
app:cardElevation="2dp">
app:cardCornerRadius="12dp"
app:cardElevation="4dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
android:padding="20dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:layout_marginBottom="18dp"
android:text="Account Information"
android:textColor="@color/text_primary"
android:textSize="18sp"
android:textStyle="bold" />
android:textSize="19sp"
android:textStyle="bold"
android:letterSpacing="0.01" />
<!-- Device ID Input Field -->
<com.google.android.material.textfield.TextInputLayout
@@ -212,13 +220,15 @@
android:hint="API Key"
app:boxBackgroundColor="@android:color/transparent"
app:boxStrokeColor="?attr/colorPrimary"
app:hintTextColor="?attr/colorPrimary">
app:hintTextColor="?attr/colorPrimary"
app:endIconMode="password_toggle"
app:endIconTint="?attr/colorPrimary">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/apiKeyEditText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="text"
android:inputType="textPassword"
android:textColor="@color/text_primary"
android:textIsSelectable="true" />
</com.google.android.material.textfield.TextInputLayout>
@@ -246,10 +256,15 @@
android:id="@+id/registerDeviceBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:backgroundTint="?attr/colorPrimary"
android:paddingHorizontal="24dp"
android:backgroundTint="@color/button_background_tint"
android:paddingHorizontal="28dp"
android:paddingVertical="12dp"
android:text="Connect"
android:textColor="@color/white" />
android:textColor="@color/button_text_color"
android:textSize="15sp"
android:letterSpacing="0.01"
style="@style/Widget.MaterialComponents.Button"
app:cornerRadius="8dp" />
<View
android:layout_width="0dp"
@@ -259,11 +274,20 @@
android:id="@+id/scanQRButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:layout_marginStart="8dp"
android:drawableLeft="@drawable/ic_baseline_qr_code_24"
android:drawablePadding="6dp"
android:paddingHorizontal="20dp"
android:paddingVertical="12dp"
android:text="Scan QR"
android:textColor="@color/black"
android:theme="@style/Theme.Design.Light" />
android:textColor="?attr/colorPrimary"
android:textSize="15sp"
android:letterSpacing="0.01"
android:backgroundTint="@android:color/transparent"
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
app:cornerRadius="8dp"
app:strokeColor="?attr/colorPrimary"
app:strokeWidth="1.5dp" />
</LinearLayout>
</LinearLayout>
@@ -276,23 +300,24 @@
android:layout_marginHorizontal="16dp"
android:layout_marginBottom="16dp"
app:cardBackgroundColor="@color/background_secondary"
app:cardCornerRadius="8dp"
app:cardElevation="2dp">
app:cardCornerRadius="12dp"
app:cardElevation="4dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
android:padding="20dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:layout_marginBottom="18dp"
android:text="Configuration"
android:textColor="@color/text_primary"
android:textSize="18sp"
android:textStyle="bold" />
android:textSize="19sp"
android:textStyle="bold"
android:letterSpacing="0.01" />
<!-- Permissions Section -->
<LinearLayout
@@ -309,30 +334,28 @@
android:textSize="16sp"
android:textStyle="bold" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="REQUIRED for textbee to function!"
android:textColor="@android:color/holo_red_dark"
android:textSize="14sp"
android:textStyle="bold"
android:layout_marginBottom="8dp" />
<Button
android:id="@+id/grantSMSPermissionBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:backgroundTint="?attr/colorPrimary"
android:backgroundTint="@color/button_background_tint"
android:paddingHorizontal="24dp"
android:paddingVertical="12dp"
android:text="Grant SMS Permissions"
android:textColor="@color/white"
android:visibility="visible" />
android:textColor="@color/button_text_color"
android:textSize="15sp"
android:letterSpacing="0.01"
android:visibility="visible"
style="@style/Widget.MaterialComponents.Button"
app:cornerRadius="8dp" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_height="1.5dp"
android:background="@color/divider"
android:layout_marginBottom="16dp" />
android:alpha="0.6"
android:layout_marginBottom="18dp" />
<!-- Receive SMS Toggle -->
<LinearLayout
@@ -400,11 +423,11 @@
android:gravity="center_vertical">
<ImageView
android:layout_width="16dp"
android:layout_height="16dp"
android:layout_width="18dp"
android:layout_height="18dp"
android:src="@android:drawable/ic_dialog_info"
android:tint="?attr/colorPrimary"
android:layout_marginEnd="4dp" />
android:layout_marginEnd="6dp" />
<TextView
android:layout_width="wrap_content"
@@ -424,9 +447,10 @@
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_height="1.5dp"
android:background="@color/divider"
android:layout_marginBottom="16dp" />
android:alpha="0.6"
android:layout_marginBottom="18dp" />
<!-- Default SIM Selection -->
<TextView
@@ -440,10 +464,11 @@
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:text="Select your preferred SIM for sending SMS"
android:layout_marginBottom="12dp"
android:text="This is the default SIM that will be used for sending SMS. You can override for individual SMS by providing the specific simSubscriptionId in your API request."
android:textColor="@color/text_secondary"
android:textSize="14sp" />
android:textSize="14sp"
android:lineSpacingMultiplier="1.2" />
<RadioGroup
android:id="@+id/defaultSimSlotRadioGroup"
@@ -462,38 +487,41 @@
android:layout_marginHorizontal="16dp"
android:layout_marginBottom="16dp"
app:cardBackgroundColor="@color/background_secondary"
app:cardCornerRadius="8dp"
app:cardElevation="2dp">
app:cardCornerRadius="12dp"
app:cardElevation="4dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
android:padding="20dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginBottom="12dp"
android:text="How To Use"
android:textColor="@color/text_primary"
android:textSize="18sp"
android:textStyle="bold" />
android:textSize="19sp"
android:textStyle="bold"
android:letterSpacing="0.01" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginBottom="8dp">
android:layout_marginBottom="10dp"
android:gravity="center_vertical">
<TextView
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginEnd="8dp"
android:layout_width="28dp"
android:layout_height="28dp"
android:layout_marginEnd="12dp"
android:background="?attr/colorPrimary"
android:gravity="center"
android:text="1"
android:textColor="@color/white"
android:textSize="14sp"
android:textStyle="bold" />
<TextView
@@ -512,16 +540,18 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginBottom="8dp">
android:layout_marginBottom="10dp"
android:gravity="center_vertical">
<TextView
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginEnd="8dp"
android:layout_width="28dp"
android:layout_height="28dp"
android:layout_marginEnd="12dp"
android:background="?attr/colorPrimary"
android:gravity="center"
android:text="2"
android:textColor="@color/white"
android:textSize="14sp"
android:textStyle="bold" />
<TextView
@@ -534,16 +564,18 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
android:orientation="horizontal"
android:gravity="center_vertical">
<TextView
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginEnd="8dp"
android:layout_width="28dp"
android:layout_height="28dp"
android:layout_marginEnd="12dp"
android:background="?attr/colorPrimary"
android:gravity="center"
android:text="3"
android:textColor="@color/white"
android:textSize="14sp"
android:textStyle="bold" />
<TextView
@@ -562,14 +594,14 @@
android:layout_marginHorizontal="16dp"
android:layout_marginBottom="24dp"
app:cardBackgroundColor="@color/background_secondary"
app:cardCornerRadius="8dp"
app:cardElevation="2dp">
app:cardCornerRadius="12dp"
app:cardElevation="4dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
android:padding="20dp">
<LinearLayout
android:layout_width="match_parent"
@@ -578,11 +610,12 @@
android:gravity="center_horizontal">
<ImageView
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_width="36dp"
android:layout_height="36dp"
android:src="@drawable/ic_baseline_info_24"
android:tint="?attr/colorPrimary"
android:layout_marginEnd="12dp" />
android:padding="4dp"
android:layout_marginEnd="14dp" />
<LinearLayout
android:layout_width="match_parent"

View File

@@ -2,12 +2,12 @@
<!-- Base application theme. -->
<style name="Theme.SMSGateway" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<!-- Primary brand color. -->
<item name="colorPrimary">#D97706</item>
<item name="colorPrimaryVariant">#B86504</item>
<item name="colorPrimary">#C4620A</item>
<item name="colorPrimaryVariant">#A04405</item>
<item name="colorOnPrimary">@color/white</item>
<!-- Secondary brand color. -->
<item name="colorSecondary">#f35b04</item>
<item name="colorSecondaryVariant">#f18701</item>
<item name="colorSecondary">#B45309</item>
<item name="colorSecondaryVariant">#92400E</item>
<item name="colorOnSecondary">@color/black</item>
<!-- Status bar color. -->
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
@@ -23,12 +23,12 @@
<!-- Theme without action bar for MainActivity -->
<style name="Theme.SMSGateway.NoActionBar" parent="Theme.MaterialComponents.DayNight.NoActionBar">
<!-- Primary brand color. -->
<item name="colorPrimary">#D97706</item>
<item name="colorPrimaryVariant">#B86504</item>
<item name="colorPrimary">#C4620A</item>
<item name="colorPrimaryVariant">#A04405</item>
<item name="colorOnPrimary">@color/white</item>
<!-- Secondary brand color. -->
<item name="colorSecondary">#f35b04</item>
<item name="colorSecondaryVariant">#f18701</item>
<item name="colorSecondary">#B45309</item>
<item name="colorSecondaryVariant">#92400E</item>
<item name="colorOnSecondary">@color/black</item>
<!-- Status bar color. -->
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>

View File

@@ -2,12 +2,12 @@
<!-- Base application theme. -->
<style name="Theme.SMSGateway" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<!-- Primary brand color. -->
<item name="colorPrimary">#D97706</item>
<item name="colorPrimaryVariant">#B86504</item>
<item name="colorPrimary">#C4620A</item>
<item name="colorPrimaryVariant">#A04405</item>
<item name="colorOnPrimary">@color/white</item>
<!-- Secondary brand color. -->
<item name="colorSecondary">#f35b04</item>
<item name="colorSecondaryVariant">#f18701</item>
<item name="colorSecondary">#B45309</item>
<item name="colorSecondaryVariant">#92400E</item>
<item name="colorOnSecondary">@color/black</item>
<!-- Status bar color. -->
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
@@ -17,12 +17,12 @@
<!-- Theme without action bar for MainActivity -->
<style name="Theme.SMSGateway.NoActionBar" parent="Theme.MaterialComponents.DayNight.NoActionBar">
<!-- Primary brand color. -->
<item name="colorPrimary">#D97706</item>
<item name="colorPrimaryVariant">#B86504</item>
<item name="colorPrimary">#C4620A</item>
<item name="colorPrimaryVariant">#A04405</item>
<item name="colorOnPrimary">@color/white</item>
<!-- Secondary brand color. -->
<item name="colorSecondary">#f35b04</item>
<item name="colorSecondaryVariant">#f18701</item>
<item name="colorSecondary">#B45309</item>
<item name="colorSecondaryVariant">#92400E</item>
<item name="colorOnSecondary">@color/black</item>
<!-- Status bar color. -->
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>