Commit e39fc38a authored by Thomas's avatar Thomas
Browse files

Change layout for statuses - Use a similar view as the old app.

parent bcc05074
Loading
Loading
Loading
Loading
+0 −144
Original line number Diff line number Diff line
package app.fedilab.android.helper
/*
 * This file is a part of Fedilab
 *
 * 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.
 *
 * Fedilab 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 Fedilab; if not,
 * see <http://www.gnu.org/licenses>. */

import android.content.Context
import android.content.res.Resources
import android.graphics.*
import android.view.View
import androidx.core.content.res.ResourcesCompat
import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.RecyclerView
import app.fedilab.android.R
import app.fedilab.android.helper.RecyclerViewThreadLines.LineInfo
import app.fedilab.android.client.entities.api.Context as StatusContext

class RecyclerViewThreadLines(context: Context, private val lineInfoList: List<LineInfo>) : DividerItemDecoration(context, VERTICAL) {
    private val lineColors = threadLineColors.map { ResourcesCompat.getColor(context.resources, it, context.theme) }
    private val dashPathEffect = DashPathEffect(floatArrayOf(3.dpToPx, 3.dpToPx), 0F)
    private val borderColor = lineColors[0]
    private val commonPaint = Paint().apply {
        isDither = false
        strokeWidth = 1.5F.dpToPx
        strokeCap = Paint.Cap.BUTT
        strokeJoin = Paint.Join.MITER
        color = borderColor
    }
    private val maxLevel = lineColors.size
    private val fontScale = PreferenceManager.getDefaultSharedPreferences(context).getFloat(context.getString(R.string.SET_FONT_SCALE), 1.1f).toInt()
    private val baseMargin: Int = 6.dpToPx.toInt()
    private val margin: Int = baseMargin * fontScale

    override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
        val position = parent.getChildAdapterPosition(view)
        if (position < 0 || position >= lineInfoList.size) return
        val level = lineInfoList[position].level
        val startMargin = margin * level + margin * fontScale
        if (parent.layoutDirection == View.LAYOUT_DIRECTION_LTR) outRect.left = startMargin else outRect.right = startMargin
    }

    override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
        val childCount = parent.childCount
        for (i in 0 until childCount) {
            val view = parent.getChildAt(i)
            val position = parent.getChildAdapterPosition(view)
            if (position < 0 || position >= lineInfoList.size) return
            val lineInfo = lineInfoList[position]
            val level = lineInfo.level

            for (j in 0..level) {
                val lineMargin = margin * j.coerceAtLeast(1) + 3.dpToPx
                val lineStart = if (parent.layoutDirection == View.LAYOUT_DIRECTION_LTR) lineMargin else c.width - lineMargin
                var lineTop: Float = (view.top - baseMargin).toFloat()
                val paint = Paint(commonPaint)
                paint.color = if (j > 0) lineColors[j - 1] else Color.GRAY

                // draw lines for below statuses
                if (j != level && lineInfo.lines.contains(j))
                    c.drawLine(lineStart, lineTop, lineStart, view.bottom.toFloat(), paint)

                // draw vertical line for current statuses
                if (j == level && position != 0) {
                    // top the line starts at the middle of the above status
                    if (i > 0) lineTop -= parent.getChildAt(i - 1).height / 2 - 1 // '- 1' is to prevent overlapping with above horizontal line

                    // bottom of the line ends at the middle of the current status
                    var lineBottom = view.bottom.toFloat() - view.height / 2

                    // if below status has a full line for current level, extend the line to the bottom
                    if (position < lineInfoList.lastIndex && lineInfoList[position + 1].lines.contains(level) && j != maxLevel)
                        lineBottom = view.bottom.toFloat()

                    // if level is max, use a dashed line
                    if (j == maxLevel) paint.pathEffect = dashPathEffect

                    c.drawLine(lineStart, lineTop, lineStart, lineBottom, paint)
                }

                // draw horizontal line for current statuses
                if (j == level) {
                    lineTop = view.bottom.toFloat() - view.height / 2
                    val lineEnd = lineStart + margin * 2
                    c.drawLine(lineStart - 1, lineTop, lineEnd, lineTop, paint) // 'lineStart - 1' is to properly connect with the vertical line
                }
            }
        }
    }

    data class LineInfo(var level: Int, var lines: List<Int>)

    private val Int.dpToPx: Float
        get() = this * Resources.getSystem().displayMetrics.density

    private val Float.dpToPx: Float
        get() = this * Resources.getSystem().displayMetrics.density

    companion object {
        val threadLineColors = listOf(R.color.decoration_1, R.color.decoration_2, R.color.decoration_3, R.color.decoration_4, R.color.decoration_5)
    }
}

