mirror of
https://github.com/oxen-io/session-android.git
synced 2025-06-08 16:58:48 +00:00
Prevent leading and trailing whitespace in group names.
This commit is contained in:
parent
9c54e39eae
commit
ab76112f5f
@ -13,7 +13,7 @@ import androidx.lifecycle.ViewModelProvider;
|
|||||||
import org.thoughtcrime.securesms.groups.GroupId;
|
import org.thoughtcrime.securesms.groups.GroupId;
|
||||||
import org.thoughtcrime.securesms.profiles.ProfileName;
|
import org.thoughtcrime.securesms.profiles.ProfileName;
|
||||||
import org.thoughtcrime.securesms.util.StringUtil;
|
import org.thoughtcrime.securesms.util.StringUtil;
|
||||||
import org.thoughtcrime.securesms.util.livedata.LiveDataPair;
|
import org.thoughtcrime.securesms.util.livedata.LiveDataUtil;
|
||||||
import org.whispersystems.libsignal.util.guava.Optional;
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
@ -22,19 +22,20 @@ class EditProfileViewModel extends ViewModel {
|
|||||||
|
|
||||||
private final MutableLiveData<String> givenName = new MutableLiveData<>();
|
private final MutableLiveData<String> givenName = new MutableLiveData<>();
|
||||||
private final MutableLiveData<String> familyName = new MutableLiveData<>();
|
private final MutableLiveData<String> familyName = new MutableLiveData<>();
|
||||||
private final LiveData<ProfileName> internalProfileName = Transformations.map(new LiveDataPair<>(givenName, familyName),
|
private final LiveData<String> trimmedGivenName = Transformations.map(givenName, StringUtil::trimToVisualBounds);
|
||||||
pair -> ProfileName.fromParts(pair.first(), pair.second()));
|
private final LiveData<String> trimmedFamilyName = Transformations.map(familyName, StringUtil::trimToVisualBounds);
|
||||||
|
private final LiveData<ProfileName> internalProfileName = LiveDataUtil.combineLatest(trimmedGivenName, trimmedFamilyName, ProfileName::fromParts);
|
||||||
private final MutableLiveData<byte[]> internalAvatar = new MutableLiveData<>();
|
private final MutableLiveData<byte[]> internalAvatar = new MutableLiveData<>();
|
||||||
private final MutableLiveData<byte[]> originalAvatar = new MutableLiveData<>();
|
private final MutableLiveData<byte[]> originalAvatar = new MutableLiveData<>();
|
||||||
private final MutableLiveData<Optional<String>> internalUsername = new MutableLiveData<>();
|
private final MutableLiveData<Optional<String>> internalUsername = new MutableLiveData<>();
|
||||||
private final MutableLiveData<String> originalDisplayName = new MutableLiveData<>();
|
private final MutableLiveData<String> originalDisplayName = new MutableLiveData<>();
|
||||||
private final LiveData<Boolean> isFormValid = Transformations.map(givenName, name -> !StringUtil.isVisuallyEmpty(name));
|
private final LiveData<Boolean> isFormValid = Transformations.map(trimmedGivenName, s -> s.length() > 0);
|
||||||
private final EditProfileRepository repository;
|
private final EditProfileRepository repository;
|
||||||
private final GroupId groupId;
|
private final GroupId groupId;
|
||||||
|
|
||||||
private EditProfileViewModel(@NonNull EditProfileRepository repository, boolean hasInstanceState, @Nullable GroupId groupId) {
|
private EditProfileViewModel(@NonNull EditProfileRepository repository, boolean hasInstanceState, @Nullable GroupId groupId) {
|
||||||
this.repository = repository;
|
this.repository = repository;
|
||||||
this.groupId = groupId;
|
this.groupId = groupId;
|
||||||
|
|
||||||
repository.getCurrentUsername(internalUsername::postValue);
|
repository.getCurrentUsername(internalUsername::postValue);
|
||||||
|
|
||||||
@ -141,9 +142,8 @@ class EditProfileViewModel extends ViewModel {
|
|||||||
this.groupId = groupId;
|
this.groupId = groupId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
@Override
|
||||||
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
|
public @NonNull <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
|
||||||
//noinspection unchecked
|
//noinspection unchecked
|
||||||
return (T) new EditProfileViewModel(repository, hasInstanceState, groupId);
|
return (T) new EditProfileViewModel(repository, hasInstanceState, groupId);
|
||||||
}
|
}
|
||||||
|
@ -45,13 +45,44 @@ public final class StringUtil {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < value.length(); i++) {
|
return indexOfFirstNonEmptyChar(value) == -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return String without any leading or trailing whitespace.
|
||||||
|
* Accounts for various unicode whitespace characters.
|
||||||
|
*/
|
||||||
|
public static String trimToVisualBounds(@NonNull String value) {
|
||||||
|
int start = indexOfFirstNonEmptyChar(value);
|
||||||
|
|
||||||
|
if (start == -1) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
int end = indexOfLastNonEmptyChar(value);
|
||||||
|
|
||||||
|
return value.substring(start, end + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int indexOfFirstNonEmptyChar(@NonNull String value) {
|
||||||
|
int length = value.length();
|
||||||
|
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
if (!isVisuallyEmpty(value.charAt(i))) {
|
if (!isVisuallyEmpty(value.charAt(i))) {
|
||||||
return false;
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int indexOfLastNonEmptyChar(@NonNull String value) {
|
||||||
|
for (int i = value.length() - 1; i >= 0; i--) {
|
||||||
|
if (!isVisuallyEmpty(value.charAt(i))) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -0,0 +1,54 @@
|
|||||||
|
package org.thoughtcrime.securesms.util;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.junit.runners.Parameterized;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
@RunWith(Parameterized.class)
|
||||||
|
public final class StringUtilTest_whitespace_handling {
|
||||||
|
|
||||||
|
private final String input;
|
||||||
|
private final String expectedTrimmed;
|
||||||
|
private final boolean isVisuallyEmpty;
|
||||||
|
|
||||||
|
@Parameterized.Parameters
|
||||||
|
public static Collection<Object[]> data() {
|
||||||
|
return Arrays.asList(new Object[][]{
|
||||||
|
|
||||||
|
{ "", "", true },
|
||||||
|
{ " ", "", true },
|
||||||
|
{ "A", "A", false },
|
||||||
|
{ " B", "B", false },
|
||||||
|
{ "C ", "C", false },
|
||||||
|
|
||||||
|
/* Unicode whitespace */
|
||||||
|
{ "\u200E", "", true },
|
||||||
|
{ "\u200F", "", true },
|
||||||
|
{ "\u2007", "", true },
|
||||||
|
{ "\u2007\u200FA\tB\u200EC\u200E\u200F", "A\tB\u200EC", false },
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public StringUtilTest_whitespace_handling(String input, String expectedTrimmed, boolean isVisuallyEmpty) {
|
||||||
|
this.input = input;
|
||||||
|
this.expectedTrimmed = expectedTrimmed;
|
||||||
|
this.isVisuallyEmpty = isVisuallyEmpty;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isVisuallyEmpty() {
|
||||||
|
assertEquals(isVisuallyEmpty, StringUtil.isVisuallyEmpty(input));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void trim() {
|
||||||
|
assertEquals(expectedTrimmed, StringUtil.trimToVisualBounds(input));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -545,7 +545,7 @@ public final class GroupsV2Operations {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String decryptTitle(ByteString cipherText) {
|
private String decryptTitle(ByteString cipherText) {
|
||||||
return decryptBlob(cipherText).getTitle();
|
return decryptBlob(cipherText).getTitle().trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
private int decryptDisappearingMessagesTimer(ByteString encryptedTimerMessage) {
|
private int decryptDisappearingMessagesTimer(ByteString encryptedTimerMessage) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user