/**
* Wire
* Copyright (C) 2016 Wire Swiss GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.waz.zclient.controllers.usernames;
import android.app.Activity;
import android.text.TextUtils;
import com.waz.api.Self;
import com.waz.api.UsernameValidation;
import com.waz.api.Usernames;
import com.waz.api.ValidatedUsernames;
import com.waz.zclient.ZApplication;
import com.waz.zclient.core.api.scala.ModelObserver;
import com.waz.zclient.utils.StringUtils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Random;
import java.util.Set;
import java.util.WeakHashMap;
public class UsernamesController implements IUsernamesController {
private static final int USERNAME_MAX_LENGTH = 21;
private static final int NORMAL_ATTEMPTS = 30;
private static final int RANDOM_ATTEMPTS = 20;
private static final int MAX_RANDOM_TRAILLING_NUMBER = 1000;
private ZApplication application = null;
private Random randomGenerator;
private Set<UsernamesControllerObserver> usernamesControllerObservers = Collections.newSetFromMap(
new WeakHashMap<UsernamesControllerObserver, Boolean>());
private class GeneratedUsername {
final String username;
final String searchedName;
GeneratedUsername(String username, String searchedName) {
this.username = username;
this.searchedName = searchedName;
}
boolean isValid() {
return !TextUtils.isEmpty(username);
}
}
private GeneratedUsername generatedUsername = null;
private String currentSearch;
private String[] currentAttemptsArray;
private ModelObserver<Self> userModelObserver = new ModelObserver<Self>() {
@Override
public void updated(Self model) {
if (model.isUpToDate() && !TextUtils.isEmpty(model.getName()) && !model.hasSetUsername()) {
if (hasGeneratedUsername() && model.getName().equals(generatedUsername.searchedName)) {
notifyObserversValidUsernameGenerated(model.getName(), getGeneratedUsername());
} else {
startUsernameGenerator(model.getName());
}
} else if (model.hasSetUsername()) {
currentAttemptsArray = null;
generatedUsername = null;
closeFirstAssignUsernameScreen();
}
}
};
private ModelObserver<ValidatedUsernames> validatedUsernamesModelObserver = new ModelObserver<ValidatedUsernames>() {
@Override
public void updated(ValidatedUsernames model) {
if (currentAttemptsArray == null) {
return;
}
UsernameValidation[] usernameValidations = model.getValidations(currentAttemptsArray);
for (UsernameValidation attemptValidation : usernameValidations) {
if (attemptValidation.isValid()) {
generatedUsername = new GeneratedUsername(attemptValidation.username(), currentSearch);
notifyObserversValidUsernameGenerated(currentSearch, attemptValidation.username());
currentSearch = "";
currentAttemptsArray = null;
return;
}
}
notifyObserversAttemptsExhausted(currentSearch);
generatedUsername = new GeneratedUsername("", currentSearch);
currentSearch = "";
currentAttemptsArray = null;
}
};
public UsernamesController() {
randomGenerator = new Random();
}
@Override
public void setActivity(Activity activity) {
if (application != null) {
return;
}
application = ZApplication.from(activity);
validatedUsernamesModelObserver.setAndUpdate(application.getStoreFactory().getZMessagingApiStore().getApi().getUsernames().getValidatedUsernames());
}
@Override
public void addUsernamesObserver(UsernamesControllerObserver usernamesControllerObserver) {
usernamesControllerObservers.add(usernamesControllerObserver);
}
@Override
public void addUsernamesObserverAndUpdate(UsernamesControllerObserver usernamesControllerObserver) {
addUsernamesObserver(usernamesControllerObserver);
if (hasGeneratedUsername()) {
if (generatedUsername.isValid()) {
usernamesControllerObserver.onValidUsernameGenerated(generatedUsername.searchedName, generatedUsername.username);
} else {
usernamesControllerObserver.onUsernameAttemptsExhausted(generatedUsername.searchedName);
}
} else {
userModelObserver.forceUpdate();
}
}
@Override
public void removeUsernamesObserver(UsernamesControllerObserver usernamesControllerObserver) {
usernamesControllerObservers.remove(usernamesControllerObserver);
}
@Override
public void closeFirstAssignUsernameScreen() {
for (UsernamesControllerObserver observer : usernamesControllerObservers) {
observer.onCloseFirstAssignUsernameScreen();
}
}
private void notifyObserversValidUsernameGenerated(String name, String generatedUsername) {
for (UsernamesControllerObserver observer : usernamesControllerObservers) {
observer.onValidUsernameGenerated(name, generatedUsername);
}
}
private void notifyObserversAttemptsExhausted(String name) {
for (UsernamesControllerObserver observer : usernamesControllerObservers) {
observer.onUsernameAttemptsExhausted(name);
}
}
@Override
public boolean hasGeneratedUsername() {
return generatedUsername != null && generatedUsername.isValid();
}
@Override
public String getGeneratedUsername() {
return hasGeneratedUsername() ? generatedUsername.username : null;
}
@Override
public void startUsernameGenerator(String baseName) {
if (currentSearch != null && currentSearch.equals(baseName)) {
return;
}
generatedUsername = null;
currentSearch = baseName;
currentAttemptsArray = null;
Usernames usernames = application.getStoreFactory().getZMessagingApiStore().getApi().getUsernames();
String baseGeneratedUsername = usernames.generateUsernameFromName(baseName, application);
String randomUsername = usernames.generateUsernameFromName("", application);
List<String> attempts = new ArrayList<>();
for (int i = 0; i < NORMAL_ATTEMPTS; i++) {
String trailingNumber = getTrailingNumber(i);
attempts.add(StringUtils.truncate(baseGeneratedUsername, USERNAME_MAX_LENGTH - trailingNumber.length()) + trailingNumber);
}
for (int i = 0; i < RANDOM_ATTEMPTS; i++) {
String trailingNumber = getTrailingNumber(i);
attempts.add(StringUtils.truncate(randomUsername, USERNAME_MAX_LENGTH - trailingNumber.length()) + trailingNumber);
}
currentAttemptsArray = new String[attempts.size()];
attempts.toArray(currentAttemptsArray);
usernames.validateUsernames(currentAttemptsArray);
}
@Override
public void setUser(Self self) {
userModelObserver.setAndUpdate(self);
}
private String getTrailingNumber(int attempt) {
if (attempt > 0) {
return String.format(Locale.getDefault(), "%d", randomGenerator.nextInt(MAX_RANDOM_TRAILLING_NUMBER * 10 ^ (attempt / 10)));
}
return "";
}
@Override
public void logout() {
tearDown();
}
@Override
public void tearDown() {
userModelObserver.clear();
usernamesControllerObservers.clear();
currentSearch = null;
currentAttemptsArray = null;
}
}