// Copyright 2015 The Project Buendia Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy
// of the License at: http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software distrib-
// uted under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES
// OR CONDITIONS OF ANY KIND, either express or implied. See the License for
// specific language governing permissions and limitations under the License.
package org.projectbuendia.client.ui.login;
import com.google.common.collect.Ordering;
import org.projectbuendia.client.R;
import org.projectbuendia.client.diagnostics.Troubleshooter;
import org.projectbuendia.client.events.diagnostics.TroubleshootingActionsChangedEvent;
import org.projectbuendia.client.events.user.KnownUsersLoadFailedEvent;
import org.projectbuendia.client.events.user.KnownUsersLoadedEvent;
import org.projectbuendia.client.events.user.UserAddFailedEvent;
import org.projectbuendia.client.events.user.UserAddedEvent;
import org.projectbuendia.client.json.JsonUser;
import org.projectbuendia.client.ui.dialogs.NewUserDialogFragment;
import org.projectbuendia.client.user.UserManager;
import org.projectbuendia.client.utils.EventBusRegistrationInterface;
import org.projectbuendia.client.utils.Logger;
import org.projectbuendia.client.utils.Utils;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
/** Controller for {@link LoginActivity}. */
public final class LoginController {
private static final Logger LOG = Logger.create();
private final EventBusRegistrationInterface mEventBus;
private final Ui mUi;
private final FragmentUi mFragmentUi;
private final DialogActivityUi mDialogUi = new DialogActivityUi();
private final UserManager mUserManager;
private final List<JsonUser> mUsersSortedByName = new ArrayList<>();
private final BusEventSubscriber mSubscriber = new BusEventSubscriber();
private final Troubleshooter mTroubleshooter;
public interface Ui {
void showAddNewUserDialog();
void showSettings();
void showErrorToast(int stringResourceId);
void showSyncFailedDialog(boolean show);
void showTentSelectionScreen();
}
public interface FragmentUi {
void showSpinner(boolean show);
void showUsers(List<JsonUser> users);
}
/**
* Instantiates a {@link LoginController}.
* @param userManager a {@link UserManager} from which users will be fetched
* @param eventBus an {@link EventBusRegistrationInterface} for listening to user fetch and
* modification events
* @param troubleshooter a {@link Troubleshooter} for monitoring server health; if the server
* becomes available and the controller has no data available, the
* controller will automatically retry fetching users
* @param ui a {@link Ui} for handling activity changes
* @param fragmentUi a {@link FragmentUi} for displaying users
*/
public LoginController(
UserManager userManager,
EventBusRegistrationInterface eventBus,
Troubleshooter troubleshooter,
Ui ui,
FragmentUi fragmentUi) {
mUserManager = userManager;
mEventBus = eventBus;
mTroubleshooter = troubleshooter;
mUi = ui;
mFragmentUi = fragmentUi;
}
/**
* Requests any necessary resources. Note that some resources may be fetched asynchronously
* after this function returns.
*/
public void init() {
mEventBus.register(mSubscriber);
mFragmentUi.showSpinner(true);
mUserManager.loadKnownUsers();
}
/** Attempts to reload users. */
public void onSyncRetry() {
mFragmentUi.showSpinner(true);
mUserManager.loadKnownUsers();
}
public void suspend() {
mEventBus.unregister(mSubscriber);
}
/** Call when the user presses the 'add user' button. */
public void onAddUserPressed() {
Utils.logEvent("add_user_button_pressed");
mUi.showAddNewUserDialog();
}
/** Call when the user presses the settings button. */
public void onSettingsPressed() {
Utils.logEvent("settings_button_pressed");
mUi.showSettings();
}
/** Call when the user taps to select a user. */
public void onUserSelected(JsonUser user) {
mUserManager.setActiveUser(user);
Utils.logUserAction("logged_in");
mUi.showTentSelectionScreen();
}
public NewUserDialogFragment.ActivityUi getDialogUi() {
return mDialogUi;
}
public final class DialogActivityUi implements NewUserDialogFragment.ActivityUi {
@Override public void showSpinner(boolean show) {
mFragmentUi.showSpinner(show);
}
}
/** Converts a {@link UserAddFailedEvent} to an error string resource id. */
private static int errorToStringId(UserAddFailedEvent event) {
switch (event.reason) {
case UserAddFailedEvent.REASON_UNKNOWN:
return R.string.add_user_unknown_error;
case UserAddFailedEvent.REASON_INVALID_USER:
return R.string.add_user_invalid_user;
case UserAddFailedEvent.REASON_USER_EXISTS_LOCALLY:
return R.string.add_user_user_exists_locally;
case UserAddFailedEvent.REASON_USER_EXISTS_ON_SERVER:
return R.string.add_user_user_exists_on_server;
case UserAddFailedEvent.REASON_CONNECTION_ERROR:
return R.string.add_user_connection_error;
default:
return R.string.add_user_unknown_error;
}
}
/**
* Given a sorted list, inserts a new element in the correct position to maintain the sorted
* order.
*/
private static <T> void insertIntoSortedList(
List<T> list, Comparator<T> comparator, T newItem) {
int index;
for (index = 0; index < list.size(); index++) {
if (comparator.compare(list.get(index), newItem) > 0) break;
}
list.add(index, newItem);
}
@SuppressWarnings("unused") // Called by reflection from event bus.
private final class BusEventSubscriber {
/** Restart user fetch if we have no users and the Buendia API just became available. */
public void onEventMainThread(TroubleshootingActionsChangedEvent event) {
if (mUsersSortedByName.isEmpty() && mTroubleshooter.isServerHealthy()) {
LOG.d("Buendia API is available and users are not, retrying sync.");
onSyncRetry();
}
}
/** Updates the UI when the list of users is loaded. */
public void onEventMainThread(KnownUsersLoadedEvent event) {
LOG.d("Loaded list of " + event.knownUsers.size() + " users");
mUsersSortedByName.clear();
mUsersSortedByName
.addAll(Ordering.from(JsonUser.COMPARATOR_BY_NAME).sortedCopy(event.knownUsers));
mFragmentUi.showUsers(mUsersSortedByName);
mFragmentUi.showSpinner(false);
mUi.showSyncFailedDialog(false);
}
public void onEventMainThread(KnownUsersLoadFailedEvent event) {
LOG.e("Failed to load list of users");
mUi.showSyncFailedDialog(true);
}
public void onEventMainThread(UserAddedEvent event) {
mUi.showSyncFailedDialog(false); // Just in case.
LOG.d("User added");
insertIntoSortedList(mUsersSortedByName, JsonUser.COMPARATOR_BY_NAME, event.addedUser);
mFragmentUi.showUsers(mUsersSortedByName);
mFragmentUi.showSpinner(false);
}
public void onEventMainThread(UserAddFailedEvent event) {
LOG.d("Failed to add user");
mUi.showErrorToast(errorToStringId(event));
mFragmentUi.showSpinner(false);
}
}
}