session-android/src/org/thoughtcrime/securesms/CreateProfileActivity.java

391 lines
15 KiB
Java
Raw Normal View History

2017-08-08 23:37:15 +00:00
package org.thoughtcrime.securesms;
import android.app.Activity;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.BitmapFactory;
import android.media.CameraProfile;
2017-08-08 23:37:15 +00:00
import android.net.Uri;
import android.os.AsyncTask;
2017-08-08 23:37:15 +00:00
import android.os.Bundle;
import android.provider.MediaStore;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.Editable;
2017-08-08 23:37:15 +00:00
import android.text.TextUtils;
import android.text.TextWatcher;
2017-08-08 23:37:15 +00:00
import android.util.Log;
import android.util.Pair;
2017-08-08 23:37:15 +00:00
import android.view.KeyEvent;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.Toast;
import com.bumptech.glide.Glide;
2017-08-08 23:37:15 +00:00
import com.soundcloud.android.crop.Crop;
import org.thoughtcrime.securesms.components.InputAwareLayout;
import org.thoughtcrime.securesms.components.emoji.EmojiDrawer;
import org.thoughtcrime.securesms.components.emoji.EmojiToggle;
import org.thoughtcrime.securesms.contacts.avatars.BitmapContactPhoto;
import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto;
2017-08-08 23:37:15 +00:00
import org.thoughtcrime.securesms.contacts.avatars.ContactPhotoFactory;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.Address;
2017-08-08 23:37:15 +00:00
import org.thoughtcrime.securesms.dependencies.InjectableType;
import org.thoughtcrime.securesms.profiles.AvatarHelper;
import org.thoughtcrime.securesms.profiles.AvatarPhotoUriLoader;
2017-08-08 23:37:15 +00:00
import org.thoughtcrime.securesms.profiles.ProfileMediaConstraints;
import org.thoughtcrime.securesms.profiles.SystemProfileUtil;
import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.BitmapDecodingException;
import org.thoughtcrime.securesms.util.BitmapUtil;
import org.thoughtcrime.securesms.util.IntentUtils;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.concurrent.ListenableFuture;
import org.thoughtcrime.securesms.util.task.ProgressDialogAsyncTask;
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
import org.whispersystems.signalservice.api.crypto.ProfileCipher;
2017-08-08 23:37:15 +00:00
import org.whispersystems.signalservice.api.util.StreamDetails;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
2017-08-08 23:37:15 +00:00
import java.util.concurrent.ExecutionException;
import javax.inject.Inject;
import static android.provider.MediaStore.EXTRA_OUTPUT;
2017-08-08 23:37:15 +00:00
public class CreateProfileActivity extends PassphraseRequiredActionBarActivity implements InjectableType {
private static final String TAG = CreateProfileActivity.class.getSimpleName();
public static final String NEXT_INTENT = "next_intent";
public static final String EXCLUDE_SYSTEM = "exclude_system";
2017-08-08 23:37:15 +00:00
private static final int REQUEST_CODE_AVATAR = 1;
@Inject SignalServiceAccountManager accountManager;
private InputAwareLayout container;
private ImageView avatar;
private Button finishButton;
private EditText name;
private EmojiToggle emojiToggle;
private EmojiDrawer emojiDrawer;
private Intent nextIntent;
2017-08-08 23:37:15 +00:00
private byte[] avatarBytes;
private File captureFile;
2017-08-08 23:37:15 +00:00
@Override
public void onCreate(Bundle bundle, @NonNull MasterSecret masterSecret) {
super.onCreate(bundle, masterSecret);
setContentView(R.layout.profile_create_activity);
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
getSupportActionBar().setTitle(R.string.CreateProfileActivity_your_profile_info);
2017-08-08 23:37:15 +00:00
initializeResources();
initializeEmojiInput();
initializeProfileName(getIntent().getBooleanExtra(EXCLUDE_SYSTEM, false));
initializeProfileAvatar(getIntent().getBooleanExtra(EXCLUDE_SYSTEM, false));
2017-08-08 23:37:15 +00:00
ApplicationContext.getInstance(this).injectDependencies(this);
}
@Override
public void onBackPressed() {
if (container.isInputOpen()) container.hideCurrentInput(name);
else super.onBackPressed();
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (container.getCurrentInput() == emojiDrawer) {
container.hideAttachedInput(true);
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case REQUEST_CODE_AVATAR:
if (resultCode == Activity.RESULT_OK) {
Uri outputFile = Uri.fromFile(new File(getCacheDir(), "cropped"));
Uri inputFile = data.getData();
if (inputFile == null && captureFile != null) {
inputFile = Uri.fromFile(captureFile);
}
if (data.getBooleanExtra("delete", false)) {
avatarBytes = null;
avatar.setImageDrawable(ContactPhotoFactory.getResourceContactPhoto(R.drawable.ic_camera_alt_white_24dp)
.asDrawable(this, getResources().getColor(R.color.grey_400)));
} else {
new Crop(inputFile).output(outputFile).asSquare().start(this);
}
2017-08-08 23:37:15 +00:00
}
break;
case Crop.REQUEST_CROP:
if (resultCode == Activity.RESULT_OK) {
try {
avatarBytes = BitmapUtil.createScaledBytes(this, Crop.getOutput(data), new ProfileMediaConstraints());
avatar.setImageDrawable(ContactPhotoFactory.getGroupContactPhoto(avatarBytes).asDrawable(this, 0));
} catch (BitmapDecodingException e) {
Log.w(TAG, e);
Toast.makeText(this, R.string.CreateProfileActivity_error_setting_profile_photo, Toast.LENGTH_LONG).show();
2017-08-08 23:37:15 +00:00
}
}
break;
}
}
private void initializeResources() {
this.avatar = ViewUtil.findById(this, R.id.avatar);
this.name = ViewUtil.findById(this, R.id.name);
this.emojiToggle = ViewUtil.findById(this, R.id.emoji_toggle);
this.emojiDrawer = ViewUtil.findById(this, R.id.emoji_drawer);
this.container = ViewUtil.findById(this, R.id.container);
this.finishButton = ViewUtil.findById(this, R.id.finish_button);
this.nextIntent = getIntent().getParcelableExtra(NEXT_INTENT);
2017-08-08 23:37:15 +00:00
this.avatar.setImageDrawable(ContactPhotoFactory.getResourceContactPhoto(R.drawable.ic_camera_alt_white_24dp)
.asDrawable(this, getResources().getColor(R.color.grey_400)));
this.avatar.setOnClickListener(view -> {
try {
captureFile = File.createTempFile("capture", "jpg", getExternalCacheDir());
} catch (IOException e) {
Log.w(TAG, e);
captureFile = null;
2017-08-08 23:37:15 +00:00
}
Intent chooserIntent = createAvatarSelectionIntent(captureFile, avatarBytes != null);
2017-08-08 23:37:15 +00:00
startActivityForResult(chooserIntent, REQUEST_CODE_AVATAR);
});
this.name.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {}
@Override
public void afterTextChanged(Editable s) {
if (s.toString().getBytes().length > ProfileCipher.NAME_PADDED_LENGTH) {
name.setError(getString(R.string.CreateProfileActivity_too_long));
finishButton.setEnabled(false);
} else if (name.getError() != null || !finishButton.isEnabled()) {
name.setError(null);
finishButton.setEnabled(true);
}
}
});
2017-08-08 23:37:15 +00:00
this.finishButton.setOnClickListener(view -> {
handleUpload();
});
}
private void initializeProfileName(boolean excludeSystem) {
if (!TextUtils.isEmpty(TextSecurePreferences.getProfileName(this))) {
String profileName = TextSecurePreferences.getProfileName(this);
name.setText(profileName);
name.setSelection(profileName.length(), profileName.length());
} else if (!excludeSystem) {
SystemProfileUtil.getSystemProfileName(this).addListener(new ListenableFuture.Listener<String>() {
@Override
public void onSuccess(String result) {
if (!TextUtils.isEmpty(result)) {
name.setText(result);
name.setSelection(result.length(), result.length());
}
2017-08-08 23:37:15 +00:00
}
@Override
public void onFailure(ExecutionException e) {
Log.w(TAG, e);
}
});
}
}
2017-08-08 23:37:15 +00:00
private void initializeProfileAvatar(boolean excludeSystem) {
Address ourAddress = Address.fromSerialized(TextSecurePreferences.getLocalNumber(this));
if (AvatarHelper.getAvatarFile(this, ourAddress).exists() && AvatarHelper.getAvatarFile(this, ourAddress).length() > 0) {
new AsyncTask<Void, Void, Pair<byte[], ContactPhoto>>() {
@Override
protected Pair<byte[], ContactPhoto> doInBackground(Void... params) {
try {
byte[] data =Util.readFully(AvatarHelper.getInputStreamFor(CreateProfileActivity.this, ourAddress));
return new Pair<>(data, ContactPhotoFactory.getSignalAvatarContactPhoto(CreateProfileActivity.this, ourAddress, null, getResources().getDimensionPixelSize(R.dimen.contact_photo_target_size)));
} catch (IOException e) {
Log.w(TAG, e);
return null;
}
2017-08-08 23:37:15 +00:00
}
@Override
protected void onPostExecute(Pair<byte[], ContactPhoto> result) {
if (result != null) {
avatarBytes = result.first;
avatar.setImageDrawable(result.second.asDrawable(CreateProfileActivity.this, 0));
}
}
}.execute();
} else if (!excludeSystem) {
SystemProfileUtil.getSystemProfileAvatar(this, new ProfileMediaConstraints()).addListener(new ListenableFuture.Listener<byte[]>() {
@Override
public void onSuccess(byte[] result) {
if (result != null) {
avatarBytes = result;
avatar.setImageDrawable(ContactPhotoFactory.getGroupContactPhoto(result).asDrawable(CreateProfileActivity.this, 0));
}
}
@Override
public void onFailure(ExecutionException e) {
Log.w(TAG, e);
}
});
}
2017-08-08 23:37:15 +00:00
}
private void initializeEmojiInput() {
this.emojiToggle.attach(emojiDrawer);
this.emojiToggle.setOnClickListener(v -> {
if (container.getCurrentInput() == emojiDrawer) {
container.showSoftkey(name);
} else {
container.show(name, emojiDrawer);
}
});
this.emojiDrawer.setEmojiEventListener(new EmojiDrawer.EmojiEventListener() {
@Override
public void onKeyEvent(KeyEvent keyEvent) {
name.dispatchKeyEvent(keyEvent);
}
@Override
public void onEmojiSelected(String emoji) {
final int start = name.getSelectionStart();
final int end = name.getSelectionEnd();
name.getText().replace(Math.min(start, end), Math.max(start, end), emoji);
name.setSelection(start + emoji.length());
}
});
this.container.addOnKeyboardShownListener(() -> emojiToggle.setToEmoji());
this.name.setOnClickListener(v -> container.showSoftkey(name));
}
private Intent createAvatarSelectionIntent(@Nullable File captureFile, boolean includeClear) {
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/*");
}
Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
cameraIntent.putExtra(EXTRA_OUTPUT, Uri.fromFile(captureFile));
if (captureFile != null && cameraIntent.resolveActivity(getPackageManager()) != null) {
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));
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, extraIntents.toArray(new Intent[0]));
return chooserIntent;
}
2017-08-08 23:37:15 +00:00
private void handleUpload() {
final String name;
final StreamDetails avatar;
if (TextUtils.isEmpty(this.name.getText().toString())) name = null;
else name = this.name.getText().toString();
if (avatarBytes == null || avatarBytes.length == 0) avatar = null;
else avatar = new StreamDetails(new ByteArrayInputStream(avatarBytes),
"image/jpeg", avatarBytes.length);
new ProgressDialogAsyncTask<Void, Void, Boolean>(this,
getString(R.string.CreateProfileActivity_updating_and_encrypting_profile),
getString(R.string.CreateProfileActivity_updating_profile))
{
2017-08-08 23:37:15 +00:00
@Override
protected Boolean doInBackground(Void... params) {
String encodedProfileKey = TextSecurePreferences.getProfileKey(CreateProfileActivity.this);
if (encodedProfileKey == null) {
encodedProfileKey = Util.getSecret(32);
TextSecurePreferences.setProfileKey(CreateProfileActivity.this, encodedProfileKey);
}
try {
accountManager.setProfileName(Base64.decode(encodedProfileKey), name);
TextSecurePreferences.setProfileName(getContext(), name);
2017-08-08 23:37:15 +00:00
} catch (IOException e) {
Log.w(TAG, e);
return false;
}
try {
accountManager.setProfileAvatar(Base64.decode(encodedProfileKey), avatar);
AvatarHelper.setAvatar(getContext(), Address.fromSerialized(TextSecurePreferences.getLocalNumber(getContext())), avatarBytes);
2017-08-08 23:37:15 +00:00
} catch (IOException e) {
Log.w(TAG, e);
return false;
}
return true;
}
@Override
public void onPostExecute(Boolean result) {
super.onPostExecute(result);
if (result) {
if (captureFile != null) captureFile.delete();
if (nextIntent != null) startActivity(nextIntent);
finish();
} else {
Toast.makeText(CreateProfileActivity.this, R.string.CreateProfileActivity_problem_setting_profile, Toast.LENGTH_LONG).show();
}
2017-08-08 23:37:15 +00:00
}
}.execute();
}
}