diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 75c0a5bdea..5ef6716825 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -384,6 +384,10 @@
+
+
diff --git a/build.gradle b/build.gradle
index 8057ac4374..12ce72dd8f 100644
--- a/build.gradle
+++ b/build.gradle
@@ -63,7 +63,7 @@ dependencies {
compile 'org.whispersystems:jobmanager:1.0.2'
compile 'org.whispersystems:libpastelog:1.0.7'
- compile 'org.whispersystems:signal-service-android:2.5.16'
+ compile 'org.whispersystems:signal-service-android:2.6.0'
compile 'org.whispersystems:webrtc-android:M59-S1'
compile "me.leolin:ShortcutBadger:1.1.16"
@@ -83,7 +83,6 @@ dependencies {
compile ("com.doomonafireball.betterpickers:library:1.5.3") {
exclude group: 'com.android.support', module: 'support-v4'
}
- compile 'com.madgag.spongycastle:prov:1.51.0.0'
provided 'com.squareup.dagger:dagger-compiler:1.2.2'
compile 'com.amulyakhare:com.amulyakhare.textdrawable:1.0.1'
compile 'com.h6ah4i.android.compat:mulsellistprefcompat:1.0.0'
@@ -139,7 +138,7 @@ dependencyVerification {
'com.google.android.exoplayer:exoplayer:955085aa611a8f7cf6c61b88ae03d1a392f4ad94c9bfbc153f3dedb9ffb14718',
'org.whispersystems:jobmanager:506f679fc2fcf7bb6d10f00f41d6f6ea0abf75c70dc95b913398661ad538a181',
'org.whispersystems:libpastelog:bb331d9a98240fc139101128ba836c1edec3c40e000597cdbb29ebf4cbf34d88',
- 'org.whispersystems:signal-service-android:ff0fecf08c0f96514637b551c8bd3848007c67d13ce812a746a1211e77f21d16',
+ 'org.whispersystems:signal-service-android:a25bfa4fd86f2d5f6cedb54d2c917af7b521dc4dda14ee35a3ac7267f1d29c7e',
'org.whispersystems:webrtc-android:de647643afbbea45a26a4f24db75aa10bc8de45426e8eb0d9d563cc10af4f582',
'me.leolin:ShortcutBadger:e3cb3e7625892129b0c92dd5e4bc649faffdd526d5af26d9c45ee31ff8851774',
'se.emilsjolander:stickylistheaders:a08ca948aa6b220f09d82f16bbbac395f6b78897e9eeac6a9f0b0ba755928eeb',
@@ -156,7 +155,6 @@ dependencyVerification {
'com.google.zxing:android-integration:89e56aadf1164bd71e57949163c53abf90af368b51669c0d4a47a163335f95c4',
'com.squareup.dagger:dagger:789aca24537022e49f91fc6444078d9de8f1dd99e1bfb090f18491b186967883',
'com.doomonafireball.betterpickers:library:132ecd685c95a99e7377c4e27bfadbb2d7ed0bea995944060cd62d4369fdaf3d',
- 'com.madgag.spongycastle:prov:b8c3fec3a59aac1aa04ccf4dad7179351e54ef7672f53f508151b614c131398a',
'com.amulyakhare:com.amulyakhare.textdrawable:54c92b5fba38cfd316a07e5a30528068f45ce8515a6890f1297df4c401af5dcb',
'com.h6ah4i.android.compat:mulsellistprefcompat:47167c5cb796de1a854788e9ff318358e36c8fb88123baaa6e38fb78511dfabe',
'com.google.zxing:core:b4d82452e7a6bf6ec2698904b332431717ed8f9a850224f295aec89de80f2259',
@@ -174,23 +172,24 @@ dependencyVerification {
'com.google.android.gms:play-services-base:0ca636a8fc9a5af45e607cdcd61783bf5d561cbbb0f862021ce69606eee5ad49',
'com.google.android.gms:play-services-basement:95dd882c5ffba15b9a99de3fefb05d3a01946623af67454ca00055d222f85a8d',
'com.google.android.gms:play-services-iid:54e919f9957b8b7820da7ee9b83471d00d0cac1cf08ddea8b5b41aea80bb1a70',
- 'org.whispersystems:signal-service-java:edc71f8244fb7792877ffc85914dbcc6ddf9181799a395fdac31a9e65b30aabb',
'org.whispersystems:signal-protocol-android:5b8acded7f2a40178eb90ab8e8cbfec89d170d91b3ff5e78487d1098df6185a1',
+ 'org.whispersystems:signal-service-java:6524523dc774943a31784fa088c8185ad32f240e7c5f63245ab1e61081f76a83',
'com.nineoldandroids:library:68025a14e3e7673d6ad2f95e4b46d78d7d068343aa99256b686fe59de1b3163a',
'javax.inject:javax.inject:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
- 'com.madgag.spongycastle:core:8d6240b974b0aca4d3da9c7dd44d42339d8a374358aca5fc98e50a995764511f',
'com.klinkerapps:logger:177e325259a8b111ad6745ec10db5861723c99f402222b80629f576f49408541',
'com.google.android.gms:play-services-tasks:69ec265168e601d0203d04cd42e34bb019b2f029aa1e16fabd38a5153eea2086',
- 'com.google.protobuf:protobuf-java:e0c1c64575c005601725e7c6a02cebf9e1285e888f756b2a1d73ffa8d725cc74',
- 'com.googlecode.libphonenumber:libphonenumber:141ebcafba7070a13d879c44e7648ddbe10beab665cb64d7b0c1bea93afb8dc2',
- 'com.fasterxml.jackson.core:jackson-databind:835097bcdd11f5bc8a08378c70d4c8054dfa4b911691cc2752063c75534d198d',
- 'com.squareup.okhttp3:okhttp:a992938d7203ca557cd7a116f002e8c427ec9cdae7ea852441abb8aec891f948',
'org.whispersystems:curve25519-android:82595394422b957d4a5b5f1b27b75ba25cf6dc4db4d312418ca38cd6fff279ca',
'org.whispersystems:signal-protocol-java:5152c2b01a25147967d6bf82e540f947901bdfa79260be3eb3e96b03f787d6b5',
+ 'com.google.protobuf:protobuf-java:e0c1c64575c005601725e7c6a02cebf9e1285e888f756b2a1d73ffa8d725cc74',
+ 'com.googlecode.libphonenumber:libphonenumber:be23ec6195df9f328364a3122ddd111e30f42d18a841dd06f84d2685c7fabb9f',
+ 'com.fasterxml.jackson.core:jackson-databind:835097bcdd11f5bc8a08378c70d4c8054dfa4b911691cc2752063c75534d198d',
+ 'com.squareup.okhttp3:okhttp:c1d57f913f74f61d424d4250a92723ba9a61affc12a0ab194d84cc179b472841',
+ 'com.madgag.spongycastle:prov:b8c3fec3a59aac1aa04ccf4dad7179351e54ef7672f53f508151b614c131398a',
+ 'org.whispersystems:curve25519-java:7dd659d8822c06c3aea1a47f18fac9e5761e29cab8100030b877db445005f03e',
'com.fasterxml.jackson.core:jackson-annotations:0ca408c24202a7626ec8b861e99d85eca5e38b73311dd6dd12e3e9deecc3fe94',
'com.fasterxml.jackson.core:jackson-core:cbf4604784b4de226262845447a1ad3bb38a6728cebe86562e2c5afada8be2c0',
- 'com.squareup.okio:okio:8c5436cadfab36bbd97db5f5c43b7bfdb5bf2f5f894ec8709b1929f14bdd010c',
- 'org.whispersystems:curve25519-java:7dd659d8822c06c3aea1a47f18fac9e5761e29cab8100030b877db445005f03e',
+ 'com.squareup.okio:okio:734269c3ebc5090e3b23566db558f421f0b4027277c79ad5d176b8ec168bb850',
+ 'com.madgag.spongycastle:core:8d6240b974b0aca4d3da9c7dd44d42339d8a374358aca5fc98e50a995764511f',
'com.android.support:support-annotations:47a2a30eab487a490a8a8f16678007c3d2b6dcae1e09b0485a12bbf921200ec3',
'com.android.support:support-media-compat:8d6a1a5ba3d9eb1a25cb8f21bb312ac6280202e3d2900cb0b447d065d0d8a125',
'com.android.support:support-core-utils:a7649e18c04143dde40c218c5ce9a030e7ae674089cd7b18c6cf8ed2a22cf01a',
@@ -220,6 +219,7 @@ android {
buildConfigField "long", "BUILD_TIMESTAMP", getLastCommitTimestamp() + "L"
buildConfigField "String", "SIGNAL_URL", "\"https://textsecure-service.whispersystems.org\""
+ buildConfigField "String", "SIGNAL_CDN_URL", "\"https://cdn.signal.org\""
buildConfigField "String", "GIPHY_PROXY_HOST", "\"giphy-proxy-production.whispersystems.org\""
buildConfigField "int", "GIPHY_PROXY_PORT", "80"
buildConfigField "String", "USER_AGENT", "\"OWA\""
diff --git a/res/drawable-hdpi/ic_camera_alt_white_24dp.png b/res/drawable-hdpi/ic_camera_alt_white_24dp.png
new file mode 100644
index 0000000000..497c88ca82
Binary files /dev/null and b/res/drawable-hdpi/ic_camera_alt_white_24dp.png differ
diff --git a/res/drawable-mdpi/ic_camera_alt_white_24dp.png b/res/drawable-mdpi/ic_camera_alt_white_24dp.png
new file mode 100644
index 0000000000..e830522008
Binary files /dev/null and b/res/drawable-mdpi/ic_camera_alt_white_24dp.png differ
diff --git a/res/drawable-xhdpi/ic_camera_alt_white_24dp.png b/res/drawable-xhdpi/ic_camera_alt_white_24dp.png
new file mode 100644
index 0000000000..be9fb226a5
Binary files /dev/null and b/res/drawable-xhdpi/ic_camera_alt_white_24dp.png differ
diff --git a/res/drawable-xxhdpi/ic_camera_alt_white_24dp.png b/res/drawable-xxhdpi/ic_camera_alt_white_24dp.png
new file mode 100644
index 0000000000..c8e69dcebb
Binary files /dev/null and b/res/drawable-xxhdpi/ic_camera_alt_white_24dp.png differ
diff --git a/res/drawable-xxxhdpi/ic_camera_alt_white_24dp.png b/res/drawable-xxxhdpi/ic_camera_alt_white_24dp.png
new file mode 100644
index 0000000000..777658e955
Binary files /dev/null and b/res/drawable-xxxhdpi/ic_camera_alt_white_24dp.png differ
diff --git a/res/layout/profile_create_activity.xml b/res/layout/profile_create_activity.xml
new file mode 100644
index 0000000000..37fe9e3723
--- /dev/null
+++ b/res/layout/profile_create_activity.xml
@@ -0,0 +1,93 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/org/thoughtcrime/securesms/ConversationListActivity.java b/src/org/thoughtcrime/securesms/ConversationListActivity.java
index 377405983b..cf5d65f04a 100644
--- a/src/org/thoughtcrime/securesms/ConversationListActivity.java
+++ b/src/org/thoughtcrime/securesms/ConversationListActivity.java
@@ -72,6 +72,9 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit
initializeContactUpdatesReceiver();
+ Intent intent = new Intent(this, CreateProfileActivity.class);
+ startActivity(intent);
+
RatingManager.showRatingDialogIfNecessary(this);
}
diff --git a/src/org/thoughtcrime/securesms/CreateProfileActivity.java b/src/org/thoughtcrime/securesms/CreateProfileActivity.java
new file mode 100644
index 0000000000..fa3ad9e3c3
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/CreateProfileActivity.java
@@ -0,0 +1,267 @@
+package org.thoughtcrime.securesms;
+
+
+import android.app.Activity;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.MediaStore;
+import android.support.annotation.NonNull;
+import android.text.TextUtils;
+import android.util.Log;
+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.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.ContactPhotoFactory;
+import org.thoughtcrime.securesms.crypto.MasterSecret;
+import org.thoughtcrime.securesms.dependencies.InjectableType;
+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.util.StreamDetails;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+
+import javax.inject.Inject;
+
+public class CreateProfileActivity extends PassphraseRequiredActionBarActivity implements InjectableType {
+
+ private static final String TAG = CreateProfileActivity.class.getSimpleName();
+
+ 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 byte[] avatarBytes;
+
+ @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("Your profile info");
+
+ initializeResources();
+ initializeEmojiInput();
+ initializeDeviceOwner();
+
+ 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"));
+ new Crop(data.getData()).output(outputFile).asSquare().start(this);
+ }
+
+ 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, "Error setting profile photo", Toast.LENGTH_LONG).show();
+ }
+ }
+ 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.avatar.setImageDrawable(ContactPhotoFactory.getResourceContactPhoto(R.drawable.ic_camera_alt_white_24dp)
+ .asDrawable(this, getResources().getColor(R.color.grey_400)));
+
+ this.avatar.setOnClickListener(view -> {
+ 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);
+ Intent chooserIntent = Intent.createChooser(galleryIntent, "Profile photo");
+ chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[] { cameraIntent });
+
+ startActivityForResult(chooserIntent, REQUEST_CODE_AVATAR);
+ });
+
+ this.finishButton.setOnClickListener(view -> {
+ handleUpload();
+ });
+ }
+
+ private void initializeDeviceOwner() {
+ SystemProfileUtil.getSystemProfileName(this).addListener(new ListenableFuture.Listener() {
+ @Override
+ public void onSuccess(String result) {
+ if (!TextUtils.isEmpty(result)) {
+ name.setText(result);
+ name.setSelection(result.length(), result.length());
+ }
+ }
+
+ @Override
+ public void onFailure(ExecutionException e) {
+ Log.w(TAG, e);
+ }
+ });
+
+ SystemProfileUtil.getSystemProfileAvatar(this, new ProfileMediaConstraints()).addListener(new ListenableFuture.Listener() {
+ @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);
+ }
+ });
+ }
+
+ 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 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(this, "Updating and encrypting profile", "Updating profile") {
+ @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);
+ } catch (IOException e) {
+ Log.w(TAG, e);
+ return false;
+ }
+
+ try {
+ accountManager.setProfileAvatar(Base64.decode(encodedProfileKey), avatar);;
+ } catch (IOException e) {
+ Log.w(TAG, e);
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public void onPostExecute(Boolean result) {
+ super.onPostExecute(result);
+
+ if (result) finish();
+ else Toast.makeText(CreateProfileActivity.this, "Problem setting profile", Toast.LENGTH_LONG).show();;
+ }
+ }.execute();
+ }
+
+
+}
diff --git a/src/org/thoughtcrime/securesms/dependencies/SignalCommunicationModule.java b/src/org/thoughtcrime/securesms/dependencies/SignalCommunicationModule.java
index 97f4ec7358..e812c801ed 100644
--- a/src/org/thoughtcrime/securesms/dependencies/SignalCommunicationModule.java
+++ b/src/org/thoughtcrime/securesms/dependencies/SignalCommunicationModule.java
@@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.dependencies;
import android.content.Context;
import org.thoughtcrime.securesms.BuildConfig;
+import org.thoughtcrime.securesms.CreateProfileActivity;
import org.thoughtcrime.securesms.DeviceListFragment;
import org.thoughtcrime.securesms.crypto.storage.SignalProtocolStoreImpl;
import org.thoughtcrime.securesms.jobs.AttachmentDownloadJob;
@@ -63,7 +64,8 @@ import dagger.Provides;
RotateSignedPreKeyJob.class,
WebRtcCallService.class,
RetrieveProfileJob.class,
- MultiDeviceVerifiedUpdateJob.class})
+ MultiDeviceVerifiedUpdateJob.class,
+ CreateProfileActivity.class})
public class SignalCommunicationModule {
private final Context context;
diff --git a/src/org/thoughtcrime/securesms/jobs/RetrieveProfileJob.java b/src/org/thoughtcrime/securesms/jobs/RetrieveProfileJob.java
index 9212656a33..dc4c24ae74 100644
--- a/src/org/thoughtcrime/securesms/jobs/RetrieveProfileJob.java
+++ b/src/org/thoughtcrime/securesms/jobs/RetrieveProfileJob.java
@@ -17,8 +17,8 @@ import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.libsignal.InvalidKeyException;
import org.whispersystems.signalservice.api.SignalServiceMessagePipe;
import org.whispersystems.signalservice.api.SignalServiceMessageReceiver;
+import org.whispersystems.signalservice.api.profiles.SignalServiceProfile;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
-import org.whispersystems.signalservice.api.push.SignalServiceProfile;
import org.whispersystems.signalservice.api.util.InvalidNumberException;
import java.io.IOException;
diff --git a/src/org/thoughtcrime/securesms/profiles/ProfileMediaConstraints.java b/src/org/thoughtcrime/securesms/profiles/ProfileMediaConstraints.java
new file mode 100644
index 0000000000..c456d80eeb
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/profiles/ProfileMediaConstraints.java
@@ -0,0 +1,43 @@
+package org.thoughtcrime.securesms.profiles;
+
+
+import android.content.Context;
+
+import org.thoughtcrime.securesms.mms.MediaConstraints;
+
+public class ProfileMediaConstraints extends MediaConstraints {
+ @Override
+ public int getImageMaxWidth(Context context) {
+ return 640;
+ }
+
+ @Override
+ public int getImageMaxHeight(Context context) {
+ return 640;
+ }
+
+ @Override
+ public int getImageMaxSize(Context context) {
+ return 5 * 1024 * 1024;
+ }
+
+ @Override
+ public int getGifMaxSize(Context context) {
+ return 0;
+ }
+
+ @Override
+ public int getVideoMaxSize(Context context) {
+ return 0;
+ }
+
+ @Override
+ public int getAudioMaxSize(Context context) {
+ return 0;
+ }
+
+ @Override
+ public int getDocumentMaxSize(Context context) {
+ return 0;
+ }
+}
diff --git a/src/org/thoughtcrime/securesms/profiles/SystemProfileUtil.java b/src/org/thoughtcrime/securesms/profiles/SystemProfileUtil.java
new file mode 100644
index 0000000000..b68a3304c0
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/profiles/SystemProfileUtil.java
@@ -0,0 +1,110 @@
+package org.thoughtcrime.securesms.profiles;
+
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Build;
+import android.provider.ContactsContract;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.bumptech.glide.load.data.StreamLocalUriFetcher;
+
+import org.thoughtcrime.securesms.mms.MediaConstraints;
+import org.thoughtcrime.securesms.util.BitmapDecodingException;
+import org.thoughtcrime.securesms.util.BitmapUtil;
+import org.thoughtcrime.securesms.util.concurrent.ListenableFuture;
+import org.thoughtcrime.securesms.util.concurrent.SettableFuture;
+
+public class SystemProfileUtil {
+
+ private static final String TAG = SystemProfileUtil.class.getSimpleName();
+
+ public static ListenableFuture getSystemProfileAvatar(final @NonNull Context context, MediaConstraints mediaConstraints) {
+ SettableFuture future = new SettableFuture<>();
+
+ new AsyncTask() {
+ @Override
+ protected @Nullable byte[] doInBackground(Void... params) {
+ if (Build.VERSION.SDK_INT >= 14) {
+ try (Cursor cursor = context.getContentResolver().query(ContactsContract.Profile.CONTENT_URI, null, null, null, null)) {
+ while (cursor != null && cursor.moveToNext()) {
+ String photoUri = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.Profile.PHOTO_URI));
+
+ if (!TextUtils.isEmpty(photoUri)) {
+ try {
+ return BitmapUtil.createScaledBytes(context, Uri.parse(photoUri), mediaConstraints);
+ } catch (BitmapDecodingException e) {
+ Log.w(TAG, e);
+ }
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(@Nullable byte[] result) {
+ future.set(result);
+ }
+
+ }.execute();
+
+ return future;
+ }
+
+ public static ListenableFuture getSystemProfileName(final @NonNull Context context) {
+ SettableFuture future = new SettableFuture<>();
+
+ new AsyncTask() {
+ @Override
+ protected String doInBackground(Void... params) {
+ String name = null;
+
+ if (Build.VERSION.SDK_INT >= 14) {
+ try (Cursor cursor = context.getContentResolver().query(ContactsContract.Profile.CONTENT_URI, null, null, null, null)) {
+ if (cursor != null && cursor.moveToNext()) {
+ name = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.Profile.DISPLAY_NAME));
+ }
+ }
+ }
+
+ if (name == null) {
+ AccountManager accountManager = AccountManager.get(context);
+ Account[] accounts = accountManager.getAccountsByType("com.google");
+
+ for (Account account : accounts) {
+ if (!TextUtils.isEmpty(account.name)) {
+ if (account.name.contains("@")) {
+ name = account.name.substring(0, account.name.indexOf("@")).replace('.', ' ');
+ } else {
+ name = account.name.replace('.', ' ');
+ }
+
+ break;
+ }
+ }
+ }
+
+ return name;
+ }
+
+ @Override
+ protected void onPostExecute(@Nullable String result) {
+ future.set(result);
+ }
+ }.execute();
+
+ return future;
+ }
+
+
+}
diff --git a/src/org/thoughtcrime/securesms/push/AccountManagerFactory.java b/src/org/thoughtcrime/securesms/push/AccountManagerFactory.java
index 544e12c3d9..544c6015a6 100644
--- a/src/org/thoughtcrime/securesms/push/AccountManagerFactory.java
+++ b/src/org/thoughtcrime/securesms/push/AccountManagerFactory.java
@@ -1,18 +1,14 @@
package org.thoughtcrime.securesms.push;
import android.content.Context;
-import android.content.Intent;
import android.os.AsyncTask;
import android.util.Log;
-import com.google.android.gms.common.GooglePlayServicesNotAvailableException;
-import com.google.android.gms.common.GooglePlayServicesRepairableException;
import com.google.android.gms.security.ProviderInstaller;
import org.thoughtcrime.securesms.BuildConfig;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
-import org.whispersystems.signalservice.internal.push.SignalServiceUrl;
public class AccountManagerFactory {
diff --git a/src/org/thoughtcrime/securesms/push/SignalServiceNetworkAccess.java b/src/org/thoughtcrime/securesms/push/SignalServiceNetworkAccess.java
index a6d8901bb4..ad7a72cbc2 100644
--- a/src/org/thoughtcrime/securesms/push/SignalServiceNetworkAccess.java
+++ b/src/org/thoughtcrime/securesms/push/SignalServiceNetworkAccess.java
@@ -2,13 +2,14 @@ package org.thoughtcrime.securesms.push;
import android.content.Context;
-import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import org.thoughtcrime.securesms.BuildConfig;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.signalservice.api.push.TrustStore;
-import org.whispersystems.signalservice.internal.push.SignalServiceUrl;
+import org.whispersystems.signalservice.internal.configuration.SignalCdnUrl;
+import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration;
+import org.whispersystems.signalservice.internal.configuration.SignalServiceUrl;
import java.util.HashMap;
import java.util.Map;
@@ -93,9 +94,9 @@ public class SignalServiceNetworkAccess {
.build();
- private final Map censorshipConfiguration;
- private final String[] censoredCountries;
- private final SignalServiceUrl[] uncensoredConfiguration;
+ private final Map censorshipConfiguration;
+ private final String[] censoredCountries;
+ private final SignalServiceConfiguration uncensoredConfiguration;
public SignalServiceNetworkAccess(Context context) {
final TrustStore googleTrustStore = new GoogleFrontingTrustStore(context);
@@ -105,41 +106,43 @@ public class SignalServiceNetworkAccess {
final SignalServiceUrl mapsTwoAndroid = new SignalServiceUrl("https://clients4.google.com", APPSPOT_REFLECTOR_HOST, googleTrustStore, GMAPS_CONNECTION_SPEC);
final SignalServiceUrl mailAndroid = new SignalServiceUrl("https://mail.google.com", APPSPOT_REFLECTOR_HOST, googleTrustStore, GMAIL_CONNECTION_SPEC);
- this.censorshipConfiguration = new HashMap() {{
- put("+20", new SignalServiceUrl[] {new SignalServiceUrl("https://www.google.com.eg",
- APPSPOT_REFLECTOR_HOST,
- googleTrustStore, GMAIL_CONNECTION_SPEC),
- baseAndroid, mapsOneAndroid, mapsTwoAndroid, mailAndroid});
+ this.censorshipConfiguration = new HashMap() {{
+ put("+20", new SignalServiceConfiguration(new SignalServiceUrl[] {new SignalServiceUrl("https://www.google.com.eg",
+ APPSPOT_REFLECTOR_HOST,
+ googleTrustStore, GMAIL_CONNECTION_SPEC),
+ baseAndroid, mapsOneAndroid, mapsTwoAndroid, mailAndroid},
+ new SignalCdnUrl[] {})); // XXX
- put("+971", new SignalServiceUrl[] {new SignalServiceUrl("https://www.google.ae",
- APPSPOT_REFLECTOR_HOST,
- googleTrustStore, GMAIL_CONNECTION_SPEC),
- baseAndroid, baseGoogle, mapsOneAndroid, mapsTwoAndroid, mailAndroid});
+ put("+971", new SignalServiceConfiguration(new SignalServiceUrl[] {new SignalServiceUrl("https://www.google.ae",
+ APPSPOT_REFLECTOR_HOST,
+ googleTrustStore, GMAIL_CONNECTION_SPEC),
+ baseAndroid, baseGoogle, mapsOneAndroid, mapsTwoAndroid, mailAndroid},
+ new SignalCdnUrl[] {})); // XXX
// put("+53", new SignalServiceUrl[] {new SignalServiceUrl("https://www.google.com.cu",
// APPSPOT_REFLECTOR_HOST,
// googleTrustStore, GMAIL_CONNECTION_SPEC),
// baseAndroid, baseGoogle, mapsOneAndroid, mapsTwoAndroid, mailAndroid});
- put("+968", new SignalServiceUrl[] {new SignalServiceUrl("https://www.google.com.om",
- APPSPOT_REFLECTOR_HOST,
- googleTrustStore, GMAIL_CONNECTION_SPEC),
- baseAndroid, baseGoogle, mapsOneAndroid, mapsTwoAndroid, mailAndroid});
+ put("+968", new SignalServiceConfiguration(new SignalServiceUrl[] {new SignalServiceUrl("https://www.google.com.om",
+ APPSPOT_REFLECTOR_HOST,
+ googleTrustStore, GMAIL_CONNECTION_SPEC),
+ baseAndroid, baseGoogle, mapsOneAndroid, mapsTwoAndroid, mailAndroid},
+ new SignalCdnUrl[] {})); // XXX
}};
- this.uncensoredConfiguration = new SignalServiceUrl[] {
- new SignalServiceUrl(BuildConfig.SIGNAL_URL, new SignalServiceTrustStore(context))
- };
+ this.uncensoredConfiguration = new SignalServiceConfiguration(new SignalServiceUrl[] {new SignalServiceUrl(BuildConfig.SIGNAL_URL, new SignalServiceTrustStore(context))},
+ new SignalCdnUrl[] {new SignalCdnUrl(BuildConfig.SIGNAL_CDN_URL, new SignalServiceTrustStore(context))});
this.censoredCountries = this.censorshipConfiguration.keySet().toArray(new String[0]);
}
- public SignalServiceUrl[] getConfiguration(Context context) {
+ public SignalServiceConfiguration getConfiguration(Context context) {
String localNumber = TextSecurePreferences.getLocalNumber(context);
return getConfiguration(localNumber);
}
- public SignalServiceUrl[] getConfiguration(@Nullable String localNumber) {
+ public SignalServiceConfiguration getConfiguration(@Nullable String localNumber) {
if (localNumber == null) return this.uncensoredConfiguration;
for (String censoredRegion : this.censoredCountries) {
diff --git a/src/org/thoughtcrime/securesms/util/IntentUtils.java b/src/org/thoughtcrime/securesms/util/IntentUtils.java
new file mode 100644
index 0000000000..31295d2406
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/util/IntentUtils.java
@@ -0,0 +1,18 @@
+package org.thoughtcrime.securesms.util;
+
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+import android.support.annotation.NonNull;
+
+import java.util.List;
+
+public class IntentUtils {
+
+ public static boolean isResolvable(@NonNull Context context, @NonNull Intent intent) {
+ List resolveInfoList = context.getPackageManager().queryIntentActivities(intent, 0);
+ return resolveInfoList != null && resolveInfoList.size() > 1;
+ }
+
+}
diff --git a/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java b/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java
index 7dcc4e8b46..50c7c095d6 100644
--- a/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java
+++ b/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java
@@ -8,6 +8,7 @@ import android.preference.PreferenceManager;
import android.provider.Settings;
import android.support.annotation.ArrayRes;
import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
@@ -109,6 +110,15 @@ public class TextSecurePreferences {
private static final String MULTI_DEVICE_PROVISIONED_PREF = "pref_multi_device";
public static final String DIRECT_CAPTURE_CAMERA_ID = "pref_direct_capture_camera_id";
private static final String ALWAYS_RELAY_CALLS_PREF = "pref_turn_only";
+ private static final String PROFILE_KEY_PREF = "pref_profile_key";
+
+ public static @Nullable String getProfileKey(Context context) {
+ return getStringPreference(context, PROFILE_KEY_PREF, null);
+ }
+
+ public static void setProfileKey(Context context, String key) {
+ setStringPreference(context, PROFILE_KEY_PREF, key);
+ }
public static int getNotificationPriority(Context context) {
return Integer.valueOf(getStringPreference(context, NOTIFICATION_PRIORITY_PREF, String.valueOf(NotificationCompat.PRIORITY_HIGH)));