/**
* 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.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.Fragment;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
import android.util.TypedValue;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.TextView;
import com.waz.api.IConversation;
import com.waz.api.Message;
import com.waz.api.NetworkMode;
import com.waz.api.OtrClient;
import com.waz.api.User;
import com.waz.api.UsersList;
import com.waz.api.Verification;
import com.waz.zclient.R;
import com.waz.zclient.controllers.accentcolor.AccentColorObserver;
import com.waz.zclient.controllers.currentfocus.IFocusController;
import com.waz.zclient.controllers.globallayout.KeyboardVisibilityObserver;
import com.waz.zclient.core.stores.connect.ConnectStoreObserver;
import com.waz.zclient.core.stores.connect.IConnectStore;
import com.waz.zclient.core.stores.network.NetworkAction;
import com.waz.zclient.core.stores.participants.ParticipantsStoreObserver;
import com.waz.zclient.pages.BaseFragment;
import com.waz.zclient.pages.main.conversation.controller.ConversationScreenControllerObserver;
import com.waz.zclient.pages.main.conversation.controller.IConversationScreenController;
import com.waz.zclient.ui.text.AccentColorEditText;
import com.waz.zclient.ui.utils.KeyboardUtils;
import com.waz.zclient.ui.utils.MathUtils;
import com.waz.zclient.ui.views.UserDetailsView;
import com.waz.zclient.ui.views.e2ee.ShieldView;
import com.waz.zclient.utils.LayoutSpec;
import com.waz.zclient.utils.ViewUtils;
public class ParticipantHeaderFragment extends BaseFragment<ParticipantHeaderFragment.Container> implements KeyboardVisibilityObserver,
ParticipantsStoreObserver,
View.OnClickListener,
ConversationScreenControllerObserver,
ConnectStoreObserver,
AccentColorObserver {
public static final String TAG = ParticipantHeaderFragment.class.getName();
private static final String ARG_USER_REQUESTER = "ARG_USER_REQUESTER";
private Toolbar toolbar;
private TextView membersCountTextView;
private UserDetailsView userDetailsView;
private AccentColorEditText headerEditText;
private TextView headerReadOnlyTextView;
private View bottomBorder;
private TextView penIcon;
private ShieldView shieldView;
private IConnectStore.UserRequester userRequester;
private boolean isGroupConversation;
public static ParticipantHeaderFragment newInstance(IConnectStore.UserRequester userRequester) {
ParticipantHeaderFragment participantHeaderFragment = new ParticipantHeaderFragment();
Bundle args = new Bundle();
args.putSerializable(ARG_USER_REQUESTER, userRequester);
participantHeaderFragment.setArguments(args);
return participantHeaderFragment;
}
@Override
public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
final Fragment parent = getParentFragment();
// Apply the workaround only if this is a child fragment, and the parent
// is being removed.
if (!enter && parent != null && parent.isRemoving()) {
// This is a workaround for the bug where child fragments disappear when
// the parent is removed (as all children are first removed from the parent)
// See https://code.google.com/p/android/issues/detail?id=55228
Animation doNothingAnim = new AlphaAnimation(1, 1);
doNothingAnim.setDuration(ViewUtils.getNextAnimationDuration(parent));
return doNothingAnim;
} else {
return super.onCreateAnimation(transit, enter, nextAnim);
}
}
@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, final ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_participants_header, container, false);
toolbar = ViewUtils.getView(rootView, R.id.t__participants__toolbar);
membersCountTextView = ViewUtils.getView(rootView, R.id.ttv__participants__sub_header);
userDetailsView = ViewUtils.getView(rootView, R.id.udv__participants__user_details);
headerReadOnlyTextView = ViewUtils.getView(rootView, R.id.ttv__participants__header);
headerEditText = ViewUtils.getView(rootView, R.id.taet__participants__header__editable);
bottomBorder = ViewUtils.getView(rootView, R.id.v_participants__header__bottom_border);
shieldView = ViewUtils.getView(rootView, R.id.sv__otr__verified_shield);
shieldView.setVisibility(View.GONE);
penIcon = ViewUtils.getView(rootView, R.id.gtv__participants_header__pen_icon);
penIcon.setVisibility(View.GONE);
membersCountTextView.setVisibility(View.GONE);
userDetailsView.setVisibility(View.GONE);
headerEditText.setOnTouchListener(headerOnTouchListener);
headerEditText.setOnEditorActionListener(editorActionListener);
// Hide bottom border initially
bottomBorder.setVisibility(View.GONE);
if (LayoutSpec.isTablet(getContext())) {
toolbar.setNavigationIcon(null);
toolbar.setContentInsetsAbsolute(toolbar.getContentInsetEnd(),
getResources().getDimensionPixelSize(R.dimen.content__padding_left));
} else {
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (userRequester == IConnectStore.UserRequester.POPOVER) {
getContainer().dismissDialog();
} else {
getControllerFactory().getConversationScreenController().hideParticipants(true, false);
}
}
});
}
return rootView;
}
@Override
public void onStart() {
super.onStart();
if (userRequester == IConnectStore.UserRequester.POPOVER) {
getStoreFactory().getConnectStore().addConnectRequestObserver(this);
final User user = getStoreFactory().getSingleParticipantStore().getUser();
getStoreFactory().getConnectStore().loadUser(user.getId(), userRequester);
} else {
getStoreFactory().getParticipantsStore().addParticipantsStoreObserver(this);
}
getControllerFactory().getConversationScreenController().addConversationControllerObservers(this);
getControllerFactory().getAccentColorController().addAccentColorObserver(this);
if (!getControllerFactory().getThemeController().isDarkTheme()) {
headerEditText.setAccentColor(getControllerFactory().getAccentColorController().getColor());
}
}
@Override
public void onResume() {
super.onResume();
headerEditText.clearFocus();
getControllerFactory().getGlobalLayoutController().addKeyboardVisibilityObserver(this);
penIcon.setOnClickListener(this);
}
@Override
public void onPause() {
getControllerFactory().getGlobalLayoutController().removeKeyboardVisibilityObserver(this);
KeyboardUtils.hideKeyboard(getActivity());
penIcon.setOnClickListener(null);
super.onPause();
}
@Override
public void onStop() {
getControllerFactory().getAccentColorController().removeAccentColorObserver(this);
getStoreFactory().getConnectStore().removeConnectRequestObserver(this);
getControllerFactory().getConversationScreenController().removeConversationControllerObservers(this);
getStoreFactory().getParticipantsStore().removeParticipantsStoreObserver(this);
super.onStop();
}
@Override
public void onDestroyView() {
membersCountTextView = null;
userDetailsView = null;
headerReadOnlyTextView = null;
headerEditText = null;
bottomBorder = null;
penIcon = null;
shieldView = null;
super.onDestroyView();
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.gtv__participants_header__pen_icon:
triggerEditingOfConversationNameIfInternet();
break;
}
}
@Override
public void conversationUpdated(IConversation conversation) {
if (conversation == null) {
return;
}
isGroupConversation = conversation.getType() == IConversation.Type.GROUP;
if (isGroupConversation) {
membersCountTextView.setVisibility(View.VISIBLE);
userDetailsView.setVisibility(View.GONE);
headerReadOnlyTextView.setText(conversation.getName());
if (conversation.isMemberOfConversation()) {
headerEditText.setText(conversation.getName());
headerEditText.setVisibility(View.VISIBLE);
} else {
headerEditText.setVisibility(View.GONE);
}
shieldView.setVisibility(View.GONE);
penIcon.setVisibility(View.VISIBLE);
} else {
membersCountTextView.setVisibility(View.GONE);
userDetailsView.setVisibility(View.VISIBLE);
headerEditText.setVisibility(View.GONE);
penIcon.setVisibility(View.GONE);
shieldView.setVisibility(conversation.getOtherParticipant().getVerified() == Verification.VERIFIED ?
View.VISIBLE : View.GONE);
}
}
@Override
public void participantsUpdated(UsersList participants) {
membersCountTextView.setText(getResources().getQuantityString(R.plurals.participants__sub_header_xx_people,
participants.size(), participants.size()));
membersCountTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX,
getResources().getDimension(R.dimen.wire__text_size__small));
membersCountTextView.setOnClickListener(null);
// Hide header bottom border
bottomBorder.setVisibility(View.GONE);
new Handler().post(new Runnable() {
@Override
public void run() {
if (getView() != null && getContainer() != null) {
final int height = getView().getMeasuredHeight();
getControllerFactory().getConversationScreenController().setParticipantHeaderHeight(height);
}
}
});
}
@Override
public void otherUserUpdated(final User otherUser) {
setParticipant(otherUser);
}
private View.OnTouchListener headerOnTouchListener = new View.OnTouchListener() {
private boolean downAction = false;
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP && downAction) {
triggerEditingOfConversationNameIfInternet();
downAction = false;
} else if (event.getAction() == MotionEvent.ACTION_DOWN) {
downAction = true;
}
// consume touch event if there is no network.
return !getStoreFactory().getNetworkStore().hasInternetConnection();
}
};
private void triggerEditingOfConversationNameIfInternet() {
if (getStoreFactory() == null ||
getStoreFactory().isTornDown()) {
return;
}
if (MathUtils.floatEqual(headerEditText.getAlpha(), 0f)) {
// only if not already visible and network is available
getStoreFactory().getNetworkStore().doIfHasInternetOrNotifyUser(new NetworkAction() {
@Override
public void execute(NetworkMode networkMode) {
toggleEditModeForConversationName(true);
}
@Override
public void onNoNetwork() {
showOfflineRenameError();
}
});
}
}
private void showOfflineRenameError() {
ViewUtils.showAlertDialog(getActivity(),
R.string.alert_dialog__no_network__header,
R.string.rename_conversation__no_network__message,
R.string.alert_dialog__confirmation,
null, true);
}
private TextView.OnEditorActionListener editorActionListener = new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(final TextView textView, int actionId, KeyEvent event) {
if (actionId == EditorInfo.IME_ACTION_DONE || (event != null && event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) {
if (LayoutSpec.isPhone(getActivity())) {
renameConversation();
}
closeHeaderEditing();
return true;
}
return false;
}
private void closeHeaderEditing() {
headerEditText.clearFocus();
final int height = getView().getMeasuredHeight();
new Handler().post(new Runnable() {
@Override
public void run() {
getControllerFactory().getConversationScreenController().setParticipantHeaderHeight(height);
}
});
InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(headerEditText.getWindowToken(), 0);
}
};
private void resetName() {
headerReadOnlyTextView.setText(getStoreFactory().getConversationStore().getCurrentConversation().getName());
}
private void renameConversation() {
getStoreFactory().getNetworkStore().doIfHasInternetOrNotifyUser(new NetworkAction() {
@Override
public void execute(NetworkMode networkMode) {
updateConversationName();
}
@Override
public void onNoNetwork() {
resetName();
showOfflineRenameError();
}
}
);
}
private void updateConversationName() {
if (headerEditText == null) {
return;
}
String text = headerEditText.getText().toString();
if (text.equals(getStoreFactory().getConversationStore().getCurrentConversation().getName())) {
return;
}
if (!TextUtils.isEmpty(text.trim())) {
getStoreFactory().getConversationStore().getCurrentConversation().setConversationName(text);
headerReadOnlyTextView.setText(text);
}
}
//////////////////////////////////////////////////////////////////////////////////////////
//
// Conversation Manager Notifications
//
//////////////////////////////////////////////////////////////////////////////////////////
@Override
public void onShowParticipants(View anchorView, boolean isSingleConversation, boolean isMemberOfConversation, boolean showDeviceTabIfSingle) {
if (!isSingleConversation && isMemberOfConversation) {
penIcon.setVisibility(View.VISIBLE);
} else {
penIcon.setVisibility(View.GONE);
}
}
@Override
public void onHideParticipants(boolean orButtonPressed,
boolean hideByConversationChange,
boolean backOrButtonPressed) {
if (LayoutSpec.isTablet(getActivity()) &&
!hideByConversationChange) {
renameConversation();
}
}
@Override
public void onShowEditConversationName(boolean edit) {
// do nothing
}
@Override
public void setListOffset(int offset) {
}
@Override
public void onHeaderViewMeasured(int participantHeaderHeight) {
}
@Override
public void onScrollParticipantsList(int verticalOffset, boolean scrolledToBottom) {
if (bottomBorder == null) {
return;
}
if (verticalOffset > 0) {
bottomBorder.setVisibility(View.VISIBLE);
} else {
bottomBorder.setVisibility(View.GONE);
}
}
@Override
public void onConversationLoaded() {
}
@Override
public void onShowUser(User user) {
}
@Override
public void onHideUser() {
}
@Override
public void onAddPeopleToConversation() {
}
@Override
public void onShowConversationMenu(@IConversationScreenController.ConversationMenuRequester int requester,
IConversation conversation,
View anchorView) {
}
@Override
public void onShowOtrClient(OtrClient otrClient, User user) {
}
@Override
public void onShowCurrentOtrClient() {
}
@Override
public void onHideOtrClient() {
}
@Override
public void onShowLikesList(Message message) {
}
@Override
public void onKeyboardVisibilityChanged(boolean keyboardIsVisible, int keyboardHeight, View currentFocus) {
if (!keyboardIsVisible) {
toggleEditModeForConversationName(false);
}
}
public void onConnectUserUpdated(final User user, IConnectStore.UserRequester usertype) {
if (usertype != userRequester) {
return;
}
setParticipant(user);
}
private void setParticipant(final User user) {
headerReadOnlyTextView.setText(user.getName());
userDetailsView.setUser(user);
headerEditText.setVisibility(View.GONE);
new Handler().post(new Runnable() {
@Override
public void run() {
if (getView() != null && getControllerFactory().getConversationScreenController() != null) {
final int height = getView().getMeasuredHeight();
getControllerFactory().getConversationScreenController().setParticipantHeaderHeight(height);
}
}
});
}
@Override
public void onInviteRequestSent(IConversation conversation) {
}
@Override
public void onAccentColorHasChanged(Object sender, int color) {
if (isGroupConversation) {
return;
}
if (!getControllerFactory().getThemeController().isDarkTheme()) {
headerEditText.setAccentColor(color);
}
}
private void toggleEditModeForConversationName(boolean edit) {
if (!isGroupConversation) {
return;
}
getControllerFactory().getConversationScreenController().editConversationName(edit);
if (edit) {
headerEditText.setText(headerReadOnlyTextView.getText());
headerEditText.setAlpha(1);
headerEditText.requestFocus();
getControllerFactory().getFocusController().setFocus(IFocusController.CONVERSATION_EDIT_NAME);
headerEditText.setSelection(headerEditText.getText().length());
KeyboardUtils.showKeyboard(getActivity());
headerReadOnlyTextView.setAlpha(0);
membersCountTextView.setAlpha(0);
penIcon.setVisibility(View.GONE);
} else {
headerEditText.setAlpha(0);
headerEditText.requestLayout();
headerReadOnlyTextView.setAlpha(1);
headerEditText.clearFocus();
membersCountTextView.setAlpha(1);
penIcon.setVisibility(View.VISIBLE);
if (LayoutSpec.isTablet(getActivity())) {
renameConversation();
}
}
}
public interface Container {
void onClickedEmptyBackground();
void dismissDialog();
}
}