That fuck shit the fascists are using
1package org.tm.archive.components;
2
3import android.content.Context;
4import android.content.res.TypedArray;
5import android.graphics.Color;
6import android.graphics.PorterDuff;
7import android.os.Bundle;
8import android.os.Parcelable;
9import android.util.AttributeSet;
10import android.view.View;
11import android.view.animation.Animation;
12import android.view.animation.LinearInterpolator;
13import android.view.animation.RotateAnimation;
14
15import androidx.annotation.Nullable;
16import androidx.annotation.StringRes;
17import androidx.appcompat.widget.AppCompatImageView;
18
19import org.signal.core.util.DimensionUnit;
20import org.tm.archive.R;
21import org.tm.archive.util.ViewUtil;
22
23/**
24 * View responsible for displaying the delivery status (NONE, PENDING, SENT, DELIVERED, READ) of a given outgoing message.
25 * <p>
26 * This view manipulates its start / end padding to properly place the corresponding icon, and also performs a rotation
27 * animation on itself in the pending mode. Thus, users should be aware that padding values set in XML will be overwritten.
28 * <p>
29 * If you need to control the horizontal spacing of this view, utilize margins instead.
30 */
31public class DeliveryStatusView extends AppCompatImageView {
32
33 private static final String STATE_KEY = "DeliveryStatusView.STATE";
34 private static final String ROOT_KEY = "DeliveryStatusView.ROOT";
35
36 private final int horizontalPadding = (int) DimensionUnit.DP.toPixels(2);
37
38 private RotateAnimation rotationAnimation;
39
40 private State state = State.NONE;
41
42 public DeliveryStatusView(Context context) {
43 this(context, null);
44 }
45
46 public DeliveryStatusView(Context context, AttributeSet attrs) {
47 this(context, attrs, 0);
48 }
49
50 public DeliveryStatusView(final Context context, AttributeSet attrs, int defStyle) {
51 super(context, attrs, defStyle);
52
53 if (attrs != null) {
54 TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.DeliveryStatusView, 0, 0);
55 setTint(typedArray.getColor(R.styleable.DeliveryStatusView_iconColor, getResources().getColor(R.color.core_white)));
56 typedArray.recycle();
57 }
58
59 setNone();
60 }
61
62 @Override
63 protected void onRestoreInstanceState(Parcelable state) {
64 if (state instanceof Bundle) {
65 Bundle stateBundle = (Bundle) state;
66 State s = State.fromCode(stateBundle.getInt(STATE_KEY, State.NONE.code));
67
68 switch (s) {
69 case NONE:
70 setNone();
71 break;
72 case PENDING:
73 setPending();
74 break;
75 case SENT:
76 setSent();
77 break;
78 case DELIVERED:
79 setDelivered();
80 break;
81 case READ:
82 setRead();
83 break;
84 }
85
86 Parcelable root = stateBundle.getParcelable(ROOT_KEY);
87 super.onRestoreInstanceState(root);
88 } else {
89 super.onRestoreInstanceState(state);
90 }
91 }
92
93 @Override
94 protected @Nullable Parcelable onSaveInstanceState() {
95 Parcelable root = super.onSaveInstanceState();
96 Bundle stateBundle = new Bundle();
97
98 stateBundle.putParcelable(ROOT_KEY, root);
99 stateBundle.putInt(STATE_KEY, state.code);
100
101 return stateBundle;
102 }
103
104 @Override
105 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
106 if (state == State.PENDING && rotationAnimation == null) {
107 final float pivotXValue;
108 if (ViewUtil.isLtr(this)) {
109 pivotXValue = (w - getPaddingEnd()) / 2f;
110 } else {
111 pivotXValue = ((w - getPaddingEnd()) / 2f) + getPaddingEnd();
112 }
113
114 final float pivotYValue = (h - getPaddingTop() - getPaddingBottom()) / 2f;
115
116 rotationAnimation = new RotateAnimation(0, 360f,
117 Animation.ABSOLUTE, pivotXValue,
118 Animation.ABSOLUTE, pivotYValue);
119
120 rotationAnimation.setInterpolator(new LinearInterpolator());
121 rotationAnimation.setDuration(1500);
122 rotationAnimation.setRepeatCount(Animation.INFINITE);
123
124 startAnimation(rotationAnimation);
125 }
126 }
127
128 @Override
129 public void clearAnimation() {
130 super.clearAnimation();
131 rotationAnimation = null;
132 }
133
134 public void setNone() {
135 state = State.NONE;
136 clearAnimation();
137 setVisibility(View.GONE);
138 updateContentDescription();
139 }
140
141 public boolean isPending() {
142 return state == State.PENDING;
143 }
144
145 public void setPending() {
146 state = State.PENDING;
147 setVisibility(View.VISIBLE);
148 ViewUtil.setPaddingStart(this, 0);
149 ViewUtil.setPaddingEnd(this, horizontalPadding);
150 setImageResource(R.drawable.symbol_messagestatus_sending_24);
151 updateContentDescription();
152 }
153
154 public void setSent() {
155 state = State.SENT;
156 setVisibility(View.VISIBLE);
157 ViewUtil.setPaddingStart(this, horizontalPadding);
158 ViewUtil.setPaddingEnd(this, 0);
159 clearAnimation();
160 setImageResource(R.drawable.symbol_messagestatus_sent_24);
161 updateContentDescription();
162 }
163
164 public void setDelivered() {
165 state = State.DELIVERED;
166 setVisibility(View.VISIBLE);
167 ViewUtil.setPaddingStart(this, horizontalPadding);
168 ViewUtil.setPaddingEnd(this, 0);
169 clearAnimation();
170 setImageResource(R.drawable.symbol_messagestatus_delivered_24);
171 updateContentDescription();
172 }
173
174 public void setRead() {
175 state = State.READ;
176 setVisibility(View.VISIBLE);
177 ViewUtil.setPaddingStart(this, horizontalPadding);
178 ViewUtil.setPaddingEnd(this, 0);
179 clearAnimation();
180 setImageResource(R.drawable.symbol_messagestatus_read_24);
181 updateContentDescription();
182 }
183
184 public void setTint(int color) {
185 setColorFilter(color, PorterDuff.Mode.SRC_IN);
186 }
187
188 private void updateContentDescription() {
189 if (state.contentDescription == -1) {
190 setContentDescription(null);
191 } else {
192 setContentDescription(getContext().getString(state.contentDescription));
193 }
194 }
195
196 private enum State {
197 NONE(0, -1),
198 PENDING(1, R.string.message_details_recipient_header__pending_send),
199 SENT(2, R.string.message_details_header_sent),
200 DELIVERED(3, R.string.conversation_item_sent__delivered_description),
201 READ(4, R.string.conversation_item_sent__message_read);
202
203 final int code;
204
205 @StringRes
206 final int contentDescription;
207
208 State(int code, @StringRes int contentDescription) {
209 this.code = code;
210 this.contentDescription = contentDescription;
211 }
212
213 static State fromCode(int code) {
214 for (State state : State.values()) {
215 if (state.code == code) {
216 return state;
217 }
218 }
219
220 return NONE;
221 }
222 }
223}