PublishProfilePictureActivity.java

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