Browse Source

Replace Avatar Cropper.

pull/9/head
Alan Evans 4 years ago committed by Greyson Parrelli
parent
commit
286b64274c
  1. 3
      AndroidManifest.xml
  2. 4
      build.gradle
  3. 2
      res/values/colors.xml
  4. 4
      res/values/strings.xml
  5. 72
      src/org/thoughtcrime/securesms/CreateProfileActivity.java
  6. 15
      src/org/thoughtcrime/securesms/GroupCreateActivity.java
  7. 115
      src/org/thoughtcrime/securesms/avatar/AvatarSelection.java

3
AndroidManifest.xml

@ -403,7 +403,8 @@
android:theme="@style/TextSecure.ScribbleTheme"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name="com.soundcloud.android.crop.CropImageActivity" />
<activity android:name="com.theartofdev.edmodo.cropper.CropImageActivity"
android:theme="@style/TextSecure.DarkTheme"/>
<activity android:name=".CreateProfileActivity"
android:theme="@style/TextSecure.LightRegistrationTheme"

4
build.gradle

@ -106,7 +106,7 @@ dependencies {
compile 'com.pnikosis:materialish-progress:1.5'
compile 'org.greenrobot:eventbus:3.0.0'
compile 'pl.tajchert:waitingdots:0.1.0'
compile 'com.soundcloud.android:android-crop:0.9.10@aar'
compile 'com.theartofdev.edmodo:android-image-cropper:2.7.0'
compile 'com.melnykov:floatingactionbutton:1.3.0'
compile 'com.google.zxing:android-integration:3.1.0'
compile 'com.squareup.dagger:dagger:1.2.2'
@ -172,6 +172,7 @@ dependencyVerification {
'com.android.support:preference-v7:75eabe936d1fc3b178450a554c4d433466036f2be6d6dccdf971eac9590fdbf5',
'com.pnikosis:materialish-progress:d71d80e00717a096784482aee21001a9d299fec3833e4ebd87739ed36cf77c54',
'pl.tajchert:waitingdots:2835d49e0787dbcb606c5a60021ced66578503b1e9fddcd7a5ef0cd5f095ba2c',
'com.theartofdev.edmodo:android-image-cropper:72a1b03c5642fe8489061c732e43b10558c850129b576970e4f77a5d4c25317a',
'mobi.upod:time-duration-picker:db469ce0f48dd96b892eac424ed76870e54bf00fe0a28cdcddfbe5f2a226a0e1',
'com.codewaves.stickyheadergrid:stickyheadergrid:5b4aa6a52a957cfd55f60f4220c11c0c371385a3cb9786cae03c260dcdef5794',
'com.android.support:appcompat-v7:a3a8e5230359746ed91801579b5fbe4668e3b1c4e6a14c7d67c8f58cb0311752',
@ -202,7 +203,6 @@ dependencyVerification {
'com.github.bumptech.glide:glide:997de7ac95be6c944d3b8cbe13de11307736ea45451c1b09a6cec7c328ead59f',
'com.makeramen:roundedimageview:1f5a1865796b308c6cdd114acc6e78408b110f0a62fc63553278fbeacd489cd1',
'org.greenrobot:eventbus:180d4212467df06f2fbc9c8d8a2984533ac79c87769ad883bc421612f0b4e17c',
'com.soundcloud.android:android-crop:ffd4b973cf6e97f7d64118a0dc088df50e9066fd5634fe6911dd0c0c5d346177',
'com.google.zxing:android-integration:89e56aadf1164bd71e57949163c53abf90af368b51669c0d4a47a163335f95c4',
'com.squareup.dagger:dagger:789aca24537022e49f91fc6444078d9de8f1dd99e1bfb090f18491b186967883',
'com.amulyakhare:com.amulyakhare.textdrawable:54c92b5fba38cfd316a07e5a30528068f45ce8515a6890f1297df4c401af5dcb',

2
res/values/colors.xml

@ -65,4 +65,6 @@
<color name="logsubmit_confirmation_background">#44ff2d00</color>
<color name="avatar_background">@color/transparent_black_90</color>
</resources>

4
res/values/strings.xml

@ -381,6 +381,10 @@
<!-- GroupMembersDialog -->
<string name="GroupMembersDialog_me">Me</string>
<!-- CropImageActivity -->
<string name="CropImageActivity_group_avatar">@string/GroupCreateActivity_avatar_content_description</string>
<string name="CropImageActivity_profile_avatar">Avatar</string>
<!-- InputPanel -->
<string name="InputPanel_tap_and_hold_to_record_a_voice_message_release_to_send">Tap and hold to record a voice message, release to send</string>

72
src/org/thoughtcrime/securesms/CreateProfileActivity.java

@ -12,28 +12,25 @@ import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.provider.MediaStore;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import org.thoughtcrime.securesms.avatar.AvatarSelection;
import org.thoughtcrime.securesms.components.LabeledEditText;
import org.thoughtcrime.securesms.logging.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewAnimationUtils;
import android.view.WindowManager;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.dd.CircularProgressButton;
import com.soundcloud.android.crop.Crop;
import org.thoughtcrime.securesms.components.InputAwareLayout;
import org.thoughtcrime.securesms.components.emoji.EmojiDrawer;
@ -53,8 +50,6 @@ import org.thoughtcrime.securesms.util.BitmapUtil;
import org.thoughtcrime.securesms.util.DynamicLanguage;
import org.thoughtcrime.securesms.util.DynamicRegistrationTheme;
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.FileProviderUtil;
import org.thoughtcrime.securesms.util.IntentUtils;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.ViewUtil;
@ -67,14 +62,10 @@ import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.security.SecureRandom;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import javax.inject.Inject;
import static android.provider.MediaStore.EXTRA_OUTPUT;
@SuppressLint("StaticFieldLeak")
public class CreateProfileActivity extends BaseActionBarActivity implements InjectableType {
@ -83,8 +74,6 @@ public class CreateProfileActivity extends BaseActionBarActivity implements Inje
public static final String NEXT_INTENT = "next_intent";
public static final String EXCLUDE_SYSTEM = "exclude_system";
private static final int REQUEST_CODE_AVATAR = 1;
private final DynamicTheme dynamicTheme = new DynamicRegistrationTheme();
private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
@ -153,7 +142,7 @@ public class CreateProfileActivity extends BaseActionBarActivity implements Inje
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case REQUEST_CODE_AVATAR:
case AvatarSelection.REQUEST_CODE_AVATAR:
if (resultCode == Activity.RESULT_OK) {
Uri outputFile = Uri.fromFile(new File(getCacheDir(), "cropped"));
Uri inputFile = (data != null ? data.getData() : null);
@ -166,18 +155,18 @@ public class CreateProfileActivity extends BaseActionBarActivity implements Inje
avatarBytes = null;
avatar.setImageDrawable(new ResourceContactPhoto(R.drawable.ic_camera_alt_white_24dp).asDrawable(this, getResources().getColor(R.color.grey_400)));
} else {
new Crop(inputFile).output(outputFile).asSquare().start(this);
AvatarSelection.circularCropImage(this, inputFile, outputFile, R.string.CropImageActivity_profile_avatar);
}
}
break;
case Crop.REQUEST_CROP:
case AvatarSelection.REQUEST_CODE_CROP_IMAGE:
if (resultCode == Activity.RESULT_OK) {
new AsyncTask<Void, Void, byte[]>() {
@Override
protected byte[] doInBackground(Void... params) {
try {
BitmapUtil.ScaleResult result = BitmapUtil.createScaledBytes(CreateProfileActivity.this, Crop.getOutput(data), new ProfileMediaConstraints());
BitmapUtil.ScaleResult result = BitmapUtil.createScaledBytes(CreateProfileActivity.this, AvatarSelection.getResultUri(data), new ProfileMediaConstraints());
return result.getBitmap();
} catch (BitmapDecodingException e) {
Log.w(TAG, e);
@ -220,7 +209,7 @@ public class CreateProfileActivity extends BaseActionBarActivity implements Inje
this.avatar.setOnClickListener(view -> Permissions.with(this)
.request(Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE)
.ifNecessary()
.onAnyResult(this::handleAvatarSelectionWithPermissions)
.onAnyResult(this::startAvatarSelection)
.execute());
this.name.getInput().addTextChangedListener(new TextWatcher() {
@ -354,53 +343,8 @@ public class CreateProfileActivity extends BaseActionBarActivity implements Inje
this.name.setOnClickListener(v -> container.showSoftkey(name.getInput()));
}
private Intent createAvatarSelectionIntent(@Nullable File captureFile, boolean includeClear, boolean includeCamera) {
List<Intent> extraIntents = new LinkedList<>();
Intent galleryIntent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI);
galleryIntent.setType("image/*");
if (!IntentUtils.isResolvable(CreateProfileActivity.this, galleryIntent)) {
galleryIntent = new Intent(Intent.ACTION_GET_CONTENT);
galleryIntent.setType("image/*");
}
if (includeCamera) {
Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (captureFile != null && cameraIntent.resolveActivity(getPackageManager()) != null) {
cameraIntent.putExtra(EXTRA_OUTPUT, FileProviderUtil.getUriFor(this, captureFile));
extraIntents.add(cameraIntent);
}
}
if (includeClear) {
extraIntents.add(new Intent("org.thoughtcrime.securesms.action.CLEAR_PROFILE_PHOTO"));
}
Intent chooserIntent = Intent.createChooser(galleryIntent, getString(R.string.CreateProfileActivity_profile_photo));
if (!extraIntents.isEmpty()) {
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, extraIntents.toArray(new Intent[0]));
}
return chooserIntent;
}
private void handleAvatarSelectionWithPermissions() {
boolean hasCameraPermission = Permissions.hasAll(this, Manifest.permission.CAMERA);
if (hasCameraPermission) {
try {
captureFile = File.createTempFile("capture", "jpg", getExternalCacheDir());
} catch (IOException e) {
Log.w(TAG, e);
captureFile = null;
}
}
Intent chooserIntent = createAvatarSelectionIntent(captureFile, avatarBytes != null, hasCameraPermission);
startActivityForResult(chooserIntent, REQUEST_CODE_AVATAR);
private void startAvatarSelection() {
captureFile = AvatarSelection.startAvatarSelection(this, avatarBytes != null, true);
}
private void handleUpload() {

15
src/org/thoughtcrime/securesms/GroupCreateActivity.java

@ -27,6 +27,7 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import org.thoughtcrime.securesms.avatar.AvatarSelection;
import org.thoughtcrime.securesms.conversation.ConversationActivity;
import org.thoughtcrime.securesms.logging.Log;
import android.view.Menu;
@ -42,7 +43,6 @@ import android.widget.Toast;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.request.target.SimpleTarget;
import com.bumptech.glide.request.transition.Transition;
import com.soundcloud.android.crop.Crop;
import org.thoughtcrime.securesms.components.PushRecipientsPanel;
import org.thoughtcrime.securesms.components.PushRecipientsPanel.RecipientsPanelChangedListener;
@ -190,7 +190,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
recipientsPanel.setPanelChangeListener(this);
findViewById(R.id.contacts_button).setOnClickListener(new AddRecipientButtonListener());
avatar.setImageDrawable(new ResourceContactPhoto(R.drawable.ic_group_white_24dp).asDrawable(this, ContactColors.UNKNOWN_COLOR.toConversationColor(this)));
avatar.setOnClickListener(view -> Crop.pickImage(GroupCreateActivity.this));
avatar.setOnClickListener(view -> AvatarSelection.startAvatarSelection(this, false, false));
}
private void initializeExistingGroup() {
@ -293,13 +293,14 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
}
break;
case Crop.REQUEST_PICK:
new Crop(data.getData()).output(outputFile).asSquare().start(this);
case AvatarSelection.REQUEST_CODE_AVATAR:
AvatarSelection.circularCropImage(this, data.getData(), outputFile, R.string.CropImageActivity_group_avatar);
break;
case Crop.REQUEST_CROP:
case AvatarSelection.REQUEST_CODE_CROP_IMAGE:
final Uri resultUri = AvatarSelection.getResultUri(data);
GlideApp.with(this)
.asBitmap()
.load(Crop.getOutput(data))
.load(resultUri)
.skipMemoryCache(true)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.centerCrop()
@ -307,7 +308,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
.into(new SimpleTarget<Bitmap>() {
@Override
public void onResourceReady(@NonNull Bitmap resource, Transition<? super Bitmap> transition) {
setAvatar(Crop.getOutput(data), resource);
setAvatar(resultUri, resource);
}
});
}

115
src/org/thoughtcrime/securesms/avatar/AvatarSelection.java

@ -0,0 +1,115 @@
package org.thoughtcrime.securesms.avatar;
import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.provider.MediaStore;
import android.support.annotation.Nullable;
import android.support.annotation.StringRes;
import android.support.v4.content.ContextCompat;
import com.theartofdev.edmodo.cropper.CropImage;
import com.theartofdev.edmodo.cropper.CropImageView;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.util.FileProviderUtil;
import org.thoughtcrime.securesms.util.IntentUtils;
import java.io.File;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import static android.provider.MediaStore.EXTRA_OUTPUT;
public final class AvatarSelection {
private static final String TAG = AvatarSelection.class.getSimpleName();
private AvatarSelection() {
}
public static final int REQUEST_CODE_CROP_IMAGE = CropImage.CROP_IMAGE_ACTIVITY_REQUEST_CODE;
public static final int REQUEST_CODE_AVATAR = REQUEST_CODE_CROP_IMAGE + 1;
/**
* Returns result on {@link #REQUEST_CODE_CROP_IMAGE}
*/
public static void circularCropImage(Activity activity, Uri inputFile, Uri outputFile, @StringRes int title) {
CropImage.activity(inputFile)
.setGuidelines(CropImageView.Guidelines.ON)
.setAspectRatio(1, 1)
.setCropShape(CropImageView.CropShape.OVAL)
.setOutputUri(outputFile)
.setAllowRotation(true)
.setAllowFlipping(true)
.setBackgroundColor(ContextCompat.getColor(activity, R.color.avatar_background))
.setActivityTitle(activity.getString(title))
.start(activity);
}
public static Uri getResultUri(Intent data) {
return CropImage.getActivityResult(data).getUri();
}
/**
* Returns result on {@link #REQUEST_CODE_AVATAR}
*
* @return Temporary capture file if created.
*/
public static File startAvatarSelection(Activity activity, boolean includeClear, boolean attemptToIncludeCamera) {
File captureFile = null;
if (attemptToIncludeCamera) {
if (Permissions.hasAll(activity, Manifest.permission.CAMERA)) {
try {
captureFile = File.createTempFile("capture", "jpg", activity.getExternalCacheDir());
} catch (IOException e) {
Log.w(TAG, e);
captureFile = null;
}
}
}
Intent chooserIntent = createAvatarSelectionIntent(activity, captureFile, includeClear);
activity.startActivityForResult(chooserIntent, REQUEST_CODE_AVATAR);
return captureFile;
}
private static Intent createAvatarSelectionIntent(Context context, @Nullable File tempCaptureFile, boolean includeClear) {
List<Intent> extraIntents = new LinkedList<>();
Intent galleryIntent = new Intent(Intent.ACTION_PICK);
galleryIntent.setDataAndType(android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI, "image/*");
if (!IntentUtils.isResolvable(context, galleryIntent)) {
galleryIntent = new Intent(Intent.ACTION_GET_CONTENT);
galleryIntent.setType("image/*");
}
if (tempCaptureFile != null) {
Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (cameraIntent.resolveActivity(context.getPackageManager()) != null) {
cameraIntent.putExtra(EXTRA_OUTPUT, FileProviderUtil.getUriFor(context, tempCaptureFile));
extraIntents.add(cameraIntent);
}
}
if (includeClear) {
extraIntents.add(new Intent("org.thoughtcrime.securesms.action.CLEAR_PROFILE_PHOTO"));
}
Intent chooserIntent = Intent.createChooser(galleryIntent, context.getString(R.string.CreateProfileActivity_profile_photo));
if (!extraIntents.isEmpty()) {
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, extraIntents.toArray(new Intent[0]));
}
return chooserIntent;
}
}
Loading…
Cancel
Save