Merge branch 'devel' into feature/suggestions_v2
This commit is contained in:
@@ -3,6 +3,7 @@ package org.koitharu.kotatsu.base.ui
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.KeyEvent
|
import android.view.KeyEvent
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
@@ -122,10 +123,14 @@ abstract class BaseActivity<B : ViewBinding> :
|
|||||||
override fun onSupportActionModeStarted(mode: ActionMode) {
|
override fun onSupportActionModeStarted(mode: ActionMode) {
|
||||||
super.onSupportActionModeStarted(mode)
|
super.onSupportActionModeStarted(mode)
|
||||||
actionModeDelegate.onSupportActionModeStarted(mode)
|
actionModeDelegate.onSupportActionModeStarted(mode)
|
||||||
val actionModeColor = ColorUtils.compositeColors(
|
val actionModeColor = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
ContextCompat.getColor(this, com.google.android.material.R.color.m3_appbar_overlay_color),
|
ColorUtils.compositeColors(
|
||||||
getThemeColor(com.google.android.material.R.attr.colorSurface),
|
ContextCompat.getColor(this, com.google.android.material.R.color.m3_appbar_overlay_color),
|
||||||
)
|
getThemeColor(com.google.android.material.R.attr.colorSurface),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
ContextCompat.getColor(this, R.color.kotatsu_secondaryContainer)
|
||||||
|
}
|
||||||
val insets = ViewCompat.getRootWindowInsets(binding.root)
|
val insets = ViewCompat.getRootWindowInsets(binding.root)
|
||||||
?.getInsets(WindowInsetsCompat.Type.systemBars()) ?: return
|
?.getInsets(WindowInsetsCompat.Type.systemBars()) ?: return
|
||||||
findViewById<ActionBarContextView?>(androidx.appcompat.R.id.action_mode_bar).apply {
|
findViewById<ActionBarContextView?>(androidx.appcompat.R.id.action_mode_bar).apply {
|
||||||
|
|||||||
@@ -0,0 +1,79 @@
|
|||||||
|
package org.koitharu.kotatsu.base.ui.dialog
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.DialogInterface
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import androidx.annotation.DrawableRes
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import com.google.android.material.button.MaterialButton
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
|
import org.koitharu.kotatsu.databinding.DialogTwoButtonsBinding
|
||||||
|
|
||||||
|
class TwoButtonsAlertDialog private constructor(
|
||||||
|
private val delegate: AlertDialog
|
||||||
|
) : DialogInterface by delegate {
|
||||||
|
|
||||||
|
fun show() = delegate.show()
|
||||||
|
|
||||||
|
class Builder(context: Context) {
|
||||||
|
|
||||||
|
private val binding = DialogTwoButtonsBinding.inflate(LayoutInflater.from(context))
|
||||||
|
|
||||||
|
private val delegate = MaterialAlertDialogBuilder(context)
|
||||||
|
.setView(binding.root)
|
||||||
|
|
||||||
|
fun setTitle(@StringRes titleResId: Int): Builder {
|
||||||
|
binding.title.setText(titleResId)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setTitle(title: CharSequence): Builder {
|
||||||
|
binding.title.text = title
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setIcon(@DrawableRes iconId: Int): Builder {
|
||||||
|
binding.icon.setImageResource(iconId)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setPositiveButton(
|
||||||
|
@StringRes textId: Int,
|
||||||
|
listener: DialogInterface.OnClickListener,
|
||||||
|
): Builder {
|
||||||
|
initButton(binding.button1, DialogInterface.BUTTON_POSITIVE, textId, listener)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setNegativeButton(
|
||||||
|
@StringRes textId: Int,
|
||||||
|
listener: DialogInterface.OnClickListener? = null
|
||||||
|
): Builder {
|
||||||
|
initButton(binding.button2, DialogInterface.BUTTON_NEGATIVE, textId, listener)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun create(): TwoButtonsAlertDialog {
|
||||||
|
val dialog = delegate.create()
|
||||||
|
binding.root.tag = dialog
|
||||||
|
return TwoButtonsAlertDialog(dialog)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initButton(
|
||||||
|
button: MaterialButton,
|
||||||
|
which: Int,
|
||||||
|
@StringRes textId: Int,
|
||||||
|
listener: DialogInterface.OnClickListener?,
|
||||||
|
) {
|
||||||
|
button.setText(textId)
|
||||||
|
button.isVisible = true
|
||||||
|
button.setOnClickListener {
|
||||||
|
val dialog = binding.root.tag as DialogInterface
|
||||||
|
listener?.onClick(dialog, which)
|
||||||
|
dialog.dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -92,7 +92,6 @@ class DownloadNotificationFactory @AssistedInject constructor(
|
|||||||
createChannel()
|
createChannel()
|
||||||
builder.setOnlyAlertOnce(true)
|
builder.setOnlyAlertOnce(true)
|
||||||
builder.setDefaults(0)
|
builder.setDefaults(0)
|
||||||
builder.color = ContextCompat.getColor(context, R.color.blue_primary)
|
|
||||||
builder.foregroundServiceBehavior = NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE
|
builder.foregroundServiceBehavior = NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE
|
||||||
builder.setSilent(true)
|
builder.setSilent(true)
|
||||||
builder.setGroup(GROUP_ID)
|
builder.setGroup(GROUP_ID)
|
||||||
|
|||||||
@@ -70,7 +70,6 @@ class ImportWorker @AssistedInject constructor(
|
|||||||
.setContentTitle(title)
|
.setContentTitle(title)
|
||||||
.setPriority(NotificationCompat.PRIORITY_MIN)
|
.setPriority(NotificationCompat.PRIORITY_MIN)
|
||||||
.setDefaults(0)
|
.setDefaults(0)
|
||||||
.setColor(ContextCompat.getColor(applicationContext, R.color.blue_primary_dark))
|
|
||||||
.setSilent(true)
|
.setSilent(true)
|
||||||
.setProgress(0, 0, true)
|
.setProgress(0, 0, true)
|
||||||
.setSmallIcon(android.R.drawable.stat_sys_download)
|
.setSmallIcon(android.R.drawable.stat_sys_download)
|
||||||
@@ -85,7 +84,6 @@ class ImportWorker @AssistedInject constructor(
|
|||||||
val notification = NotificationCompat.Builder(applicationContext, CHANNEL_ID)
|
val notification = NotificationCompat.Builder(applicationContext, CHANNEL_ID)
|
||||||
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
|
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
|
||||||
.setDefaults(0)
|
.setDefaults(0)
|
||||||
.setColor(ContextCompat.getColor(applicationContext, R.color.blue_primary_dark))
|
|
||||||
.setSilent(true)
|
.setSilent(true)
|
||||||
result.onSuccess { manga ->
|
result.onSuccess { manga ->
|
||||||
notification.setLargeIcon(
|
notification.setLargeIcon(
|
||||||
|
|||||||
@@ -56,7 +56,6 @@ class LocalChaptersRemoveService : CoroutineIntentService() {
|
|||||||
.setContentTitle(getString(R.string.error_occurred))
|
.setContentTitle(getString(R.string.error_occurred))
|
||||||
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
|
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
|
||||||
.setDefaults(0)
|
.setDefaults(0)
|
||||||
.setColor(ContextCompat.getColor(this, R.color.blue_primary_dark))
|
|
||||||
.setSilent(true)
|
.setSilent(true)
|
||||||
.setContentText(error.getDisplayMessage(resources))
|
.setContentText(error.getDisplayMessage(resources))
|
||||||
.setSmallIcon(android.R.drawable.stat_notify_error)
|
.setSmallIcon(android.R.drawable.stat_notify_error)
|
||||||
@@ -82,7 +81,6 @@ class LocalChaptersRemoveService : CoroutineIntentService() {
|
|||||||
.setContentTitle(title)
|
.setContentTitle(title)
|
||||||
.setPriority(NotificationCompat.PRIORITY_MIN)
|
.setPriority(NotificationCompat.PRIORITY_MIN)
|
||||||
.setDefaults(0)
|
.setDefaults(0)
|
||||||
.setColor(ContextCompat.getColor(this, R.color.blue_primary_dark))
|
|
||||||
.setSilent(true)
|
.setSilent(true)
|
||||||
.setProgress(0, 0, true)
|
.setProgress(0, 0, true)
|
||||||
.setSmallIcon(android.R.drawable.stat_notify_sync)
|
.setSmallIcon(android.R.drawable.stat_notify_sync)
|
||||||
|
|||||||
@@ -20,8 +20,9 @@ class SyncAuthApi @Inject constructor(
|
|||||||
val body = JSONObject(
|
val body = JSONObject(
|
||||||
mapOf("email" to email, "password" to password),
|
mapOf("email" to email, "password" to password),
|
||||||
).toRequestBody()
|
).toRequestBody()
|
||||||
|
val scheme = getScheme(host)
|
||||||
val request = Request.Builder()
|
val request = Request.Builder()
|
||||||
.url("http://$host/auth")
|
.url("$scheme://$host/auth")
|
||||||
.post(body)
|
.post(body)
|
||||||
.build()
|
.build()
|
||||||
val response = okHttpClient.newCall(request).await()
|
val response = okHttpClient.newCall(request).await()
|
||||||
@@ -33,4 +34,13 @@ class SyncAuthApi @Inject constructor(
|
|||||||
throw SyncApiException(message, code)
|
throw SyncApiException(message, code)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private suspend fun getScheme(host: String): String {
|
||||||
|
val request = Request.Builder()
|
||||||
|
.url("http://$host/")
|
||||||
|
.head()
|
||||||
|
.build()
|
||||||
|
val response = okHttpClient.newCall(request).await()
|
||||||
|
return response.request.url.scheme
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import okhttp3.Request
|
|||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
import okhttp3.Route
|
import okhttp3.Route
|
||||||
import org.koitharu.kotatsu.R
|
import org.koitharu.kotatsu.R
|
||||||
|
import org.koitharu.kotatsu.core.network.CommonHeaders
|
||||||
|
|
||||||
class SyncAuthenticator(
|
class SyncAuthenticator(
|
||||||
context: Context,
|
context: Context,
|
||||||
@@ -24,7 +25,7 @@ class SyncAuthenticator(
|
|||||||
val newToken = tryRefreshToken() ?: return null
|
val newToken = tryRefreshToken() ?: return null
|
||||||
accountManager.setAuthToken(account, tokenType, newToken)
|
accountManager.setAuthToken(account, tokenType, newToken)
|
||||||
return response.request.newBuilder()
|
return response.request.newBuilder()
|
||||||
.header("Authorization", "Bearer $newToken")
|
.header(CommonHeaders.AUTHORIZATION, "Bearer $newToken")
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import okhttp3.Response
|
|||||||
import org.koitharu.kotatsu.BuildConfig
|
import org.koitharu.kotatsu.BuildConfig
|
||||||
import org.koitharu.kotatsu.R
|
import org.koitharu.kotatsu.R
|
||||||
import org.koitharu.kotatsu.core.db.DATABASE_VERSION
|
import org.koitharu.kotatsu.core.db.DATABASE_VERSION
|
||||||
|
import org.koitharu.kotatsu.core.network.CommonHeaders
|
||||||
|
|
||||||
class SyncInterceptor(
|
class SyncInterceptor(
|
||||||
context: Context,
|
context: Context,
|
||||||
@@ -21,10 +22,10 @@ class SyncInterceptor(
|
|||||||
val token = accountManager.peekAuthToken(account, tokenType)
|
val token = accountManager.peekAuthToken(account, tokenType)
|
||||||
val requestBuilder = chain.request().newBuilder()
|
val requestBuilder = chain.request().newBuilder()
|
||||||
if (token != null) {
|
if (token != null) {
|
||||||
requestBuilder.header("Authorization", "Bearer $token")
|
requestBuilder.header(CommonHeaders.AUTHORIZATION, "Bearer $token")
|
||||||
}
|
}
|
||||||
requestBuilder.header("X-App-Version", BuildConfig.VERSION_CODE.toString())
|
requestBuilder.header("X-App-Version", BuildConfig.VERSION_CODE.toString())
|
||||||
requestBuilder.header("X-Db-Version", DATABASE_VERSION.toString())
|
requestBuilder.header("X-Db-Version", DATABASE_VERSION.toString())
|
||||||
return chain.proceed(requestBuilder.build())
|
return chain.proceed(requestBuilder.build())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,8 +54,11 @@ class SyncHelper(
|
|||||||
.addInterceptor(SyncInterceptor(context, account))
|
.addInterceptor(SyncInterceptor(context, account))
|
||||||
.addInterceptor(GZipInterceptor())
|
.addInterceptor(GZipInterceptor())
|
||||||
.build()
|
.build()
|
||||||
private val baseUrl: String
|
private val baseUrl: String by lazy {
|
||||||
get() = "http://${settings.host}"
|
val host = settings.host
|
||||||
|
val scheme = getScheme(host)
|
||||||
|
"$scheme://$host"
|
||||||
|
}
|
||||||
private val defaultGcPeriod: Long // gc period if sync enabled
|
private val defaultGcPeriod: Long // gc period if sync enabled
|
||||||
get() = TimeUnit.DAYS.toMillis(4)
|
get() = TimeUnit.DAYS.toMillis(4)
|
||||||
|
|
||||||
@@ -260,6 +263,15 @@ class SyncHelper(
|
|||||||
return requireNotNull(tag)
|
return requireNotNull(tag)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getScheme(host: String): String {
|
||||||
|
val request = Request.Builder()
|
||||||
|
.url("http://$host/")
|
||||||
|
.head()
|
||||||
|
.build()
|
||||||
|
val response = httpClient.newCall(request).execute()
|
||||||
|
return response.request.url.scheme
|
||||||
|
}
|
||||||
|
|
||||||
private fun gcFavourites() {
|
private fun gcFavourites() {
|
||||||
val deletedAt = System.currentTimeMillis() - defaultGcPeriod
|
val deletedAt = System.currentTimeMillis() - defaultGcPeriod
|
||||||
val selection = "deleted_at != 0 AND deleted_at < ?"
|
val selection = "deleted_at != 0 AND deleted_at < ?"
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import android.text.Editable
|
|||||||
import android.text.TextWatcher
|
import android.text.TextWatcher
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.Button
|
import android.widget.Button
|
||||||
|
import android.widget.Toast
|
||||||
import androidx.activity.OnBackPressedCallback
|
import androidx.activity.OnBackPressedCallback
|
||||||
import androidx.activity.viewModels
|
import androidx.activity.viewModels
|
||||||
import androidx.core.graphics.Insets
|
import androidx.core.graphics.Insets
|
||||||
@@ -54,6 +55,9 @@ class SyncAuthActivity : BaseActivity<ActivitySyncAuthBinding>(), View.OnClickLi
|
|||||||
viewModel.onTokenObtained.observe(this, ::onTokenReceived)
|
viewModel.onTokenObtained.observe(this, ::onTokenReceived)
|
||||||
viewModel.onError.observe(this, ::onError)
|
viewModel.onError.observe(this, ::onError)
|
||||||
viewModel.isLoading.observe(this, ::onLoadingStateChanged)
|
viewModel.isLoading.observe(this, ::onLoadingStateChanged)
|
||||||
|
viewModel.onAccountAlreadyExists.observe(this) {
|
||||||
|
onAccountAlreadyExists()
|
||||||
|
}
|
||||||
|
|
||||||
supportFragmentManager.setFragmentResultListener(SyncHostDialogFragment.REQUEST_KEY, this, this)
|
supportFragmentManager.setFragmentResultListener(SyncHostDialogFragment.REQUEST_KEY, this, this)
|
||||||
pageBackCallback.update()
|
pageBackCallback.update()
|
||||||
@@ -151,6 +155,16 @@ class SyncAuthActivity : BaseActivity<ActivitySyncAuthBinding>(), View.OnClickLi
|
|||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun onAccountAlreadyExists() {
|
||||||
|
Toast.makeText(this, R.string.account_already_exists, Toast.LENGTH_SHORT)
|
||||||
|
.show()
|
||||||
|
accountAuthenticatorResponse?.onError(
|
||||||
|
AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION,
|
||||||
|
getString(R.string.account_already_exists),
|
||||||
|
)
|
||||||
|
super.finishAfterTransition()
|
||||||
|
}
|
||||||
|
|
||||||
private class EmailTextWatcher(
|
private class EmailTextWatcher(
|
||||||
private val button: Button,
|
private val button: Button,
|
||||||
) : TextWatcher {
|
) : TextWatcher {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.koitharu.kotatsu.sync.ui
|
package org.koitharu.kotatsu.sync.ui
|
||||||
|
|
||||||
|
import android.accounts.AccountManager
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
@@ -19,11 +20,22 @@ class SyncAuthViewModel @Inject constructor(
|
|||||||
private val api: SyncAuthApi,
|
private val api: SyncAuthApi,
|
||||||
) : BaseViewModel() {
|
) : BaseViewModel() {
|
||||||
|
|
||||||
|
val onAccountAlreadyExists = SingleLiveEvent<Unit>()
|
||||||
val onTokenObtained = SingleLiveEvent<SyncAuthResult>()
|
val onTokenObtained = SingleLiveEvent<SyncAuthResult>()
|
||||||
val host = MutableLiveData("")
|
val host = MutableLiveData("")
|
||||||
|
|
||||||
private val defaultHost = context.getString(R.string.sync_host_default)
|
private val defaultHost = context.getString(R.string.sync_host_default)
|
||||||
|
|
||||||
|
init {
|
||||||
|
launchJob(Dispatchers.Default) {
|
||||||
|
val am = AccountManager.get(context)
|
||||||
|
val accounts = am.getAccountsByType(context.getString(R.string.account_type_sync))
|
||||||
|
if (accounts.isNotEmpty()) {
|
||||||
|
onAccountAlreadyExists.emitCall(Unit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun obtainToken(email: String, password: String) {
|
fun obtainToken(email: String, password: String) {
|
||||||
val hostValue = host.value.ifNullOrEmpty { defaultHost }
|
val hostValue = host.value.ifNullOrEmpty { defaultHost }
|
||||||
launchLoadingJob(Dispatchers.Default) {
|
launchLoadingJob(Dispatchers.Default) {
|
||||||
|
|||||||
@@ -182,7 +182,6 @@ class TrackWorker @AssistedInject constructor(
|
|||||||
setAutoCancel(true)
|
setAutoCancel(true)
|
||||||
setCategory(NotificationCompat.CATEGORY_PROMO)
|
setCategory(NotificationCompat.CATEGORY_PROMO)
|
||||||
setVisibility(if (manga.isNsfw) VISIBILITY_SECRET else VISIBILITY_PUBLIC)
|
setVisibility(if (manga.isNsfw) VISIBILITY_SECRET else VISIBILITY_PUBLIC)
|
||||||
color = colorPrimary
|
|
||||||
setShortcutId(manga.id.toString())
|
setShortcutId(manga.id.toString())
|
||||||
priority = NotificationCompat.PRIORITY_DEFAULT
|
priority = NotificationCompat.PRIORITY_DEFAULT
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||||
@@ -220,7 +219,6 @@ class TrackWorker @AssistedInject constructor(
|
|||||||
.setPriority(NotificationCompat.PRIORITY_MIN)
|
.setPriority(NotificationCompat.PRIORITY_MIN)
|
||||||
.setCategory(NotificationCompat.CATEGORY_SERVICE)
|
.setCategory(NotificationCompat.CATEGORY_SERVICE)
|
||||||
.setDefaults(0)
|
.setDefaults(0)
|
||||||
.setColor(ContextCompat.getColor(applicationContext, R.color.blue_primary_dark))
|
|
||||||
.setSilent(true)
|
.setSilent(true)
|
||||||
.setProgress(0, 0, true)
|
.setProgress(0, 0, true)
|
||||||
.setSmallIcon(android.R.drawable.stat_notify_sync)
|
.setSmallIcon(android.R.drawable.stat_notify_sync)
|
||||||
|
|||||||
56
app/src/main/res/layout/dialog_two_buttons.xml
Normal file
56
app/src/main/res/layout/dialog_two_buttons.xml
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="?dialogPreferredPadding">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@android:id/icon"
|
||||||
|
android:layout_width="32dp"
|
||||||
|
android:layout_height="32dp"
|
||||||
|
android:layout_gravity="center_horizontal"
|
||||||
|
app:tint="?colorPrimary"
|
||||||
|
tools:src="@drawable/ic_notification" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@android:id/title"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginVertical="18dp"
|
||||||
|
android:gravity="center_horizontal"
|
||||||
|
android:textAlignment="center"
|
||||||
|
android:textAppearance="@style/TextAppearance.Material3.LabelLarge"
|
||||||
|
android:textSize="18sp"
|
||||||
|
tools:text="@string/suggestions_summary" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@android:id/button1"
|
||||||
|
style="@style/Widget.Material3.Button.TonalButton"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:minHeight="62dp"
|
||||||
|
android:textAllCaps="true"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:shapeAppearance="?shapeAppearanceCornerMedium"
|
||||||
|
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.Material3.Corner.Top"
|
||||||
|
tools:text="Enable"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@android:id/button2"
|
||||||
|
style="@style/Widget.Material3.Button.TonalButton"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:minHeight="62dp"
|
||||||
|
android:textAllCaps="true"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:shapeAppearance="?shapeAppearanceCornerMedium"
|
||||||
|
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.Material3.Corner.Bottom"
|
||||||
|
tools:text="No thanks"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
@@ -451,4 +451,6 @@
|
|||||||
<string name="suggestion_manga">Suggestion: %s</string>
|
<string name="suggestion_manga">Suggestion: %s</string>
|
||||||
<string name="suggestions_notifications_summary">Sometimes show notifications with suggested manga</string>
|
<string name="suggestions_notifications_summary">Sometimes show notifications with suggested manga</string>
|
||||||
<string name="more">More</string>
|
<string name="more">More</string>
|
||||||
|
<string name="enable">Enable</string>
|
||||||
|
<string name="no_thanks">No thanks</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
Reference in New Issue
Block a user