com.waz.zclient.pages.main.conversationlist.ConversationListManagerFragment.java Source code

Java tutorial

Introduction

Here is the source code for com.waz.zclient.pages.main.conversationlist.ConversationListManagerFragment.java

Source

/**
 * 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.pages.main.conversationlist;

import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.Toast;
import com.waz.api.ConversationsList;
import com.waz.api.ErrorType;
import com.waz.api.ErrorsList;
import com.waz.api.IConversation;
import com.waz.api.ImageAsset;
import com.waz.api.Message;
import com.waz.api.OtrClient;
import com.waz.api.SyncState;
import com.waz.api.User;
import com.waz.api.Verification;
import com.waz.zclient.LaunchActivity;
import com.waz.zclient.OnBackPressedListener;
import com.waz.zclient.R;
import com.waz.zclient.controllers.accentcolor.AccentColorObserver;
import com.waz.zclient.controllers.confirmation.ConfirmationCallback;
import com.waz.zclient.controllers.confirmation.ConfirmationObserver;
import com.waz.zclient.controllers.confirmation.ConfirmationRequest;
import com.waz.zclient.controllers.confirmation.IConfirmationController;
import com.waz.zclient.controllers.confirmation.TwoButtonConfirmationCallback;
import com.waz.zclient.controllers.currentfocus.IFocusController;
import com.waz.zclient.controllers.navigation.NavigationControllerObserver;
import com.waz.zclient.controllers.navigation.Page;
import com.waz.zclient.controllers.tracking.events.connect.BlockingEvent;
import com.waz.zclient.controllers.tracking.events.conversation.ArchivedConversationEvent;
import com.waz.zclient.controllers.tracking.events.conversation.DeleteConversationEvent;
import com.waz.zclient.controllers.tracking.events.conversation.UnarchivedConversationEvent;
import com.waz.zclient.controllers.tracking.events.group.CreatedGroupConversationEvent;
import com.waz.zclient.controllers.tracking.events.group.LeaveGroupConversationEvent;
import com.waz.zclient.controllers.tracking.events.peoplepicker.PeoplePickerResultsUsed;
import com.waz.zclient.core.controllers.tracking.attributes.ConversationType;
import com.waz.zclient.core.controllers.tracking.attributes.RangedAttribute;
import com.waz.zclient.core.stores.connect.IConnectStore;
import com.waz.zclient.core.stores.conversation.ConversationChangeRequester;
import com.waz.zclient.core.stores.conversation.ConversationStoreObserver;
import com.waz.zclient.core.stores.conversation.InboxLoadRequester;
import com.waz.zclient.core.stores.conversation.OnInboxLoadedListener;
import com.waz.zclient.core.stores.inappnotification.InAppNotificationStoreObserver;
import com.waz.zclient.core.stores.inappnotification.KnockingEvent;
import com.waz.zclient.pages.BaseFragment;
import com.waz.zclient.pages.main.connect.BlockedUserProfileFragment;
import com.waz.zclient.pages.main.connect.ConnectRequestLoadMode;
import com.waz.zclient.pages.main.connect.PendingConnectRequestManagerFragment;
import com.waz.zclient.pages.main.connect.SendConnectRequestFragment;
import com.waz.zclient.pages.main.connect.UserProfile;
import com.waz.zclient.pages.main.conversation.controller.ConversationScreenControllerObserver;
import com.waz.zclient.pages.main.conversation.controller.IConversationScreenController;
import com.waz.zclient.pages.main.participants.OptionsMenuControl;
import com.waz.zclient.pages.main.participants.OptionsMenuFragment;
import com.waz.zclient.pages.main.participants.SingleParticipantFragment;
import com.waz.zclient.pages.main.participants.dialog.ParticipantsDialogFragment;
import com.waz.zclient.pages.main.pickuser.PickUserFragment;
import com.waz.zclient.pages.main.pickuser.controller.IPickUserController;
import com.waz.zclient.pages.main.pickuser.controller.PickUserControllerScreenObserver;
import com.waz.zclient.pages.main.profile.ZetaPreferencesActivity;
import com.waz.zclient.pages.main.profile.camera.CameraContext;
import com.waz.zclient.pages.main.profile.camera.CameraFragment;
import com.waz.zclient.ui.animation.interpolators.penner.Expo;
import com.waz.zclient.ui.animation.interpolators.penner.Quart;
import com.waz.zclient.ui.optionsmenu.OptionsMenu;
import com.waz.zclient.ui.optionsmenu.OptionsMenuItem;
import com.waz.zclient.ui.utils.KeyboardUtils;
import com.waz.zclient.utils.IntentUtils;
import com.waz.zclient.utils.LayoutSpec;
import com.waz.zclient.utils.ViewUtils;
import com.waz.zclient.views.LoadingIndicatorView;
import com.waz.zclient.views.menus.ConfirmationMenu;

import java.util.List;

public class ConversationListManagerFragment extends BaseFragment<ConversationListManagerFragment.Container>
        implements PickUserControllerScreenObserver, ConversationStoreObserver, OnInboxLoadedListener,
        OnBackPressedListener, ConversationListFragment.Container, OptionsMenuFragment.Container,
        PickUserFragment.Container, CameraFragment.Container, SingleParticipantFragment.Container,
        SendConnectRequestFragment.Container, BlockedUserProfileFragment.Container,
        ParticipantsDialogFragment.Container, PendingConnectRequestManagerFragment.Container,
        ConversationScreenControllerObserver, NavigationControllerObserver, ConfirmationObserver,
        AccentColorObserver, InAppNotificationStoreObserver {
    public static final String TAG = ConversationListManagerFragment.class.getName();

    private LoadingIndicatorView startuiLoadingIndicatorView;
    private LoadingIndicatorView listLoadingIndicatorView;
    private FrameLayout mainContainer;
    private OptionsMenuControl optionsMenuControl;
    private ConfirmationMenu confirmationMenu;

    //////////////////////////////////////////////////////////////////////////////////////////
    //
    //  Lifecycle
    //
    //////////////////////////////////////////////////////////////////////////////////////////

    public static Fragment newInstance() {
        Fragment fragment = new ConversationListManagerFragment();
        Bundle arguments = new Bundle();
        fragment.setArguments(arguments);
        return fragment;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_conversation_list_manager, container, false);

        mainContainer = ViewUtils.getView(view, R.id.fl__conversation_list_main);
        startuiLoadingIndicatorView = ViewUtils.getView(view, R.id.liv__conversations__loading_indicator);
        listLoadingIndicatorView = ViewUtils.getView(view, R.id.lbv__conversation_list__loading_indicator);

        startuiLoadingIndicatorView.setColor(getResources().getColor(R.color.people_picker__loading__color));
        listLoadingIndicatorView.setColor(getControllerFactory().getAccentColorController().getColor());

        confirmationMenu = ViewUtils.getView(view, R.id.cm__confirm_action_light);
        confirmationMenu.setVisibility(View.GONE);
        confirmationMenu.resetFullScreenPadding();
        optionsMenuControl = new OptionsMenuControl();

        if (savedInstanceState == null) {
            // When re-starting app to open into specific page, child fragments may exist despite savedInstanceState == null
            if (getControllerFactory().getPickUserController().isShowingUserProfile()) {
                getControllerFactory().getPickUserController().hideUserProfile();
            }

            if (getControllerFactory().getPickUserController()
                    .isShowingPickUser(IPickUserController.Destination.CONVERSATION_LIST)) {
                getControllerFactory().getPickUserController()
                        .hidePickUser(IPickUserController.Destination.CONVERSATION_LIST, false);

                Fragment pickUserFragment = getChildFragmentManager().findFragmentByTag(PickUserFragment.TAG);
                if (pickUserFragment != null) {
                    getChildFragmentManager().popBackStack(PickUserFragment.TAG,
                            FragmentManager.POP_BACK_STACK_INCLUSIVE);
                }
            }

            getChildFragmentManager().beginTransaction()
                    .add(R.id.fl__conversation_list_main,
                            ConversationListFragment.newInstance(ConversationListFragment.Mode.NORMAL),
                            ConversationListFragment.TAG)
                    .add(R.id.fl__conversation_list__settings_box, OptionsMenuFragment.newInstance(true),
                            OptionsMenuFragment.TAG)
                    .commit();
        }
        return view;
    }

    @Override
    public void onStart() {
        super.onStart();
        getStoreFactory().getInAppNotificationStore().addInAppNotificationObserver(this);
        getControllerFactory().getCameraController().addCameraActionObserver(this);
        getControllerFactory().getAccentColorController().addAccentColorObserver(this);
        getControllerFactory().getPickUserController().addPickUserScreenControllerObserver(this);
        getStoreFactory().getConversationStore().addConversationStoreObserverAndUpdate(this);
        getControllerFactory().getConversationScreenController().addConversationControllerObservers(this);
        getControllerFactory().getNavigationController().addNavigationControllerObserver(this);
        getControllerFactory().getConfirmationController().addConfirmationObserver(this);

        String page = getActivity().getIntent().getStringExtra(LaunchActivity.APP_PAGE);
        if (page == null) {
            return;
        }
        getActivity().setIntent(IntentUtils.resetAppPage(getActivity().getIntent()));
        switch (page) {
        case IntentUtils.LOCALYTICS_DEEPLINK_SEARCH:
            getControllerFactory().getPickUserController()
                    .showPickUser(IPickUserController.Destination.CONVERSATION_LIST, null);
            break;
        case IntentUtils.LOCALYTICS_DEEPLINK_SETTINGS:
            startActivity(ZetaPreferencesActivity.getDefaultIntent(getActivity()));
            break;
        case IntentUtils.LOCALYTICS_DEEPLINK_PROFILE:
            getControllerFactory().getPickUserController().showUserProfile(
                    getStoreFactory().getProfileStore().getSelfUser(),
                    ViewUtils.getView(getActivity(), R.id.gtv__list_actions__settings));
            break;
        }
    }

    @Override
    public void onViewStateRestored(Bundle savedInstanceState) {
        super.onViewStateRestored(savedInstanceState);
        loadPage();
    }

    @Override
    public void onStop() {
        getStoreFactory().getInAppNotificationStore().removeInAppNotificationObserver(this);
        getControllerFactory().getCameraController().removeCameraActionObserver(this);
        getStoreFactory().getConversationStore().removeConversationStoreObserver(this);
        getControllerFactory().getPickUserController().removePickUserScreenControllerObserver(this);
        getControllerFactory().getAccentColorController().removeAccentColorObserver(this);
        getControllerFactory().getConversationScreenController().removeConversationControllerObservers(this);
        getControllerFactory().getNavigationController().removeNavigationControllerObserver(this);
        getControllerFactory().getConfirmationController().removeConfirmationObserver(this);

        super.onStop();
    }

    @Override
    public void onDestroyView() {
        mainContainer = null;
        startuiLoadingIndicatorView = null;
        listLoadingIndicatorView = null;
        confirmationMenu = null;
        super.onDestroyView();
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        Fragment fragment = getChildFragmentManager().findFragmentById(R.id.fl__left__camera);
        if (fragment != null) {
            fragment.onActivityResult(requestCode, resultCode, data);
        }
    }

    //////////////////////////////////////////////////////////////////////////////////////////
    //
    //  ConversationStoreObserver
    //
    //////////////////////////////////////////////////////////////////////////////////////////
    @Override
    public void onConversationListUpdated(@NonNull final ConversationsList conversationsList) {
        if (!Page.CONVERSATION_LIST.equals(getControllerFactory().getNavigationController().getCurrentPage())) {
            return;
        }
        if (conversationsList.size() > 0) {
            listLoadingIndicatorView.setType(LoadingIndicatorView.INFINITE_LOADING_BAR);
            getControllerFactory().getNavigationController().setPagerEnabled(true);
        } else {
            listLoadingIndicatorView.setType(LoadingIndicatorView.SPINNER);
            getControllerFactory().getNavigationController().setPagerEnabled(false);
        }
    }

    @Override
    public void onConversationListStateHasChanged(ConversationsList.ConversationsListState state) {

    }

    @Override
    public void onCurrentConversationHasChanged(IConversation fromConversation, IConversation toConversation,
            ConversationChangeRequester conversationChangerSender) {
        switch (conversationChangerSender) {
        case START_CONVERSATION:
        case START_CONVERSATION_FOR_CALL:
        case START_CONVERSATION_FOR_VIDEO_CALL:
        case START_CONVERSATION_FOR_CAMERA:
        case NOTIFICATION:
        case SHARING:
            stripToConversationList();
            break;
        case INCOMING_CALL:
            stripToConversationList();
            animateOnIncomingCall();
            break;
        }
    }

    private void stripToConversationList() {
        if (LayoutSpec.isTablet(getActivity())) {
            return;
        }
        // Hide possibly open self profile
        getControllerFactory().getPickUserController().hideUserProfile();
        // Hide possibly open start ui
        getStoreFactory().getInAppNotificationStore().setUserLookingAtPeoplePicker(false);
        if (!getControllerFactory().getPickUserController().hidePickUser(getCurrentPickerDestination(), false)) {
            getControllerFactory().getNavigationController().setLeftPage(Page.CONVERSATION_LIST, TAG);
        }
    }

    @Override
    public void onConversationSyncingStateHasChanged(SyncState syncState) {
        if (syncState == null) {
            syncState = SyncState.FAILED;
        }
        switch (syncState) {
        case SYNCING:
        case WAITING:
            listLoadingIndicatorView.show();
            return;
        case COMPLETED:
            listLoadingIndicatorView.hide();
            getControllerFactory().getLoadTimeLoggerController().loginSuccess();
            break;
        case FAILED:
        default:
            listLoadingIndicatorView.hide();
        }
    }

    @Override
    public void onMenuConversationHasChanged(IConversation fromConversation) {

    }

    @Override
    public void onVerificationStateChanged(String conversationId, Verification previousVerification,
            Verification currentVerification) {

    }

    private void animateOnIncomingCall() {
        int duration = getResources().getInteger(R.integer.calling_animation_duration_medium);
        int resetDelay = getResources().getInteger(R.integer.calling_animation_duration_long);

        final View rootView = getView();
        if (rootView != null) {
            rootView.animate().alpha(0).setInterpolator(new Quart.EaseOut()).setDuration(duration).start();
        }

        // Reset to showing conversation list
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                getStoreFactory().getInAppNotificationStore().setUserLookingAtPeoplePicker(false);
                getControllerFactory().getPickUserController()
                        .hidePickUserWithoutAnimations(getCurrentPickerDestination());
                if (rootView != null) {
                    rootView.setAlpha(1);
                }
            }
        }, resetDelay);
    }

    //////////////////////////////////////////////////////////////////////////////////////////
    //
    //  NavigationControllerObserver
    //
    //////////////////////////////////////////////////////////////////////////////////////////

    @Override
    public void onPageVisible(Page page) {
        if (page == Page.CONVERSATION_LIST) {
            boolean enablePager = getStoreFactory().getConversationStore().getCurrentConversation() != null;
            getControllerFactory().getNavigationController().setPagerEnabled(enablePager);
        }
    }

    @Override
    public void onPageStateHasChanged(Page page) {

    }

    //////////////////////////////////////////////////////////////////////////////////////////
    //
    //  PickUserFragment.Container
    //
    //////////////////////////////////////////////////////////////////////////////////////////

    @Override
    public void showIncomingPendingConnectRequest(IConversation conversation) {
        getStoreFactory().getInAppNotificationStore().setUserLookingAtPeoplePicker(false);
        getControllerFactory().getPickUserController().hidePickUser(getCurrentPickerDestination(), false);

        getStoreFactory().getConversationStore().setCurrentConversation(conversation,
                ConversationChangeRequester.INBOX);
    }

    //////////////////////////////////////////////////////////////////////////////////////////
    //
    //  PickUserFragment.Container
    //
    //////////////////////////////////////////////////////////////////////////////////////////

    @Override
    public void onSelectedUsers(List<User> users, ConversationChangeRequester requester) {
        // TODO https://wearezeta.atlassian.net/browse/AN-3730
        getControllerFactory().getPickUserController().hidePickUser(getCurrentPickerDestination(), true);

        if (users.size() == 1) {
            User user = users.get(0);
            IConversation conversation = user.getConversation();
            if (conversation != null) {
                getStoreFactory().getConversationStore().setCurrentConversation(conversation, requester);
            }
            getControllerFactory().getTrackingController().onPeoplePickerResultsUsed(1,
                    PeoplePickerResultsUsed.Usage.NAVIGATE_TO_EXISTING_CONVERSATION);
        } else {
            getStoreFactory().getConversationStore().createGroupConversation(users, requester);
            getControllerFactory().getTrackingController()
                    .tagEvent(new CreatedGroupConversationEvent(false, (users.size() + 1)));
            getControllerFactory().getTrackingController()
                    .updateSessionAggregates(RangedAttribute.GROUP_CONVERSATIONS_STARTED);
            getControllerFactory().getTrackingController().onPeoplePickerResultsUsed(users.size(),
                    PeoplePickerResultsUsed.Usage.CREATE_GROUP_CONVERSATION);
        }
    }

    @Override
    public LoadingIndicatorView getLoadingViewIndicator() {
        return startuiLoadingIndicatorView;
    }

    @Override
    public IPickUserController.Destination getCurrentPickerDestination() {
        return IPickUserController.Destination.CONVERSATION_LIST;
    }

    //////////////////////////////////////////////////////////////////////////////////////////
    //
    //  OnBackPressedListener
    //
    //////////////////////////////////////////////////////////////////////////////////////////

    @Override
    public boolean onBackPressed() {
        if (closeMenu()) {
            return true;
        }

        SingleParticipantFragment singleUserFragment = (SingleParticipantFragment) getChildFragmentManager()
                .findFragmentByTag(SingleParticipantFragment.TAG);
        if (singleUserFragment != null && singleUserFragment.onBackPressed()) {
            return true;
        }

        if (getControllerFactory().getPickUserController().isShowingCommonUserProfile()) {
            getControllerFactory().getPickUserController().hideCommonUserProfile();
        }

        PickUserFragment pickUserFragment = (PickUserFragment) getChildFragmentManager()
                .findFragmentByTag(PickUserFragment.TAG);
        if (pickUserFragment != null && pickUserFragment.onBackPressed()) {
            return true;
        }

        if (getControllerFactory().getPickUserController().isShowingPickUser(getCurrentPickerDestination())) {
            getStoreFactory().getInAppNotificationStore().setUserLookingAtPeoplePicker(false);
            getControllerFactory().getPickUserController().hidePickUser(getCurrentPickerDestination(), true);
            return true;
        }

        return false;
    }

    //////////////////////////////////////////////////////////////////////////////////////////
    //
    //  PickUserControllerScreenObserver
    //
    //////////////////////////////////////////////////////////////////////////////////////////

    @Override
    public void onShowPickUser(IPickUserController.Destination destination, View anchorView) {
        if (!getCurrentPickerDestination().equals(destination)) {
            onHidePickUser(getCurrentPickerDestination(), true);
            return;
        }

        Page page = getControllerFactory().getNavigationController().getCurrentLeftPage();
        switch (page) {
        // TODO: START is set as left page on tablet, fix
        case START:
        case CONVERSATION_LIST:
            Fragment fragment = getChildFragmentManager().findFragmentByTag(PickUserFragment.TAG);
            if (fragment == null || !(fragment instanceof PickUserFragment)) {
                getControllerFactory().getOnboardingController().incrementPeoplePickerShowCount();

                PickUserFragment pickUserFragment = PickUserFragment.newInstance(false);
                getChildFragmentManager().beginTransaction()
                        .setCustomAnimations(R.anim.slide_in_from_bottom_pick_user,
                                R.anim.open_new_conversation__thread_list_out,
                                R.anim.open_new_conversation__thread_list_in, R.anim.slide_out_to_bottom_pick_user)
                        .replace(R.id.fl__conversation_list_main, pickUserFragment, PickUserFragment.TAG)
                        .addToBackStack(PickUserFragment.TAG).commit();

                getControllerFactory().getTrackingController()
                        .updateSessionAggregates(RangedAttribute.OPENED_SEARCH);
            }
            break;
        case PICK_USER:
            break;
        }

        getControllerFactory().getNavigationController().setLeftPage(Page.PICK_USER, TAG);
    }

    @Override
    public void onHidePickUser(IPickUserController.Destination destination, boolean closeWithoutSelectingPeople) {
        if (!destination.equals(getCurrentPickerDestination())) {
            return;
        }

        Page page = getControllerFactory().getNavigationController().getCurrentLeftPage();
        switch (page) {
        case SEND_CONNECT_REQUEST:
        case BLOCK_USER:
        case PENDING_CONNECT_REQUEST:
            getControllerFactory().getPickUserController().hideUserProfile();
        case PICK_USER:
            ConversationListFragment conversationListFragment = (ConversationListFragment) getChildFragmentManager()
                    .findFragmentByTag(ConversationListFragment.TAG);
            if (conversationListFragment != null) {
                conversationListFragment.setScrollToConversation(!closeWithoutSelectingPeople);
            }

            getChildFragmentManager().popBackStackImmediate(PickUserFragment.TAG,
                    FragmentManager.POP_BACK_STACK_INCLUSIVE);

            KeyboardUtils.hideKeyboard(getActivity());
            break;
        }

        getControllerFactory().getNavigationController().setLeftPage(Page.CONVERSATION_LIST, TAG);
        getControllerFactory().getFocusController().setFocus(IFocusController.CONVERSATION_CURSOR);
    }

    private void togglePeoplePicker(boolean show) {
        if (show) {
            mainContainer.animate().alpha(1).scaleY(1).scaleX(1).setInterpolator(new Expo.EaseOut())
                    .setDuration(getResources().getInteger(R.integer.reopen_profile_source__animation_duration))
                    .setStartDelay(getResources().getInteger(R.integer.reopen_profile_source__delay)).start();

        } else {
            mainContainer.animate().alpha(0).scaleY(2).scaleX(2).setInterpolator(new Expo.EaseIn())
                    .setDuration(getResources().getInteger(R.integer.reopen_profile_source__animation_duration))
                    .setStartDelay(0).start();
        }
    }

    //////////////////////////////////////////////////////////////////////////////////////////
    //
    //  UserProfileContainer
    //
    //////////////////////////////////////////////////////////////////////////////////////////

    @Override
    public void dismissUserProfile() {
        if (getControllerFactory().getPickUserController().isShowingCommonUserProfile()) {
            getControllerFactory().getPickUserController().hideCommonUserProfile();
        } else {
            getControllerFactory().getPickUserController().hideUserProfile();
        }
    }

    @Override
    public void dismissSingleUserProfile() {
        dismissUserProfile();
    }

    @Override
    public void showRemoveConfirmation(User user) {

    }

    @Override
    public void openCommonUserProfile(View anchor, User commonUser) {
        getControllerFactory().getPickUserController().showCommonUserProfile(commonUser);
    }

    //////////////////////////////////////////////////////////////////////////////////////////
    //
    //  SendConnectRequestFragment.Container
    //
    //////////////////////////////////////////////////////////////////////////////////////////

    @Override
    public void onConnectRequestWasSentToUser() {
        getControllerFactory().getPickUserController().hideUserProfile();
    }

    //////////////////////////////////////////////////////////////////////////////////////////
    //
    //  PendingConnectRequestManagerFragment.Container
    //
    //////////////////////////////////////////////////////////////////////////////////////////

    @Override
    public void onAcceptedConnectRequest(IConversation conversation) {
        getStoreFactory().getConversationStore().setCurrentConversation(conversation,
                ConversationChangeRequester.START_CONVERSATION);
    }

    @Override
    public void onAcceptedPendingOutgoingConnectRequest(IConversation conversation) {
        getStoreFactory().getConversationStore().setCurrentConversation(conversation,
                ConversationChangeRequester.CONNECT_REQUEST_ACCEPTED);
    }

    //////////////////////////////////////////////////////////////////////////////////////////
    //
    //  BlockedUserProfileFragment.Container
    //
    //////////////////////////////////////////////////////////////////////////////////////////

    @Override
    public void onUnblockedUser(IConversation restoredConversationWithUser) {
        getControllerFactory().getPickUserController().hideUserProfile();
        getStoreFactory().getConversationStore().setCurrentConversation(restoredConversationWithUser,
                ConversationChangeRequester.START_CONVERSATION);
    }

    //////////////////////////////////////////////////////////////////////////////////////////
    //
    //  PickUserControllerScreenObserver
    //
    //////////////////////////////////////////////////////////////////////////////////////////

    @Override
    public void onShowUserProfile(User user, View anchorView) {
        if (getControllerFactory().getPickUserController().isShowingUserProfile()) {
            return;
        }
        if (LayoutSpec.isPhone(getActivity())) {
            openUserProfileFragmentPhone(user);
        }
    }

    private void openUserProfileFragmentPhone(User user) {
        if (user == null) {
            return;
        }
        Fragment fragment;
        String tag;
        switch (user.getConnectionStatus()) {
        case CANCELLED:
        case UNCONNECTED:
            if (user.isConnected()) {
                return;
            }
            fragment = SendConnectRequestFragment.newInstance(user.getId(), IConnectStore.UserRequester.SEARCH);
            tag = SendConnectRequestFragment.TAG;
            getControllerFactory().getNavigationController().setLeftPage(Page.SEND_CONNECT_REQUEST, TAG);
            break;
        case PENDING_FROM_OTHER:
        case PENDING_FROM_USER:
        case IGNORED:
            fragment = PendingConnectRequestManagerFragment.newInstance(user.getId(), null,
                    ConnectRequestLoadMode.LOAD_BY_USER_ID, IConnectStore.UserRequester.SEARCH);
            tag = PendingConnectRequestManagerFragment.TAG;
            getControllerFactory().getNavigationController().setLeftPage(Page.PENDING_CONNECT_REQUEST, TAG);
            break;
        case BLOCKED:
            fragment = BlockedUserProfileFragment.newInstance(user.getId(), IConnectStore.UserRequester.SEARCH);
            tag = BlockedUserProfileFragment.TAG;
            getControllerFactory().getNavigationController().setLeftPage(Page.PENDING_CONNECT_REQUEST, TAG);
            break;
        default:
            return;
        }

        getChildFragmentManager().beginTransaction()
                .setCustomAnimations(R.anim.fragment_animation__send_connect_request__fade_in,
                        R.anim.fragment_animation__send_connect_request__zoom_exit,
                        R.anim.fragment_animation__send_connect_request__zoom_enter,
                        R.anim.fragment_animation__send_connect_request__fade_out)
                .replace(R.id.fl__conversation_list__profile_overlay, fragment, tag).addToBackStack(tag).commit();

        togglePeoplePicker(false);
    }

    @Override
    public void onHideUserProfile() {
        // Profiles are handled in dialog on tablet
        if (LayoutSpec.isTablet(getActivity())) {
            return;
        }

        getControllerFactory().getNavigationController().setLeftPage(Page.PICK_USER, TAG);
        getChildFragmentManager().popBackStackImmediate();
        togglePeoplePicker(true);
    }

    @Override
    public void onShowCommonUserProfile(final User user) {
        if (getControllerFactory().getPickUserController().isShowingCommonUserProfile()) {
            return;
        }

        UserProfile profileFragment = (UserProfile) getChildFragmentManager()
                .findFragmentById(R.id.fl__conversation_list__profile_overlay);
        if (profileFragment != null) {
            profileFragment.isBelowUserProfile(true);
        }

        getStoreFactory().getSingleParticipantStore().setUser(user);

        getChildFragmentManager().beginTransaction()
                .setCustomAnimations(R.anim.fragment_animation__send_connect_request__fade_in,
                        R.anim.fragment_animation__send_connect_request__zoom_exit,
                        R.anim.fragment_animation__send_connect_request__zoom_enter,
                        R.anim.fragment_animation__send_connect_request__fade_out)
                .replace(R.id.fl__conversation_list__profile_overlay,
                        SingleParticipantFragment.newInstance(true, IConnectStore.UserRequester.SEARCH),
                        SingleParticipantFragment.TAG)
                .addToBackStack(SingleParticipantFragment.TAG).commit();

        getControllerFactory().getNavigationController().setLeftPage(Page.COMMON_USER_PROFILE, TAG);
    }

    @Override
    public void onHideCommonUserProfile() {
        getChildFragmentManager().popBackStackImmediate();

        Fragment fragment = getChildFragmentManager().findFragmentById(R.id.fl__conversation_list__profile_overlay);
        if (fragment instanceof SendConnectRequestFragment) {
            getControllerFactory().getNavigationController().setLeftPage(Page.SEND_CONNECT_REQUEST, TAG);
        } else if (fragment instanceof PendingConnectRequestManagerFragment) {
            getControllerFactory().getNavigationController().setLeftPage(Page.PENDING_CONNECT_REQUEST, TAG);
        } else if (fragment instanceof BlockedUserProfileFragment) {
            getControllerFactory().getNavigationController().setLeftPage(Page.BLOCK_USER, TAG);
        }
    }

    /**
     * Decides based on state returned from PickUserController whether to show START UI or CONVERSATIONLIST
     */
    private void loadPage() {
        Page page = getControllerFactory().getNavigationController().getCurrentLeftPage();

        switch (page) {
        // TODO: START is set as left page on tablet, fix
        case START:
        case CONVERSATION_LIST:
            getStoreFactory().getInAppNotificationStore().setUserLookingAtPeoplePicker(false);
            break;
        case PICK_USER:
            getStoreFactory().getInAppNotificationStore().setUserLookingAtPeoplePicker(true);
            getControllerFactory().getPickUserController()
                    .showPickUser(IPickUserController.Destination.CONVERSATION_LIST, null);
            break;
        case BLOCK_USER:
        case PENDING_CONNECT_REQUEST:
        case SEND_CONNECT_REQUEST:
            togglePeoplePicker(false);
            break;
        case COMMON_USER_PROFILE:
            togglePeoplePicker(false);
            break;
        }
    }

    @Override
    public void onOpenCamera(CameraContext cameraContext) {

    }

    @Override
    public void onCloseCamera(CameraContext cameraContext) {

    }

    @Override
    public void onOpenUrl(String url) {
        getContainer().onOpenUrl(url);
    }

    //////////////////////////////////////////////////////////////////////////////////////////
    //
    //  CameraFragment.Container
    //
    //////////////////////////////////////////////////////////////////////////////////////////

    @Override
    public void onBitmapSelected(final ImageAsset imageAsset, final boolean imageFromCamera,
            CameraContext cameraContext) {
    }

    @Override
    public void onCameraNotAvailable() {

    }

    private void showNoNetworkError() {
        ViewUtils.showAlertDialog(getActivity(), R.string.alert_dialog__no_network__header,
                R.string.profile_pic__no_network__message, R.string.alert_dialog__confirmation, null, true);
    }

    //////////////////////////////////////////////////////////////////////////////////////////
    //
    //  AccentColorObserver
    //
    //////////////////////////////////////////////////////////////////////////////////////////

    @Override
    public void onAccentColorHasChanged(Object sender, int color) {
        listLoadingIndicatorView.setColor(color);
        confirmationMenu.setButtonColor(color);
    }

    //////////////////////////////////////////////////////////////////////////////////////////
    //
    //  ConfirmationObserver
    //
    //////////////////////////////////////////////////////////////////////////////////////////

    @Override
    public void onRequestConfirmation(ConfirmationRequest confirmationRequest,
            @IConfirmationController.ConfirmationMenuRequester int requester) {
        if (LayoutSpec.isPhone(getActivity()) || requester != IConfirmationController.CONVERSATION_LIST) {
            return;
        }
        confirmationMenu.onRequestConfirmation(confirmationRequest);
    }

    //////////////////////////////////////////////////////////////////////////////////////////
    //
    //  SlidingListener
    //
    //////////////////////////////////////////////////////////////////////////////////////////

    @Override
    public void onOptionMenuStateHasChanged(OptionsMenu.State state) {
        switch (state) {
        case OPEN:
            break;
        case OPENING:
            if (LayoutSpec.isPhone(getActivity())) {
                getControllerFactory().getNavigationController()
                        .setLeftPage(Page.CONVERSATION_MENU_OVER_CONVERSATION_LIST, TAG);
            }
            break;
        case CLOSING:
            break;
        case CLOSED:
            if (LayoutSpec.isPhone(getActivity())) {
                getControllerFactory().getNavigationController().setLeftPage(Page.CONVERSATION_LIST, TAG);
            }
            break;
        }
    }

    @Override
    public void onOptionsItemClicked(IConversation conversation, User user, OptionsMenuItem item) {
        switch (item) {
        case ARCHIVE:
            getStoreFactory().getConversationStore().archive(conversation, true);
            getControllerFactory().getTrackingController()
                    .tagEvent(new ArchivedConversationEvent(conversation.getType().toString()));
            break;
        case UNARCHIVE:
            getStoreFactory().getConversationStore().archive(conversation, false);
            getControllerFactory().getTrackingController()
                    .tagEvent(new UnarchivedConversationEvent(conversation.getType().toString()));
            break;
        case SILENCE:
            conversation.setMuted(true);
            break;
        case UNSILENCE:
            conversation.setMuted(false);
            break;
        case LEAVE:
            leaveConversation(conversation);
            break;
        case DELETE:
            deleteConversation(conversation);
            break;
        case BLOCK:
            showBlockConfirmation(user);
            break;
        case UNBLOCK:
            user.unblock();
            break;
        case CALL:
            callConversation(conversation);
            break;
        case PICTURE:
            sendPictureToConversation(conversation);
            break;
        }

        closeMenu();
    }

    @Override
    public OptionsMenuControl getOptionsMenuControl() {
        return optionsMenuControl;
    }

    private boolean closeMenu() {
        return optionsMenuControl.close();
    }

    //////////////////////////////////////////////////////////////////////////////////////////
    //
    //  ConversationScreenControllerObserver
    //
    //////////////////////////////////////////////////////////////////////////////////////////

    @Override
    public void onShowParticipants(View anchorView, boolean isSingleConversation, boolean isMemberOfConversation,
            boolean showDeviceTabIfSingle) {

    }

    @Override
    public void onHideParticipants(boolean backOrButtonPressed, boolean hideByConversationChange,
            boolean isSingleConversation) {

    }

    @Override
    public void onShowEditConversationName(boolean show) {

    }

    @Override
    public void setListOffset(int offset) {

    }

    @Override
    public void onHeaderViewMeasured(int participantHeaderHeight) {

    }

    @Override
    public void onScrollParticipantsList(int verticalOffset, boolean scrolledToBottom) {

    }

    @Override
    public void onConversationLoaded() {

    }

    @Override
    public void onShowUser(User user) {

    }

    @Override
    public void onHideUser() {

    }

    @Override
    public void onShowCommonUser(User user) {

    }

    @Override
    public void onHideCommonUser() {

    }

    @Override
    public void onAddPeopleToConversation() {

    }

    @Override
    public void onShowConversationMenu(@IConversationScreenController.ConversationMenuRequester int requester,
            IConversation conversation, View anchorView) {
        if ((requester != IConversationScreenController.CONVERSATION_LIST_SWIPE
                && requester != IConversationScreenController.CONVERSATION_LIST_LONG_PRESS)
                || getControllerFactory() == null || getControllerFactory().isTornDown()) {
            return;
        }

        optionsMenuControl.setTitle(conversation.getName());
        optionsMenuControl.createMenu(conversation, requester,
                getControllerFactory().getThemeController().getOptionsDarkTheme());
        optionsMenuControl.open();
    }

    @Override
    public void onShowOtrClient(OtrClient otrClient, User user) {

    }

    @Override
    public void onShowCurrentOtrClient() {

    }

    @Override
    public void onHideOtrClient() {

    }

    @Override
    public void onShowLikesList(Message message) {

    }

    //////////////////////////////////////////////////////////////////////////////////////////
    //
    //  ConversationListFragment.Container
    //
    //////////////////////////////////////////////////////////////////////////////////////////

    public void leaveConversation(final IConversation conversation) {
        closeMenu();
        ConfirmationCallback callback = new TwoButtonConfirmationCallback() {
            @Override
            public void positiveButtonClicked(boolean checkboxIsSelected) {
                if (getStoreFactory() == null || getControllerFactory() == null || getStoreFactory().isTornDown()
                        || getControllerFactory().isTornDown()) {
                    return;
                }
                getControllerFactory().getTrackingController().tagEvent(new LeaveGroupConversationEvent(true,
                        getStoreFactory().getConversationStore().getCurrentConversation().getUsers().size()));

                getStoreFactory().getConversationStore().leave(conversation);
                getStoreFactory().getConversationStore()
                        .setCurrentConversationToNext(ConversationChangeRequester.LEAVE_CONVERSATION);
            }

            @Override
            public void negativeButtonClicked() {
                if (getControllerFactory() == null || getControllerFactory().isTornDown()) {
                    return;
                }
                getControllerFactory().getTrackingController().tagEvent(new LeaveGroupConversationEvent(false,
                        getStoreFactory().getConversationStore().getCurrentConversation().getUsers().size()));
            }

            @Override
            public void onHideAnimationEnd(boolean confirmed, boolean canceled, boolean checkboxIsSelected) {

            }
        };
        String header = getString(R.string.confirmation_menu__meta_remove);
        String text = getString(R.string.confirmation_menu__meta_remove_text);
        String confirm = getString(R.string.confirmation_menu__confirm_leave);
        String cancel = getString(R.string.confirmation_menu__cancel);

        ConfirmationRequest request = new ConfirmationRequest.Builder(IConfirmationController.LEAVE_CONVERSATION)
                .withHeader(header).withMessage(text).withPositiveButton(confirm).withNegativeButton(cancel)
                .withConfirmationCallback(callback)
                .withWireTheme(getControllerFactory().getThemeController().getOptionsDarkTheme()).build();

        getControllerFactory().getConfirmationController().requestConfirmation(request,
                IConfirmationController.CONVERSATION_LIST);

        getStoreFactory().getMediaStore().playSound(R.raw.alert);
        getControllerFactory().getVibratorController().vibrate(R.array.alert);
    }

    public void callConversation(final IConversation conversation) {
        getStoreFactory().getConversationStore().setCurrentConversation(conversation,
                ConversationChangeRequester.CONVERSATION_LIST);
        getControllerFactory().getCallingController().startCall(false);
    }

    public void sendPictureToConversation(final IConversation conversation) {
        getStoreFactory().getConversationStore().setCurrentConversation(conversation,
                ConversationChangeRequester.CONVERSATION_LIST);
        getControllerFactory().getCameraController().openCamera(CameraContext.MESSAGE);
    }

    public void deleteConversation(final IConversation conversation) {
        closeMenu();
        ConfirmationCallback callback = new TwoButtonConfirmationCallback() {
            @Override
            public void positiveButtonClicked(boolean checkboxIsSelected) {

            }

            @Override
            public void negativeButtonClicked() {
            }

            @Override
            public void onHideAnimationEnd(boolean confirmed, boolean canceled, boolean checkboxIsSelected) {
                if (getStoreFactory() == null || getStoreFactory().isTornDown() || getControllerFactory() == null
                        || getControllerFactory().isTornDown()) {
                    return;
                }

                if (!confirmed) {
                    getControllerFactory().getTrackingController()
                            .tagEvent(new DeleteConversationEvent(ConversationType.getValue(conversation),
                                    DeleteConversationEvent.Context.LIST, DeleteConversationEvent.Response.CANCEL));
                    return;
                }

                boolean deleteCurrentConversation = conversation.getId()
                        .equals(getStoreFactory().getConversationStore().getCurrentConversation().getId());
                getStoreFactory().getConversationStore().deleteConversation(conversation, checkboxIsSelected);
                getControllerFactory().getTrackingController()
                        .tagEvent(new DeleteConversationEvent(ConversationType.getValue(conversation),
                                DeleteConversationEvent.Context.LIST, DeleteConversationEvent.Response.DELETE));

                if (deleteCurrentConversation) {
                    getStoreFactory().getConversationStore()
                            .setCurrentConversationToNext(ConversationChangeRequester.DELETE_CONVERSATION);
                }
            }
        };
        String header = getString(R.string.confirmation_menu__meta_delete);
        String text = getString(R.string.confirmation_menu__meta_delete_text);
        String confirm = getString(R.string.confirmation_menu__confirm_delete);
        String cancel = getString(R.string.confirmation_menu__cancel);
        String checkboxLabel = "";
        if (conversation.getType() == IConversation.Type.GROUP) {
            checkboxLabel = getString(R.string.confirmation_menu__delete_conversation__checkbox__label);
        }

        ConfirmationRequest request = new ConfirmationRequest.Builder(IConfirmationController.DELETE_CONVERSATION)
                .withHeader(header).withMessage(text).withPositiveButton(confirm).withNegativeButton(cancel)
                .withConfirmationCallback(callback).withCheckboxLabel(checkboxLabel)
                .withWireTheme(getControllerFactory().getThemeController().getOptionsDarkTheme()).build();

        getControllerFactory().getConfirmationController().requestConfirmation(request,
                IConfirmationController.CONVERSATION_LIST);

        getStoreFactory().getMediaStore().playSound(R.raw.alert);
        getControllerFactory().getVibratorController().vibrate(R.array.alert);
    }

    private void showBlockConfirmation(final User user) {
        ConfirmationCallback callback = new TwoButtonConfirmationCallback() {
            @Override
            public void positiveButtonClicked(boolean checkboxIsSelected) {
                if (getStoreFactory() == null || getControllerFactory() == null || getStoreFactory().isTornDown()
                        || getControllerFactory().isTornDown()) {
                    return;
                }
                boolean blockingCurrentConversation = user.getConversation().getId()
                        .equals(getStoreFactory().getConversationStore().getCurrentConversation().getId());
                getStoreFactory().getConnectStore().blockUser(user);
                if (blockingCurrentConversation) {
                    getStoreFactory().getConversationStore()
                            .setCurrentConversationToNext(ConversationChangeRequester.BLOCK_USER);
                }
                getControllerFactory().getTrackingController()
                        .tagEvent(new BlockingEvent(BlockingEvent.ConformationResponse.BLOCK));
            }

            @Override
            public void negativeButtonClicked() {
                if (getControllerFactory() == null || getControllerFactory().isTornDown()) {
                    return;
                }
                getControllerFactory().getTrackingController()
                        .tagEvent(new BlockingEvent(BlockingEvent.ConformationResponse.CANCEL));
            }

            @Override
            public void onHideAnimationEnd(boolean confirmed, boolean canceled, boolean checkboxIsSelected) {

            }
        };
        String header = getString(R.string.confirmation_menu__block_header);
        String text = getString(R.string.confirmation_menu__block_text_with_name, user.getDisplayName());
        String confirm = getString(R.string.confirmation_menu__confirm_block);
        String cancel = getString(R.string.confirmation_menu__cancel);

        ConfirmationRequest request = new ConfirmationRequest.Builder(IConfirmationController.BLOCK_CONNECTED)
                .withHeader(header).withMessage(text).withPositiveButton(confirm).withNegativeButton(cancel)
                .withConfirmationCallback(callback)
                .withWireTheme(getControllerFactory().getThemeController().getOptionsDarkTheme()).build();

        getControllerFactory().getConfirmationController().requestConfirmation(request,
                IConfirmationController.CONVERSATION_LIST);

        getStoreFactory().getMediaStore().playSound(R.raw.alert);
        getControllerFactory().getVibratorController().vibrate(R.array.alert);
    }

    @Override
    public void onConnectRequestInboxConversationsLoaded(List<IConversation> conversations,
            InboxLoadRequester inboxLoadRequester) {

    }

    @Override
    public void onIncomingMessage(Message message) {

    }

    @Override
    public void onIncomingKnock(KnockingEvent knock) {

    }

    @Override
    public void onSyncError(ErrorsList.ErrorDescription errorDescription) {
        if (errorDescription.getType() != ErrorType.CANNOT_SEND_MESSAGE_TO_UNVERIFIED_CONVERSATION) {
            return;
        }

        if (getControllerFactory().getNavigationController().getCurrentPage() == Page.MESSAGE_STREAM) {
            return;
        }

        Toast.makeText(getActivity(), "New to unverified - will dismiss!", Toast.LENGTH_SHORT).show();
        errorDescription.dismiss();
    }

    public interface Container {
        void onOpenUrl(String url);
    }
}