fun getThreadDecorationInfo(statusContext: StatusContext): MutableList<LineInfo> {
    val lineInfoList = mutableListOf<LineInfo>()
    repeat(statusContext.ancestors.size) { lineInfoList.add(LineInfo(0, listOf(0))) }
    lineInfoList.add(LineInfo(0, listOf(0)))
    val descendantsLineInfoList = List(statusContext.descendants.size) { LineInfo(0, listOf()) }
    for (i in statusContext.descendants.indices) {
        statusContext.descendants[i].let { status ->
            var level = 0
            if (status.in_reply_to_id != null) {
                var replyToId: String? = status.in_reply_to_id
                while (replyToId != null && level < RecyclerViewThreadLines.threadLineColors.size) {
                    level += 1
                    replyToId = statusContext.descendants.firstOrNull { it.id == replyToId }?.in_reply_to_id
                }
            }
            descendantsLineInfoList[i].level = level
        }
    }
    for (i in descendantsLineInfoList.indices.reversed()) {
        val lines: MutableList<Int> = mutableListOf()
        val lineInfo = descendantsLineInfoList[i]
        lines.add(lineInfo.level)
        if (i < descendantsLineInfoList.lastIndex) {
            val belowLineInfo = descendantsLineInfoList[i + 1]
            lines.addAll(belowLineInfo.lines.filter { it < lineInfo.level })
        }
        descendantsLineInfoList[i].lines = lines
    }

    lineInfoList.addAll(descendantsLineInfoList)
    return lineInfoList
}
+18 −4
Original line number Diff line number Diff line
@@ -142,7 +142,7 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
    }


    private static boolean isVisble(Timeline.TimeLineEnum timelineType, Status status) {
    private static boolean isVisible(Timeline.TimeLineEnum timelineType, Status status) {
        if (timelineType == Timeline.TimeLineEnum.HOME && !show_boosts && status.reblog != null) {
            return false;
        }
@@ -316,9 +316,10 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
            holder.binding.translationLabel.setBackgroundColor(theme_statuses_color);
        }
        if (theme_boost_header_color != -1 && status.reblog != null) {
            holder.binding.headerContainer.setBackgroundColor(theme_boost_header_color);
            holder.binding.statusBoosterInfo.setBackgroundColor(theme_boost_header_color);

        } else {
            holder.binding.headerContainer.setBackgroundColor(0);
            holder.binding.statusBoosterInfo.setBackgroundColor(0);
        }
        if (theme_text_color != -1) {
            holder.binding.statusContent.setTextColor(theme_text_color);
@@ -750,9 +751,19 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
        //--- BOOSTER INFO ---
        if (status.reblog != null) {
            MastodonHelper.loadPPMastodon(holder.binding.statusBoosterAvatar, status.account);
            holder.binding.statusBoosterDisplayName.setText(status.account.span_display_name, TextView.BufferType.SPANNABLE);
            holder.binding.statusBoosterInfo.setVisibility(View.VISIBLE);
            holder.binding.boosterDivider.setVisibility(View.VISIBLE);
            if (theme_text_header_1_line != -1) {
                holder.binding.statusBoosterDisplayName.setTextColor(theme_text_header_1_line);
            }
            holder.binding.statusBoosterUsername.setText(String.format("@%s", status.account.acct));
            if (theme_text_header_2_line != -1) {
                holder.binding.statusBoosterUsername.setTextColor(theme_text_header_2_line);
            }
        } else {
            holder.binding.statusBoosterInfo.setVisibility(View.GONE);
            holder.binding.boosterDivider.setVisibility(View.GONE);
        }
        //--- BOOST VISIBILITY ---
        switch (status.visibility) {
@@ -1171,6 +1182,9 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
            return false;
        });
        if (!minified) {
            holder.binding.mainContainer.setOnClickListener(v -> {
                holder.binding.statusContent.callOnClick();
            });
            holder.binding.statusContent.setOnClickListener(v -> {
                if (status.isFocused || v.getTag() == SpannableHelper.CLICKABLE_SPAN) {
                    if (v.getTag() == SpannableHelper.CLICKABLE_SPAN) {
@@ -1568,7 +1582,7 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
        if (timelineType == Timeline.TimeLineEnum.ART) {
            return STATUS_ART;
        } else {
            return isVisble(timelineType, statusList.get(position)) ? STATUS_VISIBLE : STATUS_HIDDEN;
            return isVisible(timelineType, statusList.get(position)) ? STATUS_VISIBLE : STATUS_HIDDEN;
        }
    }

+123 −65
Original line number Diff line number Diff line
@@ -30,10 +30,58 @@
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="6dp"
        android:id="@+id/main_container"
        android:clipChildren="false"
        android:clipToPadding="false"
        android:orientation="vertical">

        <androidx.appcompat.widget.LinearLayoutCompat
            android:id="@+id/status_booster_info"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center_vertical"
            android:padding="6dp">

            <androidx.appcompat.widget.AppCompatImageView
                android:id="@+id/status_boost_icon"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:scaleType="centerInside"
                android:src="@drawable/ic_repeat" />

            <androidx.appcompat.widget.AppCompatImageView
                android:id="@+id/status_booster_avatar"
                android:layout_width="20dp"
                android:layout_height="20dp"
                android:scaleType="centerInside"
                android:src="@drawable/ic_person"
                tools:src="@tools:sample/avatars" />

            <androidx.appcompat.widget.AppCompatTextView
                android:id="@+id/status_booster_display_name"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginStart="6dp"
                android:ellipsize="end"
                android:maxLines="1"
                tools:text="@tools:sample/full_names" />

            <androidx.appcompat.widget.AppCompatTextView
                android:id="@+id/status_booster_username"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginStart="6dp"
                android:layout_weight="1"
                android:ellipsize="end"
                android:maxLines="1"
                tools:text="@tools:sample/full_names" />
        </androidx.appcompat.widget.LinearLayoutCompat>

        <com.google.android.material.divider.MaterialDivider
            android:id="@+id/booster_divider"
            android:layout_width="match_parent"
            android:layout_height="1dp" />

        <androidx.appcompat.widget.LinearLayoutCompat
            android:id="@+id/header_container"
            android:layout_width="match_parent"
@@ -41,27 +89,49 @@

            <androidx.appcompat.widget.LinearLayoutCompat
                android:id="@+id/status_user_info"
                android:layout_width="0dp"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:gravity="center_vertical"
                android:orientation="horizontal"
                android:padding="6dp">

                <androidx.appcompat.widget.AppCompatImageView
                    android:id="@+id/avatar"
                    android:layout_width="20dp"
                    android:layout_height="20dp"
                    android:layout_width="36dp"
                    android:layout_height="36dp"
                    android:scaleType="centerInside"
                    tools:src="@drawable/ic_person" />
                    tools:src="@tools:sample/avatars" />

                <androidx.appcompat.widget.LinearLayoutCompat
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:orientation="vertical">

                    <androidx.appcompat.widget.LinearLayoutCompat
                        android:layout_width="match_parent"
                        android:layout_height="match_parent"
                        android:orientation="horizontal">

                        <androidx.appcompat.widget.AppCompatTextView
                            android:id="@+id/display_name"
                    android:layout_width="wrap_content"
                            android:layout_width="0dp"
                            android:layout_height="wrap_content"
                            android:layout_marginStart="6dp"
                            android:layout_weight="1"
                            android:ellipsize="end"
                            android:maxLines="1"
                    tools:text="Display Name" />
                            tools:text="@tools:sample/full_names" />

                        <TextView
                            android:id="@+id/date_short"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:layout_gravity="center"
                            android:layout_marginEnd="10dp" />

                    </androidx.appcompat.widget.LinearLayoutCompat>


                    <androidx.appcompat.widget.AppCompatTextView
                        android:id="@+id/username"
@@ -72,29 +142,10 @@
                        android:ellipsize="end"
                        android:maxLines="1"
                        tools:text="\@username\@instance.test" />
            </androidx.appcompat.widget.LinearLayoutCompat>

            <androidx.appcompat.widget.LinearLayoutCompat
                android:id="@+id/status_booster_info"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:padding="6dp">

                <androidx.appcompat.widget.AppCompatImageView
                    android:id="@+id/status_boost_icon"
                    android:layout_width="wrap_content"
                    android:layout_height="match_parent"
                    android:scaleType="centerInside"
                    android:src="@drawable/ic_repeat" />

                <androidx.appcompat.widget.AppCompatImageView
                    android:id="@+id/status_booster_avatar"
                    android:layout_width="20dp"
                    android:layout_height="20dp"
                    android:scaleType="centerInside"
                    android:src="@drawable/ic_person" />
                </androidx.appcompat.widget.LinearLayoutCompat>

            </androidx.appcompat.widget.LinearLayoutCompat>

            <androidx.appcompat.widget.AppCompatImageView
                android:id="@+id/status_pinned"
@@ -107,12 +158,12 @@

            <androidx.appcompat.widget.AppCompatImageView
                android:id="@+id/type_of_notification"
                android:layout_width="20dp"
                android:layout_height="20dp"
                android:layout_gravity="center_vertical|end"
                android:layout_marginStart="6dp"
                android:layout_marginEnd="6dp"
                android:tint="@color/cyanea_accent_reference"
                android:layout_width="20dp"
                android:visibility="gone" />

        </androidx.appcompat.widget.LinearLayoutCompat>
@@ -125,27 +176,29 @@
            android:id="@+id/spoiler"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginHorizontal="6dp"
            android:layout_marginStart="48dp"
            android:layout_marginTop="6dp"
            android:layout_marginEnd="6dp"
            tools:text="Warning: Lorem Ipsum below" />

        <app.fedilab.android.helper.CustomTextView
            android:id="@+id/spoiler_expand"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginHorizontal="6dp"
            android:layout_marginStart="48dp"
            android:layout_marginTop="6dp"
            android:layout_marginEnd="6dp"
            android:text="@string/show_content"
            android:textAppearance="@style/TextAppearance.AppCompat.Body2"
            android:textColor="@color/cyanea_accent_dark_reference"
            android:text="@string/show_content" />

            android:textColor="@color/cyanea_accent_dark_reference" />

        <app.fedilab.android.helper.CustomTextView
            android:id="@+id/status_content"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginHorizontal="6dp"
            android:layout_marginStart="48dp"
            android:layout_marginTop="6dp"
            android:layout_marginEnd="6dp"
            android:textIsSelectable="true"
            tools:maxLines="10"
            tools:text="@tools:sample/lorem/random" />
@@ -158,20 +211,22 @@
            android:layout_gravity="center"
            android:drawableEnd="@drawable/ic_display_more"
            android:gravity="center_vertical"
            app:strokeColor="@color/cyanea_accent_dark_reference"
            android:singleLine="true"
            android:text="@string/display_toot_truncate"
            app:iconTint="@color/cyanea_accent_reference"
            android:textAllCaps="false"
            app:iconTint="@color/cyanea_accent_reference"
            app:strokeColor="@color/cyanea_accent_dark_reference" />

            />
        <androidx.constraintlayout.widget.ConstraintLayout
            android:id="@+id/container_trans"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:layout_marginStart="48dp"
            android:layout_marginTop="6dp"
            android:layout_marginEnd="6dp"
            android:orientation="vertical"
            android:visibility="gone">

            <View
                android:id="@+id/translation_border_view"
                android:layout_width="match_parent"
@@ -218,8 +273,9 @@
            android:id="@+id/card"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginHorizontal="6dp"
            android:layout_marginStart="48dp"
            android:layout_marginTop="6dp"
            android:layout_marginEnd="6dp"
            android:visibility="gone"
            app:cardCornerRadius="8dp"
            app:cardElevation="2dp"
@@ -297,16 +353,18 @@
            android:id="@+id/media_container"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginHorizontal="6dp"
            android:layout_marginStart="48dp"
            android:layout_marginTop="6dp"
            android:layout_marginEnd="6dp"
            android:visibility="gone" />

        <HorizontalScrollView
            android:id="@+id/attachments_list_container"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginHorizontal="6dp"
            android:layout_marginStart="48dp"
            android:layout_marginTop="6dp"
            android:layout_marginEnd="6dp"
            android:visibility="gone">

            <androidx.appcompat.widget.LinearLayoutCompat
@@ -321,13 +379,20 @@

        <include
            android:id="@+id/poll"
            layout="@layout/layout_poll" />
            layout="@layout/layout_poll"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginStart="48dp"
            android:layout_marginTop="6dp"
            android:layout_marginEnd="6dp" />

        <androidx.appcompat.widget.LinearLayoutCompat
            android:id="@+id/status_info"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginStart="48dp"
            android:layout_marginTop="6dp"
            android:layout_marginEnd="6dp"
            android:alpha="0.8"
            android:gravity="center_vertical"
            android:padding="6dp"
@@ -370,10 +435,10 @@
                android:orientation="horizontal">

                <androidx.appcompat.widget.AppCompatImageView
                    android:id="@+id/fav_info"
                    android:layout_width="20dp"
                    android:layout_height="20dp"
                    android:layout_marginStart="12dp"
                    android:id="@+id/fav_info"
                    app:srcCompat="@drawable/ic_star_outline" />

                <androidx.appcompat.widget.AppCompatTextView
@@ -403,7 +468,9 @@
            android:id="@+id/action_buttons"
            android:layout_width="match_parent"
            android:layout_height="28dp"
            android:layout_marginStart="48dp"
            android:layout_marginTop="6dp"
            android:layout_marginEnd="6dp"
            android:clipChildren="false"
            android:clipToPadding="false">

@@ -472,20 +539,11 @@
                android:layout_height="wrap_content"
                android:layout_weight="1" />

            <TextView
                android:id="@+id/date_short"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:layout_marginEnd="10dp"
                android:visibility="gone" />

            <androidx.appcompat.widget.AppCompatImageButton
                android:id="@+id/action_button_more"
                style="@style/Widget.AppCompat.Button.Borderless"
                android:layout_width="28dp"
                android:layout_height="28dp"
                android:layout_marginEnd="6dp"
                android:adjustViewBounds="true"
                app:srcCompat="@drawable/ic_more_horiz" />

+498 −0

File added.

Preview size limit exceeded, changes collapsed.