/**
* 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.participants;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
import android.os.Build;
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.support.v4.app.FragmentTransaction;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import com.waz.api.ConversationsList;
import com.waz.api.IConversation;
import com.waz.api.Message;
import com.waz.api.OtrClient;
import com.waz.api.SyncState;
import com.waz.api.User;
import com.waz.api.UsersList;
import com.waz.zclient.BaseActivity;
import com.waz.zclient.OnBackPressedListener;
import com.waz.zclient.R;
import com.waz.zclient.controllers.confirmation.ConfirmationCallback;
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.navigation.NavigationController;
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.AddedMemberToGroupEvent;
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.group.RemoveContactEvent;
import com.waz.zclient.core.api.scala.ModelObserver;
import com.waz.zclient.core.controllers.tracking.attributes.ConversationType;
import com.waz.zclient.core.stores.connect.IConnectStore;
import com.waz.zclient.core.stores.connect.InboxLinkConversation;
import com.waz.zclient.core.stores.conversation.ConversationChangeRequester;
import com.waz.zclient.core.stores.conversation.ConversationStoreObserver;
import com.waz.zclient.core.stores.conversation.OnConversationLoadedListener;
import com.waz.zclient.core.stores.participants.ParticipantsStoreObserver;
import com.waz.zclient.media.SoundController;
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.PendingConnectRequestFragment;
import com.waz.zclient.pages.main.connect.SendConnectRequestFragment;
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.dialog.DialogLaunchMode;
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.tracking.GlobalTrackingController;
import com.waz.zclient.ui.animation.interpolators.penner.Expo;
import com.waz.zclient.ui.animation.interpolators.penner.Linear;
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.theme.OptionsTheme;
import com.waz.zclient.utils.LayoutSpec;
import com.waz.zclient.utils.ViewUtils;
import com.waz.zclient.views.DefaultPageTransitionAnimation;
import com.waz.zclient.views.LoadingIndicatorView;
import java.util.ArrayList;
import java.util.List;
public class ParticipantFragment extends BaseFragment<ParticipantFragment.Container> implements
ParticipantHeaderFragment.Container,
ParticipantBodyFragment.Container,
TabbedParticipantBodyFragment.Container,
ParticipantsStoreObserver,
SingleParticipantFragment.Container,
SendConnectRequestFragment.Container,
BlockedUserProfileFragment.Container,
PendingConnectRequestFragment.Container,
OptionsMenuFragment.Container,
PickUserFragment.Container,
OnConversationLoadedListener,
ConversationScreenControllerObserver,
ConversationStoreObserver,
OnBackPressedListener,
PickUserControllerScreenObserver,
SingleOtrClientFragment.Container {
public static final String TAG = ParticipantFragment.class.getName();
private static final String ARG_USER_REQUESTER = "ARG_USER_REQUESTER";
private static final String ARG__FIRST__PAGE = "ARG__FIRST__PAGE";
private View bodyContainer;
private View participantsContainerView;
private View pickUserContainerView;
private LoadingIndicatorView loadingIndicatorView;
private View conversationSettingsOverlayLayout;
private IConnectStore.UserRequester userRequester;
private OptionsMenuControl optionsMenuControl;
private boolean groupConversation;
private User otherUser;
private final ModelObserver<IConversation> conversationModelObserver = new ModelObserver<IConversation>() {
@Override
public void updated(IConversation model) {
groupConversation = IConversation.Type.GROUP.equals(model.getType());
if (groupConversation) {
otherUser = null;
} else {
otherUser = model.getOtherParticipant();
}
}
};
public static ParticipantFragment newInstance(IConnectStore.UserRequester userRequester, int firstPage) {
ParticipantFragment participantFragment = new ParticipantFragment();
Bundle args = new Bundle();
args.putSerializable(ARG_USER_REQUESTER, userRequester);
args.putInt(ARG__FIRST__PAGE, firstPage);
participantFragment.setArguments(args);
return participantFragment;
}
@Override
public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
if (nextAnim == 0 ||
getContainer() == null ||
getControllerFactory().isTornDown() ||
LayoutSpec.isTablet(getActivity())) {
return super.onCreateAnimation(transit, enter, nextAnim);
}
if (enter) {
return new DefaultPageTransitionAnimation(0,
getResources().getDimensionPixelSize(R.dimen.open_new_conversation__thread_list__max_top_distance),
enter,
getResources().getInteger(R.integer.framework_animation_duration_medium),
getResources().getInteger(R.integer.framework_animation_duration_medium),
1f);
}
return new DefaultPageTransitionAnimation(0,
getResources().getDimensionPixelSize(R.dimen.open_new_conversation__thread_list__max_top_distance),
enter,
getResources().getInteger(R.integer.framework_animation_duration_medium),
0,
1f);
}
//////////////////////////////////////////////////////////////////////////////////////////
//
// Lifecycle
//
//////////////////////////////////////////////////////////////////////////////////////////
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle args = getArguments();
userRequester = (IConnectStore.UserRequester) args.getSerializable(ARG_USER_REQUESTER);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_participant, container, false);
FragmentManager fragmentManager = getChildFragmentManager();
optionsMenuControl = new OptionsMenuControl();
if (savedInstanceState == null) {
fragmentManager.beginTransaction()
.replace(R.id.fl__participant__header__container,
ParticipantHeaderFragment.newInstance(userRequester),
ParticipantHeaderFragment.TAG)
.commit();
IConversation currentConversation = getStoreFactory() != null &&
!getStoreFactory().isTornDown() ?
getStoreFactory().getConversationStore().getCurrentConversation() : null;
if (currentConversation != null &&
(currentConversation.getType() == IConversation.Type.ONE_TO_ONE ||
userRequester == IConnectStore.UserRequester.POPOVER)) {
fragmentManager.beginTransaction()
.replace(R.id.fl__participant__container,
TabbedParticipantBodyFragment.newInstance(getArguments().getInt(ARG__FIRST__PAGE)),
TabbedParticipantBodyFragment.TAG)
.commit();
} else {
fragmentManager.beginTransaction()
.replace(R.id.fl__participant__container,
ParticipantBodyFragment.newInstance(userRequester),
ParticipantBodyFragment.TAG)
.commit();
}
getChildFragmentManager().beginTransaction()
.replace(R.id.fl__participant__settings_box,
OptionsMenuFragment.newInstance(false),
OptionsMenuFragment.TAG)
.commit();
}
Fragment overlayFragment = fragmentManager.findFragmentById(R.id.fl__participant__overlay);
if (overlayFragment != null) {
fragmentManager.beginTransaction()
.remove(overlayFragment)
.commit();
}
bodyContainer = ViewUtils.getView(view, R.id.fl__participant__container);
loadingIndicatorView = ViewUtils.getView(view, R.id.liv__participants__loading_indicator);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
//noinspection deprecation
loadingIndicatorView.setColor(getResources().getColor(R.color.people_picker__loading__color));
} else {
loadingIndicatorView.setColor(getResources().getColor(R.color.people_picker__loading__color, getContext().getTheme()));
}
participantsContainerView = ViewUtils.getView(view, R.id.ll__participant__container);
pickUserContainerView = ViewUtils.getView(view, R.id.fl__add_to_conversation__pickuser__container);
conversationSettingsOverlayLayout = ViewUtils.getView(view, R.id.fl__conversation_actions__sliding_overlay);
conversationSettingsOverlayLayout.setVisibility(View.GONE);
return view;
}
@Override
public void onStart() {
super.onStart();
getStoreFactory().getParticipantsStore().addParticipantsStoreObserver(this);
getStoreFactory().getConversationStore().addConversationStoreObserver(this);
if (userRequester == IConnectStore.UserRequester.POPOVER) {
final User user = getStoreFactory().getSingleParticipantStore().getUser();
getStoreFactory().getConnectStore().loadUser(user.getId(), userRequester);
} else {
getStoreFactory().getConversationStore().loadCurrentConversation(this);
}
if (LayoutSpec.isPhone(getActivity())) {
// ConversationScreenController is handled in ParticipantDialogFragment for tablets
getControllerFactory().getConversationScreenController().addConversationControllerObservers(this);
}
getControllerFactory().getPickUserController().addPickUserScreenControllerObserver(this);
}
@Override
public void onStop() {
getStoreFactory().getParticipantsStore().setCurrentConversation(null);
getStoreFactory().getConversationStore().removeConversationStoreObserver(this);
if (LayoutSpec.isPhone(getActivity())) {
getControllerFactory().getConversationScreenController().removeConversationControllerObservers(this);
}
getStoreFactory().getParticipantsStore().removeParticipantsStoreObserver(this);
getControllerFactory().getPickUserController().removePickUserScreenControllerObserver(this);
super.onStop();
}
@Override
public void onDestroyView() {
if (!getControllerFactory().isTornDown()) {
getControllerFactory()
.getSingleImageController()
.clearReferences();
}
bodyContainer = null;
loadingIndicatorView = null;
participantsContainerView = null;
pickUserContainerView = null;
conversationSettingsOverlayLayout = null;
super.onDestroyView();
}
@Override
public void onConversationListUpdated(@NonNull ConversationsList conversationsList) { }
@Override
public void onConversationListStateHasChanged(ConversationsList.ConversationsListState state) { }
@Override
public void onCurrentConversationHasChanged(IConversation fromConversation,
IConversation toConversation,
ConversationChangeRequester conversationChangeRequester) {
if (toConversation == null || toConversation instanceof InboxLinkConversation) {
return;
}
onConversationLoaded(toConversation);
if (conversationChangeRequester == ConversationChangeRequester.START_CONVERSATION ||
conversationChangeRequester == ConversationChangeRequester.START_CONVERSATION_FOR_VIDEO_CALL ||
conversationChangeRequester == ConversationChangeRequester.START_CONVERSATION_FOR_CALL ||
conversationChangeRequester == ConversationChangeRequester.START_CONVERSATION_FOR_CAMERA) {
getChildFragmentManager().popBackStackImmediate(PickUserFragment.TAG,
FragmentManager.POP_BACK_STACK_INCLUSIVE);
getControllerFactory().getPickUserController().hidePickUserWithoutAnimations(
getCurrentPickerDestination());
}
}
@Override
public void onConversationSyncingStateHasChanged(SyncState syncState) {
}
@Override
public void onMenuConversationHasChanged(IConversation fromConversation) {
}
//////////////////////////////////////////////////////////////////////////////////////////
//
// ParticipantsStoreObserver
//
//////////////////////////////////////////////////////////////////////////////////////////
@Override
public void conversationUpdated(IConversation conversation) {
// Membership might change when user is removed by another user
getControllerFactory().getConversationScreenController().setMemberOfConversation(conversation.isMemberOfConversation());
}
@Override
public void participantsUpdated(UsersList participants) {
ArrayList<String> participantIds = new ArrayList<>();
for (int i = 0; i < participants.size(); i++) {
participantIds.add(participants.get(i).getId());
}
// Exclude existing participants of conversation when adding people
getStoreFactory().getPickUserStore().setExcludedUsers(participantIds.toArray(new String[participantIds.size()]));
}
@Override
public void otherUserUpdated(User otherUser) {
// Exclude existing participant of conversation when adding people
getStoreFactory().getPickUserStore().setExcludedUsers(new String[] {otherUser.getId()});
}
//////////////////////////////////////////////////////////////////////////////////////////
//
// ConversationSettingBox.Container
//
//////////////////////////////////////////////////////////////////////////////////////////
@Override
public void onOptionMenuStateHasChanged(OptionsMenu.State state) {
}
@Override
public void onOptionsItemClicked(IConversation conversation, User user, OptionsMenuItem item) {
switch (item) {
case ARCHIVE:
toggleArchiveConversation(conversation, true);
break;
case UNARCHIVE:
toggleArchiveConversation(conversation, false);
break;
case SILENCE:
conversation.setMuted(true);
break;
case UNSILENCE:
conversation.setMuted(false);
break;
case LEAVE:
showLeaveConfirmation(conversation);
break;
case RENAME:
getControllerFactory().getConversationScreenController().editConversationName(true);
break;
case DELETE:
deleteConversation(conversation);
break;
case BLOCK:
showBlockUserConfirmation(user);
break;
case UNBLOCK:
user.unblock();
break;
}
closeMenu();
}
private void showBlockUserConfirmation(final User user) {
ConfirmationCallback callback = new TwoButtonConfirmationCallback() {
@Override
public void positiveButtonClicked(boolean checkboxIsSelected) {
getStoreFactory().getConversationStore().setCurrentConversationToNext(ConversationChangeRequester.BLOCK_USER);
getStoreFactory().getConnectStore().blockUser(user);
getControllerFactory().getConversationScreenController().hideUser();
if (LayoutSpec.isTablet(getActivity())) {
getControllerFactory().getConversationScreenController().hideParticipants(false, true);
}
((BaseActivity) getActivity()).injectJava(GlobalTrackingController.class).tagEvent(new BlockingEvent(BlockingEvent.ConformationResponse.BLOCK));
}
@Override
public void negativeButtonClicked() {
((BaseActivity) getActivity()).injectJava(GlobalTrackingController.class).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);
OptionsTheme optionsTheme = getControllerFactory().getThemeController().getThemeDependentOptionsTheme();
ConfirmationRequest request = new ConfirmationRequest.Builder()
.withHeader(header)
.withMessage(text)
.withPositiveButton(confirm)
.withNegativeButton(cancel)
.withConfirmationCallback(callback)
.withWireTheme(optionsTheme)
.build();
getControllerFactory().getConfirmationController().requestConfirmation(request, IConfirmationController.PARTICIPANTS);
SoundController ctrl = inject(SoundController.class);
if (ctrl != null) {
ctrl.playAlert();
}
}
public void deleteConversation(final IConversation conversation) {
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) {
((BaseActivity) getActivity()).injectJava(GlobalTrackingController.class).tagEvent(new DeleteConversationEvent(ConversationType.getValue(conversation),
DeleteConversationEvent.Context.LIST,
DeleteConversationEvent.Response.CANCEL));
return;
}
IConversation currentConversation = getStoreFactory().getConversationStore().getCurrentConversation();
boolean deleteCurrentConversation = conversation != null && currentConversation != null &&
conversation.getId().equals(currentConversation.getId());
getStoreFactory().getConversationStore().deleteConversation(conversation, checkboxIsSelected);
((BaseActivity) getActivity()).injectJava(GlobalTrackingController.class).tagEvent(new DeleteConversationEvent(ConversationType.getValue(
conversation),
DeleteConversationEvent.Context.PARTICIPANTS,
DeleteConversationEvent.Response.DELETE));
if (deleteCurrentConversation) {
getStoreFactory().getConversationStore().setCurrentConversationToNext(ConversationChangeRequester.DELETE_CONVERSATION);
}
if (LayoutSpec.isTablet(getActivity())) {
getControllerFactory().getConversationScreenController().hideParticipants(false, true);
}
}
};
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);
ConfirmationRequest.Builder builder = new ConfirmationRequest.Builder()
.withHeader(header)
.withMessage(text)
.withPositiveButton(confirm)
.withNegativeButton(cancel)
.withConfirmationCallback(callback)
.withWireTheme(getControllerFactory().getThemeController().getThemeDependentOptionsTheme());
if (conversation.getType() == IConversation.Type.GROUP) {
builder = builder
.withCheckboxLabel(getString(R.string.confirmation_menu__delete_conversation__checkbox__label))
.withCheckboxSelectedByDefault();
}
getControllerFactory().getConfirmationController().requestConfirmation(builder.build(), IConfirmationController.PARTICIPANTS);
SoundController ctrl = inject(SoundController.class);
if (ctrl != null) {
ctrl.playAlert();
}
}
public void toggleArchiveConversation(final IConversation conversation, final boolean archive) {
if (getStoreFactory().getConversationStore() != null) {
getControllerFactory().getNavigationController().setVisiblePage(Page.CONVERSATION_LIST, TAG);
getControllerFactory().getConversationScreenController().hideParticipants(false, true);
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
if (getContainer() == null) {
return;
}
getStoreFactory().getConversationStore().archive(conversation, archive);
if (getControllerFactory() == null ||
getControllerFactory().isTornDown()) {
return;
}
if (archive) {
((BaseActivity) getActivity()).injectJava(GlobalTrackingController.class).tagEvent(new ArchivedConversationEvent(conversation.getType().toString()));
} else {
((BaseActivity) getActivity()).injectJava(GlobalTrackingController.class).tagEvent(new UnarchivedConversationEvent(conversation.getType().toString()));
}
}
}, getResources().getInteger(R.integer.framework_animation_duration_medium));
}
}
@Override
public void onClickedEmptyBackground() {
if (!getControllerFactory().getConversationScreenController().isSingleConversation()) {
return;
}
if (LayoutSpec.isTablet(getActivity())) {
final User user = getStoreFactory().getSingleParticipantStore()
.getUser();
if (user == null) {
return;
}
getControllerFactory().getSingleImageController().setViewReferences(bodyContainer);
getControllerFactory().getSingleImageController().showSingleImage(user);
}
}
@Override
public void toggleBlockUser(User otherUser, boolean block) { }
@Override
public void dismissDialog() {
getContainer().dismissDialog();
}
@Override
public void onConversationUpdated(IConversation conversation) { }
//////////////////////////////////////////////////////////////////////////////////////////
//
// OnBackPressedListener
//
//////////////////////////////////////////////////////////////////////////////////////////
@Override
public boolean onBackPressed() {
PickUserFragment pickUserFragment = (PickUserFragment) getChildFragmentManager().findFragmentByTag(
PickUserFragment.TAG);
if (pickUserFragment != null && pickUserFragment.onBackPressed()) {
return true;
}
SingleOtrClientFragment singleOtrClientFragment = (SingleOtrClientFragment) getChildFragmentManager().findFragmentByTag(
SingleOtrClientFragment.TAG);
if (singleOtrClientFragment != null) {
getControllerFactory().getConversationScreenController().hideOtrClient();
return true;
}
SingleParticipantFragment singleUserFragment = (SingleParticipantFragment) getChildFragmentManager().findFragmentByTag(
SingleParticipantFragment.TAG);
if (singleUserFragment != null && singleUserFragment.onBackPressed()) {
return true;
}
if (closeMenu()) {
return true;
}
if (getControllerFactory().getPickUserController().isShowingPickUser(getCurrentPickerDestination())) {
getControllerFactory().getPickUserController().hidePickUser(getCurrentPickerDestination(), true);
return true;
}
if (getControllerFactory().getConversationScreenController().isShowingUser()) {
getControllerFactory().getConversationScreenController().hideUser();
return true;
}
if (getControllerFactory().getConversationScreenController().isShowingParticipant()) {
getControllerFactory().getConversationScreenController().hideParticipants(true, false);
return true;
}
return false;
}
//////////////////////////////////////////////////////////////////////////////////////////
//
// ConversationManagerScreenControllerObserver
//
//////////////////////////////////////////////////////////////////////////////////////////
@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) {
if (LayoutSpec.isTablet(getActivity())) {
return;
}
if (show) {
ViewUtils.fadeOutView(bodyContainer);
} else {
ViewUtils.fadeInView(bodyContainer);
}
}
@Override
public void setListOffset(int offset) { }
@Override
public void onHeaderViewMeasured(int participantHeaderHeight) { }
@Override
public void onScrollParticipantsList(int verticalOffset, boolean scrolledToBottom) { }
@Override
public void onConversationLoaded() { }
//////////////////////////////////////////////////////////////////////////////////////////
//
// OnConversationLoadedListener
//
//////////////////////////////////////////////////////////////////////////////////////////
@Override
public void onConversationLoaded(IConversation conversation) {
getControllerFactory().getConversationScreenController().setSingleConversation(conversation.getType().equals(
IConversation.Type.ONE_TO_ONE));
getControllerFactory().getConversationScreenController().setMemberOfConversation(conversation.isMemberOfConversation());
getStoreFactory().getParticipantsStore().setCurrentConversation(conversation);
conversationModelObserver.setAndUpdate(conversation);
}
//////////////////////////////////////////////////////////////////////////////////////////
//
// ConversationScreenControllerObserver
//
//////////////////////////////////////////////////////////////////////////////////////////
@Override
public void onAddPeopleToConversation() {
getControllerFactory().getPickUserController().showPickUser(IPickUserController.Destination.PARTICIPANTS, null);
}
@Override
public void onShowConversationMenu(@IConversationScreenController.ConversationMenuRequester int requester,
IConversation conversation,
View anchorView) {
if (requester != IConversationScreenController.USER_PROFILE_PARTICIPANTS &&
requester != IConversationScreenController.CONVERSATION_DETAILS) {
return;
}
optionsMenuControl.createMenu(conversation,
requester,
getControllerFactory().getThemeController().getThemeDependentOptionsTheme());
optionsMenuControl.open();
}
@Override
public void onShowOtrClient(OtrClient otrClient, User user) {
getChildFragmentManager().beginTransaction()
.setCustomAnimations(R.anim.open_profile,
R.anim.close_profile,
R.anim.open_profile,
R.anim.close_profile)
.add(R.id.fl__participant__overlay,
SingleOtrClientFragment.newInstance(otrClient, user),
SingleOtrClientFragment.TAG)
.addToBackStack(SingleOtrClientFragment.TAG)
.commit();
}
@Override
public void onShowCurrentOtrClient() {
getChildFragmentManager().beginTransaction()
.setCustomAnimations(R.anim.open_profile,
R.anim.close_profile,
R.anim.open_profile,
R.anim.close_profile)
.add(R.id.fl__participant__overlay,
SingleOtrClientFragment.newInstance(),
SingleOtrClientFragment.TAG)
.addToBackStack(SingleOtrClientFragment.TAG)
.commit();
}
@Override
public void onHideOtrClient() {
getChildFragmentManager().popBackStackImmediate();
}
@Override
public void onShowLikesList(Message message) {
}
@Override
public OptionsMenuControl getOptionsMenuControl() {
return optionsMenuControl;
}
private boolean closeMenu() {
return optionsMenuControl.close();
}
@Override
public void onShowUser(final User user) {
if (user.isMe()) {
getStoreFactory().getSingleParticipantStore().setUser(user);
openUserProfileFragment(SingleParticipantFragment.newInstance(false,
IConnectStore.UserRequester.PARTICIPANTS),
SingleParticipantFragment.TAG);
if (LayoutSpec.isPhone(getActivity())) {
getControllerFactory().getNavigationController().setRightPage(Page.PARTICIPANT_USER_PROFILE, TAG);
}
return;
}
switch (user.getConnectionStatus()) {
case ACCEPTED:
getStoreFactory().getSingleParticipantStore().setUser(user);
openUserProfileFragment(SingleParticipantFragment.newInstance(false,
IConnectStore.UserRequester.PARTICIPANTS),
SingleParticipantFragment.TAG);
if (LayoutSpec.isPhone(getActivity())) {
getControllerFactory().getNavigationController().setRightPage(Page.PARTICIPANT_USER_PROFILE, TAG);
}
break;
case PENDING_FROM_OTHER:
case PENDING_FROM_USER:
case IGNORED:
openUserProfileFragment(PendingConnectRequestFragment.newInstance(user.getId(),
null,
ConnectRequestLoadMode.LOAD_BY_USER_ID,
IConnectStore.UserRequester.PARTICIPANTS),
PendingConnectRequestFragment.TAG);
if (LayoutSpec.isPhone(getActivity())) {
getControllerFactory().getNavigationController().setRightPage(Page.PARTICIPANT_USER_PROFILE, TAG);
}
break;
case BLOCKED:
openUserProfileFragment(BlockedUserProfileFragment.newInstance(user.getId(),
IConnectStore.UserRequester.PARTICIPANTS),
BlockedUserProfileFragment.TAG);
if (LayoutSpec.isPhone(getActivity())) {
getControllerFactory().getNavigationController().setRightPage(Page.PARTICIPANT_USER_PROFILE, TAG);
}
break;
case CANCELLED:
case UNCONNECTED:
openUserProfileFragment(SendConnectRequestFragment.newInstance(user.getId(),
IConnectStore.UserRequester.PARTICIPANTS),
SendConnectRequestFragment.TAG);
getControllerFactory().getNavigationController().setRightPage(Page.SEND_CONNECT_REQUEST, TAG);
break;
}
}
private void openUserProfileFragment(Fragment fragment, String tag) {
final FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
if (getControllerFactory().getConversationScreenController().getPopoverLaunchMode() != DialogLaunchMode.AVATAR &&
getControllerFactory().getConversationScreenController().getPopoverLaunchMode() != DialogLaunchMode.COMMON_USER) {
animateParticipantsWithConnectUserProfile(false);
transaction.setCustomAnimations(R.anim.open_profile,
R.anim.close_profile,
R.anim.open_profile,
R.anim.close_profile);
} else {
transaction.setCustomAnimations(R.anim.fade_in,
R.anim.fade_out,
R.anim.fade_in,
R.anim.fade_out);
}
transaction.add(R.id.fl__participant__overlay, fragment, tag)
.addToBackStack(tag)
.commit();
}
private void animateParticipantsWithConnectUserProfile(boolean show) {
if (show) {
if (LayoutSpec.isTablet(getActivity())) {
participantsContainerView.animate()
.translationX(0)
.setInterpolator(new Expo.EaseOut())
.setDuration(getResources().getInteger(R.integer.framework_animation_duration_long))
.setStartDelay(0)
.start();
} else {
participantsContainerView.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 {
if (LayoutSpec.isTablet(getActivity())) {
participantsContainerView.animate()
.translationX(-getResources().getDimensionPixelSize(R.dimen.participant_dialog__initial_width))
.setInterpolator(new Expo.EaseOut())
.setDuration(getResources().getInteger(R.integer.framework_animation_duration_long))
.setStartDelay(0)
.start();
} else {
participantsContainerView.animate()
.alpha(0)
.scaleY(2)
.scaleX(2)
.setInterpolator(new Expo.EaseIn())
.setDuration(getResources().getInteger(R.integer.reopen_profile_source__animation_duration))
.setStartDelay(0)
.start();
}
}
}
@Override
public void onHideUser() {
if (!getControllerFactory().getConversationScreenController().isShowingUser()) {
return;
}
getChildFragmentManager().popBackStackImmediate();
if (LayoutSpec.isPhone(getActivity())) {
Page rightPage = getControllerFactory().getConversationScreenController().isShowingParticipant() ? Page.PARTICIPANT
: Page.MESSAGE_STREAM;
getControllerFactory().getNavigationController().setRightPage(rightPage, TAG);
}
animateParticipantsWithConnectUserProfile(true);
}
//////////////////////////////////////////////////////////////////////////////////////////
//
// UserProfileContainer
//
//////////////////////////////////////////////////////////////////////////////////////////
@Override
public void showRemoveConfirmation(final User user) {
// Show confirmation dialog before removing user
ConfirmationCallback callback = new TwoButtonConfirmationCallback() {
@Override
public void positiveButtonClicked(boolean checkboxIsSelected) {
dismissUserProfile();
new Handler().post(new Runnable() {
@Override
public void run() {
getStoreFactory().getConversationStore().getCurrentConversation().removeMember(user);
((BaseActivity) getActivity()).injectJava(GlobalTrackingController.class).tagEvent(new RemoveContactEvent(true,
getParticipantsCount()));
}
});
}
@Override
public void negativeButtonClicked() {
((BaseActivity) getActivity()).injectJava(GlobalTrackingController.class).tagEvent(new RemoveContactEvent(false,
getParticipantsCount()));
}
@Override
public void onHideAnimationEnd(boolean confirmed, boolean canceled, boolean checkboxIsSelected) {
}
};
String header = getString(R.string.confirmation_menu__header);
String text = getString(R.string.confirmation_menu_text_with_name, user.getDisplayName());
String confirm = getString(R.string.confirmation_menu__confirm_remove);
String cancel = getString(R.string.confirmation_menu__cancel);
ConfirmationRequest request = new ConfirmationRequest.Builder()
.withHeader(header)
.withMessage(text)
.withPositiveButton(confirm)
.withNegativeButton(cancel)
.withConfirmationCallback(callback)
.withWireTheme(getControllerFactory().getThemeController().getThemeDependentOptionsTheme())
.build();
getControllerFactory().getConfirmationController().requestConfirmation(request, IConfirmationController.PARTICIPANTS);
SoundController ctrl = inject(SoundController.class);
if (ctrl != null) {
ctrl.playAlert();
}
}
@Override
public void onOpenUrl(String url) {
getContainer().onOpenUrl(url);
}
private int getParticipantsCount() {
return getStoreFactory().getConversationStore().getCurrentConversation().getUsers().size();
}
@Override
public void dismissUserProfile() {
getControllerFactory().getConversationScreenController().hideUser();
}
@Override
public void dismissSingleUserProfile() {
dismissUserProfile();
}
//////////////////////////////////////////////////////////////////////////////////////////
//
// PendingConnectRequestFragment.Container
//
//////////////////////////////////////////////////////////////////////////////////////////
@Override
public void onAcceptedConnectRequest(final IConversation conversation) {
getControllerFactory().getConversationScreenController().hideUser();
getStoreFactory().getConversationStore().setCurrentConversation(conversation,
ConversationChangeRequester.START_CONVERSATION);
}
//////////////////////////////////////////////////////////////////////////////////////////
//
// BlockedUserProfileFragment.Container
//
//////////////////////////////////////////////////////////////////////////////////////////
@Override
public void onUnblockedUser(IConversation restoredConversationWithUser) {
getControllerFactory().getConversationScreenController().hideUser();
getStoreFactory().getConversationStore().setCurrentConversation(restoredConversationWithUser,
ConversationChangeRequester.START_CONVERSATION);
}
//////////////////////////////////////////////////////////////////////////////////////////
//
// SendConnectRequestFragment.Container
//
//////////////////////////////////////////////////////////////////////////////////////////
@Override
public void onConnectRequestWasSentToUser() {
getControllerFactory().getConversationScreenController().hideUser();
}
@Override
public void showIncomingPendingConnectRequest(IConversation conversation) { }
//////////////////////////////////////////////////////////////////////////////////////////
//
// PickUserFragment.Container
//
//////////////////////////////////////////////////////////////////////////////////////////
@Override
public void onSelectedUsers(List<User> users, ConversationChangeRequester requester) {
IConversation currentConversation = getStoreFactory().getConversationStore().getCurrentConversation();
if (currentConversation.getType() == IConversation.Type.ONE_TO_ONE) {
getControllerFactory().getPickUserController().hidePickUser(getCurrentPickerDestination(), false);
dismissDialog();
getStoreFactory().getConversationStore().createGroupConversation(users, requester);
if (!getStoreFactory().getNetworkStore().hasInternetConnection()) {
ViewUtils.showAlertDialog(getActivity(),
R.string.conversation__create_group_conversation__no_network__title,
R.string.conversation__create_group_conversation__no_network__message,
R.string.conversation__create_group_conversation__no_network__button,
null, true);
}
((BaseActivity) getActivity()).injectJava(GlobalTrackingController.class).tagEvent(new CreatedGroupConversationEvent(true,
(users.size() + 1)));
} else if (currentConversation.getType() == IConversation.Type.GROUP) {
currentConversation.addMembers(users);
getControllerFactory().getPickUserController().hidePickUser(getCurrentPickerDestination(), false);
if (!getStoreFactory().getNetworkStore().hasInternetConnection()) {
ViewUtils.showAlertDialog(getActivity(),
R.string.conversation__add_user__no_network__title,
R.string.conversation__add_user__no_network__message,
R.string.conversation__add_user__no_network__button,
null, true);
}
((BaseActivity) getActivity()).injectJava(GlobalTrackingController.class).tagEvent(new AddedMemberToGroupEvent(getParticipantsCount(), users.size()));
}
}
@Override
public LoadingIndicatorView getLoadingViewIndicator() {
return loadingIndicatorView;
}
@Override
public IPickUserController.Destination getCurrentPickerDestination() {
return IPickUserController.Destination.PARTICIPANTS;
}
//////////////////////////////////////////////////////////////////////////////////////////
//
// PickUserControllerObserver
//
//////////////////////////////////////////////////////////////////////////////////////////
@Override
public void onShowPickUser(IPickUserController.Destination destination, View anchorView) {
if (LayoutSpec.isPhone(getActivity())) {
return;
}
if (!getCurrentPickerDestination().equals(destination)) {
onHidePickUser(getCurrentPickerDestination(), true);
return;
}
FragmentManager fragmentManager = getChildFragmentManager();
int pickUserAnimation =
LayoutSpec.isTablet(getActivity()) ? R.anim.fade_in : R.anim.slide_in_from_bottom_pick_user;
if (!groupConversation && otherUser != null) {
getControllerFactory().getPickUserController().addUser(otherUser);
}
fragmentManager
.beginTransaction()
.setCustomAnimations(pickUserAnimation, R.anim.fade_out)
.add(R.id.fl__add_to_conversation__pickuser__container,
PickUserFragment.newInstance(true, groupConversation),
PickUserFragment.TAG)
.addToBackStack(PickUserFragment.TAG)
.commit();
if (LayoutSpec.isPhone(getActivity())) {
getControllerFactory().getNavigationController().setRightPage(Page.PICK_USER_ADD_TO_CONVERSATION, TAG);
}
final ObjectAnimator hideParticipantsAnimator = ObjectAnimator.ofFloat(participantsContainerView,
View.ALPHA,
1f,
0f);
hideParticipantsAnimator.setInterpolator(new Quart.EaseOut());
hideParticipantsAnimator.setDuration(getResources().getInteger(R.integer.framework_animation_duration_medium));
hideParticipantsAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
participantsContainerView.setVisibility(View.GONE);
}
});
hideParticipantsAnimator.start();
}
@Override
public void onHidePickUser(IPickUserController.Destination destination,
boolean closeWithoutSelectingPeople) {
if (LayoutSpec.isPhone(getActivity())) {
return;
}
if (!destination.equals(getCurrentPickerDestination())) {
return;
}
// Workaround for animation bug with nested child fragments
// Animating fragment container views and then popping stack at end of animation
int showParticipantsDelay = getResources().getInteger(R.integer.framework_animation_delay_long);
int hidePickUserAnimDuration = getResources().getInteger(R.integer.framework_animation_duration_medium);
TimeInterpolator hidePickUserInterpolator = new Expo.EaseIn();
ObjectAnimator hidePickUserAnimator;
// Fade animation in participants dialog on tablet
if (LayoutSpec.isTablet(getActivity())) {
hidePickUserAnimator = ObjectAnimator.ofFloat(pickUserContainerView,
View.ALPHA,
1f,
0f);
hidePickUserAnimator.setInterpolator(new Linear.EaseIn());
} else {
hidePickUserAnimator = ObjectAnimator.ofFloat(pickUserContainerView,
View.TRANSLATION_Y,
0f,
pickUserContainerView.getMeasuredHeight());
hidePickUserAnimator.setInterpolator(hidePickUserInterpolator);
hidePickUserAnimator.setDuration(hidePickUserAnimDuration);
}
hidePickUserAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
if (!isResumed()) {
return;
}
getChildFragmentManager().popBackStackImmediate(PickUserFragment.TAG,
FragmentManager.POP_BACK_STACK_INCLUSIVE);
if (LayoutSpec.isTablet(getActivity())) {
// Reset for fade animation in participants dialog on tablet
pickUserContainerView.setAlpha(1f);
} else {
pickUserContainerView.setTranslationY(0f);
}
}
});
participantsContainerView.setAlpha(0);
participantsContainerView.setVisibility(View.VISIBLE);
ObjectAnimator showParticipantsAnimator = ObjectAnimator.ofFloat(participantsContainerView,
View.ALPHA,
0f,
1f);
showParticipantsAnimator.setInterpolator(new Quart.EaseOut());
showParticipantsAnimator.setDuration(getResources().getInteger(R.integer.framework_animation_duration_medium));
showParticipantsAnimator.setStartDelay(showParticipantsDelay);
AnimatorSet hideSet = new AnimatorSet();
hideSet.playTogether(hidePickUserAnimator, showParticipantsAnimator);
hideSet.start();
if (LayoutSpec.isPhone(getActivity()) &&
getControllerFactory().getNavigationController().getPagerPosition() == NavigationController.SECOND_PAGE) {
// TODO: https://wearezeta.atlassian.net/browse/AN-3081
if (getControllerFactory().getConversationScreenController().isShowingParticipant()) {
getControllerFactory().getNavigationController().setRightPage(Page.PARTICIPANT, TAG);
} else {
getControllerFactory().getNavigationController().setRightPage(Page.MESSAGE_STREAM, TAG);
}
}
}
@Override
public void onShowUserProfile(User user, View anchorView) { }
@Override
public void onHideUserProfile() { }
//////////////////////////////////////////////////////////////////////////////////////////
//
// Confirmation overlays
//
//////////////////////////////////////////////////////////////////////////////////////////
private void showLeaveConfirmation(final IConversation conversation) {
ConfirmationCallback callback = new TwoButtonConfirmationCallback() {
@Override
public void positiveButtonClicked(boolean checkboxIsSelected) {
((BaseActivity) getActivity()).injectJava(GlobalTrackingController.class).tagEvent(new LeaveGroupConversationEvent(true,
getStoreFactory().getConversationStore().getCurrentConversation().getUsers().size()));
getStoreFactory().getConversationStore().leave(conversation);
getStoreFactory().getConversationStore().setCurrentConversationToNext(
ConversationChangeRequester.LEAVE_CONVERSATION);
if (LayoutSpec.isTablet(getActivity())) {
getControllerFactory().getConversationScreenController().hideParticipants(false, true);
}
}
@Override
public void negativeButtonClicked() {
((BaseActivity) getActivity()).injectJava(GlobalTrackingController.class).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()
.withHeader(header)
.withMessage(text)
.withPositiveButton(confirm)
.withNegativeButton(cancel)
.withConfirmationCallback(callback)
.withWireTheme(getControllerFactory().getThemeController().getThemeDependentOptionsTheme())
.build();
getControllerFactory().getConfirmationController().requestConfirmation(request, IConfirmationController.PARTICIPANTS);
SoundController ctrl = inject(SoundController.class);
if (ctrl != null) {
ctrl.playAlert();
}
}
public interface Container {
void onOpenUrl(String url);
void dismissDialog();
}
}