Scrobbling improvements and fixes (closes #1448)

master
Koitharu 10 months ago
parent 722c4466bf
commit c576b62d51
Signed by: Koitharu
GPG Key ID: 676DEE768C17A9D7

@ -248,8 +248,10 @@ class DetailsActivity :
} }
R.id.button_scrobbling_more -> { R.id.button_scrobbling_more -> {
val manga = viewModel.getMangaOrNull() ?: return router.showScrobblingSelectorSheet(
router.showScrobblingSelectorSheet(manga, null) manga = viewModel.getMangaOrNull() ?: return,
scrobblerService = viewModel.scrobblingInfo.value.firstOrNull()?.scrobbler
)
} }
R.id.button_related_more -> { R.id.button_related_more -> {

@ -153,7 +153,7 @@ class ScrobblingInfoSheet :
R.id.action_edit -> { R.id.action_edit -> {
val manga = viewModel.manga.value ?: return false val manga = viewModel.manga.value ?: return false
val scrobblerService = viewModel.scrobblingInfo.value.getOrNull(scrobblerIndex)?.scrobbler val scrobblerService = viewModel.scrobblingInfo.value.getOrNull(scrobblerIndex)?.scrobbler
router.showScrobblingSelectorSheet(manga, scrobblerService) activity?.router?.showScrobblingSelectorSheet(manga, scrobblerService)
dismiss() dismiss()
} }
} }

