diff --git a/build.gradle b/build.gradle index d003f75977..ea4d2068e4 100644 --- a/build.gradle +++ b/build.gradle @@ -197,6 +197,7 @@ dependencies { } implementation "com.jakewharton.rxbinding3:rxbinding:3.1.0" implementation "com.github.tbruyelle:rxpermissions:0.10.2" + implementation "com.github.ybq:Android-SpinKit:1.4.0" } def canonicalVersionCode = 23 diff --git a/res/layout/activity_home.xml b/res/layout/activity_home.xml index 7bb054d771..498738cf9d 100644 --- a/res/layout/activity_home.xml +++ b/res/layout/activity_home.xml @@ -24,7 +24,7 @@ android:id="@+id/profileButton" android:layout_width="@dimen/small_profile_picture_size" android:layout_height="@dimen/small_profile_picture_size" - android:layout_marginLeft="8dp" + android:layout_marginLeft="10dp" android:layout_alignParentLeft="true" android:layout_centerVertical="true" /> diff --git a/res/layout/activity_settings.xml b/res/layout/activity_settings.xml index fb333e9969..d2b6fd429f 100644 --- a/res/layout/activity_settings.xml +++ b/res/layout/activity_settings.xml @@ -1,258 +1,283 @@ <?xml version="1.0" encoding="utf-8"?> -<ScrollView +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="wrap_content" xmlns:app="http://schemas.android.com/apk/res-auto" - android:scrollbars="none"> + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@drawable/default_session_background"> - <LinearLayout + <ScrollView android:layout_width="match_parent" android:layout_height="wrap_content" - android:background="@drawable/default_session_background" - android:orientation="vertical" - android:gravity="center_horizontal"> + android:scrollbars="none"> - <android.support.v7.widget.Toolbar - android:id="@+id/toolbar" + <LinearLayout android:layout_width="match_parent" - android:layout_height="?attr/actionBarSize" - android:minHeight="?attr/actionBarSize" - app:contentInsetLeft="20dp" - app:contentInsetRight="20dp" - android:theme="?attr/actionBarStyle"> + android:layout_height="wrap_content" + android:orientation="vertical" + android:gravity="center_horizontal"> - <LinearLayout + <android.support.v7.widget.Toolbar + android:id="@+id/toolbar" android:layout_width="match_parent" - android:layout_height="match_parent" - android:gravity="center_vertical" - android:orientation="horizontal"> + android:layout_height="?attr/actionBarSize" + android:minHeight="?attr/actionBarSize" + app:contentInsetLeft="20dp" + app:contentInsetRight="20dp" + android:theme="?attr/actionBarStyle"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:gravity="center_vertical" + android:orientation="horizontal"> + + <ImageView + android:id="@+id/cancelButton" + android:layout_width="24dp" + android:layout_height="24dp" + android:src="@drawable/ic_close_white_24dp" + android:visibility="gone" /> + + <TextView + android:id="@+id/titleTextView" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Settings" + android:textColor="@color/text" + android:textSize="@dimen/very_large_font_size" + android:fontFamily="sans-serif-medium" /> + + <View + android:layout_width="0dp" + android:layout_height="match_parent" + android:layout_weight="1" /> + + <ImageView + android:id="@+id/saveButton" + android:layout_width="24dp" + android:layout_height="24dp" + android:src="@drawable/ic_check_white_24dp" + android:visibility="gone" /> + + <ImageView + android:id="@+id/showQRCodeButton" + android:layout_width="24dp" + android:layout_height="24dp" + android:src="@drawable/ic_qr_code" /> + + </LinearLayout> + + </android.support.v7.widget.Toolbar> + + <org.thoughtcrime.securesms.loki.redesign.views.ProfilePictureView + android:id="@+id/profilePictureView" + android:layout_width="@dimen/large_profile_picture_size" + android:layout_height="@dimen/large_profile_picture_size" + android:layout_marginTop="@dimen/medium_spacing" /> + + <RelativeLayout + android:id="@+id/displayNameContainer" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="@dimen/large_spacing" + android:layout_marginTop="@dimen/small_spacing" + android:layout_marginRight="@dimen/large_spacing"> - <ImageView - android:id="@+id/cancelButton" - android:layout_width="24dp" - android:layout_height="24dp" - android:src="@drawable/ic_close_white_24dp" - android:visibility="gone" /> + <EditText + style="@style/SessionEditText" + android:id="@+id/displayNameEditText" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerInParent="true" + android:textAlignment="center" + android:paddingTop="12dp" + android:paddingBottom="12dp" + android:visibility="invisible" + android:hint="Enter a display name" /> <TextView - android:id="@+id/titleTextView" + android:id="@+id/displayNameTextView" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:text="Settings" + android:layout_centerInParent="true" android:textColor="@color/text" android:textSize="@dimen/very_large_font_size" - android:fontFamily="sans-serif-medium" /> + android:textStyle="bold" /> - <View - android:layout_width="0dp" - android:layout_height="match_parent" - android:layout_weight="1" /> + </RelativeLayout> - <ImageView - android:id="@+id/saveButton" - android:layout_width="24dp" - android:layout_height="24dp" - android:src="@drawable/ic_check_white_24dp" - android:visibility="gone" /> + <org.thoughtcrime.securesms.loki.redesign.views.SeparatorView + android:id="@+id/separatorView" + android:layout_width="match_parent" + android:layout_height="32dp" + android:layout_marginLeft="@dimen/large_spacing" + android:layout_marginTop="20dp" + android:layout_marginRight="@dimen/large_spacing"/> - <ImageView - android:id="@+id/showQRCodeButton" - android:layout_width="24dp" - android:layout_height="24dp" - android:src="@drawable/ic_qr_code" /> + <TextView + android:id="@+id/publicKeyTextView" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="@dimen/large_spacing" + android:layout_marginTop="@dimen/large_spacing" + android:layout_marginRight="@dimen/large_spacing" + android:textSize="@dimen/large_font_size" + android:textColor="@color/text" + android:fontFamily="@font/space_mono_regular" + android:textAlignment="center" + android:text="05987d601943c267879be41830888066c6a024cbdc9a548d06813924bf3372ea78" /> - </LinearLayout> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="@dimen/large_spacing" + android:layout_marginTop="@dimen/large_spacing" + android:layout_marginRight="@dimen/large_spacing" + android:orientation="horizontal"> - </android.support.v7.widget.Toolbar> + <Button + style="@style/MediumProminentOutlineButton" + android:id="@+id/copyButton" + android:layout_width="0dp" + android:layout_height="@dimen/medium_button_height" + android:layout_weight="1" + android:text="Copy" /> - <org.thoughtcrime.securesms.loki.redesign.views.ProfilePictureView - android:id="@+id/profilePictureView" - android:layout_width="@dimen/large_profile_picture_size" - android:layout_height="@dimen/large_profile_picture_size" - android:layout_marginTop="@dimen/medium_spacing" /> + <Button + style="@style/MediumUnimportantOutlineButton" + android:id="@+id/shareButton" + android:layout_width="0dp" + android:layout_height="@dimen/medium_button_height" + android:layout_weight="1" + android:layout_marginLeft="@dimen/medium_spacing" + android:text="Share" /> - <RelativeLayout - android:id="@+id/displayNameContainer" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginLeft="@dimen/large_spacing" - android:layout_marginTop="@dimen/small_spacing" - android:layout_marginRight="@dimen/large_spacing"> - - <EditText - style="@style/SessionEditText" - android:id="@+id/displayNameEditText" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_centerInParent="true" - android:paddingTop="12dp" - android:paddingBottom="12dp" - android:visibility="invisible" - android:hint="Enter a display name" /> + </LinearLayout> + + <View + android:layout_width="match_parent" + android:layout_height="1px" + android:layout_marginTop="@dimen/large_spacing" + android:background="@color/separator" /> <TextView - android:id="@+id/displayNameTextView" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_centerInParent="true" + android:id="@+id/privacyButton" + android:layout_width="match_parent" + android:layout_height="@dimen/setting_button_height" + android:background="@drawable/setting_button_background" android:textColor="@color/text" - android:textSize="@dimen/very_large_font_size" - android:textStyle="bold" /> + android:textSize="@dimen/medium_font_size" + android:textStyle="bold" + android:gravity="center" + android:text="Privacy" /> - </RelativeLayout> + <View + android:layout_width="match_parent" + android:layout_height="1px" + android:background="@color/separator" /> - <org.thoughtcrime.securesms.loki.redesign.views.SeparatorView - android:id="@+id/separatorView" - android:layout_width="match_parent" - android:layout_height="32dp" - android:layout_marginLeft="@dimen/large_spacing" - android:layout_marginTop="20dp" - android:layout_marginRight="@dimen/large_spacing"/> + <TextView + android:id="@+id/notificationsButton" + android:layout_width="match_parent" + android:layout_height="@dimen/setting_button_height" + android:background="@drawable/setting_button_background" + android:textColor="@color/text" + android:textSize="@dimen/medium_font_size" + android:textStyle="bold" + android:gravity="center" + android:text="Notifications" /> - <TextView - android:id="@+id/publicKeyTextView" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginLeft="@dimen/large_spacing" - android:layout_marginTop="@dimen/large_spacing" - android:layout_marginRight="@dimen/large_spacing" - android:textSize="@dimen/large_font_size" - android:textColor="@color/text" - android:fontFamily="@font/space_mono_regular" - android:textAlignment="center" - android:text="05987d601943c267879be41830888066c6a024cbdc9a548d06813924bf3372ea78" /> + <View + android:layout_width="match_parent" + android:layout_height="1px" + android:background="@color/separator" /> - <LinearLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginLeft="@dimen/large_spacing" - android:layout_marginTop="@dimen/large_spacing" - android:layout_marginRight="@dimen/large_spacing" - android:orientation="horizontal"> - - <Button - style="@style/MediumProminentOutlineButton" - android:id="@+id/copyButton" - android:layout_width="0dp" - android:layout_height="@dimen/medium_button_height" - android:layout_weight="1" - android:text="Copy" /> - - <Button - style="@style/MediumUnimportantOutlineButton" - android:id="@+id/shareButton" - android:layout_width="0dp" - android:layout_height="@dimen/medium_button_height" - android:layout_weight="1" - android:layout_marginLeft="@dimen/medium_spacing" - android:text="Share" /> + <TextView + android:id="@+id/chatsButton" + android:layout_width="match_parent" + android:layout_height="@dimen/setting_button_height" + android:background="@drawable/setting_button_background" + android:textColor="@color/text" + android:textSize="@dimen/medium_font_size" + android:textStyle="bold" + android:gravity="center" + android:text="Chats" /> - </LinearLayout> + <View + android:layout_width="match_parent" + android:layout_height="1px" + android:background="@color/separator" /> - <View - android:layout_width="match_parent" - android:layout_height="1px" - android:layout_marginTop="@dimen/large_spacing" - android:background="@color/separator" /> + <TextView + android:id="@+id/linkedDevicesButton" + android:layout_width="match_parent" + android:layout_height="@dimen/setting_button_height" + android:background="@drawable/setting_button_background" + android:textColor="@color/text" + android:textSize="@dimen/medium_font_size" + android:textStyle="bold" + android:gravity="center" + android:text="Linked Devices" /> - <TextView - android:id="@+id/privacyButton" - android:layout_width="match_parent" - android:layout_height="@dimen/setting_button_height" - android:background="@drawable/setting_button_background" - android:textColor="@color/text" - android:textSize="@dimen/medium_font_size" - android:textStyle="bold" - android:gravity="center" - android:text="Privacy" /> - - <View - android:layout_width="match_parent" - android:layout_height="1px" - android:background="@color/separator" /> + <View + android:layout_width="match_parent" + android:layout_height="1px" + android:background="@color/separator" /> - <TextView - android:id="@+id/notificationsButton" - android:layout_width="match_parent" - android:layout_height="@dimen/setting_button_height" - android:background="@drawable/setting_button_background" - android:textColor="@color/text" - android:textSize="@dimen/medium_font_size" - android:textStyle="bold" - android:gravity="center" - android:text="Notifications" /> - - <View - android:layout_width="match_parent" - android:layout_height="1px" - android:background="@color/separator" /> + <TextView + android:id="@+id/seedButton" + android:layout_width="match_parent" + android:layout_height="@dimen/setting_button_height" + android:background="@drawable/setting_button_background" + android:textColor="@color/text" + android:textSize="@dimen/medium_font_size" + android:textStyle="bold" + android:gravity="center" + android:text="Show Seed" /> - <TextView - android:id="@+id/chatsButton" - android:layout_width="match_parent" - android:layout_height="@dimen/setting_button_height" - android:background="@drawable/setting_button_background" - android:textColor="@color/text" - android:textSize="@dimen/medium_font_size" - android:textStyle="bold" - android:gravity="center" - android:text="Chats" /> - - <View - android:layout_width="match_parent" - android:layout_height="1px" - android:background="@color/separator" /> + <View + android:layout_width="match_parent" + android:layout_height="1px" + android:background="@color/separator" /> - <TextView - android:id="@+id/linkedDevicesButton" - android:layout_width="match_parent" - android:layout_height="@dimen/setting_button_height" - android:background="@drawable/setting_button_background" - android:textColor="@color/text" - android:textSize="@dimen/medium_font_size" - android:textStyle="bold" - android:gravity="center" - android:text="Linked Devices" /> - - <View - android:layout_width="match_parent" - android:layout_height="1px" - android:background="@color/separator" /> + <TextView + android:id="@+id/clearAllDataButton" + android:layout_width="match_parent" + android:layout_height="@dimen/setting_button_height" + android:background="@drawable/setting_button_background" + android:textColor="@color/destructive" + android:textSize="@dimen/medium_font_size" + android:textStyle="bold" + android:gravity="center" + android:text="Clear All Data" /> + + <View + android:layout_width="match_parent" + android:layout_height="1px" + android:layout_marginBottom="@dimen/large_spacing" + android:background="@color/separator" /> - <TextView - android:id="@+id/seedButton" - android:layout_width="match_parent" - android:layout_height="@dimen/setting_button_height" - android:background="@drawable/setting_button_background" - android:textColor="@color/text" - android:textSize="@dimen/medium_font_size" - android:textStyle="bold" - android:gravity="center" - android:text="Show Seed" /> - - <View - android:layout_width="match_parent" - android:layout_height="1px" - android:background="@color/separator" /> + </LinearLayout> - <TextView - android:id="@+id/clearAllDataButton" - android:layout_width="match_parent" - android:layout_height="@dimen/setting_button_height" - android:background="@drawable/setting_button_background" - android:textColor="@color/destructive" - android:textSize="@dimen/medium_font_size" - android:textStyle="bold" - android:gravity="center" - android:text="Clear All Data" /> - - <View - android:layout_width="match_parent" - android:layout_height="1px" - android:layout_marginBottom="@dimen/large_spacing" - android:background="@color/separator" /> + </ScrollView> + + <RelativeLayout + android:id="@+id/loader" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="#A4000000" + android:visibility="gone" + android:alpha="0"> + + <com.github.ybq.android.spinkit.SpinKitView + style="@style/SpinKitView.Large.ThreeBounce" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="8dp" + android:layout_centerInParent="true" + app:SpinKit_Color="@color/text" /> - </LinearLayout> + </RelativeLayout> -</ScrollView> \ No newline at end of file +</RelativeLayout> \ No newline at end of file diff --git a/src/org/thoughtcrime/securesms/contacts/avatars/ProfileContactPhoto.java b/src/org/thoughtcrime/securesms/contacts/avatars/ProfileContactPhoto.java index 62f4cd26a6..22c3ea398a 100644 --- a/src/org/thoughtcrime/securesms/contacts/avatars/ProfileContactPhoto.java +++ b/src/org/thoughtcrime/securesms/contacts/avatars/ProfileContactPhoto.java @@ -8,7 +8,6 @@ import android.support.annotation.Nullable; import org.thoughtcrime.securesms.database.Address; import org.thoughtcrime.securesms.profiles.AvatarHelper; -import org.thoughtcrime.securesms.util.Conversions; import java.io.IOException; import java.io.InputStream; @@ -17,7 +16,7 @@ import java.security.MessageDigest; public class ProfileContactPhoto implements ContactPhoto { private final @NonNull Address address; - private final @NonNull String avatarObject; + public final @NonNull String avatarObject; public ProfileContactPhoto(@NonNull Address address, @NonNull String avatarObject) { this.address = address; diff --git a/src/org/thoughtcrime/securesms/loki/redesign/activities/RegisterActivity.kt b/src/org/thoughtcrime/securesms/loki/redesign/activities/RegisterActivity.kt index 120eddf126..ef7b7d2591 100644 --- a/src/org/thoughtcrime/securesms/loki/redesign/activities/RegisterActivity.kt +++ b/src/org/thoughtcrime/securesms/loki/redesign/activities/RegisterActivity.kt @@ -91,14 +91,14 @@ class RegisterActivity : BaseActionBarActivity() { val hexEncodedPublicKey = keyPair!!.hexEncodedPublicKey val characterCount = hexEncodedPublicKey.count() var count = 0 - val limit = 40 + val limit = 32 fun animate() { - val numberOfIndexesToShuffle = (0 until (40 - count)).random() + val numberOfIndexesToShuffle = 32 - count val indexesToShuffle = (0 until characterCount).shuffled().subList(0, numberOfIndexesToShuffle) var mangledHexEncodedPublicKey = hexEncodedPublicKey for (index in indexesToShuffle) { try { - mangledHexEncodedPublicKey = mangledHexEncodedPublicKey.substring(0, index) + "0123456789abcdef________________".random() + mangledHexEncodedPublicKey.substring(index + 1, mangledHexEncodedPublicKey.count()) + mangledHexEncodedPublicKey = mangledHexEncodedPublicKey.substring(0, index) + "0123456789abcdef__".random() + mangledHexEncodedPublicKey.substring(index + 1, mangledHexEncodedPublicKey.count()) } catch (exception: Exception) { // Do nothing } @@ -108,7 +108,7 @@ class RegisterActivity : BaseActionBarActivity() { publicKeyTextView.text = mangledHexEncodedPublicKey Handler().postDelayed({ animate() - }, 40) + }, 32) } else { publicKeyTextView.text = hexEncodedPublicKey } diff --git a/src/org/thoughtcrime/securesms/loki/redesign/activities/SettingsActivity.kt b/src/org/thoughtcrime/securesms/loki/redesign/activities/SettingsActivity.kt index d991e79837..fd898e7186 100644 --- a/src/org/thoughtcrime/securesms/loki/redesign/activities/SettingsActivity.kt +++ b/src/org/thoughtcrime/securesms/loki/redesign/activities/SettingsActivity.kt @@ -1,30 +1,56 @@ package org.thoughtcrime.securesms.loki.redesign.activities +import android.animation.Animator +import android.animation.AnimatorListenerAdapter +import android.app.Activity import android.content.ClipData import android.content.ClipboardManager import android.content.Context import android.content.Intent +import android.net.Uri +import android.os.AsyncTask import android.os.Bundle +import android.os.Handler +import android.os.Looper import android.view.View import android.view.inputmethod.InputMethodManager import android.widget.LinearLayout import android.widget.Toast import kotlinx.android.synthetic.main.activity_settings.* import network.loki.messenger.R +import nl.komponents.kovenant.Promise +import nl.komponents.kovenant.all +import nl.komponents.kovenant.deferred +import nl.komponents.kovenant.ui.alwaysUi import org.thoughtcrime.securesms.ApplicationContext import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity +import org.thoughtcrime.securesms.avatar.AvatarSelection +import org.thoughtcrime.securesms.crypto.ProfileKeyUtil +import org.thoughtcrime.securesms.database.Address import org.thoughtcrime.securesms.database.DatabaseFactory import org.thoughtcrime.securesms.loki.redesign.utilities.push import org.thoughtcrime.securesms.loki.toPx import org.thoughtcrime.securesms.mms.GlideApp import org.thoughtcrime.securesms.mms.GlideRequests +import org.thoughtcrime.securesms.profiles.AvatarHelper +import org.thoughtcrime.securesms.profiles.ProfileMediaConstraints +import org.thoughtcrime.securesms.util.BitmapDecodingException +import org.thoughtcrime.securesms.util.BitmapUtil import org.thoughtcrime.securesms.util.TextSecurePreferences +import org.whispersystems.signalservice.api.crypto.ProfileCipher +import org.whispersystems.signalservice.api.util.StreamDetails +import org.whispersystems.signalservice.loki.api.LokiStorageAPI +import java.io.ByteArrayInputStream +import java.io.File +import java.security.SecureRandom class SettingsActivity : PassphraseRequiredActionBarActivity() { private lateinit var glide: GlideRequests private var isEditingDisplayName = false set(value) { field = value; handleIsEditingDisplayNameChanged() } private var displayNameToBeUploaded: String? = null + private var profilePictureToBeUploaded: ByteArray? = null + private var tempFile: File? = null private val hexEncodedPublicKey: String get() { @@ -50,6 +76,7 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() { profilePictureView.hexEncodedPublicKey = hexEncodedPublicKey profilePictureView.isLarge = true profilePictureView.update() + profilePictureView.setOnClickListener { showEditProfilePictureUI() } // Set up display name container displayNameContainer.setOnClickListener { showEditDisplayNameUI() } // Set up display name text view @@ -61,6 +88,34 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() { // Set up share button shareButton.setOnClickListener { sharePublicKey() } } + + public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + when (requestCode) { + AvatarSelection.REQUEST_CODE_AVATAR -> { + if (resultCode != Activity.RESULT_OK) { return } + val outputFile = Uri.fromFile(File(cacheDir, "cropped")) + var inputFile: Uri? = data?.data + if (inputFile == null && tempFile != null) { + inputFile = Uri.fromFile(tempFile) + } + AvatarSelection.circularCropImage(this, inputFile, outputFile, R.string.CropImageActivity_profile_avatar) + } + AvatarSelection.REQUEST_CODE_CROP_IMAGE -> { + if (resultCode != Activity.RESULT_OK) { return } + AsyncTask.execute { + try { + profilePictureToBeUploaded = BitmapUtil.createScaledBytes(this@SettingsActivity, AvatarSelection.getResultUri(data), ProfileMediaConstraints()).bitmap + Handler(Looper.getMainLooper()).post { + updateProfile(true) + } + } catch (e: BitmapDecodingException) { + e.printStackTrace() + } + } + } + } + } // endregion // region Updating @@ -82,18 +137,62 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() { } } - private fun updateProfile(isUpdatingDisplayName: Boolean, isUpdatingProfilePicture: Boolean) { - val displayName = displayNameToBeUploaded ?: TextSecurePreferences.getProfileName(this) - TextSecurePreferences.setProfileName(this, displayName) - val publicChatAPI = ApplicationContext.getInstance(this).lokiPublicChatAPI - if (publicChatAPI != null) { - val servers = DatabaseFactory.getLokiThreadDatabase(this).getAllPublicChatServers() - for (server in servers) { - publicChatAPI.setDisplayName(displayName, server) + private fun updateProfile(isUpdatingProfilePicture: Boolean) { + showLoader() + val promises = mutableListOf<Promise<*, Exception>>() + val displayName = displayNameToBeUploaded + if (displayName != null) { + val publicChatAPI = ApplicationContext.getInstance(this).lokiPublicChatAPI + if (publicChatAPI != null) { + val servers = DatabaseFactory.getLokiThreadDatabase(this).getAllPublicChatServers() + promises.addAll(servers.map { publicChatAPI.setDisplayName(displayName, it) }) } + TextSecurePreferences.setProfileName(this, displayName) + } + val profilePicture = profilePictureToBeUploaded + val encodedProfileKey = ProfileKeyUtil.generateEncodedProfileKey(this) + val profileKey = ProfileKeyUtil.getProfileKeyFromEncodedString(encodedProfileKey) + if (isUpdatingProfilePicture && profilePicture != null) { + val storageAPI = LokiStorageAPI.shared + val deferred = deferred<Unit, Exception>() + AsyncTask.execute { + val stream = StreamDetails(ByteArrayInputStream(profilePicture), "image/jpeg", profilePicture.size.toLong()) + val (_, url) = storageAPI.uploadProfilePicture(storageAPI.server, profileKey, stream) + TextSecurePreferences.setProfileAvatarUrl(this, url) + deferred.resolve(Unit) + } + promises.add(deferred.promise) + } + all(promises).alwaysUi { + if (displayName != null) { + displayNameTextView.text = displayName + } + displayNameToBeUploaded = null + if (isUpdatingProfilePicture && profilePicture != null) { + AvatarHelper.setAvatar(this, Address.fromSerialized(TextSecurePreferences.getLocalNumber(this)), profilePicture) + TextSecurePreferences.setProfileAvatarId(this, SecureRandom().nextInt()) + ProfileKeyUtil.setEncodedProfileKey(this, encodedProfileKey) + ApplicationContext.getInstance(this).updatePublicChatProfileAvatarIfNeeded() + profilePictureView.update() + } + profilePictureToBeUploaded = null + hideLoader() } - displayNameTextView.text = displayName - displayNameToBeUploaded = null + } + + private fun showLoader() { + loader.visibility = View.VISIBLE + loader.animate().setDuration(150).alpha(1.0f).start() + } + + private fun hideLoader() { + loader.animate().setDuration(150).alpha(0.0f).setListener(object : AnimatorListenerAdapter() { + + override fun onAnimationEnd(animation: Animator?) { + super.onAnimationEnd(animation) + loader.visibility = View.GONE + } + }) } // endregion @@ -103,11 +202,19 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() { } private fun saveDisplayName() { - val displayName = displayNameEditText.text.trim().toString() - // TODO: Validation + val displayName = displayNameEditText.text.toString().trim() + if (displayName.isEmpty()) { + return Toast.makeText(this, "Please pick a display name", Toast.LENGTH_SHORT).show() + } + if (!displayName.matches(Regex("[a-zA-Z0-9_]+"))) { + return Toast.makeText(this, "Please pick a display name that consists of only a-z, A-Z, 0-9 and _ characters", Toast.LENGTH_SHORT).show() + } + if (displayName.toByteArray().size > ProfileCipher.NAME_PADDED_LENGTH) { + return Toast.makeText(this, "Please pick a shorter display name", Toast.LENGTH_SHORT).show() + } isEditingDisplayName = false displayNameToBeUploaded = displayName - updateProfile(true, false) + updateProfile(false) } private fun showQRCode() { @@ -115,6 +222,10 @@ class SettingsActivity : PassphraseRequiredActionBarActivity() { push(intent) } + private fun showEditProfilePictureUI() { + tempFile = AvatarSelection.startAvatarSelection(this, false, true) + } + private fun showEditDisplayNameUI() { isEditingDisplayName = true } diff --git a/src/org/thoughtcrime/securesms/loki/redesign/views/ProfilePictureView.kt b/src/org/thoughtcrime/securesms/loki/redesign/views/ProfilePictureView.kt index 084d6a3caa..87fddbfb3e 100644 --- a/src/org/thoughtcrime/securesms/loki/redesign/views/ProfilePictureView.kt +++ b/src/org/thoughtcrime/securesms/loki/redesign/views/ProfilePictureView.kt @@ -10,6 +10,7 @@ import android.widget.RelativeLayout import com.bumptech.glide.load.engine.DiskCacheStrategy import kotlinx.android.synthetic.main.view_profile_picture.view.* import network.loki.messenger.R +import org.thoughtcrime.securesms.contacts.avatars.ProfileContactPhoto import org.thoughtcrime.securesms.database.Address import org.thoughtcrime.securesms.loki.JazzIdenticonDrawable import org.thoughtcrime.securesms.mms.GlideRequests @@ -60,7 +61,7 @@ class ProfilePictureView : RelativeLayout { glide.clear(imageView) if (hexEncodedPublicKey.isNotEmpty()) { val signalProfilePicture = Recipient.from(context, Address.fromSerialized(hexEncodedPublicKey), false).contactPhoto - if (signalProfilePicture != null) { + if (signalProfilePicture != null && (signalProfilePicture as? ProfileContactPhoto)?.avatarObject != "0") { glide.load(signalProfilePicture).diskCacheStrategy(DiskCacheStrategy.ALL).circleCrop().into(imageView) } else { val size = resources.getDimensionPixelSize(sizeID) diff --git a/src/org/thoughtcrime/securesms/recipients/Recipient.java b/src/org/thoughtcrime/securesms/recipients/Recipient.java index d3db74308e..aa70a329da 100644 --- a/src/org/thoughtcrime/securesms/recipients/Recipient.java +++ b/src/org/thoughtcrime/securesms/recipients/Recipient.java @@ -475,7 +475,7 @@ public class Recipient implements RecipientModifiedListener { } public synchronized @Nullable ContactPhoto getContactPhoto() { - if (isLocalNumber && profileAvatar != null) return new ProfileContactPhoto(address, String.valueOf(TextSecurePreferences.getProfileAvatarId(context))); + if (isLocalNumber) return new ProfileContactPhoto(address, String.valueOf(TextSecurePreferences.getProfileAvatarId(context))); else if (isGroupRecipient() && groupAvatarId != null) return new GroupRecordContactPhoto(address, groupAvatarId); else if (systemContactPhoto != null) return new SystemContactPhoto(address, systemContactPhoto, 0); else if (profileAvatar != null) return new ProfileContactPhoto(address, profileAvatar);