1package eu.siacs.conversations.ui;
2
3import android.app.PendingIntent;
4import android.content.Intent;
5import android.content.pm.PackageManager;
6import android.graphics.Bitmap;
7import android.net.Uri;
8import android.os.Bundle;
9import android.support.annotation.NonNull;
10import android.view.Menu;
11import android.view.MenuItem;
12import android.view.View;
13import android.view.View.OnLongClickListener;
14import android.widget.Button;
15import android.widget.ImageView;
16import android.widget.TextView;
17import android.widget.Toast;
18
19import com.soundcloud.android.crop.Crop;
20
21import java.io.File;
22
23import eu.siacs.conversations.R;
24import eu.siacs.conversations.entities.Account;
25import eu.siacs.conversations.persistance.FileBackend;
26import eu.siacs.conversations.utils.FileUtils;
27import eu.siacs.conversations.utils.PhoneHelper;
28import eu.siacs.conversations.xmpp.pep.Avatar;
29
30public class PublishProfilePictureActivity extends XmppActivity {
31
32 private static final int REQUEST_CHOOSE_FILE_AND_CROP = 0xac23;
33 private static final int REQUEST_CHOOSE_FILE = 0xac24;
34 private ImageView avatar;
35 private TextView hintOrWarning;
36 private TextView secondaryHint;
37 private Button cancelButton;
38 private Button publishButton;
39 private Uri avatarUri;
40 private Uri defaultUri;
41 private Account account;
42 private boolean support = false;
43 private OnLongClickListener backToDefaultListener = new OnLongClickListener() {
44
45 @Override
46 public boolean onLongClick(View v) {
47 avatarUri = defaultUri;
48 loadImageIntoPreview(defaultUri);
49 return true;
50 }
51 };
52 private boolean mInitialAccountSetup;
53 private UiCallback<Avatar> avatarPublication = new UiCallback<Avatar>() {
54
55 @Override
56 public void success(Avatar object) {
57 runOnUiThread(() -> {
58 if (mInitialAccountSetup) {
59 Intent intent = new Intent(getApplicationContext(),
60 StartConversationActivity.class);
61 intent.putExtra("init", true);
62 startActivity(intent);
63 }
64 Toast.makeText(PublishProfilePictureActivity.this,
65 R.string.avatar_has_been_published,
66 Toast.LENGTH_SHORT).show();
67 finish();
68 });
69 }
70
71 @Override
72 public void error(final int errorCode, Avatar object) {
73 runOnUiThread(() -> {
74 hintOrWarning.setText(errorCode);
75 hintOrWarning.setTextColor(getWarningTextColor());
76 hintOrWarning.setVisibility(View.VISIBLE);
77 publishButton.setText(R.string.publish);
78 enablePublishButton();
79 });
80
81 }
82
83 @Override
84 public void userInputRequried(PendingIntent pi, Avatar object) {
85 }
86 };
87
88 @Override
89 public void onCreate(Bundle savedInstanceState) {
90 super.onCreate(savedInstanceState);
91 setContentView(R.layout.activity_publish_profile_picture);
92 this.avatar = findViewById(R.id.account_image);
93 this.cancelButton = findViewById(R.id.cancel_button);
94 this.publishButton = findViewById(R.id.publish_button);
95 this.hintOrWarning = findViewById(R.id.hint_or_warning);
96 this.secondaryHint = findViewById(R.id.secondary_hint);
97 this.publishButton.setOnClickListener(v -> {
98 if (avatarUri != null) {
99 publishButton.setText(R.string.publishing);
100 disablePublishButton();
101 xmppConnectionService.publishAvatar(account, avatarUri,
102 avatarPublication);
103 }
104 });
105 this.cancelButton.setOnClickListener(v -> {
106 if (mInitialAccountSetup) {
107 Intent intent = new Intent(getApplicationContext(),
108 StartConversationActivity.class);
109 if (xmppConnectionService != null && xmppConnectionService.getAccounts().size() == 1) {
110 intent.putExtra("init", true);
111 }
112 startActivity(intent);
113 }
114 finish();
115 });
116 this.avatar.setOnClickListener(v -> {
117 if (hasStoragePermission(REQUEST_CHOOSE_FILE)) {
118 chooseAvatar(false);
119 }
120
121 });
122 this.defaultUri = PhoneHelper.getProfilePictureUri(getApplicationContext());
123 }
124
125 private void chooseAvatar(boolean crop) {
126 Intent attachFileIntent = new Intent();
127 attachFileIntent.setType("image/*");
128 attachFileIntent.setAction(Intent.ACTION_GET_CONTENT);
129 Intent chooser = Intent.createChooser(attachFileIntent, getString(R.string.attach_file));
130 startActivityForResult(chooser, crop ? REQUEST_CHOOSE_FILE_AND_CROP : REQUEST_CHOOSE_FILE);
131 }
132
133 @Override
134 public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
135 if (grantResults.length > 0)
136 if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
137 if (requestCode == REQUEST_CHOOSE_FILE_AND_CROP) {
138 chooseAvatar(true);
139 } else if (requestCode == REQUEST_CHOOSE_FILE) {
140 chooseAvatar(false);
141 }
142 } else {
143 Toast.makeText(this, R.string.no_storage_permission, Toast.LENGTH_SHORT).show();
144 }
145 }
146
147 @Override
148 public boolean onCreateOptionsMenu(Menu menu) {
149 getMenuInflater().inflate(R.menu.publish_avatar, menu);
150 return super.onCreateOptionsMenu(menu);
151 }
152
153 @Override
154 public boolean onOptionsItemSelected(final MenuItem item) {
155 if (item.getItemId() == R.id.action_crop_image) {
156 if (hasStoragePermission(REQUEST_CHOOSE_FILE_AND_CROP)) {
157 chooseAvatar(true);
158 }
159 return true;
160 } else {
161 return super.onOptionsItemSelected(item);
162 }
163 }
164
165 @Override
166 protected void onActivityResult(int requestCode, int resultCode, final Intent data) {
167 super.onActivityResult(requestCode, resultCode, data);
168 if (resultCode == RESULT_OK) {
169 Uri source = data.getData();
170 switch (requestCode) {
171 case REQUEST_CHOOSE_FILE_AND_CROP:
172 if (FileBackend.weOwnFile(this, source)) {
173 Toast.makeText(this,R.string.security_error_invalid_file_access,Toast.LENGTH_SHORT).show();
174 return;
175 }
176 String original = FileUtils.getPath(this, source);
177 if (original != null) {
178 source = Uri.parse("file://"+original);
179 }
180 Uri destination = Uri.fromFile(new File(getCacheDir(), "croppedAvatar"));
181 final int size = getPixel(192);
182 Crop.of(source, destination).asSquare().withMaxSize(size, size).start(this);
183 break;
184 case REQUEST_CHOOSE_FILE:
185 if (FileBackend.weOwnFile(this, source)) {
186 Toast.makeText(this,R.string.security_error_invalid_file_access,Toast.LENGTH_SHORT).show();
187 return;
188 }
189 this.avatarUri = source;
190 if (xmppConnectionServiceBound) {
191 loadImageIntoPreview(this.avatarUri);
192 }
193 break;
194 case Crop.REQUEST_CROP:
195 this.avatarUri = Uri.fromFile(new File(getCacheDir(), "croppedAvatar"));
196 if (xmppConnectionServiceBound) {
197 loadImageIntoPreview(this.avatarUri);
198 }
199 break;
200 }
201 } else {
202 if (requestCode == Crop.REQUEST_CROP && data != null) {
203 Throwable throwable = Crop.getError(data);
204 if (throwable != null && throwable instanceof OutOfMemoryError) {
205 Toast.makeText(this,R.string.selection_too_large, Toast.LENGTH_SHORT).show();
206 }
207 }
208 }
209 }
210
211 @Override
212 protected void onBackendConnected() {
213 this.account = extractAccount(getIntent());
214 if (this.account != null) {
215 if (this.account.getXmppConnection() != null) {
216 this.support = this.account.getXmppConnection().getFeatures().pep();
217 }
218 if (this.avatarUri == null) {
219 if (this.account.getAvatar() != null || this.defaultUri == null) {
220 this.avatar.setImageBitmap(avatarService().get(account, getPixel(192)));
221 if (this.defaultUri != null) {
222 this.avatar.setOnLongClickListener(this.backToDefaultListener);
223 } else {
224 this.secondaryHint.setVisibility(View.INVISIBLE);
225 }
226 if (!support) {
227 this.hintOrWarning.setVisibility(View.VISIBLE);
228 this.hintOrWarning.setTextColor(getWarningTextColor());
229 if (account.getStatus() == Account.State.ONLINE) {
230 this.hintOrWarning.setText(R.string.error_publish_avatar_no_server_support);
231 } else {
232 this.hintOrWarning.setText(R.string.error_publish_avatar_offline);
233 }
234 }
235 } else {
236 this.avatarUri = this.defaultUri;
237 loadImageIntoPreview(this.defaultUri);
238 this.secondaryHint.setVisibility(View.INVISIBLE);
239 }
240 } else {
241 loadImageIntoPreview(avatarUri);
242 }
243 }
244 }
245
246 @Override
247 protected void onStart() {
248 super.onStart();
249 if (getIntent() != null) {
250 this.mInitialAccountSetup = getIntent().getBooleanExtra("setup", false);
251 }
252 if (this.mInitialAccountSetup) {
253 this.cancelButton.setText(R.string.skip);
254 }
255 }
256
257 protected void loadImageIntoPreview(Uri uri) {
258 Bitmap bm = null;
259 try {
260 bm = xmppConnectionService.getFileBackend().cropCenterSquare(uri, getPixel(192));
261 } catch (Exception e) {
262 e.printStackTrace();
263 }
264
265 if (bm == null) {
266 disablePublishButton();
267 this.hintOrWarning.setVisibility(View.VISIBLE);
268 this.hintOrWarning.setTextColor(getWarningTextColor());
269 this.hintOrWarning.setText(R.string.error_publish_avatar_converting);
270 return;
271 }
272 this.avatar.setImageBitmap(bm);
273 if (support) {
274 enablePublishButton();
275 this.publishButton.setText(R.string.publish);
276 this.hintOrWarning.setVisibility(View.INVISIBLE);
277 } else {
278 disablePublishButton();
279 this.hintOrWarning.setVisibility(View.VISIBLE);
280 this.hintOrWarning.setTextColor(getWarningTextColor());
281 if (account.getStatus() == Account.State.ONLINE) {
282 this.hintOrWarning.setText(R.string.error_publish_avatar_no_server_support);
283 } else {
284 this.hintOrWarning.setText(R.string.error_publish_avatar_offline);
285 }
286 }
287 if (this.defaultUri != null && uri.equals(this.defaultUri)) {
288 this.secondaryHint.setVisibility(View.INVISIBLE);
289 this.avatar.setOnLongClickListener(null);
290 } else if (this.defaultUri != null) {
291 this.secondaryHint.setVisibility(View.VISIBLE);
292 this.avatar.setOnLongClickListener(this.backToDefaultListener);
293 }
294 }
295
296 protected void enablePublishButton() {
297 this.publishButton.setEnabled(true);
298 this.publishButton.setTextColor(getPrimaryTextColor());
299 }
300
301 protected void disablePublishButton() {
302 this.publishButton.setEnabled(false);
303 this.publishButton.setTextColor(getSecondaryTextColor());
304 }
305
306 public void refreshUiReal() {
307 //nothing to do. This Activity doesn't implement any listeners
308 }
309}