@ -133,7 +133,7 @@ class AniListRepository @Inject constructor(
""", """,
) )
val data = response.getJSONObject("data").getJSONObject("Page").getJSONArray("media") val data = response.getJSONObject("data").getJSONObject("Page").getJSONArray("media")
return data.mapJSON { ScrobblerManga(it) } return data.mapJSON { ScrobblerManga(it, query) }
} }
override suspend fun createRate(mangaId: Long, scrobblerMangaId: Long) { override suspend fun createRate(mangaId: Long, scrobblerMangaId: Long) {
@ -225,7 +225,7 @@ class AniListRepository @Inject constructor(
db.getScrobblingDao().upsert(entity) db.getScrobblingDao().upsert(entity)
} }
private fun ScrobblerManga(json: JSONObject): ScrobblerManga { private fun ScrobblerManga(json: JSONObject, sourceTitle: String): ScrobblerManga {
val title = json.getJSONObject("title") val title = json.getJSONObject("title")
return ScrobblerManga( return ScrobblerManga(
id = json.getLong("id"), id = json.getLong("id"),
@ -233,6 +233,14 @@ class AniListRepository @Inject constructor(
altName = title.getStringOrNull("native"), altName = title.getStringOrNull("native"),
cover = json.getJSONObject("coverImage").getString("medium"), cover = json.getJSONObject("coverImage").getString("medium"),
url = json.getString("siteUrl"), url = json.getString("siteUrl"),
isBestMatch = sourceTitle.let {
title.keys().forEach { key ->
if (title.getStringOrNull(key)?.equals(it, ignoreCase = true) == true) {
return@let true
}
}
false
},
) )
} }

@ -6,9 +6,11 @@ data class ScrobblerManga(
val id: Long, val id: Long,
val name: String, val name: String,
val altName: String?, val altName: String?,
val cover: String, val cover: String?,
val url: String, val url: String,
val isBestMatch: Boolean,
) : ListModel { ) : ListModel {
override fun areItemsTheSame(other: ListModel): Boolean { override fun areItemsTheSame(other: ListModel): Boolean {
return other is ScrobblerManga && other.id == id return other is ScrobblerManga && other.id == id
} }

@ -1,5 +1,6 @@
package org.koitharu.kotatsu.scrobbling.common.domain.model package org.koitharu.kotatsu.scrobbling.common.domain.model
import org.koitharu.kotatsu.list.ui.ListModelDiffCallback
import org.koitharu.kotatsu.list.ui.model.ListModel import org.koitharu.kotatsu.list.ui.model.ListModel
data class ScrobblingInfo( data class ScrobblingInfo(
@ -19,4 +20,10 @@ data class ScrobblingInfo(
override fun areItemsTheSame(other: ListModel): Boolean { override fun areItemsTheSame(other: ListModel): Boolean {
return other is ScrobblingInfo && other.scrobbler == scrobbler return other is ScrobblingInfo && other.scrobbler == scrobbler
} }
override fun getChangePayload(previousState: ListModel): Any? = when {
previousState !is ScrobblingInfo -> null
previousState.status != status || previousState.rating != rating -> ListModelDiffCallback.PAYLOAD_ANYTHING_CHANGED
else -> super.getChangePayload(previousState)
}
} }

@ -94,7 +94,7 @@ class ScrobblingSelectorSheet :
if (isLoading) { if (isLoading) {
binding.buttonDone.setProgressIcon() binding.buttonDone.setProgressIcon()
} else { } else {
binding.buttonDone.icon = null binding.buttonDone.setIconResource(R.drawable.ic_check)
} }
binding.tabs.setTabsEnabled(!isLoading) binding.tabs.setTabsEnabled(!isLoading)
} }

@ -1,6 +1,7 @@
package org.koitharu.kotatsu.scrobbling.common.ui.selector.adapter package org.koitharu.kotatsu.scrobbling.common.ui.selector.adapter
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener
import org.koitharu.kotatsu.core.util.ext.textAndVisible import org.koitharu.kotatsu.core.util.ext.textAndVisible
import org.koitharu.kotatsu.databinding.ItemMangaListBinding import org.koitharu.kotatsu.databinding.ItemMangaListBinding
@ -18,6 +19,8 @@ fun scrobblingMangaAD(
bind { bind {
binding.textViewTitle.text = item.name binding.textViewTitle.text = item.name
val endIcon = if (item.isBestMatch) R.drawable.ic_star_small else 0
binding.textViewTitle.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, endIcon, 0)
binding.textViewSubtitle.textAndVisible = item.altName binding.textViewSubtitle.textAndVisible = item.altName
binding.imageViewCover.setImageAsync(item.cover, null) binding.imageViewCover.setImageAsync(item.cover, null)
} }

@ -106,6 +106,9 @@ class KitsuRepository(
altName = titles.drop(1).joinToString(), altName = titles.drop(1).joinToString(),
cover = attrs.getJSONObject("posterImage").getStringOrNull("small").orEmpty(), cover = attrs.getJSONObject("posterImage").getStringOrNull("small").orEmpty(),
url = "$BASE_WEB_URL/manga/${attrs.getString("slug")}", url = "$BASE_WEB_URL/manga/${attrs.getString("slug")}",
isBestMatch = titles.any {
it.equals(query, ignoreCase = true)
}
) )
} }
} }

