Skip to content
Snippets Groups Projects
Verified Commit ed7f0415 authored by Janne Mareike Koschinski's avatar Janne Mareike Koschinski
Browse files

feat: further improvements on message rendering

parent 3c5958b9
No related branches found
No related tags found
No related merge requests found
Showing
with 457 additions and 125 deletions
......@@ -7,10 +7,12 @@ import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument
import de.justjanne.libquassel.protocol.models.ids.BufferId
import de.justjanne.quasseldroid.service.QuasselBackend
import de.justjanne.quasseldroid.ui.routes.CoreInfoRoute
import de.justjanne.quasseldroid.ui.routes.HomeRoute
import de.justjanne.quasseldroid.ui.routes.LoginRoute
import de.justjanne.quasseldroid.ui.routes.MessageRoute
@Composable
fun QuasseldroidRouter(backend: QuasselBackend) {
......@@ -28,7 +30,7 @@ fun QuasseldroidRouter(backend: QuasselBackend) {
"buffer/{bufferId}",
listOf(navArgument("bufferId") { type = NavType.IntType })
) {
Text("Buffer ${it.arguments?.getInt("bufferId")}")
MessageRoute(backend, navController, BufferId(it.arguments?.getInt("bufferId") ?: -1))
}
composable("bufferViewConfigs") {
Text("List of BufferViewConfigs")
......
......@@ -94,6 +94,14 @@ class MessageStore(
}
}
fun clear(bufferId: BufferId) {
scope.launch {
state.update { messages ->
messages - bufferId
}
}
}
override fun close() {
runBlocking {
disposable.cancelAndJoin()
......
......@@ -10,8 +10,13 @@ import de.justjanne.libquassel.protocol.models.flags.MessageType
import de.justjanne.libquassel.protocol.models.ids.BufferId
import de.justjanne.libquassel.protocol.models.ids.MsgId
import de.justjanne.libquassel.protocol.models.ids.NetworkId
import de.justjanne.libquassel.protocol.util.irc.HostmaskHelper
import org.threeten.bp.Instant
class SampleNickProvider : PreviewParameterProvider<String> {
override val values = SampleMessageProvider().values.map { HostmaskHelper.nick(it.sender) }
}
class SampleMessageProvider : PreviewParameterProvider<Message> {
override val values = sequenceOf(
Message(
......
......@@ -4,22 +4,32 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.paddingFrom
import androidx.compose.foundation.layout.paddingFromBaseline
import androidx.compose.foundation.layout.width
import androidx.compose.material.ContentAlpha
import androidx.compose.material.LocalContentAlpha
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.FirstBaseline
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import de.justjanne.libquassel.protocol.models.Message
import de.justjanne.libquassel.protocol.util.irc.HostmaskHelper
import de.justjanne.quasseldroid.sample.SampleMessageProvider
import de.justjanne.quasseldroid.ui.icons.AvatarIcon
import de.justjanne.quasseldroid.ui.theme.QuasselTheme
import de.justjanne.quasseldroid.ui.theme.Typography
......@@ -31,51 +41,122 @@ import org.threeten.bp.format.FormatStyle
private val formatter = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT)
@Composable
fun MessageBaseView(
fun buildNick(nick: String, senderPrefixes: String): AnnotatedString {
val senderColor = QuasselTheme.sender.colors[SenderColorUtil.senderColor(nick)]
return buildAnnotatedString {
if (senderPrefixes.isNotEmpty()) {
append(senderPrefixes)
}
pushStyle(SpanStyle(color = senderColor, fontWeight = FontWeight.Bold))
append(nick)
pop()
}
}
@Preview(name = "Message Base", showBackground = true)
@Composable
fun MessageBase(
@PreviewParameter(SampleMessageProvider::class)
message: Message,
followUp: Boolean,
avatarSize: Dp,
content: @Composable () -> Unit
followUp: Boolean = false,
// avatarSize: Dp = 32.dp
content: @Composable () -> Unit = { Text(message.content, style = Typography.body2) }
) {
val avatarSize = 32.dp
val nick = HostmaskHelper.nick(message.sender)
val senderColor = QuasselTheme.sender.colors[SenderColorUtil.senderColor(nick)]
Row(modifier = Modifier.padding(2.dp)) {
Row(
modifier = Modifier
.padding(2.dp)
.fillMaxWidth()
) {
if (!followUp) {
AvatarIcon(nick, null, modifier = Modifier.padding(vertical = 2.dp))
Spacer(Modifier.width(4.dp))
AvatarIcon(
nick,
size = avatarSize,
modifier = Modifier
.paddingFromBaseline(top = 28.sp)
)
Spacer(Modifier.width(4.dp))
} else {
Spacer(Modifier.width(avatarSize + 8.dp))
}
Column {
Column(modifier = Modifier.align(Alignment.CenterVertically)) {
if (!followUp) {
Row {
Text(
message.senderPrefixes,
style = Typography.body2,
fontWeight = FontWeight.Bold,
)
Text(
nick,
style = Typography.body2,
fontWeight = FontWeight.Bold,
color = senderColor,
buildAnnotatedString {
append(buildNick(nick, message.senderPrefixes))
append(' ')
pushStyle(SpanStyle(color = MaterialTheme.colors.onSurface.copy(alpha = ContentAlpha.medium)))
append(message.realName)
pop()
},
style = Typography.body2
)
Spacer(Modifier.width(4.dp))
}
}
Row {
Box(modifier = Modifier.weight(1.0f)) {
content()
}
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
Text(
message.realName,
modifier = Modifier.weight(1.0f),
message.time
.atZone(ZoneId.systemDefault())
.format(formatter),
style = Typography.body2,
overflow = TextOverflow.Ellipsis,
fontSize = 12.sp,
modifier = Modifier.align(Alignment.Bottom)
)
}
}
}
Row {
}
}
@Preview(name = "Message Small", showBackground = true)
@Composable
fun MessageBaseSmall(
@PreviewParameter(SampleMessageProvider::class)
message: Message,
followUp: Boolean = false,
// avatarSize: Dp = 32.dp,
content: @Composable () -> Unit = {
val nick = HostmaskHelper.nick(message.sender)
Text(buildAnnotatedString {
append("— ")
append(buildNick(nick, message.senderPrefixes))
append(" ")
append(message.content)
}, style = Typography.body2)
}
) {
val avatarSize = 16.dp
val nick = HostmaskHelper.nick(message.sender)
Row(
modifier = Modifier
.padding(2.dp)
.fillMaxWidth()
) {
Spacer(Modifier.width(20.dp))
AvatarIcon(
nick,
modifier = Modifier
.align(Alignment.Top)
.paddingFromBaseline(top = 14.sp),
size = avatarSize
)
Spacer(Modifier.width(4.dp))
Box(modifier = Modifier.weight(1.0f)) {
content()
}
Spacer(Modifier.width(4.dp))
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
Text(
message.time
......@@ -88,5 +169,3 @@ fun MessageBaseView(
}
}
}
}
}
......@@ -6,16 +6,26 @@ import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import de.justjanne.bitflags.of
import de.justjanne.libquassel.protocol.models.Message
import de.justjanne.libquassel.protocol.models.flags.MessageType
import de.justjanne.libquassel.protocol.models.ids.MsgId
import de.justjanne.libquassel.protocol.util.irc.HostmaskHelper
import de.justjanne.quasseldroid.R
import de.justjanne.quasseldroid.sample.SampleMessagesProvider
import de.justjanne.quasseldroid.ui.theme.QuasselTheme
import de.justjanne.quasseldroid.ui.theme.Typography
import de.justjanne.quasseldroid.util.extensions.OnBottomReached
import de.justjanne.quasseldroid.util.extensions.OnTopReached
import de.justjanne.quasseldroid.util.extensions.getPrevious
import de.justjanne.quasseldroid.util.format.IrcFormat
import de.justjanne.quasseldroid.util.format.IrcFormatDeserializer
import de.justjanne.quasseldroid.util.format.IrcFormatRenderer
import de.justjanne.quasseldroid.util.format.TextFormatter
import org.threeten.bp.ZoneId
@Preview(name = "Messages", showBackground = true)
......@@ -41,24 +51,43 @@ fun MessageList(
message.realName == prev.realName &&
message.avatarUrl == prev.avatarUrl
val isNew = prev != null &&
prev.messageId <= markerLine &&
val isNew = (prev == null || prev.messageId <= markerLine) &&
message.messageId > markerLine
val parsed = IrcFormatDeserializer.parse(message.content)
if (prevDate == null || !messageDate.isEqual(prevDate)) {
MessageDayChangeView(messageDate, isNew)
} else if (isNew) {
NewMessageView()
}
MessageBaseView(message, followUp, 32.dp) {
when (message.type) {
MessageType.of(MessageType.Plain) -> {
MessageBase(message, followUp) {
Text(IrcFormatRenderer.render(parsed), style = Typography.body2)
}
}
MessageType.of(MessageType.Action) -> {
MessageBaseSmall(message) {
val nick = HostmaskHelper.nick(message.sender)
Text(
message.content,
TextFormatter.format(
AnnotatedString(stringResource(R.string.message_format_action)),
buildNick(nick, message.senderPrefixes),
IrcFormatRenderer.render(
data = parsed.map { it.copy(style = it.style.flipFlag(IrcFormat.Flag.ITALIC)) }
)
),
style = Typography.body2,
color = QuasselTheme.chat.onAction
)
}
}
}
}
}
listState.OnTopReached(buffer = buffer, onLoadMore = onLoadAtStart)
listState.OnBottomReached(buffer = buffer, onLoadMore = onLoadAtEnd)
......
......@@ -2,6 +2,7 @@ package de.justjanne.quasseldroid.ui.icons
import android.graphics.Bitmap
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.ContentAlpha
......@@ -15,23 +16,21 @@ import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import de.justjanne.quasseldroid.sample.SampleNickProvider
import de.justjanne.quasseldroid.ui.theme.QuasselTheme
import irc.SenderColorUtil
import java.util.*
@Preview
@Composable
private fun AvatarIconPreview() {
AvatarIcon("justJanne", null)
}
@Composable
fun AvatarIcon(
@PreviewParameter(SampleNickProvider::class)
nick: String,
avatar: Bitmap?,
modifier: Modifier = Modifier,
avatar: Bitmap? = null,
size: Dp = 32.dp
) {
val senderColor = QuasselTheme.sender.colors[SenderColorUtil.senderColor(nick)]
......
package de.justjanne.quasseldroid.ui.routes
import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.rememberScrollState
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.material.TextField
......@@ -14,79 +16,74 @@ import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import de.justjanne.libquassel.protocol.models.Message
import de.justjanne.libquassel.protocol.models.ids.BufferId
import de.justjanne.libquassel.protocol.models.ids.MsgId
import de.justjanne.libquassel.protocol.models.BufferInfo
import de.justjanne.libquassel.protocol.models.ids.NetworkId
import de.justjanne.libquassel.protocol.models.network.NetworkInfo
import de.justjanne.libquassel.protocol.syncables.common.Network
import de.justjanne.libquassel.protocol.util.flatMap
import de.justjanne.quasseldroid.messages.MessageStore
import de.justjanne.quasseldroid.service.QuasselBackend
import de.justjanne.quasseldroid.ui.components.MessageList
import de.justjanne.quasseldroid.util.mapNullable
import de.justjanne.quasseldroid.util.rememberFlow
import de.justjanne.quasseldroid.util.saver.TextFieldValueSaver
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
private const val limit = 20
@Composable
fun HomeRoute(backend: QuasselBackend, navController: NavController) {
val session = rememberFlow(null) {
backend.flow()
.mapNullable { it.session }
val side = rememberFlow(null) {
backend.flow().mapNullable { it.session.side }
}
val (buffer, setBuffer) = rememberSaveable(stateSaver = TextFieldValueSaver) {
mutableStateOf(TextFieldValue("3747"))
}
val (position, setPosition) = rememberSaveable(stateSaver = TextFieldValueSaver) {
mutableStateOf(TextFieldValue("108113920"))
}
val bufferId = BufferId(buffer.text.toIntOrNull() ?: -1)
val positionId = MsgId(position.text.toLongOrNull() ?: -1L)
val listState = rememberLazyListState()
val messageStore: MessageStore? = rememberFlow(null) {
backend.flow()
.mapNullable { it.messages }
mutableStateOf(TextFieldValue(""))
}
val messages: List<Message> = rememberFlow(emptyList()) {
val initStatus = rememberFlow(null) {
backend.flow()
.mapNullable { it.messages }
.mapNullable { it.session }
.mapNullable { it.baseInitHandler }
.flatMap()
.mapNullable { it[bufferId] }
.map { it?.messages.orEmpty() }
}
val markerLine: MsgId? = rememberFlow(null) {
backend.flow()
val buffers: List<Pair<NetworkInfo?, BufferInfo>> = rememberFlow(emptyList()) {
val sessions = backend.flow()
.mapNullable { it.session }
.flatMap()
val networks: Flow<Map<NetworkId, Network>> = sessions
.mapNullable { it.networks }
.map { it.orEmpty() }
val buffers: Flow<List<BufferInfo>> = sessions
.mapNullable { it.bufferSyncer }
.flatMap()
.mapNullable { it.markerLines[bufferId] }
.mapNullable { it.bufferInfos.values.sortedBy(BufferInfo::bufferName) }
.map { it.orEmpty() }
combine(buffers, networks) { bufferList, networkMap ->
bufferList.map {
Pair(networkMap[it.networkId]?.networkInfo(), it)
}
}
}
val initStatus = rememberFlow(null) {
backend.flow()
.mapNullable { it.session }
.mapNullable { it.baseInitHandler }
.flatMap()
val filteredBuffers = buffers.filter { (_, info) ->
info.bufferName?.contains(buffer.text) == true
}
val context = LocalContext.current
val buttonScrollState = rememberScrollState()
val scrollState = rememberLazyListState()
Column {
Text("Side: ${session?.side}")
Text("Side: $side")
if (initStatus != null) {
val done = initStatus.total - initStatus.waiting.size
Text("Init: ${initStatus.started} $done/ ${initStatus.total}")
}
Row(modifier = Modifier.horizontalScroll(buttonScrollState)) {
Button(onClick = { navController.navigate("coreInfo") }) {
Text("Core Info")
}
......@@ -96,32 +93,29 @@ fun HomeRoute(backend: QuasselBackend, navController: NavController) {
}) {
Text("Disconnect")
}
Button(onClick = {
messageStore?.loadBefore(bufferId, limit)
}) {
Text("↑")
}
Button(onClick = {
messageStore?.loadAfter(bufferId, limit)
}) {
Text("↓")
TextField(value = buffer, onValueChange = setBuffer)
LazyColumn(state = scrollState) {
items(filteredBuffers, key = { (_, buffer) -> buffer.bufferId }) { (network, buffer) ->
Column(modifier = Modifier
.padding(4.dp)
.fillMaxWidth()
.clickable { navController.navigate("buffer/${buffer.bufferId.id}") }
) {
Text(
network?.networkName ?: "Unknown network",
modifier = Modifier.fillMaxWidth()
)
Text(
buffer.type.joinToString(", "),
modifier = Modifier.fillMaxWidth()
)
Text(
buffer.bufferName ?: "Unknown buffer",
modifier = Modifier.fillMaxWidth()
)
}
Button(onClick = {
messageStore?.loadAround(bufferId, positionId, limit)
}) {
Text("…")
}
}
TextField(value = buffer, onValueChange = setBuffer)
TextField(value = position, onValueChange = setPosition)
MessageList(
messages = messages,
listState = listState,
markerLine = markerLine ?: MsgId(-1),
buffer = 5,
onLoadAtStart = { messageStore?.loadBefore(bufferId, limit) },
onLoadAtEnd = { messageStore?.loadAfter(bufferId, limit) }
)
}
}
......
package de.justjanne.quasseldroid.ui.routes
import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.rememberScrollState
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.material.TextField
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.input.TextFieldValue
import androidx.navigation.NavController
import de.justjanne.libquassel.protocol.models.Message
import de.justjanne.libquassel.protocol.models.ids.BufferId
import de.justjanne.libquassel.protocol.models.ids.MsgId
import de.justjanne.libquassel.protocol.util.flatMap
import de.justjanne.quasseldroid.messages.MessageStore
import de.justjanne.quasseldroid.service.QuasselBackend
import de.justjanne.quasseldroid.ui.components.MessageList
import de.justjanne.quasseldroid.util.mapNullable
import de.justjanne.quasseldroid.util.rememberFlow
import de.justjanne.quasseldroid.util.saver.TextFieldValueSaver
import kotlinx.coroutines.flow.map
private const val limit = 20
@Composable
fun MessageRoute(
backend: QuasselBackend,
navController: NavController,
buffer: BufferId
) {
val listState = rememberLazyListState()
val messageStore: MessageStore? = rememberFlow(null) {
backend.flow()
.mapNullable { it.messages }
}
val messages: List<Message> = rememberFlow(emptyList()) {
backend.flow()
.mapNullable { it.messages }
.flatMap()
.mapNullable { it[buffer] }
.map { it?.messages.orEmpty() }
}
val markerLine: MsgId? = rememberFlow(null) {
backend.flow()
.mapNullable { it.session }
.flatMap()
.mapNullable { it.bufferSyncer }
.flatMap()
.mapNullable { it.markerLines[buffer] }
}
Column {
Row {
Button(onClick = { navController.navigate("home") }) {
Text("Back")
}
Button(onClick = {
messageStore?.loadBefore(buffer, limit)
}) {
Text("↑")
}
Button(onClick = {
messageStore?.loadAfter(buffer, limit)
}) {
Text("↓")
}
Button(onClick = {
messageStore?.loadAround(buffer, markerLine ?: MsgId(-1), limit)
}) {
Text("N")
}
Button(onClick = {
messageStore?.clear(buffer)
}) {
Text("Clr")
}
}
MessageList(
messages = messages,
listState = listState,
markerLine = markerLine ?: MsgId(-1),
buffer = 5,
onLoadAtStart = { messageStore?.loadBefore(buffer, limit) },
onLoadAtEnd = { messageStore?.loadAfter(buffer, limit) }
)
}
}
......@@ -9,3 +9,18 @@ fun <T> List<T>.getPrevious(index: Int): T? =
fun <T> List<T>.getNext(index: Int): T? =
getSafe(index + 1)
@Suppress("NOTHING_TO_INLINE")
inline operator fun <T> List<T>.component6(): T = get(5)
@Suppress("NOTHING_TO_INLINE")
inline operator fun <T> List<T>.component7(): T = get(6)
@Suppress("NOTHING_TO_INLINE")
inline operator fun <T> List<T>.component8(): T = get(7)
@Suppress("NOTHING_TO_INLINE")
inline operator fun <T> List<T>.component9(): T = get(8)
@Suppress("NOTHING_TO_INLINE")
inline operator fun <T> List<T>.component10(): T = get(9)
@Suppress("NOTHING_TO_INLINE")
inline operator fun <T> List<T>.component11(): T = get(10)
@Suppress("NOTHING_TO_INLINE")
inline operator fun <T> List<T>.component12(): T = get(11)
package de.justjanne.quasseldroid.util.format
import de.justjanne.quasseldroid.util.extensions.joinString
sealed class FormatString {
data class FixedValue(
val content: CharSequence
......@@ -10,26 +12,34 @@ sealed class FormatString {
}
data class FormatSpecifier(
val index: Int?,
val flags: String?,
val width: Int?,
val precision: Int?,
val time: Boolean,
val argumentIndex: Int? = null,
val flags: String? = null,
val width: Int? = null,
val precision: Int? = null,
val time: Boolean = false,
val conversion: Char
) : FormatString() {
override fun toString(): String = listOfNotNull(
index?.let { "index=$index" },
flags?.let { "flags='$flags'" },
width?.let { "width=$width" },
precision?.let { "precision=$precision" },
"time=$time",
"conversion='$conversion'"
).joinToString(", ", prefix = "FormatSpecifier(", postfix = ")")
override fun toString(): String = joinString(", ", "FormatSpecifier(", ")") {
if (argumentIndex != null) {
append("argumentIndex=$argumentIndex")
}
if (flags != null) {
append("flags=$flags")
}
if (width != null) {
append("width=$width")
}
if (precision != null) {
append("precision=$precision")
}
append("time=$time")
append("conversion=$conversion")
}
fun toFormatSpecifier(ignoreFlags: Set<Char> = emptySet()) = buildString {
append("%")
if (index != null) {
append(index)
if (argumentIndex != null) {
append(argumentIndex)
append("$")
}
if (flags != null) {
......
......@@ -12,42 +12,42 @@ abstract class DefaultContextualLifecycleObserver : ContextualLifecycleObserver
@CallSuper
override fun onCreate(owner: Context) {
require(statusInternal.compareAndSet(LifecycleStatus.DESTROYED, LifecycleStatus.CREATED)) {
"Unexpected lifecycle status: onCreate called, but status is not DESTROYED"
"Unexpected lifecycle status: onCreate called, but status is not DESTROYED: ${statusInternal.get()}"
}
}
@CallSuper
override fun onStart(owner: Context) {
require(statusInternal.compareAndSet(LifecycleStatus.CREATED, LifecycleStatus.STARTED)) {
"Unexpected lifecycle status: onStart called, but status is not CREATED"
"Unexpected lifecycle status: onStart called, but status is not CREATED: ${statusInternal.get()}"
}
}
@CallSuper
override fun onResume(owner: Context) {
require(statusInternal.compareAndSet(LifecycleStatus.STARTED, LifecycleStatus.RESUMED)) {
"Unexpected lifecycle status: onResume called, but status is not STARTED"
"Unexpected lifecycle status: onResume called, but status is not STARTED: ${statusInternal.get()}"
}
}
@CallSuper
override fun onPause(owner: Context) {
require(statusInternal.compareAndSet(LifecycleStatus.RESUMED, LifecycleStatus.STARTED)) {
"Unexpected lifecycle status: onPause called, but status is not RESUMED"
"Unexpected lifecycle status: onPause called, but status is not RESUMED: ${statusInternal.get()}"
}
}
@CallSuper
override fun onStop(owner: Context) {
require(statusInternal.compareAndSet(LifecycleStatus.STARTED, LifecycleStatus.CREATED)) {
"Unexpected lifecycle status: onStop called, but status is not RESUMED"
"Unexpected lifecycle status: onStop called, but status is not RESUMED: ${statusInternal.get()}"
}
}
@CallSuper
override fun onDestroy(owner: Context) {
require(statusInternal.compareAndSet(LifecycleStatus.CREATED, LifecycleStatus.DESTROYED)) {
"Unexpected lifecycle status: onDestroy called, but status is not RESUMED"
"Unexpected lifecycle status: onDestroy called, but status is not RESUMED: ${statusInternal.get()}"
}
}
}
<?xml version="1.0" encoding="utf-8" ?>
<resources>
<string name="message_format_action">— %1$s%2$s %3$s</string>
<string name="message_format_notice">[%1$s%2$s] %3$s</string>
<string name="message_format_nick">%1$s%2$s is now known as %3$s%4$s</string>
<string name="message_format_nick_self">You are now known as %1$s%2$s</string>
<string name="message_format_mode">Mode %1$s by %2$s%3$s</string>
<string name="message_format_join">%1$s%2$s joined %3$s</string>
<string name="message_format_part_1">%1$s%2$s left</string>
<string name="message_format_part_2">%1$s%2$s left (%3$s)</string>
<string name="message_format_quit_1">%1$s%2$s quit</string>
<string name="message_format_quit_2">%1$s%2$s quit (%3$s)</string>
<string name="message_format_kick_1">%1$s was kicked by %2$s%3$s</string>
<string name="message_format_kick_2">%1$s was kicked by %2$s%3$s (%4$s)</string>
<string name="message_format_kill_1">%1$s was killed by %2$s%3$s</string>
<string name="message_format_kill_2">%1$s was killed by %2$s%3$s (%4$s)</string>
<string name="message_format_action">— %1$s %2$s</string>
<string name="message_format_notice">[%1$s] %2$s</string>
<string name="message_format_nick">%1$s is now known as %2$s</string>
<string name="message_format_nick_self">You are now known as %1$s</string>
<string name="message_format_mode">Mode %1$s by %2$s</string>
<string name="message_format_join">%1$s joined %2$s</string>
<string name="message_format_part_1">%1$s left</string>
<string name="message_format_part_2">%1$s left (%2$s)</string>
<string name="message_format_quit_1">%1$s quit</string>
<string name="message_format_quit_2">%1$s quit (%2$s)</string>
<string name="message_format_kick_1">%1$s was kicked by %2$s</string>
<string name="message_format_kick_2">%1$s was kicked by %2$s (%3$s)</string>
<string name="message_format_kill_1">%1$s was killed by %2$s</string>
<string name="message_format_kill_2">%1$s was killed by %2$s (%3$s)</string>
</resources>
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment