Fadhili Sumaye commited on
Commit
e1b9530
·
1 Parent(s): 5d2afe2

Enhance frontend UX: add ripple feedback, placeholder text, loading progress indicator, button blocking, and auto-scrolling

Browse files
app/src/main/java/com/example/pestdetection/MainActivity.java CHANGED
@@ -32,6 +32,13 @@ public class MainActivity extends AppCompatActivity {
32
  Uri imageUri;
33
  EditText etServerUrl;
34
 
 
 
 
 
 
 
 
35
  private ConnectivityManager connectivityManager;
36
  private ConnectivityManager.NetworkCallback networkCallback;
37
 
@@ -40,6 +47,12 @@ public class MainActivity extends AppCompatActivity {
40
  if (result.getResultCode() == RESULT_OK && result.getData() != null) {
41
  imageUri = result.getData().getData();
42
  imageView.setImageURI(imageUri);
 
 
 
 
 
 
43
  }
44
  });
45
 
@@ -47,6 +60,12 @@ public class MainActivity extends AppCompatActivity {
47
  registerForActivityResult(new ActivityResultContracts.TakePicture(), success -> {
48
  if (success) {
49
  imageView.setImageURI(imageUri);
 
 
 
 
 
 
50
  }
51
  });
52
 
@@ -64,10 +83,14 @@ public class MainActivity extends AppCompatActivity {
64
  super.onCreate(savedInstanceState);
65
  setContentView(R.layout.activity_main);
66
 
 
67
  imageView = findViewById(R.id.imageView);
68
- Button btnSelect = findViewById(R.id.btnSelect);
69
- Button btnCamera = findViewById(R.id.btnCamera);
70
- Button btnUpload = findViewById(R.id.btnUpload);
 
 
 
71
  resultText = findViewById(R.id.resultText);
72
  treatmentText = findViewById(R.id.treatmentText);
73
  tvConnectionStatus = findViewById(R.id.tvConnectionStatus);
@@ -81,6 +104,10 @@ public class MainActivity extends AppCompatActivity {
81
  ImageView settingsToggleArrow = findViewById(R.id.settingsToggleArrow);
82
  Button btnTestConnection = findViewById(R.id.btnTestConnection);
83
 
 
 
 
 
84
  etServerUrl.setText(ApiConfig.getPredictUrl(this));
85
 
86
  settingsHeader.setOnClickListener(v -> {
@@ -117,7 +144,19 @@ public class MainActivity extends AppCompatActivity {
117
  protected void onStop() {
118
  super.onStop();
119
  if (imageView != null) {
120
- imageView.setImageResource(R.mipmap.ic_launcher);
 
 
 
 
 
 
 
 
 
 
 
 
121
  }
122
  if (resultText != null) {
123
  resultText.setText("Waiting for scan...");
@@ -279,6 +318,13 @@ public class MainActivity extends AppCompatActivity {
279
  resultText.setText("Analyzing...");
280
  treatmentText.setText("Please wait...");
281
 
 
 
 
 
 
 
 
282
  // Scaled and compressed image bytes on-device
283
  byte[] imageBytes = getScaledAndCompressedImage(imageUri);
284
 
@@ -288,6 +334,18 @@ public class MainActivity extends AppCompatActivity {
288
  resultText.setText(pest + " (" + (int) (confidence * 100) + "%)");
289
  treatmentText.setText(treatment);
290
  updateConnectionStatus(true, "Connected");
 
 
 
 
 
 
 
 
 
 
 
 
291
  }
292
 
293
  @Override
@@ -295,6 +353,14 @@ public class MainActivity extends AppCompatActivity {
295
  resultText.setText("FAILED: " + message);
296
  treatmentText.setText("Check server URL in Connection Settings");
297
  updateConnectionStatus(false, message);
 
 
 
 
 
 
 
 
298
  android.util.Log.e("PestScanner", "Connection Error to " + serverUrl + ": " + message);
299
  }
300
  });
@@ -302,6 +368,13 @@ public class MainActivity extends AppCompatActivity {
302
  } catch (Exception e) {
303
  e.printStackTrace();
304
  resultText.setText("Error: " + e.getMessage());
 
 
 
 
 
 
 
305
  }
306
  }
307
 
 
32
  Uri imageUri;
33
  EditText etServerUrl;
34
 
35
+ // View references for enhanced UX
36
+ ScrollView mainScrollView;
37
+ TextView tvImagePlaceholder;
38
+ View loadingOverlay;
39
+ ProgressBar progressBar;
40
+ Button btnSelect, btnCamera, btnUpload;
41
+
42
  private ConnectivityManager connectivityManager;
43
  private ConnectivityManager.NetworkCallback networkCallback;
44
 
 
47
  if (result.getResultCode() == RESULT_OK && result.getData() != null) {
48
  imageUri = result.getData().getData();
49
  imageView.setImageURI(imageUri);
50
+ if (tvImagePlaceholder != null) {
51
+ tvImagePlaceholder.setVisibility(View.GONE);
52
+ }
53
+ if (btnUpload != null) {
54
+ btnUpload.setEnabled(true);
55
+ }
56
  }
57
  });
58
 
 
60
  registerForActivityResult(new ActivityResultContracts.TakePicture(), success -> {
61
  if (success) {
62
  imageView.setImageURI(imageUri);
63
+ if (tvImagePlaceholder != null) {
64
+ tvImagePlaceholder.setVisibility(View.GONE);
65
+ }
66
+ if (btnUpload != null) {
67
+ btnUpload.setEnabled(true);
68
+ }
69
  }
70
  });
71
 
 
83
  super.onCreate(savedInstanceState);
84
  setContentView(R.layout.activity_main);
85
 
86
+ mainScrollView = findViewById(R.id.mainScrollView);
87
  imageView = findViewById(R.id.imageView);
88
+ tvImagePlaceholder = findViewById(R.id.tvImagePlaceholder);
89
+ loadingOverlay = findViewById(R.id.loadingOverlay);
90
+ progressBar = findViewById(R.id.progressBar);
91
+ btnSelect = findViewById(R.id.btnSelect);
92
+ btnCamera = findViewById(R.id.btnCamera);
93
+ btnUpload = findViewById(R.id.btnUpload);
94
  resultText = findViewById(R.id.resultText);
95
  treatmentText = findViewById(R.id.treatmentText);
96
  tvConnectionStatus = findViewById(R.id.tvConnectionStatus);
 
104
  ImageView settingsToggleArrow = findViewById(R.id.settingsToggleArrow);
105
  Button btnTestConnection = findViewById(R.id.btnTestConnection);
106
 
107
+ if (btnUpload != null) {
108
+ btnUpload.setEnabled(false); // Initially disable upload button
109
+ }
110
+
111
  etServerUrl.setText(ApiConfig.getPredictUrl(this));
112
 
113
  settingsHeader.setOnClickListener(v -> {
 
144
  protected void onStop() {
145
  super.onStop();
146
  if (imageView != null) {
147
+ imageView.setImageDrawable(null);
148
+ }
149
+ if (tvImagePlaceholder != null) {
150
+ tvImagePlaceholder.setVisibility(View.VISIBLE);
151
+ }
152
+ if (btnUpload != null) {
153
+ btnUpload.setEnabled(false);
154
+ }
155
+ if (progressBar != null) {
156
+ progressBar.setVisibility(View.GONE);
157
+ }
158
+ if (loadingOverlay != null) {
159
+ loadingOverlay.setVisibility(View.GONE);
160
  }
161
  if (resultText != null) {
162
  resultText.setText("Waiting for scan...");
 
318
  resultText.setText("Analyzing...");
319
  treatmentText.setText("Please wait...");
320
 
321
+ // Show loaders and disable action controls to avoid duplicate clicks
322
+ if (progressBar != null) progressBar.setVisibility(View.VISIBLE);
323
+ if (loadingOverlay != null) loadingOverlay.setVisibility(View.VISIBLE);
324
+ if (btnUpload != null) btnUpload.setEnabled(false);
325
+ if (btnSelect != null) btnSelect.setEnabled(false);
326
+ if (btnCamera != null) btnCamera.setEnabled(false);
327
+
328
  // Scaled and compressed image bytes on-device
329
  byte[] imageBytes = getScaledAndCompressedImage(imageUri);
330
 
 
334
  resultText.setText(pest + " (" + (int) (confidence * 100) + "%)");
335
  treatmentText.setText(treatment);
336
  updateConnectionStatus(true, "Connected");
337
+
338
+ // Hide loaders and re-enable buttons
339
+ if (progressBar != null) progressBar.setVisibility(View.GONE);
340
+ if (loadingOverlay != null) loadingOverlay.setVisibility(View.GONE);
341
+ if (btnUpload != null) btnUpload.setEnabled(true);
342
+ if (btnSelect != null) btnSelect.setEnabled(true);
343
+ if (btnCamera != null) btnCamera.setEnabled(true);
344
+
345
+ // Auto-scroll layout to reveal results
346
+ if (mainScrollView != null) {
347
+ mainScrollView.post(() -> mainScrollView.fullScroll(View.FOCUS_DOWN));
348
+ }
349
  }
350
 
351
  @Override
 
353
  resultText.setText("FAILED: " + message);
354
  treatmentText.setText("Check server URL in Connection Settings");
355
  updateConnectionStatus(false, message);
356
+
357
+ // Hide loaders and re-enable buttons
358
+ if (progressBar != null) progressBar.setVisibility(View.GONE);
359
+ if (loadingOverlay != null) loadingOverlay.setVisibility(View.GONE);
360
+ if (btnUpload != null) btnUpload.setEnabled(true);
361
+ if (btnSelect != null) btnSelect.setEnabled(true);
362
+ if (btnCamera != null) btnCamera.setEnabled(true);
363
+
364
  android.util.Log.e("PestScanner", "Connection Error to " + serverUrl + ": " + message);
365
  }
366
  });
 
368
  } catch (Exception e) {
369
  e.printStackTrace();
370
  resultText.setText("Error: " + e.getMessage());
371
+
372
+ // Reset loaders and re-enable buttons on error
373
+ if (progressBar != null) progressBar.setVisibility(View.GONE);
374
+ if (loadingOverlay != null) loadingOverlay.setVisibility(View.GONE);
375
+ if (btnUpload != null) btnUpload.setEnabled(true);
376
+ if (btnSelect != null) btnSelect.setEnabled(true);
377
+ if (btnCamera != null) btnCamera.setEnabled(true);
378
  }
379
  }
380
 
app/src/main/res/layout/activity_main.xml CHANGED
@@ -3,6 +3,7 @@
3
  xmlns:android="http://schemas.android.com/apk/res/android"
4
  xmlns:app="http://schemas.android.com/apk/res-auto"
5
  xmlns:tools="http://schemas.android.com/tools"
 
6
  android:layout_width="match_parent"
7
  android:layout_height="match_parent"
8
  android:background="@color/bg_soft_white"
@@ -98,7 +99,8 @@
98
  android:orientation="horizontal"
99
  android:gravity="center_vertical"
100
  android:clickable="true"
101
- android:focusable="true">
 
102
 
103
  <ImageView
104
  android:layout_width="20dp"
@@ -196,14 +198,37 @@
196
  android:id="@+id/imageView"
197
  android:layout_width="match_parent"
198
  android:layout_height="match_parent"
199
- android:scaleType="centerCrop"
200
- android:src="@mipmap/ic_launcher" />
201
 
202
- <!-- Overlay for when no image is selected -->
 
 
 
 
 
 
 
 
 
 
 
 
 
203
  <View
 
204
  android:layout_width="match_parent"
205
  android:layout_height="match_parent"
206
- android:background="#1A000000" />
 
 
 
 
 
 
 
 
 
 
207
  </com.google.android.material.card.MaterialCardView>
208
 
209
  <!-- Action Buttons Section -->
 
3
  xmlns:android="http://schemas.android.com/apk/res/android"
4
  xmlns:app="http://schemas.android.com/apk/res-auto"
5
  xmlns:tools="http://schemas.android.com/tools"
6
+ android:id="@+id/mainScrollView"
7
  android:layout_width="match_parent"
8
  android:layout_height="match_parent"
9
  android:background="@color/bg_soft_white"
 
99
  android:orientation="horizontal"
100
  android:gravity="center_vertical"
101
  android:clickable="true"
102
+ android:focusable="true"
103
+ android:background="?android:attr/selectableItemBackground">
104
 
105
  <ImageView
106
  android:layout_width="20dp"
 
198
  android:id="@+id/imageView"
199
  android:layout_width="match_parent"
200
  android:layout_height="match_parent"
201
+ android:scaleType="centerCrop" />
 
202
 
203
+ <!-- Placeholder text when no image is selected -->
204
+ <TextView
205
+ android:id="@+id/tvImagePlaceholder"
206
+ android:layout_width="wrap_content"
207
+ android:layout_height="wrap_content"
208
+ android:layout_gravity="center"
209
+ android:gravity="center"
210
+ android:text="No crop image selected\nTap Gallery or Camera below"
211
+ android:textColor="@color/text_hint"
212
+ android:textSize="14sp"
213
+ android:textStyle="bold"
214
+ android:padding="24dp" />
215
+
216
+ <!-- Semi-transparent overlay to dim image when analyzing -->
217
  <View
218
+ android:id="@+id/loadingOverlay"
219
  android:layout_width="match_parent"
220
  android:layout_height="match_parent"
221
+ android:background="#99000000"
222
+ android:visibility="gone" />
223
+
224
+ <!-- Circular Loading Spinner -->
225
+ <ProgressBar
226
+ android:id="@+id/progressBar"
227
+ android:layout_width="wrap_content"
228
+ android:layout_height="wrap_content"
229
+ android:layout_gravity="center"
230
+ android:visibility="gone"
231
+ android:indeterminateTint="@color/primary_green" />
232
  </com.google.android.material.card.MaterialCardView>
233
 
234
  <!-- Action Buttons Section -->