0
# Feedback and Communication
1
2
Components for user feedback, notifications, dialogs, progress indicators, and status communication.
3
4
## Core Imports
5
6
```java
7
import com.google.android.material.snackbar.Snackbar;
8
import com.google.android.material.snackbar.BaseTransientBottomBar;
9
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
10
import com.google.android.material.progressindicator.CircularProgressIndicator;
11
import com.google.android.material.progressindicator.LinearProgressIndicator;
12
import com.google.android.material.progressindicator.BaseProgressIndicator;
13
import com.google.android.material.loadingindicator.LoadingIndicator;
14
import com.google.android.material.badge.BadgeDrawable;
15
import com.google.android.material.badge.BadgeUtils;
16
import com.google.android.material.tooltip.TooltipDrawable;
17
```
18
19
## Snackbar
20
21
Brief messages displayed at the bottom of the screen for user feedback.
22
23
```java { .api }
24
class Snackbar extends BaseTransientBottomBar<Snackbar> {
25
// Factory methods
26
static Snackbar make(View view, CharSequence text, int duration);
27
static Snackbar make(Context context, View view, CharSequence text, int duration);
28
29
// Action configuration
30
Snackbar setAction(CharSequence text, View.OnClickListener listener);
31
Snackbar setAction(@StringRes int resId, View.OnClickListener listener);
32
Snackbar setActionTextColor(@ColorInt int color);
33
Snackbar setActionTextColor(ColorStateList colors);
34
35
// Text configuration
36
Snackbar setText(CharSequence message);
37
Snackbar setText(@StringRes int resId);
38
Snackbar setTextColor(@ColorInt int color);
39
Snackbar setTextColor(ColorStateList colors);
40
Snackbar setTextMaxLines(int maxLines);
41
int getTextMaxLines();
42
43
// Background configuration
44
Snackbar setBackgroundTint(@ColorInt int color);
45
Snackbar setBackgroundTintList(ColorStateList colorStateList);
46
Snackbar setBackgroundTintMode(PorterDuff.Mode mode);
47
48
// Action width
49
Snackbar setMaxInlineActionWidth(@Dimension int width);
50
int getMaxInlineActionWidth();
51
52
// Display control
53
void show();
54
void dismiss();
55
boolean isShown();
56
boolean isShownOrQueued();
57
58
// Callbacks
59
Snackbar addCallback(BaseCallback<Snackbar> callback);
60
Snackbar removeCallback(BaseCallback<Snackbar> callback);
61
}
62
```
63
64
### Snackbar Duration Constants
65
66
```java { .api }
67
public static final int LENGTH_INDEFINITE = -2;
68
public static final int LENGTH_SHORT = -1;
69
public static final int LENGTH_LONG = 0;
70
```
71
72
### Usage Example
73
74
```java
75
// Simple snackbar
76
Snackbar.make(coordinatorLayout, "Item deleted", Snackbar.LENGTH_SHORT).show();
77
78
// Snackbar with action
79
Snackbar snackbar = Snackbar.make(coordinatorLayout, "Item deleted", Snackbar.LENGTH_LONG);
80
snackbar.setAction("UNDO", v -> {
81
// Restore deleted item
82
restoreItem();
83
});
84
snackbar.setActionTextColor(ContextCompat.getColor(this, R.color.accent_color));
85
snackbar.show();
86
87
// Customized snackbar
88
Snackbar customSnackbar = Snackbar.make(view, "Custom message", Snackbar.LENGTH_INDEFINITE);
89
customSnackbar.setText("Network error occurred");
90
customSnackbar.setTextColor(Color.WHITE);
91
customSnackbar.setBackgroundTint(ContextCompat.getColor(this, R.color.error_color));
92
customSnackbar.setAction("RETRY", v -> {
93
// Retry action
94
retryNetworkRequest();
95
});
96
customSnackbar.show();
97
98
// Snackbar with callback
99
snackbar.addCallback(new BaseTransientBottomBar.BaseCallback<Snackbar>() {
100
@Override
101
public void onShown(Snackbar transientBottomBar) {
102
// Snackbar is now visible
103
}
104
105
@Override
106
public void onDismissed(Snackbar transientBottomBar, int event) {
107
switch (event) {
108
case DISMISS_EVENT_TIMEOUT:
109
// Dismissed due to timeout
110
break;
111
case DISMISS_EVENT_ACTION:
112
// Dismissed due to action click
113
break;
114
case DISMISS_EVENT_SWIPE:
115
// Dismissed by swipe
116
break;
117
}
118
}
119
});
120
```
121
122
## Base Transient Bottom Bar
123
124
Base class for transient bottom bars like Snackbar.
125
126
```java { .api }
127
abstract class BaseTransientBottomBar<B extends BaseTransientBottomBar<B>> {
128
// Animation mode
129
B setAnimationMode(int animationMode);
130
int getAnimationMode();
131
132
// Anchor view
133
B setAnchorView(View anchorView);
134
View getAnchorView();
135
136
// Gesture insets
137
B setGestureInsetBottomIgnored(boolean gestureInsetBottomIgnored);
138
boolean isGestureInsetBottomIgnored();
139
140
// Callbacks
141
B addCallback(BaseCallback<B> callback);
142
B removeCallback(BaseCallback<B> callback);
143
144
// Display control
145
void show();
146
void dismiss();
147
boolean isShown();
148
boolean isShownOrQueued();
149
150
// Properties
151
int getDuration();
152
View getView();
153
Context getContext();
154
}
155
156
abstract class BaseTransientBottomBar.BaseCallback<B> {
157
void onShown(B transientBottomBar);
158
void onDismissed(B transientBottomBar, int event);
159
160
// Dismiss event constants
161
public static final int DISMISS_EVENT_SWIPE = 0;
162
public static final int DISMISS_EVENT_ACTION = 1;
163
public static final int DISMISS_EVENT_TIMEOUT = 2;
164
public static final int DISMISS_EVENT_MANUAL = 3;
165
public static final int DISMISS_EVENT_CONSECUTIVE = 4;
166
}
167
```
168
169
### Animation Mode Constants
170
171
```java { .api }
172
public static final int ANIMATION_MODE_SLIDE = 0;
173
public static final int ANIMATION_MODE_FADE = 1;
174
```
175
176
## Material Alert Dialog Builder
177
178
Builder for creating Material Design alert dialogs.
179
180
```java { .api }
181
class MaterialAlertDialogBuilder extends AlertDialog.Builder {
182
MaterialAlertDialogBuilder(Context context);
183
MaterialAlertDialogBuilder(Context context, int overrideThemeResId);
184
185
// Background configuration
186
MaterialAlertDialogBuilder setBackground(Drawable background);
187
MaterialAlertDialogBuilder setBackgroundInsetStart(@Dimension int backgroundInsetStart);
188
MaterialAlertDialogBuilder setBackgroundInsetTop(@Dimension int backgroundInsetTop);
189
MaterialAlertDialogBuilder setBackgroundInsetEnd(@Dimension int backgroundInsetEnd);
190
MaterialAlertDialogBuilder setBackgroundInsetBottom(@Dimension int backgroundInsetBottom);
191
}
192
```
193
194
### Usage Example
195
196
```java
197
// Simple alert dialog
198
new MaterialAlertDialogBuilder(this)
199
.setTitle("Delete item?")
200
.setMessage("This action cannot be undone.")
201
.setPositiveButton("Delete", (dialog, which) -> {
202
// Handle delete
203
deleteItem();
204
})
205
.setNegativeButton("Cancel", null)
206
.show();
207
208
// Dialog with custom styling
209
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this);
210
builder.setTitle("Confirm Action")
211
.setMessage("Are you sure you want to proceed?")
212
.setIcon(R.drawable.ic_warning)
213
.setPositiveButton("Yes", (dialog, which) -> {
214
// Handle confirmation
215
})
216
.setNegativeButton("No", null)
217
.setNeutralButton("Learn More", (dialog, which) -> {
218
// Show more info
219
});
220
221
AlertDialog dialog = builder.create();
222
dialog.show();
223
224
// Single choice dialog
225
String[] options = {"Option 1", "Option 2", "Option 3"};
226
new MaterialAlertDialogBuilder(this)
227
.setTitle("Choose an option")
228
.setSingleChoiceItems(options, selectedIndex, (dialog, which) -> {
229
selectedIndex = which;
230
})
231
.setPositiveButton("OK", (dialog, which) -> {
232
// Handle selection
233
processSelection(selectedIndex);
234
})
235
.setNegativeButton("Cancel", null)
236
.show();
237
238
// Multi-choice dialog
239
boolean[] checkedItems = {false, true, false};
240
new MaterialAlertDialogBuilder(this)
241
.setTitle("Select items")
242
.setMultiChoiceItems(options, checkedItems, (dialog, which, isChecked) -> {
243
checkedItems[which] = isChecked;
244
})
245
.setPositiveButton("OK", (dialog, which) -> {
246
// Handle multiple selections
247
processMultipleSelections(checkedItems);
248
})
249
.setNegativeButton("Cancel", null)
250
.show();
251
```
252
253
## Progress Indicators
254
255
Material Design progress indicators for showing loading states and progress.
256
257
### Base Progress Indicator
258
259
```java { .api }
260
abstract class BaseProgressIndicator<S> extends ProgressBar {
261
// Show/hide animation
262
void setShowAnimationBehavior(int showAnimationBehavior);
263
int getShowAnimationBehavior();
264
void setHideAnimationBehavior(int hideAnimationBehavior);
265
int getHideAnimationBehavior();
266
267
// Colors
268
void setIndicatorColor(@ColorInt int... colors);
269
void setIndicatorColor(@NonNull int[] colors);
270
int[] getIndicatorColor();
271
void setTrackColor(@ColorInt int trackColor);
272
int getTrackColor();
273
void setTrackCornerRadius(int trackCornerRadius);
274
int getTrackCornerRadius();
275
276
// Visibility control
277
void show();
278
void hide();
279
boolean isShown();
280
void setVisibilityAfterHide(int visibility);
281
int getVisibilityAfterHide();
282
}
283
```
284
285
### Animation Behavior Constants
286
287
```java { .api }
288
public static final int SHOW_NONE = 0;
289
public static final int SHOW_OUTWARD = 1;
290
public static final int SHOW_INWARD = 2;
291
292
public static final int HIDE_NONE = 0;
293
public static final int HIDE_OUTWARD = 1;
294
public static final int HIDE_INWARD = 2;
295
public static final int HIDE_ESCAPE = 3;
296
```
297
298
### Circular Progress Indicator
299
300
```java { .api }
301
class CircularProgressIndicator extends BaseProgressIndicator<CircularProgressIndicatorSpec> {
302
CircularProgressIndicator(Context context);
303
CircularProgressIndicator(Context context, AttributeSet attrs);
304
305
// Size configuration
306
void setIndicatorSize(int indicatorSize);
307
int getIndicatorSize();
308
void setTrackThickness(int trackThickness);
309
int getTrackThickness();
310
void setIndicatorInset(int indicatorInset);
311
int getIndicatorInset();
312
313
// Direction
314
void setIndicatorDirection(int indicatorDirection);
315
int getIndicatorDirection();
316
}
317
```
318
319
### Linear Progress Indicator
320
321
```java { .api }
322
class LinearProgressIndicator extends BaseProgressIndicator<LinearProgressIndicatorSpec> {
323
LinearProgressIndicator(Context context);
324
LinearProgressIndicator(Context context, AttributeSet attrs);
325
326
// Track thickness
327
void setTrackThickness(int trackThickness);
328
int getTrackThickness();
329
330
// Direction
331
void setIndicatorDirection(int indicatorDirection);
332
int getIndicatorDirection();
333
}
334
```
335
336
### Progress Direction Constants
337
338
```java { .api }
339
// Circular progress
340
public static final int INDICATOR_DIRECTION_CLOCKWISE = 0;
341
public static final int INDICATOR_DIRECTION_COUNTERCLOCKWISE = 1;
342
343
// Linear progress
344
public static final int INDICATOR_DIRECTION_LEFT_TO_RIGHT = 0;
345
public static final int INDICATOR_DIRECTION_RIGHT_TO_LEFT = 1;
346
```
347
348
### Usage Example
349
350
```java
351
// Determinate circular progress
352
CircularProgressIndicator circularProgress = findViewById(R.id.circular_progress);
353
circularProgress.setProgress(0);
354
circularProgress.setMax(100);
355
356
// Animate progress
357
ValueAnimator animator = ValueAnimator.ofInt(0, 75);
358
animator.setDuration(2000);
359
animator.addUpdateListener(animation -> {
360
int progress = (int) animation.getAnimatedValue();
361
circularProgress.setProgress(progress);
362
});
363
animator.start();
364
365
// Indeterminate linear progress with custom colors
366
LinearProgressIndicator linearProgress = findViewById(R.id.linear_progress);
367
linearProgress.setIndeterminate(true);
368
linearProgress.setIndicatorColor(
369
ContextCompat.getColor(this, R.color.primary),
370
ContextCompat.getColor(this, R.color.secondary)
371
);
372
373
// Show/hide with animation
374
linearProgress.setShowAnimationBehavior(BaseProgressIndicator.SHOW_INWARD);
375
linearProgress.setHideAnimationBehavior(BaseProgressIndicator.HIDE_OUTWARD);
376
linearProgress.show();
377
378
// Hide after delay
379
new Handler().postDelayed(() -> {
380
linearProgress.hide();
381
}, 3000);
382
383
// Progress with custom appearance
384
CircularProgressIndicator customProgress = findViewById(R.id.custom_progress);
385
customProgress.setIndicatorSize(64);
386
customProgress.setTrackThickness(8);
387
customProgress.setIndicatorDirection(CircularProgressIndicator.INDICATOR_DIRECTION_COUNTERCLOCKWISE);
388
```
389
390
## Loading Indicator
391
392
Material Design loading indicator component with show/hide delay capabilities.
393
394
```java { .api }
395
public class LoadingIndicator extends View {
396
public LoadingIndicator(Context context);
397
public LoadingIndicator(Context context, AttributeSet attrs);
398
399
// Visibility control
400
public void show();
401
public void hide();
402
403
// Indicator appearance
404
public void setIndicatorSize(@Px int indicatorSize);
405
public int getIndicatorSize();
406
public void setIndicatorColor(@ColorInt int... indicatorColors);
407
public int[] getIndicatorColor();
408
409
// Container dimensions
410
public void setContainerWidth(@Px int containerWidth);
411
public int getContainerWidth();
412
public void setContainerHeight(@Px int containerHeight);
413
public int getContainerHeight();
414
public void setContainerColor(@ColorInt int containerColor);
415
public int getContainerColor();
416
}
417
```
418
419
**Usage Example:**
420
421
```java
422
LoadingIndicator loadingIndicator = findViewById(R.id.loading_indicator);
423
424
// Configure appearance
425
loadingIndicator.setIndicatorSize(48);
426
loadingIndicator.setContainerWidth(100);
427
loadingIndicator.setContainerHeight(100);
428
loadingIndicator.setIndicatorColor(
429
ContextCompat.getColor(this, R.color.primary),
430
ContextCompat.getColor(this, R.color.secondary),
431
ContextCompat.getColor(this, R.color.tertiary)
432
);
433
434
// Show loading indicator
435
loadingIndicator.show();
436
437
// Hide after operation completes
438
performLongRunningOperation(() -> {
439
loadingIndicator.hide();
440
});
441
```
442
443
## Badge Drawable
444
445
Drawable for displaying notification badges with numbers or text.
446
447
```java { .api }
448
class BadgeDrawable extends Drawable {
449
// Factory methods
450
static BadgeDrawable create(Context context);
451
static BadgeDrawable createFromResource(Context context, @XmlRes int id);
452
453
// Number badge
454
void setNumber(int number);
455
int getNumber();
456
void clearNumber();
457
boolean hasNumber();
458
void setMaxCharacterCount(int maxCharacterCount);
459
int getMaxCharacterCount();
460
461
// Text badge
462
void setText(String text);
463
String getText();
464
void clearText();
465
boolean hasText();
466
467
// Colors
468
void setBackgroundColor(@ColorInt int backgroundColor);
469
int getBackgroundColor();
470
void setBadgeTextColor(@ColorInt int badgeTextColor);
471
int getBadgeTextColor();
472
473
// Visibility
474
boolean isVisible();
475
void setVisible(boolean visible);
476
477
// Additional configuration methods for positioning, etc.
478
}
479
```
480
481
### Badge Utils
482
483
```java { .api }
484
class BadgeUtils {
485
static void attachBadgeDrawable(BadgeDrawable badgeDrawable, View anchor);
486
static void attachBadgeDrawable(BadgeDrawable badgeDrawable, View anchor, FrameLayout customBadgeParent);
487
static void detachBadgeDrawable(BadgeDrawable badgeDrawable, View anchor);
488
static void setBadgeDrawableBounds(BadgeDrawable badgeDrawable, View anchor, FrameLayout customBadgeParent);
489
}
490
```
491
492
### Usage Example
493
494
```java
495
// Create and attach badge to view
496
ImageView notificationIcon = findViewById(R.id.notification_icon);
497
BadgeDrawable badge = BadgeDrawable.create(this);
498
badge.setNumber(5);
499
badge.setBackgroundColor(ContextCompat.getColor(this, R.color.red));
500
badge.setBadgeTextColor(Color.WHITE);
501
BadgeUtils.attachBadgeDrawable(badge, notificationIcon);
502
503
// Update badge count
504
badge.setNumber(badge.getNumber() + 1);
505
506
// Text badge
507
BadgeDrawable textBadge = BadgeDrawable.create(this);
508
textBadge.setText("NEW");
509
textBadge.setVisible(true);
510
511
// Hide badge
512
badge.setVisible(false);
513
514
// Clear badge
515
badge.clearNumber();
516
BadgeUtils.detachBadgeDrawable(badge, notificationIcon);
517
518
// Badge with navigation components
519
BottomNavigationView bottomNav = findViewById(R.id.bottom_navigation);
520
BadgeDrawable navBadge = bottomNav.getOrCreateBadge(R.id.nav_messages);
521
navBadge.setNumber(3);
522
navBadge.setVisible(true);
523
524
// Remove navigation badge
525
bottomNav.removeBadge(R.id.nav_messages);
526
```
527
528
## Tooltip Drawable
529
530
Drawable for creating Material Design tooltips.
531
532
```java { .api }
533
class TooltipDrawable extends Drawable {
534
// Factory methods
535
static TooltipDrawable create(Context context);
536
static TooltipDrawable createFromAttributes(Context context, AttributeSet attrs);
537
static TooltipDrawable createFromAttributes(Context context, AttributeSet attrs, @AttrRes int defStyleAttr);
538
static TooltipDrawable createFromAttributes(Context context, AttributeSet attrs, @AttrRes int defStyleAttr, @StyleRes int defStyleRes);
539
540
// Text content
541
void setText(CharSequence text);
542
CharSequence getText();
543
void setTextAppearance(@StyleRes int textAppearanceResId);
544
TextAppearance getTextAppearance();
545
void setTextColor(@ColorInt int textColor);
546
547
// Positioning
548
void setTooltipPivotX(float tooltipPivotX);
549
float getTooltipPivotX();
550
void setTooltipPivotY(float tooltipPivotY);
551
float getTooltipPivotY();
552
553
// Layout
554
void setLayoutMargin(int layoutMargin);
555
int getLayoutMargin();
556
void setMinWidth(int minWidth);
557
int getMinWidth();
558
void setMinHeight(int minHeight);
559
int getMinHeight();
560
561
// Arrow
562
void setArrowSize(int arrowSize);
563
int getArrowSize();
564
565
// Corner radius
566
void setCornerRadius(float cornerRadius);
567
float getCornerRadius();
568
569
// Background
570
void setBackgroundTint(ColorStateList backgroundTint);
571
572
// View attachment
573
void setRelativeToView(View view);
574
void detachView(View view);
575
}
576
```
577
578
### Usage Example
579
580
```java
581
// Create tooltip for a view
582
MaterialButton helpButton = findViewById(R.id.help_button);
583
TooltipDrawable tooltip = TooltipDrawable.create(this);
584
tooltip.setText("This button provides help information");
585
tooltip.setBackgroundTint(ColorStateList.valueOf(ContextCompat.getColor(this, R.color.tooltip_bg)));
586
587
// Show tooltip on long click
588
helpButton.setOnLongClickListener(v -> {
589
showTooltip(v, tooltip);
590
return true;
591
});
592
593
// Custom tooltip appearance
594
TooltipDrawable customTooltip = TooltipDrawable.create(this);
595
customTooltip.setText("Custom styled tooltip");
596
customTooltip.setCornerRadius(8f);
597
customTooltip.setArrowSize(16);
598
customTooltip.setMinWidth(200);
599
customTooltip.setTextColor(Color.WHITE);
600
```
601
602
## Loading and Error States Example
603
604
Complete example showing various feedback patterns:
605
606
```java
607
public class FeedbackExampleActivity extends AppCompatActivity {
608
private LinearProgressIndicator progressBar;
609
private MaterialButton loadDataButton;
610
private MaterialButton showErrorButton;
611
private TextView statusText;
612
private CoordinatorLayout coordinatorLayout;
613
614
@Override
615
protected void onCreate(Bundle savedInstanceState) {
616
super.onCreate(savedInstanceState);
617
setContentView(R.layout.activity_feedback_example);
618
619
initViews();
620
setupClickListeners();
621
}
622
623
private void initViews() {
624
progressBar = findViewById(R.id.progress_bar);
625
loadDataButton = findViewById(R.id.load_data_button);
626
showErrorButton = findViewById(R.id.show_error_button);
627
statusText = findViewById(R.id.status_text);
628
coordinatorLayout = findViewById(R.id.coordinator_layout);
629
630
// Configure progress bar
631
progressBar.setIndicatorColor(
632
ContextCompat.getColor(this, R.color.primary),
633
ContextCompat.getColor(this, R.color.secondary)
634
);
635
progressBar.setShowAnimationBehavior(BaseProgressIndicator.SHOW_INWARD);
636
progressBar.setHideAnimationBehavior(BaseProgressIndicator.HIDE_OUTWARD);
637
}
638
639
private void setupClickListeners() {
640
loadDataButton.setOnClickListener(v -> simulateDataLoading());
641
showErrorButton.setOnClickListener(v -> showErrorDialog());
642
}
643
644
private void simulateDataLoading() {
645
// Show loading state
646
progressBar.show();
647
loadDataButton.setEnabled(false);
648
statusText.setText("Loading data...");
649
650
// Simulate network request
651
new Handler().postDelayed(() -> {
652
// Hide loading and show success
653
progressBar.hide();
654
loadDataButton.setEnabled(true);
655
statusText.setText("Data loaded successfully");
656
657
// Show success snackbar
658
Snackbar.make(coordinatorLayout, "Data loaded successfully", Snackbar.LENGTH_SHORT)
659
.setBackgroundTint(ContextCompat.getColor(this, R.color.success))
660
.show();
661
}, 2000);
662
}
663
664
private void showErrorDialog() {
665
new MaterialAlertDialogBuilder(this)
666
.setTitle("Network Error")
667
.setMessage("Failed to connect to the server. Please check your internet connection and try again.")
668
.setIcon(R.drawable.ic_error)
669
.setPositiveButton("Retry", (dialog, which) -> {
670
// Retry action
671
simulateDataLoading();
672
})
673
.setNegativeButton("Cancel", null)
674
.setNeutralButton("Settings", (dialog, which) -> {
675
// Open network settings
676
openNetworkSettings();
677
})
678
.show();
679
}
680
681
private void showNetworkErrorSnackbar() {
682
Snackbar errorSnackbar = Snackbar.make(coordinatorLayout,
683
"Network error occurred", Snackbar.LENGTH_INDEFINITE);
684
errorSnackbar.setAction("RETRY", v -> simulateDataLoading());
685
errorSnackbar.setActionTextColor(ContextCompat.getColor(this, R.color.accent));
686
errorSnackbar.setBackgroundTint(ContextCompat.getColor(this, R.color.error));
687
errorSnackbar.show();
688
}
689
690
private void showProgressWithCallback() {
691
CircularProgressIndicator progressIndicator = findViewById(R.id.circular_progress);
692
693
// Simulate determinate progress
694
progressIndicator.setProgress(0);
695
progressIndicator.setMax(100);
696
progressIndicator.show();
697
698
ValueAnimator progressAnimator = ValueAnimator.ofInt(0, 100);
699
progressAnimator.setDuration(3000);
700
progressAnimator.addUpdateListener(animation -> {
701
int progress = (int) animation.getAnimatedValue();
702
progressIndicator.setProgress(progress);
703
704
if (progress == 100) {
705
// Hide progress when complete
706
new Handler().postDelayed(() -> {
707
progressIndicator.hide();
708
showCompletionFeedback();
709
}, 500);
710
}
711
});
712
progressAnimator.start();
713
}
714
715
private void showCompletionFeedback() {
716
// Show completion badge
717
ImageView completionIcon = findViewById(R.id.completion_icon);
718
BadgeDrawable completionBadge = BadgeDrawable.create(this);
719
completionBadge.setText("✓");
720
completionBadge.setBackgroundColor(ContextCompat.getColor(this, R.color.success));
721
completionBadge.setBadgeTextColor(Color.WHITE);
722
BadgeUtils.attachBadgeDrawable(completionBadge, completionIcon);
723
724
// Auto-hide badge after delay
725
new Handler().postDelayed(() -> {
726
BadgeUtils.detachBadgeDrawable(completionBadge, completionIcon);
727
}, 2000);
728
}
729
}
730
```