@ -4,18 +4,25 @@ import android.annotation.SuppressLint
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.text.Editable import android.text.Editable
import android.view.KeyEvent
import android.view.View import android.view.View
import android.view.ViewGroup.MarginLayoutParams
import android.widget.TextView
import androidx.core.net.toUri import androidx.core.net.toUri
import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updateLayoutParams
import androidx.core.view.updatePadding
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.ui.BaseActivity import org.koitharu.kotatsu.core.ui.BaseActivity
import org.koitharu.kotatsu.core.ui.util.DefaultTextWatcher import org.koitharu.kotatsu.core.ui.util.DefaultTextWatcher
import org.koitharu.kotatsu.core.util.ext.consumeAllSystemBarsInsets import org.koitharu.kotatsu.core.util.ext.consume
import org.koitharu.kotatsu.core.util.ext.systemBarsInsets
import org.koitharu.kotatsu.databinding.ActivityKitsuAuthBinding import org.koitharu.kotatsu.databinding.ActivityKitsuAuthBinding
import org.koitharu.kotatsu.parsers.util.urlEncoded import org.koitharu.kotatsu.parsers.util.urlEncoded
class KitsuAuthActivity : BaseActivity<ActivityKitsuAuthBinding>(), View.OnClickListener, DefaultTextWatcher { class KitsuAuthActivity : BaseActivity<ActivityKitsuAuthBinding>(),
View.OnClickListener,
DefaultTextWatcher,
TextView.OnEditorActionListener {
private val regexEmail = Regex("^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,6}$", RegexOption.IGNORE_CASE) private val regexEmail = Regex("^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,6}$", RegexOption.IGNORE_CASE)
@ -25,22 +32,33 @@ class KitsuAuthActivity : BaseActivity<ActivityKitsuAuthBinding>(), View.OnClick
viewBinding.buttonCancel.setOnClickListener(this) viewBinding.buttonCancel.setOnClickListener(this)
viewBinding.buttonDone.setOnClickListener(this) viewBinding.buttonDone.setOnClickListener(this)
viewBinding.editEmail.addTextChangedListener(this) viewBinding.editEmail.addTextChangedListener(this)
viewBinding.editEmail.setOnEditorActionListener(this)
viewBinding.editPassword.addTextChangedListener(this) viewBinding.editPassword.addTextChangedListener(this)
viewBinding.editPassword.setOnEditorActionListener(this)
} }
override fun onApplyWindowInsets( override fun onApplyWindowInsets(
v: View, v: View,
insets: WindowInsetsCompat insets: WindowInsetsCompat
): WindowInsetsCompat { ): WindowInsetsCompat {
val barsInsets = insets.systemBarsInsets val typeMask = WindowInsetsCompat.Type.systemBars()
val basePadding = resources.getDimensionPixelOffset(R.dimen.screen_padding) val screenPadding = v.resources.getDimensionPixelOffset(R.dimen.screen_padding)
viewBinding.root.setPadding( val barsInsets = insets.getInsets(typeMask)
barsInsets.left + basePadding, viewBinding.root.updatePadding(top = barsInsets.top)
barsInsets.top + basePadding, viewBinding.dockedToolbarChild.updateLayoutParams<MarginLayoutParams> {
barsInsets.right + basePadding, leftMargin = barsInsets.left
barsInsets.bottom + basePadding, rightMargin = barsInsets.right
) bottomMargin = barsInsets.bottom
return insets.consumeAllSystemBarsInsets() }
viewBinding.layoutEmail.updateLayoutParams<MarginLayoutParams> {
leftMargin = barsInsets.left + screenPadding
rightMargin = barsInsets.right + screenPadding
}
viewBinding.layoutPassword.updateLayoutParams<MarginLayoutParams> {
leftMargin = barsInsets.left + screenPadding
rightMargin = barsInsets.right + screenPadding
}
return insets.consume(v, typeMask)
} }
override fun onClick(v: View) { override fun onClick(v: View) {
@ -50,6 +68,28 @@ class KitsuAuthActivity : BaseActivity<ActivityKitsuAuthBinding>(), View.OnClick
} }
} }
override fun onEditorAction(
v: TextView,
actionId: Int,
event: KeyEvent?
): Boolean = when (v.id) {
R.id.edit_email -> {
viewBinding.editPassword.requestFocus()
true
}
R.id.edit_password -> {
if (viewBinding.buttonDone.isEnabled) {
continueAuth()
true
} else {
false
}
}
else -> false
}
override fun afterTextChanged(s: Editable?) { override fun afterTextChanged(s: Editable?) {
val email = viewBinding.editEmail.text?.toString()?.trim() val email = viewBinding.editEmail.text?.toString()?.trim()
val password = viewBinding.editPassword.text?.toString()?.trim() val password = viewBinding.editPassword.text?.toString()?.trim()

@ -99,7 +99,7 @@ class MALRepository @Inject constructor(
val response = okHttp.newCall(request).await().parseJson() val response = okHttp.newCall(request).await().parseJson()
check(response.has("data")) { "Invalid response: \"$response\"" } check(response.has("data")) { "Invalid response: \"$response\"" }
val data = response.getJSONArray("data") val data = response.getJSONArray("data")
return data.mapJSONNotNull { jsonToManga(it) } return data.mapJSONNotNull { jsonToManga(it, query) }
} }
override suspend fun getMangaInfo(id: Long): ScrobblerMangaInfo { override suspend fun getMangaInfo(id: Long): ScrobblerMangaInfo {
@ -183,18 +183,17 @@ class MALRepository @Inject constructor(
storage.clear() storage.clear()
} }
private fun jsonToManga(json: JSONObject): ScrobblerManga? { private fun jsonToManga(json: JSONObject, sourceTitle: String): ScrobblerManga? {
for (i in 0 until json.length()) { val node = json.getJSONObject("node")
val node = json.getJSONObject("node") val title = node.getString("title")
return ScrobblerManga( return ScrobblerManga(
id = node.getLong("id"), id = node.getLong("id"),
name = node.getString("title"), name = title,
altName = null, altName = null,
cover = node.getJSONObject("main_picture").getString("large"), cover = node.optJSONObject("main_picture")?.getStringOrNull("large"),
url = "$BASE_WEB_URL/manga/${node.getLong("id")}", url = "$BASE_WEB_URL/manga/${node.getLong("id")}",
) isBestMatch = title.equals(sourceTitle, ignoreCase = true),
} )
return null
} }
private fun ScrobblerMangaInfo(json: JSONObject) = ScrobblerMangaInfo( private fun ScrobblerMangaInfo(json: JSONObject) = ScrobblerMangaInfo(

@ -104,7 +104,7 @@ class ShikimoriRepository @Inject constructor(
.build() .build()
val request = Request.Builder().url(url).get().build() val request = Request.Builder().url(url).get().build()
val response = okHttp.newCall(request).await().parseJsonArray() val response = okHttp.newCall(request).await().parseJsonArray()
val list = response.mapJSON { ScrobblerManga(it) } val list = response.mapJSON { ScrobblerManga(it, query) }
return if (pageOffset != 0) list.drop(pageOffset) else list return if (pageOffset != 0) list.drop(pageOffset) else list
} }
@ -195,12 +195,14 @@ class ShikimoriRepository @Inject constructor(
db.getScrobblingDao().upsert(entity) db.getScrobblingDao().upsert(entity)
} }
private fun ScrobblerManga(json: JSONObject) = ScrobblerManga( private fun ScrobblerManga(json: JSONObject, sourceTitle: String) = ScrobblerManga(
id = json.getLong("id"), id = json.getLong("id"),
name = json.getString("name"), name = json.getString("name"),
altName = json.getStringOrNull("russian"), altName = json.getStringOrNull("russian"),
cover = json.getJSONObject("image").getString("preview").toAbsoluteUrl(DOMAIN), cover = json.getJSONObject("image").getString("preview").toAbsoluteUrl(DOMAIN),
url = json.getString("url").toAbsoluteUrl(DOMAIN), url = json.getString("url").toAbsoluteUrl(DOMAIN),
isBestMatch = sourceTitle.equals(json.getString("name"), ignoreCase = true)
|| json.getStringOrNull("russian")?.equals(sourceTitle, ignoreCase = true) == true
) )
private fun ScrobblerMangaInfo(json: JSONObject) = ScrobblerMangaInfo( private fun ScrobblerMangaInfo(json: JSONObject) = ScrobblerMangaInfo(

@ -0,0 +1,11 @@
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:width="16dp"
android:height="16dp"
android:tint="@color/common_yellow"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M14.43,10l-2.43,-8l-2.43,8l-7.57,0l6.18,4.41l-2.35,7.59l6.17,-4.69l6.18,4.69l-2.35,-7.59l6.17,-4.41z" />
</vector>

@ -5,15 +5,14 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical" android:orientation="vertical">
android:padding="@dimen/screen_padding">
<TextView <TextView
android:id="@+id/textView_title" android:id="@+id/textView_title"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:drawablePadding="16dp" android:drawablePadding="@dimen/screen_padding"
android:gravity="center_horizontal" android:gravity="center_horizontal"
android:text="@string/kitsu" android:text="@string/kitsu"
android:textAppearance="?textAppearanceHeadline5" android:textAppearance="?textAppearanceHeadline5"
@ -23,108 +22,98 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<RelativeLayout <TextView
android:id="@+id/textView_subtitle"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="wrap_content"
android:layout_marginTop="12dp"
<TextView android:gravity="center_horizontal"
android:id="@+id/textView_subtitle" android:text="@string/email_password_enter_hint"
android:layout_width="0dp" android:textAppearance="?textAppearanceSubtitle1" />
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_alignParentEnd="true"
android:layout_marginTop="12dp"
android:gravity="center_horizontal"
android:text="@string/email_password_enter_hint"
android:textAppearance="?textAppearanceSubtitle1" />
<com.google.android.material.textfield.TextInputLayout <com.google.android.material.textfield.TextInputLayout
android:id="@+id/layout_email" android:id="@+id/layout_email"
style="?textInputOutlinedStyle" style="?textInputOutlinedStyle"
android:layout_width="0dp" android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/screen_padding"
android:layout_marginTop="30dp"
app:errorIconDrawable="@null"
app:hintEnabled="false">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/edit_email"
android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_below="@id/textView_subtitle" android:autofillHints="emailAddress"
android:layout_alignParentStart="true" android:hint="@string/email"
android:layout_alignParentEnd="true" android:imeOptions="actionNext"
android:layout_marginTop="30dp" android:inputType="textEmailAddress"
app:errorIconDrawable="@null" android:maxLength="512"
app:hintEnabled="false"> android:singleLine="true"
android:textSize="16sp" />
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/edit_email" </com.google.android.material.textfield.TextInputLayout>
android:layout_width="match_parent"
android:layout_height="wrap_content" <com.google.android.material.textfield.TextInputLayout
android:autofillHints="emailAddress" android:id="@+id/layout_password"
android:hint="@string/email" style="?textInputOutlinedStyle"
android:imeOptions="actionDone" android:layout_width="match_parent"
android:inputType="textEmailAddress" android:layout_height="wrap_content"
android:maxLength="512" android:layout_marginHorizontal="@dimen/screen_padding"
android:singleLine="true" android:layout_marginTop="8dp"
android:textSize="16sp" /> app:endIconMode="password_toggle"
app:errorIconDrawable="@null"
</com.google.android.material.textfield.TextInputLayout> app:hintEnabled="false">
<com.google.android.material.textfield.TextInputLayout <com.google.android.material.textfield.TextInputEditText
android:id="@+id/layout_password" android:id="@+id/edit_password"
style="?textInputOutlinedStyle" android:layout_width="match_parent"
android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_below="@id/layout_email" android:autofillHints="password"
android:layout_alignParentStart="true" android:hint="@string/password"
android:layout_alignParentEnd="true" android:imeOptions="actionDone"
android:layout_marginTop="8dp" android:inputType="textPassword"
app:endIconMode="password_toggle" android:maxLength="512"
app:errorIconDrawable="@null" android:singleLine="true"
app:hintEnabled="false"> android:textSize="16sp" />
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/edit_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:autofillHints="password"
android:hint="@string/password"
android:imeOptions="actionDone"
android:inputType="textPassword"
android:maxLength="512"
android:singleLine="true"
android:textSize="16sp" />
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
<Button <View
android:id="@+id/button_cancel" android:layout_width="match_parent"
style="?materialButtonOutlinedStyle" android:layout_height="0dp"
android:layout_width="wrap_content" android:layout_weight="1"
android:layout_height="wrap_content" android:visibility="invisible" />
android:layout_alignParentStart="true"
android:layout_alignParentBottom="true"
android:text="@android:string/cancel" />
<Button <com.google.android.material.dockedtoolbar.DockedToolbarLayout
android:id="@+id/button_done" android:id="@+id/docked_toolbar"
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentEnd="true" android:fitsSystemWindows="false">
android:layout_alignParentBottom="true"
android:enabled="false"
android:text="@string/done"
tools:ignore="RelativeOverlap" />
</RelativeLayout> <FrameLayout
android:id="@+id/docked_toolbar_child"
android:layout_width="match_parent"
android:layout_height="@dimen/m3_comp_toolbar_docked_container_height">
<FrameLayout <Button
android:id="@+id/layout_progress" android:id="@+id/button_cancel"
android:layout_width="match_parent" android:layout_width="wrap_content"
android:layout_height="match_parent" android:layout_height="wrap_content"
android:visibility="gone"> android:layout_gravity="center_vertical|start"
android:text="@android:string/cancel" />
<com.google.android.material.progressindicator.CircularProgressIndicator <Button
android:layout_width="wrap_content" android:id="@+id/button_done"
android:layout_height="wrap_content" style="?materialButtonTonalStyle"
android:layout_gravity="center" android:layout_width="wrap_content"
android:indeterminate="true" /> android:layout_height="wrap_content"
android:layout_gravity="center_vertical|end"
android:enabled="false"
android:text="@string/_continue" />
</FrameLayout> </FrameLayout>
</com.google.android.material.dockedtoolbar.DockedToolbarLayout>
</LinearLayout> </LinearLayout>

@ -60,6 +60,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:layout_marginTop="4dp" android:layout_marginTop="4dp"
android:singleLine="true"
android:textAppearance="?textAppearanceBody1" android:textAppearance="?textAppearanceBody1"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/imageView_icon" app:layout_constraintStart_toEndOf="@id/imageView_icon"

@ -16,7 +16,9 @@
<com.google.android.material.appbar.MaterialToolbar <com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar" android:id="@+id/toolbar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content"
app:contentInsetStart="0dp"
tools:menu="@menu/opt_search">
<RelativeLayout <RelativeLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -27,20 +29,25 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentStart="true" android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:layout_toStartOf="@id/button_done" android:layout_toStartOf="@id/button_done"
android:background="@android:color/transparent" android:background="@android:color/transparent"
android:clipToPadding="false"
android:paddingStart="@dimen/list_spacing"
android:scrollIndicators="start|end" android:scrollIndicators="start|end"
app:tabGravity="start" app:tabGravity="start"
app:tabMode="scrollable" app:tabMode="scrollable"
tools:ignore="UnusedAttribute" /> tools:ignore="RtlSymmetry,UnusedAttribute" />
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/button_done" android:id="@+id/button_done"
style="?materialIconButtonFilledStyle"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentEnd="true" android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:layout_marginHorizontal="@dimen/toolbar_button_margin" android:layout_marginHorizontal="@dimen/toolbar_button_margin"
android:text="@string/done" /> app:icon="@drawable/ic_check" />
</RelativeLayout> </RelativeLayout>

@ -8,6 +8,7 @@
<color name="warning">#FB8C00</color> <color name="warning">#FB8C00</color>
<color name="launcher_background">#222222</color> <color name="launcher_background">#222222</color>
<color name="common_green">#81C784</color> <color name="common_green">#81C784</color>
<color name="common_yellow">#FFF176</color>
<color name="common_red">#E57373</color> <color name="common_red">#E57373</color>
<color name="dim2">#C8000000</color> <color name="dim2">#C8000000</color>
<color name="nsfw_18">#BF360C</color> <color name="nsfw_18">#BF360C</color>

@ -20,6 +20,7 @@
<color name="launcher_background">#FFFFFF</color> <color name="launcher_background">#FFFFFF</color>
<color name="common_green">#388E3C</color> <color name="common_green">#388E3C</color>
<color name="common_red">#D32F2F</color> <color name="common_red">#D32F2F</color>
<color name="common_yellow">#FBC02D</color>
<color name="nsfw_18">#FF8A65</color> <color name="nsfw_18">#FF8A65</color>
<color name="nsfw_16">#FFD54F</color> <color name="nsfw_16">#FFD54F</color>

Loading…
Cancel
Save