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 publishButton.setText(R.string.publish);
77 enablePublishButton();
78 });
79
80 }
81
82 @Override
83 public void userInputRequried(PendingIntent pi, Avatar object) {
84 }
85 };
86
87 @Override
88 public void onCreate(Bundle savedInstanceState) {
89 super.onCreate(savedInstanceState);
90 setContentView(R.layout.activity_publish_profile_picture);
91 this.avatar = findViewById(R.id.account_image);
92 this.cancelButton = findViewById(R.id.cancel_button);
93 this.publishButton = findViewById(R.id.publish_button);
94 this.hintOrWarning = findViewById(R.id.hint_or_warning);
95 this.secondaryHint = findViewById(R.id.secondary_hint);
96 this.publishButton.setOnClickListener(v -> {
97 if (avatarUri != null) {
98 publishButton.setText(R.string.publishing);
99 disablePublishButton();
100 xmppConnectionService.publishAvatar(account, avatarUri,
101 avatarPublication);
102 }
103 });
104 this.cancelButton.setOnClickListener(v -> {
105 if (mInitialAccountSetup) {
106 Intent intent = new Intent(getApplicationContext(),
107 StartConversationActivity.class);
108 if (xmppConnectionService != null && xmppConnectionService.getAccounts().size() == 1) {
109 intent.putExtra("init", true);
110 }
111 startActivity(intent);
112 }
113 finish();
114 });
115 this.avatar.setOnClickListener(v -> {
116 if (hasStoragePermission(REQUEST_CHOOSE_FILE)) {
117 chooseAvatar(false);
118 }
119
120 });
121 this.defaultUri = PhoneHelper.getProfilePictureUri(getApplicationContext());
122 }
123
124 private void chooseAvatar(boolean crop) {
125 Intent attachFileIntent = new Intent();
126 attachFileIntent.setType("image/*");
127 attachFileIntent.setAction(Intent.ACTION_GET_CONTENT);
128 Intent chooser = Intent.createChooser(attachFileIntent, getString(R.string.attach_file));
129 startActivityForResult(chooser, crop ? REQUEST_CHOOSE_FILE_AND_CROP : REQUEST_CHOOSE_FILE);
130 }
131
132 @Override
133 public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
134 if (grantResults.length > 0)
135 if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
136 if (requestCode == REQUEST_CHOOSE_FILE_AND_CROP) {
137 chooseAvatar(true);
138 } else if (requestCode == REQUEST_CHOOSE_FILE) {
139 chooseAvatar(false);
140 }
141 } else {
142 Toast.makeText(this, R.string.no_storage_permission, Toast.LENGTH_SHORT).show();
143 }
144 }
145
146 @Override
147 public boolean onCreateOptionsMenu(Menu menu) {
148 getMenuInflater().inflate(R.menu.publish_avatar, menu);
149 return super.onCreateOptionsMenu(menu);
150 }
151
152 @Override
153 public boolean onOptionsItemSelected(final MenuItem item) {
154 if (item.getItemId() == R.id.action_crop_image) {
155 if (hasStoragePermission(REQUEST_CHOOSE_FILE_AND_CROP)) {
156 chooseAvatar(true);
157 }
158 return true;
159 } else {
160 return super.onOptionsItemSelected(item);
161 }
162 }
163
164 @Override
165 protected void onActivityResult(int requestCode, int resultCode, final Intent data) {
166 super.onActivityResult(requestCode, resultCode, data);
167 if (resultCode == RESULT_OK) {
168 Uri source = data.getData();
169 switch (requestCode) {
170 case REQUEST_CHOOSE_FILE_AND_CROP:
171 if (FileBackend.weOwnFile(this, source)) {
172 Toast.makeText(this,R.string.security_error_invalid_file_access,Toast.LENGTH_SHORT).show();
173 return;
174 }
175 String original = FileUtils.getPath(this, source);
176 if (original != null) {
177 source = Uri.parse("file://"+original);
178 }
179 Uri destination = Uri.fromFile(new File(getCacheDir(), "croppedAvatar"));
180 final int size = getPixel(192);
181 Crop.of(source, destination).asSquare().withMaxSize(size, size).start(this);
182 break;
183 case REQUEST_CHOOSE_FILE:
184 if (FileBackend.weOwnFile(this, source)) {
185 Toast.makeText(this,R.string.security_error_invalid_file_access,Toast.LENGTH_SHORT).show();
186 return;
187 }
188 this.avatarUri = source;
189 if (xmppConnectionServiceBound) {
190 loadImageIntoPreview(this.avatarUri);
191 }
192 break;
193 case Crop.REQUEST_CROP:
194 this.avatarUri = Uri.fromFile(new File(getCacheDir(), "croppedAvatar"));
195 if (xmppConnectionServiceBound) {
196 loadImageIntoPreview(this.avatarUri);
197 }
198 break;
199 }
200 } else {
201 if (requestCode == Crop.REQUEST_CROP && data != null) {
202 Throwable throwable = Crop.getError(data);
203 if (throwable != null && throwable instanceof OutOfMemoryError) {
204 Toast.makeText(this,R.string.selection_too_large, Toast.LENGTH_SHORT).show();
205 }
206 }
207 }
208 }
209
210 @Override
211 protected void onBackendConnected() {
212 this.account = extractAccount(getIntent());
213 if (this.account != null) {
214 if (this.account.getXmppConnection() != null) {
215 this.support = this.account.getXmppConnection().getFeatures().pep();
216 }
217 if (this.avatarUri == null) {
218 if (this.account.getAvatar() != null
219 || this.defaultUri == null) {
220 this.avatar.setImageBitmap(avatarService().get(account, getPixel(192)));
221 if (this.defaultUri != null) {
222 this.avatar
223 .setOnLongClickListener(this.backToDefaultListener);
224 } else {
225 this.secondaryHint.setVisibility(View.INVISIBLE);
226 }
227 if (!support) {
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.setTextColor(getWarningTextColor());
268 this.hintOrWarning.setText(R.string.error_publish_avatar_converting);
269 return;
270 }
271 this.avatar.setImageBitmap(bm);
272 if (support) {
273 enablePublishButton();
274 this.publishButton.setText(R.string.publish);
275 this.hintOrWarning.setText(R.string.publish_avatar_explanation);
276 this.hintOrWarning.setTextColor(getPrimaryTextColor());
277 } else {
278 disablePublishButton();
279 this.hintOrWarning.setTextColor(getWarningTextColor());
280 if (account.getStatus() == Account.State.ONLINE) {
281 this.hintOrWarning.setText(R.string.error_publish_avatar_no_server_support);
282 } else {
283 this.hintOrWarning.setText(R.string.error_publish_avatar_offline);
284 }
285 }
286 if (this.defaultUri != null && uri.equals(this.defaultUri)) {
287 this.secondaryHint.setVisibility(View.INVISIBLE);
288 this.avatar.setOnLongClickListener(null);
289 } else if (this.defaultUri != null) {
290 this.secondaryHint.setVisibility(View.VISIBLE);
291 this.avatar.setOnLongClickListener(this.backToDefaultListener);
292 }
293 }
294
295 protected void enablePublishButton() {
296 this.publishButton.setEnabled(true);
297 this.publishButton.setTextColor(getPrimaryTextColor());
298 }
299
300 protected void disablePublishButton() {
301 this.publishButton.setEnabled(false);
302 this.publishButton.setTextColor(getSecondaryTextColor());
303 }
304
305 public void refreshUiReal() {
306 //nothing to do. This Activity doesn't implement any listeners
307 }
308}