From 6102e9aa72b802a4022f29d8d63329c428b91232 Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Tue, 2 Jun 2020 15:02:35 -0300 Subject: [PATCH] Apply better coordinatorlayout animation and RTL support. --- ...roupSettingsCoordinatorLayoutBehavior.java | 129 +++++++++++++ .../ui/managegroup/ManageGroupFragment.java | 4 +- .../main/res/layout/group_manage_fragment.xml | 171 +++++++++++++----- 3 files changed, 254 insertions(+), 50 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/groups/ui/creategroup/GroupSettingsCoordinatorLayoutBehavior.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/ui/creategroup/GroupSettingsCoordinatorLayoutBehavior.java b/app/src/main/java/org/thoughtcrime/securesms/groups/ui/creategroup/GroupSettingsCoordinatorLayoutBehavior.java new file mode 100644 index 00000000000..01482cf0237 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/ui/creategroup/GroupSettingsCoordinatorLayoutBehavior.java @@ -0,0 +1,129 @@ +package org.thoughtcrime.securesms.groups.ui.creategroup; + +import android.content.Context; +import android.graphics.Rect; +import android.util.AttributeSet; +import android.view.View; +import android.view.animation.DecelerateInterpolator; +import android.view.animation.Interpolator; +import android.widget.TextView; + +import androidx.annotation.IdRes; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.coordinatorlayout.widget.CoordinatorLayout; + +import com.google.android.material.appbar.AppBarLayout; + +import org.thoughtcrime.securesms.R; + +import java.lang.ref.WeakReference; + +public final class GroupSettingsCoordinatorLayoutBehavior extends CoordinatorLayout.Behavior { + + private static final Interpolator INTERPOLATOR = new DecelerateInterpolator(); + + private final ViewRef avatarTargetRef = new ViewRef(R.id.avatar_target); + private final ViewRef groupNameRef = new ViewRef(R.id.group_name); + private final ViewRef groupNameTargetRef = new ViewRef(R.id.group_name_target); + private final Rect targetRect = new Rect(); + private final Rect childRect = new Rect(); + + public GroupSettingsCoordinatorLayoutBehavior(@NonNull Context context, @Nullable AttributeSet attrs) { + } + + @Override + public boolean layoutDependsOn(@NonNull CoordinatorLayout parent, @NonNull View child, @NonNull View dependency) { + return dependency instanceof AppBarLayout; + } + + @Override + public boolean onDependentViewChanged(@NonNull CoordinatorLayout parent, @NonNull View child, @NonNull View dependency) { + AppBarLayout appBarLayout = (AppBarLayout) dependency; + int range = appBarLayout.getTotalScrollRange(); + float factor = INTERPOLATOR.getInterpolation(-appBarLayout.getY() / range); + + updateAvatarPositionAndScale(parent, child, factor); + updateNamePosition(parent, factor); + + return true; + } + + private void updateAvatarPositionAndScale(@NonNull CoordinatorLayout parent, @NonNull View child, float factor) { + View target = avatarTargetRef.require(parent); + + targetRect.set(target.getLeft(), target.getTop(), target.getRight(), target.getBottom()); + childRect.set(child.getLeft(), child.getTop(), child.getRight(), child.getBottom()); + + float widthScale = 1f - (1f - (targetRect.width() / (float) childRect.width())) * factor; + float heightScale = 1f - (1f - (targetRect.height() / (float) childRect.height())) * factor; + + float superimposedLeft = childRect.left + (childRect.width() - targetRect.width()) / 2f; + float superimposedTop = childRect.top + (childRect.height() - targetRect.height()) / 2f; + + float xTranslation = (targetRect.left - superimposedLeft) * factor; + float yTranslation = (targetRect.top - superimposedTop) * factor; + + child.setScaleX(widthScale); + child.setScaleY(heightScale); + child.setTranslationX(xTranslation); + child.setTranslationY(yTranslation); + } + + private void updateNamePosition(@NonNull CoordinatorLayout parent, float factor) { + TextView child = (TextView) groupNameRef.require(parent); + View target = groupNameTargetRef.require(parent); + + targetRect.set(target.getLeft(), target.getTop(), target.getRight(), target.getBottom()); + childRect.set(child.getLeft(), child.getTop(), child.getRight(), child.getBottom()); + + if (child.getMaxWidth() != targetRect.width()) { + child.setMaxWidth(targetRect.width()); + } + + float deltaTop = targetRect.top - childRect.top; + float deltaStart = getStart(parent, targetRect) - getStart(parent, childRect); + + float yTranslation = deltaTop * factor; + float xTranslation = deltaStart * factor; + + child.setTranslationY(yTranslation); + child.setTranslationX(xTranslation); + } + + private static int getStart(@NonNull CoordinatorLayout parent, @NonNull Rect rect) { + return parent.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR ? rect.left : rect.right; + } + + private static final class ViewRef { + + private WeakReference ref = new WeakReference<>(null); + + private final @IdRes int idRes; + + private ViewRef(@IdRes int idRes) { + this.idRes = idRes; + } + + private @NonNull View require(@NonNull View parent) { + View view = ref.get(); + + if (view == null) { + view = getChildOrThrow(parent, idRes); + ref = new WeakReference<>(view); + } + + return view; + } + + private static @NonNull View getChildOrThrow(@NonNull View parent, @IdRes int id) { + View child = parent.findViewById(id); + + if (child == null) { + throw new AssertionError("Can't find view with ID " + R.id.avatar_target); + } else { + return child; + } + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/ui/managegroup/ManageGroupFragment.java b/app/src/main/java/org/thoughtcrime/securesms/groups/ui/managegroup/ManageGroupFragment.java index 5f05a3ecbcc..7dd77da01ef 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/ui/managegroup/ManageGroupFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/ui/managegroup/ManageGroupFragment.java @@ -64,6 +64,7 @@ public class ManageGroupFragment extends Fragment { private View pendingMembersRow; private TextView pendingMembersCount; private Toolbar toolbar; + private TextView groupName; private TextView memberCountUnderAvatar; private TextView memberCountAboveList; private AvatarImageView avatar; @@ -115,6 +116,7 @@ static ManageGroupFragment newInstance(@NonNull String groupId) { avatar = view.findViewById(R.id.group_avatar); toolbar = view.findViewById(R.id.toolbar); + groupName = view.findViewById(R.id.group_name); memberCountUnderAvatar = view.findViewById(R.id.member_count); memberCountAboveList = view.findViewById(R.id.member_count_2); groupMemberList = view.findViewById(R.id.group_members); @@ -185,7 +187,7 @@ public void onActivityCreated(@Nullable Bundle savedInstanceState) { toolbar.inflateMenu(R.menu.manage_group_fragment); viewModel.getCanEditGroupAttributes().observe(getViewLifecycleOwner(), canEdit -> toolbar.getMenu().findItem(R.id.action_edit).setVisible(canEdit)); - viewModel.getTitle().observe(getViewLifecycleOwner(), toolbar::setTitle); + viewModel.getTitle().observe(getViewLifecycleOwner(), groupName::setText); viewModel.getMemberCountSummary().observe(getViewLifecycleOwner(), memberCountUnderAvatar::setText); viewModel.getFullMemberCountSummary().observe(getViewLifecycleOwner(), memberCountAboveList::setText); viewModel.getGroupRecipient().observe(getViewLifecycleOwner(), groupRecipient -> { diff --git a/app/src/main/res/layout/group_manage_fragment.xml b/app/src/main/res/layout/group_manage_fragment.xml index 47f44236a2c..81056ec7cb1 100644 --- a/app/src/main/res/layout/group_manage_fragment.xml +++ b/app/src/main/res/layout/group_manage_fragment.xml @@ -30,19 +30,19 @@ android:paddingTop="16dp" android:paddingBottom="16dp"> - - + android:ellipsize="end" + android:maxLines="1" + android:textAppearance="@style/TextAppearance.Signal.Body1.Bold" /> + app:navigationIcon="@drawable/ic_arrow_left_24"> + + + + + + + + + + + android:orientation="horizontal"> @@ -151,9 +195,10 @@ android:id="@+id/group_mute_notifications" android:layout_width="0dp" android:layout_height="wrap_content" - android:layout_marginStart="@dimen/group_manage_fragment_card_vertical_padding" - android:gravity="start|center_vertical" + android:layout_marginStart="@dimen/group_manage_fragment_row_horizontal_padding" + android:gravity="center_vertical|start" android:text="@string/ManageGroupActivity_mute_notifications" + android:textAlignment="viewStart" android:textAppearance="@style/Signal.Text.Body" app:layout_constraintBottom_toTopOf="@id/group_mute_notifications_until" app:layout_constraintEnd_toStartOf="@id/group_mute_notifications_switch" @@ -164,7 +209,7 @@ android:id="@+id/group_mute_notifications_until" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginStart="@dimen/group_manage_fragment_card_vertical_padding" + android:layout_marginStart="@dimen/group_manage_fragment_row_horizontal_padding" android:textAppearance="@style/TextSecure.SubtitleTextStyle" android:visibility="gone" app:layout_constraintBottom_toBottomOf="parent" @@ -175,10 +220,11 @@ + android:focusable="true"> @@ -261,9 +313,10 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:focusableInTouchMode="true" - android:gravity="end|center_vertical" - android:paddingStart="16dp" - android:paddingEnd="16dp" + android:gravity="center_vertical" + android:minWidth="48dp" + android:paddingStart="@dimen/group_manage_fragment_row_horizontal_padding" + android:paddingEnd="@dimen/group_manage_fragment_row_horizontal_padding" android:text="@string/ManageGroupActivity_see_all" android:textAppearance="@style/Signal.Text.Body" app:layout_constraintEnd_toEndOf="parent" @@ -303,23 +356,28 @@ android:layout_height="@dimen/group_manage_fragment_row_height" android:background="?selectableItemBackground" android:clickable="true" - android:focusable="true" - android:paddingStart="@dimen/group_manage_fragment_row_horizontal_padding" - android:paddingEnd="@dimen/group_manage_fragment_row_horizontal_padding"> + android:focusable="true"> @@ -332,23 +390,28 @@ android:layout_height="@dimen/group_manage_fragment_row_height" android:background="?selectableItemBackground" android:clickable="true" - android:focusable="true" - android:paddingStart="@dimen/group_manage_fragment_row_horizontal_padding" - android:paddingEnd="@dimen/group_manage_fragment_row_horizontal_padding"> + android:focusable="true"> @@ -393,6 +456,7 @@ android:paddingStart="@dimen/group_manage_fragment_row_horizontal_padding" android:paddingEnd="@dimen/group_manage_fragment_row_horizontal_padding" android:text="@string/ManageGroupActivity_add_members" + android:textAlignment="viewStart" android:textAppearance="@style/Signal.Text.Body" android:textColor="@color/ultramarine_text_button" android:visibility="gone" @@ -415,6 +479,7 @@ android:paddingStart="@dimen/group_manage_fragment_row_horizontal_padding" android:paddingEnd="@dimen/group_manage_fragment_row_horizontal_padding" android:text="@string/ManageGroupActivity_view_all_members" + android:textAlignment="viewStart" android:textAppearance="@style/Signal.Text.Body" android:textColor="?title_text_color_secondary" android:visibility="gone" @@ -439,22 +504,28 @@ android:background="?selectableItemBackground" android:clickable="true" android:focusable="true" - android:orientation="horizontal" - android:paddingStart="@dimen/group_manage_fragment_row_horizontal_padding" - android:paddingEnd="@dimen/group_manage_fragment_row_horizontal_padding"> + android:orientation="horizontal"> @@ -485,6 +556,7 @@ android:paddingStart="@dimen/group_manage_fragment_row_horizontal_padding" android:paddingEnd="@dimen/group_manage_fragment_row_horizontal_padding" android:text="@string/ManageGroupActivity_block_group" + android:textAlignment="viewStart" android:textAppearance="@style/Signal.Text.Body" android:textColor="@color/core_red" /> @@ -497,6 +569,7 @@ android:paddingStart="@dimen/group_manage_fragment_row_horizontal_padding" android:paddingEnd="@dimen/group_manage_fragment_row_horizontal_padding" android:text="@string/ManageGroupActivity_leave_group" + android:textAlignment="viewStart" android:textAppearance="@style/Signal.Text.Body" android:textColor="@color/core_red" />