Detailed changes
@@ -58,7 +58,7 @@ dependencies {
conversationsPlaystoreImplementation("com.android.installreferrer:installreferrer:2.2")
quicksyPlaystoreImplementation 'com.google.android.gms:play-services-auth-api-phone:18.2.0'
implementation 'com.github.open-keychain.open-keychain:openpgp-api:v5.7.1'
- implementation("com.github.CanHub:Android-Image-Cropper:2.0.0")
+ implementation("com.vanniktech:android-image-cropper:4.6.0")
implementation "androidx.sharetarget:sharetarget:1.2.0"
implementation 'androidx.appcompat:appcompat:1.7.0'
@@ -1395,34 +1395,36 @@ public class FileBackend {
return Uri.fromFile(getAvatarFile(avatar));
}
- public Bitmap cropCenterSquare(Uri image, int size) {
+ public Bitmap cropCenterSquare(final Uri image, final int size) {
if (image == null) {
return null;
}
- InputStream is = null;
+ final BitmapFactory.Options options = new BitmapFactory.Options();
try {
- BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = calcSampleSize(image, size);
- is = mXmppConnectionService.getContentResolver().openInputStream(image);
+ } catch (final IOException | SecurityException e) {
+ Log.d(Config.LOGTAG, "unable to calculate sample size for " + image, e);
+ return null;
+ }
+ try (final InputStream is =
+ mXmppConnectionService.getContentResolver().openInputStream(image)) {
if (is == null) {
return null;
}
- Bitmap input = BitmapFactory.decodeStream(is, null, options);
- if (input == null) {
+ final var originalBitmap = BitmapFactory.decodeStream(is, null, options);
+ if (originalBitmap == null) {
return null;
} else {
- input = rotate(input, getRotation(image));
- return cropCenterSquare(input, size);
+ final var bitmap = rotate(originalBitmap, getRotation(image));
+ return cropCenterSquare(bitmap, size);
}
- } catch (FileNotFoundException | SecurityException e) {
- Log.d(Config.LOGTAG, "unable to open file " + image.toString(), e);
+ } catch (final SecurityException | IOException e) {
+ Log.d(Config.LOGTAG, "unable to open file " + image, e);
return null;
- } finally {
- close(is);
}
}
- public Bitmap cropCenter(Uri image, int newHeight, int newWidth) {
+ public Bitmap cropCenter(final Uri image, final int newHeight, final int newWidth) {
if (image == null) {
return null;
}
@@ -1458,7 +1460,7 @@ public class FileBackend {
return dest;
} catch (SecurityException e) {
return null; // android 6.0 with revoked permissions for example
- } catch (FileNotFoundException e) {
+ } catch (IOException e) {
return null;
} finally {
close(is);
@@ -1486,15 +1488,14 @@ public class FileBackend {
return output;
}
- private int calcSampleSize(Uri image, int size)
- throws FileNotFoundException, SecurityException {
+ private int calcSampleSize(final Uri image, int size) throws IOException, SecurityException {
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
- final InputStream inputStream =
- mXmppConnectionService.getContentResolver().openInputStream(image);
- BitmapFactory.decodeStream(inputStream, null, options);
- close(inputStream);
- return calcSampleSize(options, size);
+ try (final InputStream inputStream =
+ mXmppConnectionService.getContentResolver().openInputStream(image)) {
+ BitmapFactory.decodeStream(inputStream, null, options);
+ return calcSampleSize(options, size);
+ }
}
public void updateFileParams(final Message message) {
@@ -24,7 +24,7 @@ public class ChooseAccountForProfilePictureActivity extends XmppActivity {
}
@Override
- protected void onCreate(Bundle savedInstanceState) {
+ protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final ActivityManageAccountsBinding binding =
DataBindingUtil.setContentView(this, R.layout.activity_manage_accounts);
@@ -64,11 +64,12 @@ public class ChooseAccountForProfilePictureActivity extends XmppActivity {
}
}
- private void goToProfilePictureActivity(Account account) {
+ private void goToProfilePictureActivity(final Account account) {
final Intent startIntent = getIntent();
final Uri uri = startIntent == null ? null : startIntent.getData();
if (uri != null) {
Intent intent = new Intent(this, PublishProfilePictureActivity.class);
+ intent.setAction(Intent.ACTION_ATTACH_DATA);
intent.putExtra(EXTRA_ACCOUNT, account.getJid().asBareJid().toString());
intent.setData(uri);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
@@ -1202,6 +1202,10 @@ public class EditAccountActivity extends OmemoActivity
this.binding.accountPassword.setFocusableInTouchMode(editPassword);
this.binding.accountPassword.setCursorVisible(editPassword);
this.binding.accountPassword.setEnabled(editPassword);
+ if (!editPassword && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ this.binding.accountJid.setImportantForAutofill(View.IMPORTANT_FOR_AUTOFILL_NO);
+ this.binding.accountPassword.setImportantForAutofill(View.IMPORTANT_FOR_AUTOFILL_NO);
+ }
if (!mInitMode) {
this.binding.avater.setVisibility(View.VISIBLE);
@@ -29,18 +29,17 @@
package eu.siacs.conversations.ui;
-import static eu.siacs.conversations.ui.PublishProfilePictureActivity.REQUEST_CHOOSE_PICTURE;
-
-import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
+import androidx.activity.result.ActivityResultLauncher;
import androidx.annotation.StringRes;
import androidx.databinding.DataBindingUtil;
-import com.canhub.cropper.CropImage;
+import com.canhub.cropper.CropImageContract;
+import com.canhub.cropper.CropImageContractOptions;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.databinding.ActivityPublishProfilePictureBinding;
@@ -55,6 +54,15 @@ public class PublishGroupChatProfilePictureActivity extends XmppActivity
private Conversation conversation;
private Uri uri;
+ final ActivityResultLauncher<CropImageContractOptions> cropImage =
+ registerForActivityResult(
+ new CropImageContract(),
+ cropResult -> {
+ if (cropResult.isSuccessful()) {
+ onAvatarPicked(cropResult.getUriContent());
+ }
+ });
+
@Override
protected void refreshUiReal() {}
@@ -94,8 +102,8 @@ public class PublishGroupChatProfilePictureActivity extends XmppActivity
configureActionBar(getSupportActionBar());
this.binding.cancelButton.setOnClickListener((v) -> this.finish());
this.binding.secondaryHint.setVisibility(View.GONE);
- this.binding.accountImage.setOnClickListener(
- (v) -> PublishProfilePictureActivity.chooseAvatar(this));
+ this.binding.accountImage.setOnClickListener((v) -> pickAvatar());
+
final var intent = getIntent();
final var uuid = intent == null ? null : intent.getStringExtra("uuid");
if (uuid != null) {
@@ -111,26 +119,16 @@ public class PublishGroupChatProfilePictureActivity extends XmppActivity
xmppConnectionService.publishMucAvatar(conversation, uri, this);
}
- @Override
- public void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
- if (requestCode == CropImage.CROP_IMAGE_ACTIVITY_REQUEST_CODE) {
- final CropImage.ActivityResult result = CropImage.getActivityResult(data);
- if (resultCode == RESULT_OK) {
- this.uri = result == null ? null : result.getUri();
- if (xmppConnectionServiceBound) {
- reloadAvatar();
- }
- } else if (resultCode == CropImage.CROP_IMAGE_ACTIVITY_RESULT_ERROR_CODE) {
- final var error = result == null ? null : result.getError();
- if (error != null) {
- Toast.makeText(this, error.getMessage(), Toast.LENGTH_SHORT).show();
- }
- }
- } else if (requestCode == REQUEST_CHOOSE_PICTURE) {
- if (resultCode == RESULT_OK) {
- PublishProfilePictureActivity.cropUri(this, data.getData());
- }
+ public void pickAvatar() {
+ this.cropImage.launch(
+ new CropImageContractOptions(
+ null, PublishProfilePictureActivity.getCropImageOptions()));
+ }
+
+ private void onAvatarPicked(final Uri uri) {
+ this.uri = uri;
+ if (xmppConnectionServiceBound) {
+ reloadAvatar();
}
}
@@ -1,10 +1,8 @@
package eu.siacs.conversations.ui;
-import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
-import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
@@ -12,10 +10,13 @@ import android.view.MenuItem;
import android.view.View;
import android.view.View.OnLongClickListener;
import android.widget.Toast;
+import androidx.activity.result.ActivityResultLauncher;
import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
import androidx.databinding.DataBindingUtil;
-import com.canhub.cropper.CropImage;
+import com.canhub.cropper.CropImageContract;
+import com.canhub.cropper.CropImageContractOptions;
+import com.canhub.cropper.CropImageOptions;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
@@ -24,7 +25,6 @@ import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.ui.interfaces.OnAvatarPublication;
import eu.siacs.conversations.utils.PhoneHelper;
-import java.util.concurrent.atomic.AtomicBoolean;
public class PublishProfilePictureActivity extends XmppActivity
implements XmppConnectionService.OnAccountUpdate, OnAvatarPublication {
@@ -37,7 +37,6 @@ public class PublishProfilePictureActivity extends XmppActivity
private Account account;
private boolean support = false;
private boolean publishing = false;
- private final AtomicBoolean handledExternalUri = new AtomicBoolean(false);
private final OnLongClickListener backToDefaultListener =
new OnLongClickListener() {
@@ -50,6 +49,15 @@ public class PublishProfilePictureActivity extends XmppActivity
};
private boolean mInitialAccountSetup;
+ final ActivityResultLauncher<CropImageContractOptions> cropImage =
+ registerForActivityResult(
+ new CropImageContract(),
+ cropResult -> {
+ if (cropResult.isSuccessful()) {
+ onAvatarPicked(cropResult.getUriContent());
+ }
+ });
+
@Override
public void onAvatarPublicationSucceeded() {
runOnUiThread(
@@ -85,6 +93,7 @@ public class PublishProfilePictureActivity extends XmppActivity
@Override
public void onCreate(final Bundle savedInstanceState) {
+
super.onCreate(savedInstanceState);
this.binding =
@@ -123,12 +132,10 @@ public class PublishProfilePictureActivity extends XmppActivity
}
finish();
});
- this.binding.accountImage.setOnClickListener(v -> chooseAvatar(this));
+ this.binding.accountImage.setOnClickListener(v -> pickAvatar(null));
this.defaultUri = PhoneHelper.getProfilePictureUri(getApplicationContext());
if (savedInstanceState != null) {
this.avatarUri = savedInstanceState.getParcelable("uri");
- this.handledExternalUri.set(
- savedInstanceState.getBoolean("handle_external_uri", false));
}
}
@@ -171,47 +178,32 @@ public class PublishProfilePictureActivity extends XmppActivity
if (this.avatarUri != null) {
outState.putParcelable("uri", this.avatarUri);
}
- outState.putBoolean("handle_external_uri", handledExternalUri.get());
super.onSaveInstanceState(outState);
}
- @Override
- public void onActivityResult(int requestCode, int resultCode, Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
- if (requestCode == CropImage.CROP_IMAGE_ACTIVITY_REQUEST_CODE) {
- CropImage.ActivityResult result = CropImage.getActivityResult(data);
- if (resultCode == RESULT_OK) {
- this.avatarUri = result.getUri();
- if (xmppConnectionServiceBound) {
- loadImageIntoPreview(this.avatarUri);
- }
- } else if (resultCode == CropImage.CROP_IMAGE_ACTIVITY_RESULT_ERROR_CODE) {
- Exception error = result.getError();
- if (error != null) {
- Toast.makeText(this, error.getMessage(), Toast.LENGTH_SHORT).show();
- }
- }
- } else if (requestCode == REQUEST_CHOOSE_PICTURE) {
- if (resultCode == RESULT_OK) {
- cropUri(this, data.getData());
- }
- }
+ public void pickAvatar(final Uri image) {
+ this.cropImage.launch(new CropImageContractOptions(image, getCropImageOptions()));
}
- public static void chooseAvatar(final Activity activity) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
- final Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
- intent.setType("image/*");
- activity.startActivityForResult(
- Intent.createChooser(
- intent, activity.getString(R.string.attach_choose_picture)),
- REQUEST_CHOOSE_PICTURE);
+ public static CropImageOptions getCropImageOptions() {
+ final var cropImageOptions = new CropImageOptions();
+ cropImageOptions.aspectRatioX = 1;
+ cropImageOptions.aspectRatioY = 1;
+ cropImageOptions.fixAspectRatio = true;
+ cropImageOptions.outputCompressFormat = Bitmap.CompressFormat.PNG;
+ cropImageOptions.imageSourceIncludeCamera = false;
+ cropImageOptions.minCropResultHeight = Config.AVATAR_SIZE;
+ cropImageOptions.minCropResultWidth = Config.AVATAR_SIZE;
+ return cropImageOptions;
+ }
+
+ private void onAvatarPicked(final Uri uri) {
+ Log.d(Config.LOGTAG, "onAvatarPicked(" + uri + ")");
+ this.avatarUri = uri;
+ if (xmppConnectionServiceBound) {
+ loadImageIntoPreview(uri);
} else {
- CropImage.activity()
- .setOutputCompressFormat(Bitmap.CompressFormat.PNG)
- .setAspectRatio(1, 1)
- .setMinCropResultSize(Config.AVATAR_SIZE, Config.AVATAR_SIZE)
- .start(activity);
+ Log.d(Config.LOGTAG, "not ready during avatarPick");
}
}
@@ -243,61 +235,54 @@ public class PublishProfilePictureActivity extends XmppActivity
public void onStart() {
super.onStart();
final Intent intent = getIntent();
- this.mInitialAccountSetup = intent != null && intent.getBooleanExtra("setup", false);
-
- final Uri uri = intent != null ? intent.getData() : null;
+ if (intent == null) {
+ return;
+ }
+ this.mInitialAccountSetup = intent.getBooleanExtra("setup", false);
- if (uri != null && handledExternalUri.compareAndSet(false, true)) {
- cropUri(this, uri);
+ final var data = intent.getData();
+ final var account = intent.getStringExtra(EXTRA_ACCOUNT);
+ if (Intent.ACTION_ATTACH_DATA.equals(intent.getAction())
+ && data != null
+ && account != null) {
+ pickAvatar(data);
+ final var replacement = new Intent(Intent.ACTION_MAIN);
+ replacement.putExtra(EXTRA_ACCOUNT, account);
+ setIntent(replacement);
return;
}
if (this.mInitialAccountSetup) {
this.binding.cancelButton.setText(R.string.skip);
}
- configureActionBar(
- getSupportActionBar(), !this.mInitialAccountSetup && !handledExternalUri.get());
- }
-
- public static void cropUri(final Activity activity, final Uri uri) {
- CropImage.activity(uri)
- .setOutputCompressFormat(Bitmap.CompressFormat.PNG)
- .setAspectRatio(1, 1)
- .setMinCropResultSize(Config.AVATAR_SIZE, Config.AVATAR_SIZE)
- .start(activity);
+ configureActionBar(getSupportActionBar(), !this.mInitialAccountSetup);
}
protected void loadImageIntoPreview(final Uri uri) {
-
- Bitmap bm = null;
+ Log.d(Config.LOGTAG, "loadImageIntoPreview(" + uri + ")");
+ final Bitmap bitmap;
if (uri == null) {
- bm =
+ bitmap =
avatarService()
.get(
account,
(int) getResources().getDimension(R.dimen.publish_avatar_size));
} else {
- try {
- bm =
- xmppConnectionService
- .getFileBackend()
- .cropCenterSquare(
- uri,
- (int)
- getResources()
- .getDimension(R.dimen.publish_avatar_size));
- } catch (final Exception e) {
- Log.d(Config.LOGTAG, "unable to load bitmap into image view", e);
- }
+ bitmap =
+ xmppConnectionService
+ .getFileBackend()
+ .cropCenterSquare(
+ uri,
+ (int) getResources().getDimension(R.dimen.publish_avatar_size));
}
- if (bm == null) {
+ if (bitmap == null) {
togglePublishButton(false, R.string.publish);
this.binding.hintOrWarning.setVisibility(View.VISIBLE);
this.binding.hintOrWarning.setText(R.string.error_publish_avatar_converting);
return;
}
- this.binding.accountImage.setImageBitmap(bm);
+ this.binding.accountImage.setImageBitmap(bitmap);
if (support) {
togglePublishButton(uri != null, R.string.publish);
this.binding.hintOrWarning.setVisibility(View.INVISIBLE);
@@ -843,7 +843,7 @@
<string name="ebook">e-book</string>
<string name="video_original">Original (uncompressed)</string>
<string name="open_with">Open withβ¦</string>
- <string name="set_profile_picture">Conversations profile picture</string>
+ <string name="set_profile_picture">Avatar</string>
<string name="choose_account">Choose account</string>
<string name="restore_backup">Restore backup</string>
<string name="restore">Restore</string>