Update multi-forward work with tweaks from design.
This commit is contained in:
parent
8e2a265cf3
commit
7448183ff4
8 changed files with 50 additions and 18 deletions
|
@ -15,6 +15,7 @@ import android.widget.ImageView;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.StringRes;
|
import androidx.annotation.StringRes;
|
||||||
import androidx.core.widget.TextViewCompat;
|
import androidx.core.widget.TextViewCompat;
|
||||||
|
|
||||||
|
@ -101,7 +102,6 @@ public final class ContactFilterView extends FrameLayout {
|
||||||
|
|
||||||
expandTapArea(toggleContainer, dialpadToggle);
|
expandTapArea(toggleContainer, dialpadToggle);
|
||||||
applyAttributes(searchText, context, attrs, defStyleAttr);
|
applyAttributes(searchText, context, attrs, defStyleAttr);
|
||||||
searchText.requestFocus();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void applyAttributes(@NonNull EditText searchText,
|
private void applyAttributes(@NonNull EditText searchText,
|
||||||
|
@ -121,6 +121,11 @@ public final class ContactFilterView extends FrameLayout {
|
||||||
if (!attributes.getBoolean(R.styleable.ContactFilterToolbar_showDialpad, true)) {
|
if (!attributes.getBoolean(R.styleable.ContactFilterToolbar_showDialpad, true)) {
|
||||||
dialpadToggle.setVisibility(GONE);
|
dialpadToggle.setVisibility(GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (attributes.getBoolean(R.styleable.ContactFilterToolbar_cfv_autoFocus, true)) {
|
||||||
|
searchText.requestFocus();
|
||||||
|
}
|
||||||
|
|
||||||
attributes.recycle();
|
attributes.recycle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,6 +142,10 @@ public final class ContactFilterView extends FrameLayout {
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setOnSearchInputFocusChangedListener(@Nullable OnFocusChangeListener listener) {
|
||||||
|
searchText.setOnFocusChangeListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
public void setHint(@StringRes int hint) {
|
public void setHint(@StringRes int hint) {
|
||||||
searchText.setHint(hint);
|
searchText.setHint(hint);
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,8 @@ import org.thoughtcrime.securesms.util.ViewUtil
|
||||||
*/
|
*/
|
||||||
abstract class FixedRoundedCornerBottomSheetDialogFragment : BottomSheetDialogFragment() {
|
abstract class FixedRoundedCornerBottomSheetDialogFragment : BottomSheetDialogFragment() {
|
||||||
|
|
||||||
|
protected open val peekHeightPercentage: Float = 0.5f
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setStyle(STYLE_NORMAL, R.style.Widget_Signal_FixedRoundedCorners)
|
setStyle(STYLE_NORMAL, R.style.Widget_Signal_FixedRoundedCorners)
|
||||||
|
@ -27,7 +29,7 @@ abstract class FixedRoundedCornerBottomSheetDialogFragment : BottomSheetDialogFr
|
||||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
val dialog = super.onCreateDialog(savedInstanceState) as BottomSheetDialog
|
val dialog = super.onCreateDialog(savedInstanceState) as BottomSheetDialog
|
||||||
|
|
||||||
dialog.behavior.peekHeight = (resources.displayMetrics.heightPixels * 0.50).toInt()
|
dialog.behavior.peekHeight = (resources.displayMetrics.heightPixels * peekHeightPercentage).toInt()
|
||||||
|
|
||||||
val shapeAppearanceModel = ShapeAppearanceModel.builder()
|
val shapeAppearanceModel = ShapeAppearanceModel.builder()
|
||||||
.setTopLeftCorner(CornerFamily.ROUNDED, ViewUtil.dpToPx(requireContext(), 18).toFloat())
|
.setTopLeftCorner(CornerFamily.ROUNDED, ViewUtil.dpToPx(requireContext(), 18).toFloat())
|
||||||
|
|
|
@ -541,8 +541,11 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
|
||||||
} else if (multiselectPart instanceof MultiselectPart.Text && hasThumbnail(messageRecord)) {
|
} else if (multiselectPart instanceof MultiselectPart.Text && hasThumbnail(messageRecord)) {
|
||||||
Projection projection = Projection.relativeToViewRoot(mediaThumbnailStub.require(), null);
|
Projection projection = Projection.relativeToViewRoot(mediaThumbnailStub.require(), null);
|
||||||
return (int) projection.getY() + projection.getHeight();
|
return (int) projection.getY() + projection.getHeight();
|
||||||
} else {
|
} else if (hasNoBubble(messageRecord)) {
|
||||||
return getTop();
|
return getTop();
|
||||||
|
} else {
|
||||||
|
Projection projection = Projection.relativeToViewRoot(bodyBubble, null);
|
||||||
|
return (int) projection.getY();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -551,8 +554,11 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
|
||||||
if (multiselectPart instanceof MultiselectPart.Attachments && hasThumbnail(messageRecord)) {
|
if (multiselectPart instanceof MultiselectPart.Attachments && hasThumbnail(messageRecord)) {
|
||||||
Projection projection = Projection.relativeToViewRoot(mediaThumbnailStub.require(), null);
|
Projection projection = Projection.relativeToViewRoot(mediaThumbnailStub.require(), null);
|
||||||
return (int) projection.getY() + projection.getHeight();
|
return (int) projection.getY() + projection.getHeight();
|
||||||
} else {
|
} else if (hasNoBubble(messageRecord)) {
|
||||||
return getBottom();
|
return getBottom();
|
||||||
|
} else {
|
||||||
|
Projection projection = Projection.relativeToViewRoot(bodyBubble, null);
|
||||||
|
return (int) projection.getY() + projection.getHeight();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,6 @@ class MultiselectItemDecoration(context: Context, private val chatWallpaperProvi
|
||||||
private val path = Path()
|
private val path = Path()
|
||||||
private val rect = Rect()
|
private val rect = Rect()
|
||||||
private val gutter = ViewUtil.dpToPx(48)
|
private val gutter = ViewUtil.dpToPx(48)
|
||||||
private val paddingBottom = ViewUtil.dpToPx(9)
|
|
||||||
private val paddingStart = ViewUtil.dpToPx(17)
|
private val paddingStart = ViewUtil.dpToPx(17)
|
||||||
private val circleRadius = ViewUtil.dpToPx(11)
|
private val circleRadius = ViewUtil.dpToPx(11)
|
||||||
private val checkDrawable = requireNotNull(AppCompatResources.getDrawable(context, R.drawable.ic_check_circle_solid_24)).apply {
|
private val checkDrawable = requireNotNull(AppCompatResources.getDrawable(context, R.drawable.ic_check_circle_solid_24)).apply {
|
||||||
|
@ -37,7 +36,6 @@ class MultiselectItemDecoration(context: Context, private val chatWallpaperProvi
|
||||||
}
|
}
|
||||||
private val photoCircleRadius = ViewUtil.dpToPx(12)
|
private val photoCircleRadius = ViewUtil.dpToPx(12)
|
||||||
private val photoCirclePaddingStart = ViewUtil.dpToPx(16)
|
private val photoCirclePaddingStart = ViewUtil.dpToPx(16)
|
||||||
private val photoCirclePaddingBottom = ViewUtil.dpToPx(8)
|
|
||||||
|
|
||||||
private val transparentBlack20 = ContextCompat.getColor(context, R.color.transparent_black_20)
|
private val transparentBlack20 = ContextCompat.getColor(context, R.color.transparent_black_20)
|
||||||
private val transparentWhite20 = ContextCompat.getColor(context, R.color.transparent_white_20)
|
private val transparentWhite20 = ContextCompat.getColor(context, R.color.transparent_white_20)
|
||||||
|
@ -155,15 +153,16 @@ class MultiselectItemDecoration(context: Context, private val chatWallpaperProvi
|
||||||
val parts: MultiselectCollection = child.conversationMessage.multiselectCollection
|
val parts: MultiselectCollection = child.conversationMessage.multiselectCollection
|
||||||
|
|
||||||
parts.toSet().forEach {
|
parts.toSet().forEach {
|
||||||
val boundary = child.getBottomBoundaryOfMultiselectPart(it)
|
val topBoundary = child.getTopBoundaryOfMultiselectPart(it)
|
||||||
|
val bottomBoundary = child.getBottomBoundaryOfMultiselectPart(it)
|
||||||
if (drawCircleBehindSelector) {
|
if (drawCircleBehindSelector) {
|
||||||
drawPhotoCircle(canvas, parent, boundary)
|
drawPhotoCircle(canvas, parent, topBoundary, bottomBoundary)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (adapter.selectedItems.contains(it)) {
|
if (adapter.selectedItems.contains(it)) {
|
||||||
drawSelectedCircle(canvas, parent, boundary)
|
drawSelectedCircle(canvas, parent, topBoundary, bottomBoundary)
|
||||||
} else {
|
} else {
|
||||||
drawUnselectedCircle(canvas, parent, boundary)
|
drawUnselectedCircle(canvas, parent, topBoundary, bottomBoundary)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -173,14 +172,14 @@ class MultiselectItemDecoration(context: Context, private val chatWallpaperProvi
|
||||||
* Draws an extra circle behind the selection circle. This is to make it easier to see and
|
* Draws an extra circle behind the selection circle. This is to make it easier to see and
|
||||||
* is specifically for when a photo wallpaper is being used.
|
* is specifically for when a photo wallpaper is being used.
|
||||||
*/
|
*/
|
||||||
private fun drawPhotoCircle(canvas: Canvas, parent: RecyclerView, bottomBoundary: Int) {
|
private fun drawPhotoCircle(canvas: Canvas, parent: RecyclerView, topBoundary: Int, bottomBoundary: Int) {
|
||||||
val centerX: Float = if (ViewUtil.isLtr(parent)) {
|
val centerX: Float = if (ViewUtil.isLtr(parent)) {
|
||||||
photoCirclePaddingStart + photoCircleRadius
|
photoCirclePaddingStart + photoCircleRadius
|
||||||
} else {
|
} else {
|
||||||
parent.right - photoCircleRadius - photoCirclePaddingStart
|
parent.right - photoCircleRadius - photoCirclePaddingStart
|
||||||
}.toFloat()
|
}.toFloat()
|
||||||
|
|
||||||
val centerY: Float = bottomBoundary - photoCircleRadius - photoCirclePaddingBottom.toFloat()
|
val centerY: Float = topBoundary + (bottomBoundary - topBoundary).toFloat() / 2
|
||||||
|
|
||||||
canvas.drawCircle(centerX, centerY, photoCircleRadius.toFloat(), photoCirclePaint)
|
canvas.drawCircle(centerX, centerY, photoCircleRadius.toFloat(), photoCirclePaint)
|
||||||
}
|
}
|
||||||
|
@ -188,14 +187,14 @@ class MultiselectItemDecoration(context: Context, private val chatWallpaperProvi
|
||||||
/**
|
/**
|
||||||
* Draws the checkmark for selected content
|
* Draws the checkmark for selected content
|
||||||
*/
|
*/
|
||||||
private fun drawSelectedCircle(canvas: Canvas, parent: RecyclerView, bottomBoundary: Int) {
|
private fun drawSelectedCircle(canvas: Canvas, parent: RecyclerView, topBoundary: Int, bottomBoundary: Int) {
|
||||||
val topX: Float = if (ViewUtil.isLtr(parent)) {
|
val topX: Float = if (ViewUtil.isLtr(parent)) {
|
||||||
paddingStart
|
paddingStart
|
||||||
} else {
|
} else {
|
||||||
parent.right - paddingStart - circleRadius * 2
|
parent.right - paddingStart - circleRadius * 2
|
||||||
}.toFloat()
|
}.toFloat()
|
||||||
|
|
||||||
val topY: Float = bottomBoundary - circleRadius * 2 - paddingBottom.toFloat()
|
val topY: Float = topBoundary + (bottomBoundary - topBoundary).toFloat() / 2 - circleRadius
|
||||||
|
|
||||||
canvas.save()
|
canvas.save()
|
||||||
canvas.translate(topX, topY)
|
canvas.translate(topX, topY)
|
||||||
|
@ -206,14 +205,14 @@ class MultiselectItemDecoration(context: Context, private val chatWallpaperProvi
|
||||||
/**
|
/**
|
||||||
* Draws the empty circle for unselected content
|
* Draws the empty circle for unselected content
|
||||||
*/
|
*/
|
||||||
private fun drawUnselectedCircle(c: Canvas, parent: RecyclerView, bottomBoundary: Int) {
|
private fun drawUnselectedCircle(c: Canvas, parent: RecyclerView, topBoundary: Int, bottomBoundary: Int) {
|
||||||
val centerX: Float = if (ViewUtil.isLtr(parent)) {
|
val centerX: Float = if (ViewUtil.isLtr(parent)) {
|
||||||
paddingStart + circleRadius
|
paddingStart + circleRadius
|
||||||
} else {
|
} else {
|
||||||
parent.right - circleRadius - paddingStart
|
parent.right - circleRadius - paddingStart
|
||||||
}.toFloat()
|
}.toFloat()
|
||||||
|
|
||||||
val centerY: Float = bottomBoundary - circleRadius - paddingBottom.toFloat()
|
val centerY: Float = topBoundary + (bottomBoundary - topBoundary).toFloat() / 2
|
||||||
|
|
||||||
c.drawCircle(centerX, centerY, circleRadius.toFloat(), unselectedPaint)
|
c.drawCircle(centerX, centerY, circleRadius.toFloat(), unselectedPaint)
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,8 @@ import androidx.core.view.isVisible
|
||||||
import androidx.fragment.app.FragmentManager
|
import androidx.fragment.app.FragmentManager
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||||
|
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||||
import org.signal.core.util.logging.Log
|
import org.signal.core.util.logging.Log
|
||||||
import org.thoughtcrime.securesms.ContactSelectionListFragment
|
import org.thoughtcrime.securesms.ContactSelectionListFragment
|
||||||
import org.thoughtcrime.securesms.R
|
import org.thoughtcrime.securesms.R
|
||||||
|
@ -35,9 +37,12 @@ private val TAG = Log.tag(MultiselectForwardFragment::class.java)
|
||||||
|
|
||||||
class MultiselectForwardFragment : FixedRoundedCornerBottomSheetDialogFragment(), ContactSelectionListFragment.OnContactSelectedListener, ContactSelectionListFragment.OnSelectionLimitReachedListener {
|
class MultiselectForwardFragment : FixedRoundedCornerBottomSheetDialogFragment(), ContactSelectionListFragment.OnContactSelectedListener, ContactSelectionListFragment.OnSelectionLimitReachedListener {
|
||||||
|
|
||||||
|
override val peekHeightPercentage: Float = 0.67f
|
||||||
|
|
||||||
private val viewModel: MultiselectForwardViewModel by viewModels(factoryProducer = this::createViewModelFactory)
|
private val viewModel: MultiselectForwardViewModel by viewModels(factoryProducer = this::createViewModelFactory)
|
||||||
|
|
||||||
private lateinit var selectionFragment: ContactSelectionListFragment
|
private lateinit var selectionFragment: ContactSelectionListFragment
|
||||||
|
private lateinit var contactFilterView: ContactFilterView
|
||||||
|
|
||||||
private fun createViewModelFactory(): MultiselectForwardViewModel.Factory {
|
private fun createViewModelFactory(): MultiselectForwardViewModel.Factory {
|
||||||
return MultiselectForwardViewModel.Factory(getMultiShareArgs(), MultiselectForwardRepository(requireContext()))
|
return MultiselectForwardViewModel.Factory(getMultiShareArgs(), MultiselectForwardRepository(requireContext()))
|
||||||
|
@ -70,7 +75,13 @@ class MultiselectForwardFragment : FixedRoundedCornerBottomSheetDialogFragment()
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
selectionFragment = childFragmentManager.findFragmentById(R.id.contact_selection_list_fragment) as ContactSelectionListFragment
|
selectionFragment = childFragmentManager.findFragmentById(R.id.contact_selection_list_fragment) as ContactSelectionListFragment
|
||||||
|
|
||||||
val contactFilterView: ContactFilterView = view.findViewById(R.id.contact_filter_edit_text)
|
contactFilterView = view.findViewById(R.id.contact_filter_edit_text)
|
||||||
|
|
||||||
|
contactFilterView.setOnSearchInputFocusChangedListener { _, hasFocus ->
|
||||||
|
if (hasFocus) {
|
||||||
|
(requireDialog() as BottomSheetDialog).behavior.state = BottomSheetBehavior.STATE_EXPANDED
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
contactFilterView.setOnFilterChangedListener {
|
contactFilterView.setOnFilterChangedListener {
|
||||||
if (it.isNullOrEmpty()) {
|
if (it.isNullOrEmpty()) {
|
||||||
|
@ -145,6 +156,7 @@ class MultiselectForwardFragment : FixedRoundedCornerBottomSheetDialogFragment()
|
||||||
if (recipientId.isPresent) {
|
if (recipientId.isPresent) {
|
||||||
viewModel.addSelectedContact(recipientId, null)
|
viewModel.addSelectedContact(recipientId, null)
|
||||||
callback.accept(true)
|
callback.accept(true)
|
||||||
|
contactFilterView.clear()
|
||||||
} else {
|
} else {
|
||||||
Log.w(TAG, "Rejecting non-present recipient. Can't forward to an unknown contact.")
|
Log.w(TAG, "Rejecting non-present recipient. Can't forward to an unknown contact.")
|
||||||
callback.accept(false)
|
callback.accept(false)
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<View
|
<View
|
||||||
|
@ -28,7 +29,8 @@
|
||||||
android:layout_marginLeft="@dimen/dsl_settings_gutter"
|
android:layout_marginLeft="@dimen/dsl_settings_gutter"
|
||||||
android:layout_marginTop="17dp"
|
android:layout_marginTop="17dp"
|
||||||
android:layout_marginRight="@dimen/dsl_settings_gutter"
|
android:layout_marginRight="@dimen/dsl_settings_gutter"
|
||||||
android:minHeight="44dp" />
|
android:minHeight="44dp"
|
||||||
|
app:cfv_autoFocus="false" />
|
||||||
|
|
||||||
<androidx.fragment.app.FragmentContainerView
|
<androidx.fragment.app.FragmentContainerView
|
||||||
android:id="@+id/contact_selection_list_fragment"
|
android:id="@+id/contact_selection_list_fragment"
|
||||||
|
|
|
@ -44,6 +44,7 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="@dimen/dsl_settings_gutter"
|
android:layout_marginStart="@dimen/dsl_settings_gutter"
|
||||||
android:layout_marginEnd="@dimen/dsl_settings_gutter"
|
android:layout_marginEnd="@dimen/dsl_settings_gutter"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
android:background="@drawable/rounded_rectangle_secondary"
|
android:background="@drawable/rounded_rectangle_secondary"
|
||||||
android:hint="@string/MultiselectForwardFragment__add_a_message"
|
android:hint="@string/MultiselectForwardFragment__add_a_message"
|
||||||
android:minHeight="44dp"
|
android:minHeight="44dp"
|
||||||
|
|
|
@ -227,6 +227,7 @@
|
||||||
<declare-styleable name="ContactFilterToolbar">
|
<declare-styleable name="ContactFilterToolbar">
|
||||||
<attr name="searchTextStyle" format="reference" />
|
<attr name="searchTextStyle" format="reference" />
|
||||||
<attr name="showDialpad" format="boolean" />
|
<attr name="showDialpad" format="boolean" />
|
||||||
|
<attr name="cfv_autoFocus" format="boolean" />
|
||||||
</declare-styleable>
|
</declare-styleable>
|
||||||
|
|
||||||
<declare-styleable name="SquareImageView">
|
<declare-styleable name="SquareImageView">
|
||||||
|
|
Loading…
Add table
Reference in a new issue