1
0
Fork 0

For #631 - Adds session item layout

master
Jeff Boek 2019-02-25 13:45:26 -08:00
parent 8379c3d2cb
commit 3ecf9d338e
4 changed files with 475 additions and 4 deletions

View File

@ -4,6 +4,7 @@
package org.mozilla.fenix.home.sessions package org.mozilla.fenix.home.sessions
import android.graphics.PorterDuff
import android.text.SpannableString import android.text.SpannableString
import android.text.style.ClickableSpan import android.text.style.ClickableSpan
import android.text.style.ForegroundColorSpan import android.text.style.ForegroundColorSpan
@ -11,11 +12,12 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.TextView import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.session_item.view.*
import org.mozilla.fenix.R import org.mozilla.fenix.R
class SessionsAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() { class SessionsAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private var isPrivate = false
private var items = listOf<ArchivedSession>() private var items = listOf<ArchivedSession>()
@ -30,6 +32,7 @@ class SessionsAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
return when (viewType) { return when (viewType) {
HeaderViewHolder.LAYOUT_ID -> HeaderViewHolder(view) HeaderViewHolder.LAYOUT_ID -> HeaderViewHolder(view)
EmptyListViewHolder.LAYOUT_ID -> EmptyListViewHolder(view) EmptyListViewHolder.LAYOUT_ID -> EmptyListViewHolder(view)
SessionItemViewHolder.LAYOUT_ID -> SessionItemViewHolder(view)
PrivateEmptyListViewHolder.LAYOUT_ID -> PrivateEmptyListViewHolder(view) PrivateEmptyListViewHolder.LAYOUT_ID -> PrivateEmptyListViewHolder(view)
else -> EmptyListViewHolder(view) else -> EmptyListViewHolder(view)
} }
@ -37,7 +40,7 @@ class SessionsAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
override fun getItemViewType(position: Int) = when (position) { override fun getItemViewType(position: Int) = when (position) {
0 -> HeaderViewHolder.LAYOUT_ID 0 -> HeaderViewHolder.LAYOUT_ID
else -> EmptyListViewHolder.LAYOUT_ID else -> SessionItemViewHolder.LAYOUT_ID
} }
override fun getItemCount(): Int = items.size + 1 override fun getItemCount(): Int = items.size + 1
@ -45,6 +48,7 @@ class SessionsAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (holder) { when (holder) {
is HeaderViewHolder -> holder.headerText.text = "Today" is HeaderViewHolder -> holder.headerText.text = "Today"
is SessionItemViewHolder -> holder.bind(items[position - 1])
is PrivateEmptyListViewHolder -> { is PrivateEmptyListViewHolder -> {
// Format the description text to include a hyperlink // Format the description text to include a hyperlink
val descriptionText = String val descriptionText = String
@ -80,6 +84,35 @@ class SessionsAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
} }
} }
private class SessionItemViewHolder(view: View) : RecyclerView.ViewHolder(view) {
private val thumbnail = view.session_card_thumbnail
private val timestampLabel = view.session_card_timestamp
private val titlesLabel = view.session_card_titles
private val extrasLabel = view.session_card_extras
private var session: ArchivedSession? = null
fun bind(session: ArchivedSession) {
this.session = session
thumbnail.setColorFilter(
ContextCompat.getColor(itemView.context, AVAILABLE_COLOR_IDS.random()),
PorterDuff.Mode.MULTIPLY)
timestampLabel.text = session.formattedSavedAt
titlesLabel.text = session.titles
extrasLabel.text = if (session.extrasLabel > 0) {
"+${session.extrasLabel} sites..."
} else { "" }
}
companion object {
private val AVAILABLE_COLOR_IDS = listOf(
R.color.photonBlue40, R.color.photonGreen50, R.color.photonYellow50, R.color.photonOrange50,
R.color.photonPurple50, R.color.photonInk70)
const val LAYOUT_ID = R.layout.session_item
}
}
private class EmptyListViewHolder(view: View) : RecyclerView.ViewHolder(view) { private class EmptyListViewHolder(view: View) : RecyclerView.ViewHolder(view) {
companion object { companion object {
const val LAYOUT_ID = R.layout.session_list_empty const val LAYOUT_ID = R.layout.session_list_empty

View File

@ -6,14 +6,70 @@ package org.mozilla.fenix.home.sessions
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import mozilla.components.browser.session.Session
import org.mozilla.fenix.mvi.Action import org.mozilla.fenix.mvi.Action
import org.mozilla.fenix.mvi.ActionBusFactory import org.mozilla.fenix.mvi.ActionBusFactory
import org.mozilla.fenix.mvi.Change import org.mozilla.fenix.mvi.Change
import org.mozilla.fenix.mvi.UIComponent import org.mozilla.fenix.mvi.UIComponent
import org.mozilla.fenix.mvi.ViewState import org.mozilla.fenix.mvi.ViewState
import java.net.URL
import java.text.SimpleDateFormat
import java.util.*
data class ArchivedSession(val id: Long, private val savedAt: Long, val urls: List<String>) data class ArchivedSession(val id: Long, private val savedAt: Long, private val _urls: List<String>) {
val formattedSavedAt by lazy {
val isSameDay: (Calendar, Calendar) -> Boolean = { a, b ->
a.get(Calendar.ERA) == b.get(Calendar.ERA) &&
a.get(Calendar.YEAR) == b.get(Calendar.YEAR) &&
a.get(Calendar.DAY_OF_YEAR) == b.get(Calendar.DAY_OF_YEAR)
}
val parse: (Date) -> String = { date ->
val dateCal = Calendar.getInstance().apply { time = date }
val today = Calendar.getInstance()
val yesterday = Calendar.getInstance().apply { add(Calendar.DAY_OF_YEAR, -1) }
val time = TIME_FORMATTER.format(date)
val month = MONTH_FORMATTER.format(date)
val day = DAY_FORMATTER.format(date)
val dayOfWeek = DAY_OF_WEEK_FORMATTER.format(date)
when {
isSameDay(dateCal, today) -> "Today @ $time"
isSameDay(dateCal, yesterday) -> "Yesterday @ $time"
else -> "$dayOfWeek $month/$day @ $time"
}
}
parse(Date(savedAt))
}
val titles by lazy {
val urlFormatter: (String) -> String = { url ->
try {
URL(url).host
} catch (e: Exception) {
url
}
}
_urls
.take(NUMBER_OF_URLS_TO_DISPLAY)
.joinToString(", ", transform = urlFormatter)
}
val extrasLabel = maxOf(_urls.size - NUMBER_OF_URLS_TO_DISPLAY, 0)
private companion object {
private const val NUMBER_OF_URLS_TO_DISPLAY = 5
private val TIME_FORMATTER = SimpleDateFormat("h:mm a", Locale.US)
private val MONTH_FORMATTER = SimpleDateFormat("M", Locale.US)
private val DAY_FORMATTER = SimpleDateFormat("d", Locale.US)
private val DAY_OF_WEEK_FORMATTER = SimpleDateFormat("EEEE", Locale.US)
}
}
class SessionsComponent( class SessionsComponent(
private val container: ViewGroup, private val container: ViewGroup,

View File

@ -0,0 +1,302 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated by PaintCode - http://www.paintcodeapp.com -->
<!-- Canvas 1 -->
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:name="canvas1"
android:width="64dp"
android:height="64dp"
android:viewportWidth="64"
android:viewportHeight="64">
<!-- Group 2 -->
<group
android:name="group2">
<!-- Group 3 -->
<group
android:name="group3">
<!-- Clip 3 Clip -->
<clip-path
android:name="clip3"
android:pathData="M 12,8 L 60,8 C 62.21,8 64,9.79 64,12 L 64,60 C 64,62.21 62.21,64 60,64 L 12,64 C 9.79,64 8,62.21 8,60 L 8,12 C 8,9.79 9.79,8 12,8 Z" />
<!-- Group 4 -->
<group
android:name="group4">
<!-- Clip Clip -->
<clip-path
android:name="clip"
android:pathData="M 8,64 L 64,64 L 64,8 L 8,8 L 8,64 Z" />
<!-- Picture -->
<group
android:name="picture-transform"
android:translateX="-1.33"
android:translateY="8"
android:scaleX="0.53"
android:scaleY="0.53">
<path
android:name="picture"
android:pathData="M 0,105 L 140,105 L 140,0 L 0,0 L 0,105 Z" />
</group>
</group>
<!-- Rectangle 3 -->
<path
android:name="rectangle3"
android:pathData="M 8,64 L 64,64 L 64,8 L 8,8 L 8,64 Z">
<aapt:attr name="android:fillColor">
<gradient
android:type="linear"
android:tileMode="clamp"
android:startX="3"
android:startY="3"
android:endX="69"
android:endY="69">
<item android:color="#FF9952FF" android:offset="0" />
<item android:color="#FF9952FF" android:offset="1" />
</gradient>
</aapt:attr>
</path>
<!-- Group 5 -->
<group
android:name="group5">
<!-- Rectangle 4 -->
<path
android:name="rectangle4"
android:pathData="M 8,64 L 64,64 L 64,8 L 8,8 L 8,64 Z">
<aapt:attr name="android:fillColor">
<gradient
android:type="linear"
android:tileMode="clamp"
android:startX="3"
android:startY="3"
android:endX="69"
android:endY="69">
<item android:color="#FF9952FF" android:offset="0" />
<item android:color="#FF9952FF" android:offset="1" />
</gradient>
</aapt:attr>
</path>
</group>
<!-- Group 6 -->
<group
android:name="group6">
<!-- Clip 2 Clip -->
<clip-path
android:name="clip2"
android:pathData="M 8,64 L 64,64 L 64,8 L 8,8 L 8,64 Z" />
<!-- Picture 2 -->
<group
android:name="picture2-transform"
android:translateX="-1.2"
android:translateY="8"
android:scaleX="0.63"
android:scaleY="0.63">
<path
android:name="picture2"
android:pathData="M 0,89 L 118,89 L 118,0 L 0,0 L 0,89 Z" />
</group>
</group>
<!-- Rectangle 7 -->
<path
android:name="rectangle7"
android:pathData="M 8,64 L 64,64 L 64,8 L 8,8 L 8,64 Z"
android:fillColor="#660C0C0D" />
</group>
<!-- Group 7 -->
<group
android:name="group7">
<!-- Clip 6 Clip -->
<clip-path
android:name="clip6"
android:pathData="M 4,56 C 4,58.21 5.79,60 8,60 L 56,60 C 58.21,60 60,58.21 60,56 L 60,8 C 60,5.79 58.21,4 56,4 L 8,4 C 5.79,4 4,5.79 4,8 L 4,56 Z" />
<!-- Group 8 -->
<group
android:name="group8">
<!-- Clip 4 Clip -->
<clip-path
android:name="clip4"
android:pathData="M 4,60 L 60,60 L 60,4 L 4,4 L 4,60 Z" />
<!-- Picture 3 -->
<group
android:name="picture3-transform"
android:translateX="-5.33"
android:translateY="4"
android:scaleX="0.53"
android:scaleY="0.53">
<path
android:name="picture3"
android:pathData="M 0,105 L 140,105 L 140,0 L 0,0 L 0,105 Z" />
</group>
</group>
<!-- Rectangle 10 -->
<path
android:name="rectangle10"
android:pathData="M 4,60 L 60,60 L 60,4 L 4,4 L 4,60 Z">
<aapt:attr name="android:fillColor">
<gradient
android:type="linear"
android:tileMode="clamp"
android:startX="-1"
android:startY="-1"
android:endX="65"
android:endY="65">
<item android:color="#FF9952FF" android:offset="0" />
<item android:color="#FF9952FF" android:offset="1" />
</gradient>
</aapt:attr>
</path>
<!-- Group 9 -->
<group
android:name="group9">
<!-- Rectangle 11 -->
<path
android:name="rectangle11"
android:pathData="M 4,60 L 60,60 L 60,4 L 4,4 L 4,60 Z">
<aapt:attr name="android:fillColor">
<gradient
android:type="linear"
android:tileMode="clamp"
android:startX="-1"
android:startY="-1"
android:endX="65"
android:endY="65">
<item android:color="#FF9952FF" android:offset="0" />
<item android:color="#FF9952FF" android:offset="1" />
</gradient>
</aapt:attr>
</path>
</group>
<!-- Group 10 -->
<group
android:name="group10">
<!-- Clip 5 Clip -->
<clip-path
android:name="clip5"
android:pathData="M 4,60 L 60,60 L 60,4 L 4,4 L 4,60 Z" />
<!-- Picture 4 -->
<group
android:name="picture4-transform"
android:translateX="-5.2"
android:translateY="4"
android:scaleX="0.63"
android:scaleY="0.63">
<path
android:name="picture4"
android:pathData="M 0,89 L 118,89 L 118,0 L 0,0 L 0,89 Z" />
</group>
</group>
<!-- Rectangle 14 -->
<path
android:name="rectangle14"
android:pathData="M 4,60 L 60,60 L 60,4 L 4,4 L 4,60 Z"
android:fillColor="#330C0C0D" />
</group>
<!-- Group 11 -->
<group
android:name="group11">
<!-- Clip 9 Clip -->
<clip-path
android:name="clip9"
android:pathData="M 4,0 L 52,0 C 54.21,0 56,1.79 56,4 L 56,52 C 56,54.21 54.21,56 52,56 L 4,56 C 1.79,56 0,54.21 0,52 L 0,4 C 0,1.79 1.79,0 4,0 Z" />
<!-- Group 12 -->
<group
android:name="group12">
<!-- Clip 7 Clip -->
<clip-path
android:name="clip7"
android:pathData="M 0,56 L 56,56 L 56,0 L 0,0 L 0,56 Z" />
<!-- Picture 5 -->
<group
android:name="picture5-transform"
android:translateX="-9.33"
android:scaleX="0.53"
android:scaleY="0.53">
<path
android:name="picture5"
android:pathData="M 0,105 L 140,105 L 140,0 L 0,0 L 0,105 Z" />
</group>
</group>
<!-- Rectangle 18 -->
<path
android:name="rectangle18"
android:pathData="M 0,56 L 56,56 L 56,0 L 0,0 L 0,56 Z">
<aapt:attr name="android:fillColor">
<gradient
android:type="linear"
android:tileMode="clamp"
android:startX="-5"
android:startY="-5"
android:endX="61"
android:endY="61">
<item android:color="#FF9952FF" android:offset="0" />
<item android:color="#FF9952FF" android:offset="1" />
</gradient>
</aapt:attr>
</path>
<!-- Group 13 -->
<group
android:name="group13">
<!-- Rectangle 19 -->
<path
android:name="rectangle19"
android:pathData="M 0,56 L 56,56 L 56,0 L 0,0 L 0,56 Z">
<aapt:attr name="android:fillColor">
<gradient
android:type="linear"
android:tileMode="clamp"
android:startX="-5"
android:startY="-5"
android:endX="61"
android:endY="61">
<item android:color="#FF9952FF" android:offset="0" />
<item android:color="#FF9952FF" android:offset="1" />
</gradient>
</aapt:attr>
</path>
</group>
<!-- Group 14 -->
<group
android:name="group14">
<!-- Clip 8 Clip -->
<clip-path
android:name="clip8"
android:pathData="M 0,56 L 56,56 L 56,0 L 0,0 L 0,56 Z" />
<!-- Picture 6 -->
<group
android:name="picture6-transform"
android:translateX="-9.2"
android:scaleX="0.63"
android:scaleY="0.63">
<path
android:name="picture6"
android:pathData="M 0,89 L 118,89 L 118,0 L 0,0 L 0,89 Z" />
</group>
</group>
</group>
</group>
</vector>

View File

@ -0,0 +1,80 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:background="@android:color/white"
app:cardElevation="1dp"
app:cardCornerRadius="10dp"
android:clipChildren="true">
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="16dp">
<ImageView
android:id="@+id/session_card_thumbnail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_session_thumbnail"
android:layout_margin="16dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
<TextView
android:id="@+id/session_card_timestamp"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textColor="@color/photonInk80"
android:textSize="16sp"
android:layout_marginStart="12dp"
android:layout_marginBottom="2dp"
android:layout_marginEnd="12dp"
app:layout_constraintStart_toEndOf="@+id/session_card_thumbnail"
app:layout_constraintTop_toTopOf="@id/session_card_thumbnail"
app:layout_constraintEnd_toStartOf="@id/session_card_overflow_button"/>
<TextView
android:id="@+id/session_card_titles"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textColor="@color/photonGrey50"
android:textSize="12sp"
app:layout_constraintTop_toBottomOf="@+id/session_card_timestamp"
app:layout_constraintStart_toStartOf="@id/session_card_timestamp"
app:layout_constraintEnd_toEndOf="@id/session_card_timestamp"/>
<TextView
android:id="@+id/session_card_extras"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/photonGrey50"
android:textSize="12sp"
app:layout_constraintTop_toBottomOf="@+id/session_card_titles"
app:layout_constraintStart_toStartOf="@id/session_card_titles" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/bottomBarrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="bottom"
app:constraint_referenced_ids="session_card_thumbnail,session_card_extras" />
<ImageButton
android:id="@+id/session_card_overflow_button"
android:layout_width="@dimen/glyph_button_width"
android:layout_height="@dimen/glyph_button_height"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:src="@drawable/ic_menu"
android:tint="?attr/toolbarTextColor"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>