/**
* 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.dialog;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Handler;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.FrameLayout;
import com.waz.api.IConversation;
import com.waz.api.Message;
import com.waz.api.OtrClient;
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.accentcolor.AccentColorObserver;
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.globallayout.KeyboardHeightObserver;
import com.waz.zclient.controllers.tracking.events.group.AddedMemberToGroupEvent;
import com.waz.zclient.controllers.tracking.events.group.CreatedGroupConversationEvent;
import com.waz.zclient.core.stores.connect.IConnectStore;
import com.waz.zclient.core.stores.conversation.ConversationChangeRequester;
import com.waz.zclient.core.stores.participants.ParticipantsStoreObserver;
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.conversation.controller.ConversationScreenControllerObserver;
import com.waz.zclient.pages.main.conversation.controller.IConversationScreenController;
import com.waz.zclient.pages.main.participants.ParticipantFragment;
import com.waz.zclient.pages.main.participants.SingleParticipantFragment;
import com.waz.zclient.pages.main.participants.TabbedParticipantBodyFragment;
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.HeightEvaluator;
import com.waz.zclient.ui.animation.interpolators.penner.Quart;
import com.waz.zclient.ui.utils.KeyboardUtils;
import com.waz.zclient.ui.utils.MathUtils;
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 ParticipantsDialogFragment extends BaseFragment<ParticipantsDialogFragment.Container> implements
ParticipantsStoreObserver,
ParticipantFragment.Container,
OnBackPressedListener,
ConversationScreenControllerObserver,
KeyboardHeightObserver,
SingleParticipantFragment.Container,
SendConnectRequestFragment.Container,
PendingConnectRequestManagerFragment.Container,
BlockedUserProfileFragment.Container,
PickUserFragment.Container,
PickUserControllerScreenObserver,
ConfirmationObserver,
AccentColorObserver {
public static final String TAG = ParticipantsDialogFragment.class.getName();
private static final String ARG_ANCHOR_RECT = "argRect";
private static final String ARG_POS_X = "argPosX";
private static final String ARG_POS_Y = "argPosY";
private static final String ARG_USER_ID = "ARG_USER_ID";
private static final String ARG__FIRST__PAGE = "ARG__FIRST__PAGE";
private static final String ARG__ADD_TO_CONVERSATION = "ARG__ADD_TO_CONVERSATION";
private static final String ARG__GROUP_CONVERSATION = "ARG__GROUP_CONVERSATION";
private View mainParticipantsContainer;
private View detailParticipantContainer;
private FrameLayout dialogFrameLayout;
private View marker;
private ConfirmationMenu confirmationMenu;
private int minParticipantsDialogHeight;
private int regularParticipantsDialogHeight;
private int selfGravity;
private User user;
private int participantDialogPadding;
private int dialogTranslationX;
private int dialogTranslationY;
private int markerTranslationX;
private int markerTranslationY;
private boolean isInConfigurationChange;
public static Fragment newParticipantButtonInstance(int x, int y, Rect rect, int firstPage) {
ParticipantsDialogFragment participantsDialogFragment = new ParticipantsDialogFragment();
Bundle args = new Bundle();
args.putInt(ARG_POS_X, x);
args.putInt(ARG_POS_Y, y);
args.putParcelable(ARG_ANCHOR_RECT, rect);
args.putInt(ARG__FIRST__PAGE, firstPage);
participantsDialogFragment.setArguments(args);
return participantsDialogFragment;
}
public static Fragment newAvatarPopoverInstance(int x, int y, Rect rect, String userId) {
ParticipantsDialogFragment participantsDialogFragment = new ParticipantsDialogFragment();
Bundle args = new Bundle();
args.putInt(ARG_POS_X, x);
args.putInt(ARG_POS_Y, y);
args.putParcelable(ARG_ANCHOR_RECT, rect);
args.putString(ARG_USER_ID, userId);
args.putInt(ARG__FIRST__PAGE, TabbedParticipantBodyFragment.USER_PAGE);
participantsDialogFragment.setArguments(args);
return participantsDialogFragment;
}
public static Fragment newStartUiInstance(int x, int y, Rect rect, boolean groupConversation) {
ParticipantsDialogFragment participantsDialogFragment = new ParticipantsDialogFragment();
Bundle args = new Bundle();
args.putInt(ARG_POS_X, x);
args.putInt(ARG_POS_Y, y);
args.putParcelable(ARG_ANCHOR_RECT, rect);
args.putBoolean(ARG__ADD_TO_CONVERSATION, true);
args.putBoolean(ARG__GROUP_CONVERSATION, groupConversation);
participantsDialogFragment.setArguments(args);
return participantsDialogFragment;
}
private void adjustAccordingToAnchor() {
Bundle bundle = getArguments();
if (bundle == null || getView() == null) {
return;
}
View view = getView();
ViewGroup viewGroup = (ViewGroup) view.getParent();
Rect rect = bundle.getParcelable(ARG_ANCHOR_RECT);
int posX = bundle.getInt(ARG_POS_X);
int posY = bundle.getInt(ARG_POS_Y);
final int widthSpec = View.MeasureSpec.makeMeasureSpec(viewGroup.getMeasuredWidth()
- viewGroup.getPaddingLeft()
- viewGroup.getPaddingRight(),
View.MeasureSpec.AT_MOST);
final int heightSpec = View.MeasureSpec.makeMeasureSpec(viewGroup.getMeasuredHeight()
- viewGroup.getPaddingTop()
- viewGroup.getPaddingBottom(),
View.MeasureSpec.AT_MOST);
view.measure(widthSpec, heightSpec);
ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) dialogFrameLayout.getLayoutParams();
int rootHeight = dialogFrameLayout.getMeasuredHeight();
int rootWidth = dialogFrameLayout.getMeasuredWidth();
int dialogHeight = rootHeight + layoutParams.bottomMargin + layoutParams.topMargin;
int dialogWidth = rootWidth + layoutParams.leftMargin + layoutParams.rightMargin;
setDialogPosition(rect, posX, posY, dialogWidth, dialogHeight);
}
private void setDialogPosition(Rect rect,
int posX,
int posY,
int dialogWidth,
int dialogHeight) {
if (getControllerFactory().getConversationScreenController().getPopoverLaunchMode() == DialogLaunchMode.CONVERSATION_TOOLBAR) {
int screenWidth = ViewUtils.getRealDisplayWidth(getActivity());
dialogTranslationX = screenWidth / 2 - dialogWidth / 2;
marker.setVisibility(View.INVISIBLE);
} else {
dialogTranslationX = getResources().getDimensionPixelSize(R.dimen.framework__participants_dialog__pos_x);
marker.setVisibility(View.VISIBLE);
}
int markerHeight = marker.getMeasuredHeight() / 2; // because we draw on our own
int markerWidth = marker.getMeasuredWidth();
markerTranslationX = posX + (rect.width() - markerWidth) / 2;
int displayHeight;
int displayWidth;
boolean forceRight = getControllerFactory().getConversationScreenController().getPopoverLaunchMode() == DialogLaunchMode.SEARCH ||
getControllerFactory().getConversationScreenController().getPopoverLaunchMode() == DialogLaunchMode.CONVERSATION_MENU;
if (ViewUtils.isInPortrait(getActivity())) {
displayHeight = ViewUtils.getOrientationIndependentDisplayHeight(getActivity());
displayWidth = ViewUtils.getOrientationIndependentDisplayWidth(getActivity());
} else {
displayHeight = ViewUtils.getOrientationIndependentDisplayWidth(getActivity());
displayWidth = ViewUtils.getOrientationIndependentDisplayHeight(getActivity());
}
final int screenBottom = displayHeight - participantDialogPadding - ViewUtils.getStatusBarHeight(getActivity());
final int screenRight = displayWidth - participantDialogPadding;
final int screenLeft = participantDialogPadding;
if ((posY - dialogHeight - markerHeight) >= participantDialogPadding && !forceRight) {
// put above
markerTranslationY = posY - markerHeight;
dialogTranslationY = markerTranslationY - dialogHeight;
marker.setRotation(0f);
if (dialogTranslationX + dialogWidth > screenRight) {
// too far right
dialogTranslationX = screenRight - dialogWidth;
} else if (dialogTranslationX < participantDialogPadding) {
// too far left
dialogTranslationX = participantDialogPadding;
}
selfGravity = Gravity.TOP;
} else if (posY + rect.height() + dialogHeight + markerHeight < screenBottom && !forceRight) {
// put below
markerTranslationY = posY + rect.height() - markerHeight;
dialogTranslationY = markerTranslationY + 2 * markerHeight; // 2 * because we draw on our own
marker.setRotation(180f);
if (dialogTranslationX + dialogWidth > screenRight) {
// too far right
dialogTranslationX = screenRight - dialogWidth;
} else if (dialogTranslationX < participantDialogPadding) {
// too far left
dialogTranslationX = participantDialogPadding;
}
selfGravity = Gravity.BOTTOM;
} else if (posX + rect.width() + markerWidth + dialogWidth <= displayWidth - participantDialogPadding || forceRight) {
int tmp = markerHeight;
//noinspection SuspiciousNameCombination
markerHeight = markerWidth;
markerWidth = tmp;
// centered
markerTranslationX = posX + rect.width() - markerWidth;
dialogTranslationX = markerTranslationX + 2 * markerWidth; // 2 * because we draw on our own
markerTranslationY = (posY + rect.centerY()) - (markerHeight / 2);
dialogTranslationY = (posY + rect.centerY()) - (dialogHeight / 2);
marker.setRotation(90f);
if (dialogTranslationY < participantDialogPadding) {
// too high
dialogTranslationY = participantDialogPadding;
} else if (posY + dialogHeight > screenBottom) {
// too low
dialogTranslationY = displayHeight - participantDialogPadding - dialogHeight - ViewUtils.getStatusBarHeight(getActivity());
}
// too far right
if (dialogTranslationX + dialogWidth > screenRight) {
dialogTranslationX = screenRight - dialogWidth;
markerTranslationX = dialogTranslationX - 2 * markerWidth; // 2 * because we draw on our own
}
selfGravity = Gravity.RIGHT;
} else {
int tmp = markerHeight;
//noinspection SuspiciousNameCombination
markerHeight = markerWidth;
markerWidth = tmp;
// centered
markerTranslationX = posX - markerWidth;
dialogTranslationX = markerTranslationX - dialogWidth;
markerTranslationY = (posY + rect.centerY()) - (markerHeight / 2);
dialogTranslationY = (posY + rect.centerY()) - (dialogHeight / 2);
marker.setRotation(270f);
if (dialogTranslationY < participantDialogPadding) {
// too high
dialogTranslationY = participantDialogPadding;
} else if (posY + dialogHeight > screenBottom) {
// too low
dialogTranslationY = displayHeight - participantDialogPadding - dialogHeight - ViewUtils.getStatusBarHeight(getActivity());
}
// too far left
if (dialogTranslationX < screenLeft) {
dialogTranslationX = screenLeft;
markerTranslationX = dialogTranslationX + dialogWidth;
}
selfGravity = Gravity.LEFT;
}
dialogFrameLayout.setTranslationX(dialogTranslationX);
dialogFrameLayout.setTranslationY(dialogTranslationY);
marker.setTranslationX(markerTranslationX);
marker.setTranslationY(markerTranslationY);
}
private void hide() {
if (getControllerFactory() == null ||
getControllerFactory().isTornDown()) {
return;
}
DialogLaunchMode launchMode = getControllerFactory().getConversationScreenController().getPopoverLaunchMode();
if (launchMode == null) {
return;
}
switch (launchMode) {
case PARTICIPANT_BUTTON:
case CONVERSATION_TOOLBAR:
getControllerFactory().getConversationScreenController().hideParticipants(true, false);
if (getArguments().getBoolean(ARG__ADD_TO_CONVERSATION)) {
getControllerFactory().getPickUserController().hidePickUserWithoutAnimations(IPickUserController.Destination.CURSOR);
} else {
getControllerFactory().getPickUserController().hidePickUserWithoutAnimations(IPickUserController.Destination.PARTICIPANTS);
}
break;
case AVATAR:
case SEARCH:
getControllerFactory().getPickUserController().hideUserProfile();
break;
case CONVERSATION_MENU:
setVisible(false);
break;
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
isInConfigurationChange = false;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup viewGroup, Bundle savedInstanceState) {
ViewGroup view = (ViewGroup) inflater.inflate(R.layout.fragment_participants_dialog, viewGroup, false);
dialogFrameLayout = ViewUtils.getView(view, R.id.fl__participant_dialog__root);
marker = ViewUtils.getView(view, R.id.v__participant_dialog__marker);
mainParticipantsContainer = ViewUtils.getView(view, R.id.fl__participant_dialog__main__container);
detailParticipantContainer = ViewUtils.getView(view, R.id.fl__participant_dialog__detail__container);
confirmationMenu = ViewUtils.getView(view, R.id.cm__participants_dialog__confirm_action);
confirmationMenu.setVisibility(View.GONE);
confirmationMenu.resetFullScreenPadding();
int firstPage = getArguments().getInt(ARG__FIRST__PAGE);
if (savedInstanceState == null) {
FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
Bundle bundle = getArguments();
user = null;
if (bundle != null) {
String userId = bundle.getString(ARG_USER_ID);
if (userId != null) {
user = getStoreFactory().getPickUserStore().getUser(userId);
}
}
if (getArguments().getBoolean(ARG__ADD_TO_CONVERSATION)) {
transaction.replace(R.id.fl__participant_dialog__main__container,
PickUserFragment.newInstance(true,
getArguments().getBoolean(ARG__GROUP_CONVERSATION)),
PickUserFragment.TAG);
} else if (getControllerFactory().getConversationScreenController().getPopoverLaunchMode() == DialogLaunchMode.PARTICIPANT_BUTTON ||
getControllerFactory().getConversationScreenController().getPopoverLaunchMode() == DialogLaunchMode.CONVERSATION_TOOLBAR) {
transaction.add(R.id.fl__participant_dialog__main__container,
ParticipantFragment.newInstance(IConnectStore.UserRequester.PARTICIPANTS, firstPage),
ParticipantFragment.TAG);
} else if (getControllerFactory().getConversationScreenController().getPopoverLaunchMode() == DialogLaunchMode.CONVERSATION_MENU) { // NOPMD
// do nothing, according to design the menu should open from bottom in conversation list
} else {
getControllerFactory().getDialogBackgroundImageController().setUser(user);
transaction.add(R.id.fl__participant_dialog__detail__container,
SingleParticipantFragment.newInstance(true,
IConnectStore.UserRequester.POPOVER),
SingleParticipantFragment.TAG);
switch (user.getConnectionStatus()) {
case ACCEPTED:
case SELF:
getStoreFactory().getSingleParticipantStore().setUser(user);
if (getControllerFactory().getConversationScreenController().getPopoverLaunchMode() == DialogLaunchMode.COMMON_USER) {
transaction.add(R.id.fl__participant_dialog__main__container,
SingleParticipantFragment.newInstance(true,
IConnectStore.UserRequester.POPOVER),
SingleParticipantFragment.TAG);
} else {
transaction.add(R.id.fl__participant_dialog__main__container,
ParticipantFragment.newInstance(IConnectStore.UserRequester.POPOVER, firstPage),
ParticipantFragment.TAG);
}
break;
case CANCELLED:
case UNCONNECTED:
transaction.add(R.id.fl__participant_dialog__main__container,
SendConnectRequestFragment.newInstance(user.getId(),
IConnectStore.UserRequester.POPOVER),
SendConnectRequestFragment.TAG);
break;
case PENDING_FROM_OTHER:
case PENDING_FROM_USER:
case IGNORED:
transaction.add(R.id.fl__participant_dialog__main__container,
PendingConnectRequestManagerFragment.newInstance(user.getId(),
null,
ConnectRequestLoadMode.LOAD_BY_USER_ID,
IConnectStore.UserRequester.POPOVER),
PendingConnectRequestManagerFragment.TAG);
break;
case BLOCKED:
transaction.add(R.id.fl__participant_dialog__main__container,
BlockedUserProfileFragment.newInstance(user.getId(),
IConnectStore.UserRequester.POPOVER),
BlockedUserProfileFragment.TAG);
break;
}
}
transaction.commit();
}
new Handler().post(new Runnable() {
@Override
public void run() {
detailParticipantContainer.setTranslationX(dialogFrameLayout.getMeasuredWidth());
}
});
participantDialogPadding = getResources().getDimensionPixelSize(R.dimen.framework__participants_dialog__display_padding);
minParticipantsDialogHeight = getResources().getDimensionPixelSize(R.dimen.participant_dialog__min_height);
regularParticipantsDialogHeight = getResources().getDimensionPixelSize(R.dimen.participant_dialog__regular_height);
dialogFrameLayout.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
KeyboardUtils.hideKeyboard(getActivity());
return true;
}
});
return view;
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
adjustAccordingToAnchor();
setVisible(true);
}
@SuppressLint("RtlHardcoded")
private void setVisible(final boolean show) {
if (getView() == null ||
isInConfigurationChange) {
removeFragment();
return;
}
Animation animation;
if (show) {
switch (selfGravity) {
case Gravity.TOP:
animation = AnimationUtils.loadAnimation(getActivity(), R.anim.popover_show_gravity_top);
break;
case Gravity.RIGHT:
animation = AnimationUtils.loadAnimation(getActivity(), R.anim.popover_show_gravity_right);
break;
case Gravity.LEFT:
animation = AnimationUtils.loadAnimation(getActivity(), R.anim.popover_show_gravity_left);
break;
case Gravity.BOTTOM:
default:
animation = AnimationUtils.loadAnimation(getActivity(), R.anim.popover_show_gravity_bottom);
}
} else {
switch (selfGravity) {
case Gravity.TOP:
animation = AnimationUtils.loadAnimation(getActivity(), R.anim.popover_hide_gravity_top);
break;
case Gravity.RIGHT:
animation = AnimationUtils.loadAnimation(getActivity(), R.anim.popover_hide_gravity_right);
break;
case Gravity.LEFT:
animation = AnimationUtils.loadAnimation(getActivity(), R.anim.popover_hide_gravity_left);
break;
case Gravity.BOTTOM:
default:
animation = AnimationUtils.loadAnimation(getActivity(), R.anim.popover_hide_gravity_bottom);
}
}
animation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {}
@Override
public void onAnimationEnd(Animation animation) {
if (show) {
return;
}
removeFragment();
}
@Override
public void onAnimationRepeat(Animation animation) {}
});
getView().startAnimation(animation);
}
private boolean removeFragment() {
Fragment parentFragment = getParentFragment();
if (parentFragment == null) {
return true;
}
FragmentManager childFragmentManager = parentFragment.getChildFragmentManager();
if (childFragmentManager == null) {
return true;
}
FragmentTransaction transaction = childFragmentManager.beginTransaction();
transaction.remove(this);
transaction.commitAllowingStateLoss();
return false;
}
@Override
public void onStart() {
super.onStart();
getStoreFactory().getParticipantsStore().addParticipantsStoreObserver(this);
final IConversationScreenController conversationScreenController = getControllerFactory().getConversationScreenController();
conversationScreenController.addConversationControllerObservers(this);
getControllerFactory().getGlobalLayoutController().addKeyboardHeightObserver(this);
getControllerFactory().getPickUserController().addPickUserScreenControllerObserver(this);
getControllerFactory().getConfirmationController().addConfirmationObserver(this);
getControllerFactory().getAccentColorController().addAccentColorObserver(this);
if (conversationScreenController.shouldShowDevicesTab()) {
conversationScreenController.showUser(conversationScreenController.getRequestedDeviceTabUser());
}
}
@Override
public void onResume() {
super.onResume();
if (getView() != null) {
getView().setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
hide();
}
return true;
}
});
}
}
@Override
public void onStop() {
getControllerFactory().getConversationScreenController().removeConversationControllerObservers(this);
getControllerFactory().getConversationScreenController().resetToMessageStream();
getStoreFactory().getParticipantsStore().removeParticipantsStoreObserver(this);
getControllerFactory().getGlobalLayoutController().removeKeyboardHeightObserver(this);
getControllerFactory().getPickUserController().removePickUserScreenControllerObserver(this);
getControllerFactory().getConfirmationController().removeConfirmationObserver(this);
getControllerFactory().getAccentColorController().removeAccentColorObserver(this);
super.onStop();
}
@Override
public void onDestroyView() {
mainParticipantsContainer = null;
detailParticipantContainer = null;
marker = null;
dialogFrameLayout = null;
confirmationMenu = null;
super.onDestroyView();
}
@Override
public void conversationUpdated(final IConversation conversation) {
if (!(getControllerFactory().getConversationScreenController().getPopoverLaunchMode() == DialogLaunchMode.PARTICIPANT_BUTTON ||
getControllerFactory().getConversationScreenController().getPopoverLaunchMode() == DialogLaunchMode.CONVERSATION_TOOLBAR)) {
return;
}
updateGroupDialogBackground(conversation);
}
@Override
public void participantsUpdated(UsersList participants) {
}
@Override
public void otherUserUpdated(User otherUser) {
}
@Override
public void onOpenUrl(String url) {
getContainer().onOpenUrl(url);
}
@Override
public void dismissDialog() {
hide();
}
@Override
public boolean onBackPressed() {
Fragment fragment = getChildFragmentManager().findFragmentByTag(ParticipantFragment.TAG);
if (fragment instanceof OnBackPressedListener && ((OnBackPressedListener) fragment).onBackPressed()) {
return true;
}
if (animateBetweenMainAndDetail(true)) {
return true;
}
hide();
return true;
}
@Override
public void onShowParticipants(View anchorView, boolean isSingleConversation, boolean isMemberOfConversation, boolean showDeviceTabIfSingle) {
}
@Override
public void onHideParticipants(boolean backOrButtonPressed,
boolean hideByConversationChange,
boolean isSingleConversation) {
setVisible(false);
}
@Override
public void onShowEditConversationName(boolean show) {
Fragment fragment = getChildFragmentManager().findFragmentByTag(ParticipantFragment.TAG);
if (fragment instanceof ConversationScreenControllerObserver) {
((ConversationScreenControllerObserver) fragment).onShowEditConversationName(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() {
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////
// ConversationActionObserver
////////////////////////////////////////////////////////////////////////////////////////////////////////////
@Override
public void onShowOtrClient(OtrClient otrClient, User user) {
Fragment fragment = getChildFragmentManager().findFragmentByTag(ParticipantFragment.TAG);
if (fragment instanceof ConversationScreenControllerObserver) {
((ConversationScreenControllerObserver) fragment).onShowOtrClient(otrClient, user);
}
}
@Override
public void onShowCurrentOtrClient() {
Fragment fragment = getChildFragmentManager().findFragmentByTag(ParticipantFragment.TAG);
if (fragment instanceof ConversationScreenControllerObserver) {
((ConversationScreenControllerObserver) fragment).onShowCurrentOtrClient();
}
}
@Override
public void onHideOtrClient() {
Fragment fragment = getChildFragmentManager().findFragmentByTag(ParticipantFragment.TAG);
if (fragment instanceof ConversationScreenControllerObserver) {
((ConversationScreenControllerObserver) fragment).onHideOtrClient();
}
}
@Override
public void onShowLikesList(Message message) {
}
@Override
public void onShowUser(User user) {
Fragment fragment = getChildFragmentManager().findFragmentByTag(ParticipantFragment.TAG);
if (fragment instanceof ConversationScreenControllerObserver) {
((ConversationScreenControllerObserver) fragment).onShowUser(user);
}
}
@Override
public void onHideUser() {
if (getControllerFactory().getConversationScreenController().getPopoverLaunchMode() == DialogLaunchMode.PARTICIPANT_BUTTON ||
getControllerFactory().getConversationScreenController().getPopoverLaunchMode() == DialogLaunchMode.CONVERSATION_TOOLBAR) {
Fragment fragment = getChildFragmentManager().findFragmentByTag(ParticipantFragment.TAG);
if (fragment instanceof ConversationScreenControllerObserver) {
((ConversationScreenControllerObserver) fragment).onHideUser();
}
updateGroupDialogBackground(getStoreFactory().getConversationStore().getCurrentConversation());
} else {
setVisible(false);
}
}
private void updateGroupDialogBackground(IConversation conversation) {
if (conversation == null) {
getControllerFactory().getDialogBackgroundImageController().setImageAsset(null, false);
return;
}
boolean blurred = conversation.getType() == IConversation.Type.GROUP &&
getControllerFactory().getConversationScreenController().getPopoverLaunchMode() == DialogLaunchMode.PARTICIPANT_BUTTON;
getControllerFactory().getDialogBackgroundImageController().setImageAsset(conversation.getBackground(), blurred);
}
@Override
public void onAddPeopleToConversation() {
Fragment fragment = getChildFragmentManager().findFragmentByTag(ParticipantFragment.TAG);
if (fragment instanceof ConversationScreenControllerObserver) {
((ConversationScreenControllerObserver) fragment).onAddPeopleToConversation();
}
}
@Override
public void onShowConversationMenu(@IConversationScreenController.ConversationMenuRequester int requester, IConversation conversation, View anchorView) {
if (requester != IConversationScreenController.USER_PROFILE_PARTICIPANTS &&
requester != IConversationScreenController.CONVERSATION_DETAILS) {
return;
}
Fragment fragment = getChildFragmentManager().findFragmentByTag(ParticipantFragment.TAG);
if (fragment instanceof ConversationScreenControllerObserver) {
((ConversationScreenControllerObserver) fragment).onShowConversationMenu(requester,
conversation,
anchorView);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// KeyboardHeightObserver
//
////////////////////////////////////////////////////////////////////////////////////////////////////////////
@Override
public void onKeyboardHeightChanged(int keyboardHeight) {
if (getControllerFactory().getConversationScreenController().getPopoverLaunchMode() == null) {
setVisible(false);
getControllerFactory().getPickUserController().resetShowingPickUser(IPickUserController.Destination.CURSOR);
getControllerFactory().getPickUserController().resetShowingPickUser(IPickUserController.Destination.PARTICIPANTS);
return;
}
final boolean keyboardIsVisible = keyboardHeight > 0;
switch (getControllerFactory().getConversationScreenController().getPopoverLaunchMode()) {
case CONVERSATION_MENU:
case PARTICIPANT_BUTTON:
int minDialogHeight = ViewUtils.getOrientationDependentDisplayHeight(getActivity())
- keyboardHeight
- marker.getMeasuredHeight()
- participantDialogPadding;
if (minDialogHeight > minParticipantsDialogHeight) {
minDialogHeight = minParticipantsDialogHeight;
}
updateParticipantsDialogSizeAndPos(keyboardIsVisible, minDialogHeight, keyboardHeight);
break;
}
}
private void updateParticipantsDialogSizeAndPos(boolean keyboardIsVisible, int minParticipantsDialogHeight, int keyboardHeight) {
// Expand / collapse when needed
final boolean shouldExpand = !keyboardIsVisible &&
isParticipantsDialogMinimized();
final boolean shouldCollapse = keyboardIsVisible &&
!isParticipantsDialogMinimized() &&
ViewUtils.isInLandscape(getActivity());
final ValueAnimator sizeAnimator;
if (shouldExpand) {
sizeAnimator = ValueAnimator.ofObject(new HeightEvaluator(dialogFrameLayout),
dialogFrameLayout.getMeasuredHeight(),
regularParticipantsDialogHeight);
} else if (shouldCollapse) {
sizeAnimator = ValueAnimator.ofObject(new HeightEvaluator(dialogFrameLayout),
dialogFrameLayout.getMeasuredHeight(),
minParticipantsDialogHeight);
} else {
sizeAnimator = null;
}
if (sizeAnimator != null) {
sizeAnimator.setDuration(getResources().getInteger(R.integer.framework_animation_duration_short));
sizeAnimator.start();
}
// Update vertical position
if (keyboardIsVisible && !isParticipantsDialogShiftedUp()) {
final int navigationBarHeight = ViewUtils.getNavigationBarHeight(getActivity());
int dialogDY;
int markerDY;
if (selfGravity == Gravity.TOP) {
dialogDY = navigationBarHeight - keyboardHeight + participantDialogPadding;
markerDY = navigationBarHeight - keyboardHeight + participantDialogPadding;
} else {
dialogDY = -keyboardHeight;
markerDY = -keyboardHeight;
}
if (shouldCollapse) {
dialogDY += dialogFrameLayout.getMeasuredHeight() - minParticipantsDialogHeight;
}
dialogFrameLayout.animate()
.translationYBy(dialogDY)
.setDuration(getResources().getInteger(R.integer.framework_animation_duration_short))
.withEndAction(new Runnable() {
@Override
public void run() {
if (getView() == null) {
return;
}
// Invalidate to avoid view being incorrectly drawn multiple times due to animation
getView().invalidate();
}
})
.start();
marker.animate()
.translationYBy(markerDY)
.setDuration(getResources().getInteger(R.integer.framework_animation_duration_short))
.start();
} else if (!keyboardIsVisible && isParticipantsDialogShiftedUp()) {
dialogFrameLayout.animate()
.translationY(dialogTranslationY)
.setDuration(getResources().getInteger(R.integer.framework_animation_duration_short))
.withEndAction(new Runnable() {
@Override
public void run() {
if (getView() == null) {
return;
}
// Invalidate to avoid view being incorrectly drawn multiple times due to animation
getView().invalidate();
}
})
.start();
marker.animate()
.translationY(markerTranslationY)
.setDuration(getResources().getInteger(R.integer.framework_animation_duration_short))
.start();
}
}
private boolean isParticipantsDialogMinimized() {
return dialogFrameLayout.getMeasuredHeight() <= minParticipantsDialogHeight;
}
private boolean isParticipantsDialogShiftedUp() {
return dialogFrameLayout.getTranslationY() < dialogTranslationY;
}
@Override
public void dismissUserProfile() {
setVisible(false);
}
@Override
public void dismissSingleUserProfile() {
getControllerFactory().getDialogBackgroundImageController().setUser(user);
animateBetweenMainAndDetail(true);
}
@Override
public void showRemoveConfirmation(User user) {
}
@Override
public void onUnblockedUser(IConversation restoredConversationWithUser) {
hide();
}
@Override
public void onConnectRequestWasSentToUser() {
getControllerFactory().getPickUserController().hideUserProfile();
}
/**
* @return true if the animation was performed,
* false otherwise
*/
private boolean animateBetweenMainAndDetail(boolean showGroup) {
float startDetail;
float endDetail;
float startMain;
float endMain;
Interpolator interpolator;
if (showGroup) {
startDetail = 0;
endDetail = dialogFrameLayout.getMeasuredWidth();
startMain = -dialogFrameLayout.getMeasuredHeight();
endMain = 0;
interpolator = new Quart.EaseOut();
} else {
startDetail = dialogFrameLayout.getMeasuredWidth();
endDetail = 0;
startMain = 0;
endMain = -dialogFrameLayout.getMeasuredHeight();
interpolator = new Quart.EaseOut();
}
if (MathUtils.floatEqual(mainParticipantsContainer.getTranslationX(), endMain) &&
MathUtils.floatEqual(detailParticipantContainer.getTranslationX(), endDetail)) {
return false;
}
ObjectAnimator slideInDetailParticipantAnimation = ObjectAnimator.ofFloat(detailParticipantContainer,
View.TRANSLATION_X,
startDetail,
endDetail);
slideInDetailParticipantAnimation.setDuration(getResources().getInteger(R.integer.framework_animation_duration_long));
slideInDetailParticipantAnimation.setInterpolator(interpolator);
ObjectAnimator slideOutMainAnimation = ObjectAnimator.ofFloat(mainParticipantsContainer,
View.TRANSLATION_X,
startMain,
endMain);
slideOutMainAnimation.setDuration(getResources().getInteger(R.integer.framework_animation_duration_long));
slideOutMainAnimation.setInterpolator(interpolator);
AnimatorSet participantTransition = new AnimatorSet();
participantTransition.playTogether(slideOutMainAnimation,
slideInDetailParticipantAnimation);
participantTransition.start();
return true;
}
@Override
public void onShowPickUser(IPickUserController.Destination destination, View anchorView) {
}
@Override
public void onHidePickUser(IPickUserController.Destination destination, boolean closeWithoutSelectingPeople) {
dismissDialog();
}
@Override
public void onShowUserProfile(User user, View anchorView) {
}
@Override
public void onHideUserProfile() {
setVisible(false);
}
//////////////////////////////////////////////////////////////////////////////////////////
//
// ConfirmationObserver
//
//////////////////////////////////////////////////////////////////////////////////////////
@Override
public void onRequestConfirmation(ConfirmationRequest confirmationRequest, @IConfirmationController.ConfirmationMenuRequester int requester) {
if (requester == IConfirmationController.CONVERSATION_LIST ||
requester == IConfirmationController.CONVERSATION) {
return;
}
confirmationMenu.onRequestConfirmation(confirmationRequest);
}
//////////////////////////////////////////////////////////////////////////////////////////
//
// AccentColorObserver
//
//////////////////////////////////////////////////////////////////////////////////////////
@Override
public void onAccentColorHasChanged(Object sender, int color) {
if (getView() == null) {
return;
}
confirmationMenu.setButtonColor(color);
}
@Override
public void onAcceptedConnectRequest(IConversation conversation) {
}
@Override
public void onAcceptedPendingOutgoingConnectRequest(IConversation conversation) {
}
//////////////////////////////////////////////////////////////////////////////////////////
//
// PickUserFragment.Container
//
//////////////////////////////////////////////////////////////////////////////////////////
@Override
public void showIncomingPendingConnectRequest(IConversation conversation) {
// noop
}
@Override
public void onSelectedUsers(List<User> users, ConversationChangeRequester requester) {
// TODO https://wearezeta.atlassian.net/browse/AN-3730
getControllerFactory().getPickUserController().hidePickUser(getCurrentPickerDestination(), false);
IConversation currentConversation = getStoreFactory().getConversationStore().getCurrentConversation();
if (currentConversation.getType() == IConversation.Type.ONE_TO_ONE) {
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);
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()));
}
hide();
}
@Override
public LoadingIndicatorView getLoadingViewIndicator() {
return ViewUtils.getView(getView(), R.id.lbv__conversation__loading_indicator);
}
@Override
public IPickUserController.Destination getCurrentPickerDestination() {
return IPickUserController.Destination.CURSOR;
}
private int getParticipantsCount() {
return getStoreFactory().getConversationStore().getCurrentConversation().getUsers().size();
}
public interface Container {
void onOpenUrl(String url);
}
}