Merge branch 'feature/kitsu' of github.com:KotatsuApp/Kotatsu into devel
This commit is contained in:
@@ -19,7 +19,7 @@ Kotatsu is a free and open source manga reader for Android.
|
||||
* Tablet-optimized Material You UI
|
||||
* Standard and Webtoon-optimized reader
|
||||
* Notifications about new chapters with updates feed
|
||||
* Integration with manga tracking services: Shikimori, AniList, MyAnimeList
|
||||
* Integration with manga tracking services: Shikimori, AniList, MyAnimeList, Kitsu
|
||||
* Password/fingerprint protect access to the app
|
||||
* History and favourites [synchronization](https://github.com/KotatsuApp/kotatsu-syncserver) across devices
|
||||
|
||||
|
||||
@@ -221,12 +221,24 @@
|
||||
<data android:host="shikimori-auth" />
|
||||
<data android:host="anilist-auth" />
|
||||
<data android:host="mal-auth" />
|
||||
<data android:host="kitsu-auth" />
|
||||
</intent-filter>
|
||||
|
||||
</activity>
|
||||
<activity
|
||||
android:name="org.koitharu.kotatsu.settings.sources.catalog.SourcesCatalogActivity"
|
||||
android:label="@string/sources_catalog" />
|
||||
<activity
|
||||
android:name="org.koitharu.kotatsu.scrobbling.kitsu.ui.KitsuAuthActivity"
|
||||
android:exported="false"
|
||||
android:label="@string/kitsu"
|
||||
tools:ignore="AppLinkUrlError">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:scheme="kotatsu+kitsu" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<service
|
||||
android:name="androidx.work.impl.foreground.SystemForegroundService"
|
||||
|
||||
@@ -519,6 +519,7 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) {
|
||||
const val KEY_SHIKIMORI = "shikimori"
|
||||
const val KEY_ANILIST = "anilist"
|
||||
const val KEY_MAL = "mal"
|
||||
const val KEY_KITSU = "kitsu"
|
||||
const val KEY_DOWNLOADS_WIFI = "downloads_wifi"
|
||||
const val KEY_ALL_FAVOURITES_VISIBLE = "all_favourites_visible"
|
||||
const val KEY_DOH = "doh"
|
||||
|
||||
@@ -16,6 +16,10 @@ import org.koitharu.kotatsu.scrobbling.common.data.ScrobblerStorage
|
||||
import org.koitharu.kotatsu.scrobbling.common.domain.Scrobbler
|
||||
import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerService
|
||||
import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerType
|
||||
import org.koitharu.kotatsu.scrobbling.kitsu.data.KitsuAuthenticator
|
||||
import org.koitharu.kotatsu.scrobbling.kitsu.data.KitsuInterceptor
|
||||
import org.koitharu.kotatsu.scrobbling.kitsu.data.KitsuRepository
|
||||
import org.koitharu.kotatsu.scrobbling.kitsu.domain.KitsuScrobbler
|
||||
import org.koitharu.kotatsu.scrobbling.mal.data.MALAuthenticator
|
||||
import org.koitharu.kotatsu.scrobbling.mal.data.MALInterceptor
|
||||
import org.koitharu.kotatsu.scrobbling.mal.domain.MALScrobbler
|
||||
@@ -64,6 +68,24 @@ object ScrobblingModule {
|
||||
addInterceptor(AniListInterceptor(storage))
|
||||
}.build()
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideKitsuRepository(
|
||||
@ApplicationContext context: Context,
|
||||
@ScrobblerType(ScrobblerService.KITSU) storage: ScrobblerStorage,
|
||||
database: MangaDatabase,
|
||||
authenticator: KitsuAuthenticator,
|
||||
): KitsuRepository {
|
||||
val okHttp = OkHttpClient.Builder().apply {
|
||||
authenticator(authenticator)
|
||||
addInterceptor(KitsuInterceptor(storage))
|
||||
if (BuildConfig.DEBUG) {
|
||||
addInterceptor(CurlLoggingInterceptor())
|
||||
}
|
||||
}.build()
|
||||
return KitsuRepository(context, okHttp, storage, database)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@ScrobblerType(ScrobblerService.ANILIST)
|
||||
@@ -85,11 +107,19 @@ object ScrobblingModule {
|
||||
@ApplicationContext context: Context,
|
||||
): ScrobblerStorage = ScrobblerStorage(context, ScrobblerService.MAL)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@ScrobblerType(ScrobblerService.KITSU)
|
||||
fun provideKitsuStorage(
|
||||
@ApplicationContext context: Context,
|
||||
): ScrobblerStorage = ScrobblerStorage(context, ScrobblerService.KITSU)
|
||||
|
||||
@Provides
|
||||
@ElementsIntoSet
|
||||
fun provideScrobblers(
|
||||
shikimoriScrobbler: ShikimoriScrobbler,
|
||||
aniListScrobbler: AniListScrobbler,
|
||||
malScrobbler: MALScrobbler,
|
||||
): Set<@JvmSuppressWildcards Scrobbler> = setOf(shikimoriScrobbler, aniListScrobbler, malScrobbler)
|
||||
kitsuScrobbler: KitsuScrobbler
|
||||
): Set<@JvmSuppressWildcards Scrobbler> = setOf(shikimoriScrobbler, aniListScrobbler, malScrobbler, kitsuScrobbler)
|
||||
}
|
||||
|
||||
@@ -12,5 +12,6 @@ enum class ScrobblerService(
|
||||
|
||||
SHIKIMORI(1, R.string.shikimori, R.drawable.ic_shikimori),
|
||||
ANILIST(2, R.string.anilist, R.drawable.ic_anilist),
|
||||
MAL(3, R.string.mal, R.drawable.ic_mal)
|
||||
MAL(3, R.string.mal, R.drawable.ic_mal),
|
||||
KITSU(4, R.string.kitsu, R.drawable.ic_kitsu)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package org.koitharu.kotatsu.scrobbling.kitsu.data
|
||||
|
||||
import okhttp3.Authenticator
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import okhttp3.Route
|
||||
import org.koitharu.kotatsu.scrobbling.common.data.ScrobblerStorage
|
||||
import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerService
|
||||
import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerType
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Provider
|
||||
|
||||
class KitsuAuthenticator @Inject constructor(
|
||||
@ScrobblerType(ScrobblerService.KITSU) private val storage: ScrobblerStorage,
|
||||
private val repositoryProvider: Provider<KitsuRepository>,
|
||||
) : Authenticator {
|
||||
|
||||
override fun authenticate(route: Route?, response: Response): Request? {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package org.koitharu.kotatsu.scrobbling.kitsu.data
|
||||
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.Response
|
||||
import org.koitharu.kotatsu.core.network.CommonHeaders
|
||||
import org.koitharu.kotatsu.scrobbling.common.data.ScrobblerStorage
|
||||
|
||||
private const val JSON = "application/json"
|
||||
|
||||
class KitsuInterceptor(private val storage: ScrobblerStorage) : Interceptor {
|
||||
|
||||
override fun intercept(chain: Interceptor.Chain): Response {
|
||||
val sourceRequest = chain.request()
|
||||
val request = sourceRequest.newBuilder()
|
||||
request.header(CommonHeaders.CONTENT_TYPE, JSON)
|
||||
request.header(CommonHeaders.ACCEPT, JSON)
|
||||
if (!sourceRequest.url.pathSegments.contains("oauth")) {
|
||||
storage.accessToken?.let {
|
||||
request.header(CommonHeaders.AUTHORIZATION, "Bearer $it")
|
||||
}
|
||||
}
|
||||
return chain.proceed(request.build())
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package org.koitharu.kotatsu.scrobbling.kitsu.data
|
||||
|
||||
import android.content.Context
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import okhttp3.FormBody
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.db.MangaDatabase
|
||||
import org.koitharu.kotatsu.parsers.model.MangaChapter
|
||||
import org.koitharu.kotatsu.parsers.util.await
|
||||
import org.koitharu.kotatsu.parsers.util.parseJson
|
||||
import org.koitharu.kotatsu.scrobbling.common.data.ScrobblerRepository
|
||||
import org.koitharu.kotatsu.scrobbling.common.data.ScrobblerStorage
|
||||
import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerManga
|
||||
import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerMangaInfo
|
||||
import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerService
|
||||
import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerUser
|
||||
|
||||
private const val BASE_WEB_URL = "https://kitsu.io"
|
||||
|
||||
class KitsuRepository(
|
||||
@ApplicationContext context: Context,
|
||||
private val okHttp: OkHttpClient,
|
||||
private val storage: ScrobblerStorage,
|
||||
private val db: MangaDatabase,
|
||||
) : ScrobblerRepository {
|
||||
|
||||
private val clientId = context.getString(R.string.kitsu_clientId)
|
||||
private val clientSecret = context.getString(R.string.kitsu_clientSecret)
|
||||
|
||||
override val oauthUrl: String = "kotatsu+kitsu://auth"
|
||||
|
||||
override val isAuthorized: Boolean
|
||||
get() = storage.accessToken != null
|
||||
|
||||
override val cachedUser: ScrobblerUser?
|
||||
get() {
|
||||
return storage.user
|
||||
}
|
||||
|
||||
override suspend fun authorize(code: String?) {
|
||||
val body = FormBody.Builder()
|
||||
if (code != null) {
|
||||
body.add("grant_type", "password")
|
||||
body.add("username", "test@test")
|
||||
body.add("password", "test")
|
||||
} else {
|
||||
body.add("grant_type", "refresh_token")
|
||||
body.add("refresh_token", checkNotNull(storage.refreshToken))
|
||||
}
|
||||
val request = Request.Builder()
|
||||
.post(body.build())
|
||||
.url("${BASE_WEB_URL}/api/oauth/token")
|
||||
val response = okHttp.newCall(request.build()).await().parseJson()
|
||||
storage.accessToken = response.getString("access_token")
|
||||
storage.refreshToken = response.getString("refresh_token")
|
||||
}
|
||||
|
||||
override suspend fun loadUser(): ScrobblerUser {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun logout() {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override suspend fun unregister(mangaId: Long) {
|
||||
return db.getScrobblingDao().delete(ScrobblerService.KITSU.id, mangaId)
|
||||
}
|
||||
|
||||
override suspend fun findManga(query: String, offset: Int): List<ScrobblerManga> {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override suspend fun getMangaInfo(id: Long): ScrobblerMangaInfo {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override suspend fun createRate(mangaId: Long, scrobblerMangaId: Long) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override suspend fun updateRate(rateId: Int, mangaId: Long, chapter: MangaChapter) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override suspend fun updateRate(rateId: Int, mangaId: Long, rating: Float, status: String?, comment: String?) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package org.koitharu.kotatsu.scrobbling.kitsu.domain
|
||||
|
||||
import org.koitharu.kotatsu.core.db.MangaDatabase
|
||||
import org.koitharu.kotatsu.scrobbling.common.domain.Scrobbler
|
||||
import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerService
|
||||
import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblingStatus
|
||||
import org.koitharu.kotatsu.scrobbling.kitsu.data.KitsuRepository
|
||||
import javax.inject.Inject
|
||||
|
||||
class KitsuScrobbler @Inject constructor(
|
||||
private val repository: KitsuRepository,
|
||||
db: MangaDatabase,
|
||||
) : Scrobbler(db, ScrobblerService.KITSU, repository) {
|
||||
|
||||
init {
|
||||
statuses[ScrobblingStatus.PLANNED] = "planned"
|
||||
statuses[ScrobblingStatus.READING] = "current"
|
||||
statuses[ScrobblingStatus.COMPLETED] = "completed"
|
||||
statuses[ScrobblingStatus.ON_HOLD] = "on_hold"
|
||||
statuses[ScrobblingStatus.DROPPED] = "dropped"
|
||||
}
|
||||
|
||||
override suspend fun updateScrobblingInfo(
|
||||
mangaId: Long,
|
||||
rating: Float,
|
||||
status: ScrobblingStatus?,
|
||||
comment: String?
|
||||
) {
|
||||
val entity = db.getScrobblingDao().find(scrobblerService.id, mangaId)
|
||||
requireNotNull(entity) { "Scrobbling info for manga $mangaId not found" }
|
||||
repository.updateRate(
|
||||
rateId = entity.id,
|
||||
mangaId = entity.mangaId,
|
||||
rating = rating,
|
||||
status = statuses[status],
|
||||
comment = comment,
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package org.koitharu.kotatsu.scrobbling.kitsu.ui
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.core.graphics.Insets
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.ui.BaseActivity
|
||||
import org.koitharu.kotatsu.databinding.ActivityKitsuAuthBinding
|
||||
|
||||
class KitsuAuthActivity : BaseActivity<ActivityKitsuAuthBinding>() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(ActivityKitsuAuthBinding.inflate(layoutInflater))
|
||||
}
|
||||
|
||||
override fun onWindowInsetsChanged(insets: Insets) {
|
||||
val basePadding = resources.getDimensionPixelOffset(R.dimen.screen_padding)
|
||||
viewBinding.root.setPadding(
|
||||
basePadding + insets.left,
|
||||
basePadding + insets.top,
|
||||
basePadding + insets.right,
|
||||
basePadding + insets.bottom,
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun newIntent(context: Context) = Intent(context, KitsuAuthActivity::class.java)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -19,8 +19,10 @@ import org.koitharu.kotatsu.core.ui.BasePreferenceFragment
|
||||
import org.koitharu.kotatsu.core.util.ext.getDisplayMessage
|
||||
import org.koitharu.kotatsu.core.util.ext.viewLifecycleScope
|
||||
import org.koitharu.kotatsu.scrobbling.anilist.data.AniListRepository
|
||||
import org.koitharu.kotatsu.scrobbling.common.data.ScrobblerRepository
|
||||
import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblerService
|
||||
import org.koitharu.kotatsu.scrobbling.common.ui.config.ScrobblerConfigActivity
|
||||
import org.koitharu.kotatsu.scrobbling.kitsu.data.KitsuRepository
|
||||
import org.koitharu.kotatsu.scrobbling.mal.data.MALRepository
|
||||
import org.koitharu.kotatsu.scrobbling.shikimori.data.ShikimoriRepository
|
||||
import org.koitharu.kotatsu.sync.domain.SyncController
|
||||
@@ -41,6 +43,9 @@ class ServicesSettingsFragment : BasePreferenceFragment(R.string.services),
|
||||
@Inject
|
||||
lateinit var malRepository: MALRepository
|
||||
|
||||
@Inject
|
||||
lateinit var kitsuRepository: KitsuRepository
|
||||
|
||||
@Inject
|
||||
lateinit var syncController: SyncController
|
||||
|
||||
@@ -64,6 +69,7 @@ class ServicesSettingsFragment : BasePreferenceFragment(R.string.services),
|
||||
bindScrobblerSummary(AppSettings.KEY_SHIKIMORI, shikimoriRepository)
|
||||
bindScrobblerSummary(AppSettings.KEY_ANILIST, aniListRepository)
|
||||
bindScrobblerSummary(AppSettings.KEY_MAL, malRepository)
|
||||
bindScrobblerSummary(AppSettings.KEY_KITSU, kitsuRepository)
|
||||
bindSyncSummary()
|
||||
}
|
||||
|
||||
@@ -103,6 +109,15 @@ class ServicesSettingsFragment : BasePreferenceFragment(R.string.services),
|
||||
true
|
||||
}
|
||||
|
||||
AppSettings.KEY_KITSU -> {
|
||||
if (!kitsuRepository.isAuthorized) {
|
||||
launchScrobblerAuth(kitsuRepository)
|
||||
} else {
|
||||
startActivity(ScrobblerConfigActivity.newIntent(preference.context, ScrobblerService.KITSU))
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
AppSettings.KEY_SYNC -> {
|
||||
val am = AccountManager.get(requireContext())
|
||||
val accountType = getString(R.string.account_type_sync)
|
||||
@@ -121,7 +136,7 @@ class ServicesSettingsFragment : BasePreferenceFragment(R.string.services),
|
||||
|
||||
private fun bindScrobblerSummary(
|
||||
key: String,
|
||||
repository: org.koitharu.kotatsu.scrobbling.common.data.ScrobblerRepository
|
||||
repository: ScrobblerRepository
|
||||
) {
|
||||
val pref = findPreference<Preference>(key) ?: return
|
||||
if (!repository.isAuthorized) {
|
||||
@@ -147,7 +162,7 @@ class ServicesSettingsFragment : BasePreferenceFragment(R.string.services),
|
||||
}
|
||||
}
|
||||
|
||||
private fun launchScrobblerAuth(repository: org.koitharu.kotatsu.scrobbling.common.data.ScrobblerRepository) {
|
||||
private fun launchScrobblerAuth(repository: ScrobblerRepository) {
|
||||
runCatching {
|
||||
val intent = Intent(Intent.ACTION_VIEW)
|
||||
intent.data = Uri.parse(repository.oauthUrl)
|
||||
|
||||
10
app/src/main/res/drawable/ic_kitsu.xml
Normal file
10
app/src/main/res/drawable/ic_kitsu.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="?colorControlNormal"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M1.429,5.441a12.478,12.478 0,0 0,1.916 2.056c0.011,0.011 0.022,0.011 0.022,0.022 0.452,0.387 1.313,0.947 1.937,1.173 0,0 3.886,1.496 4.091,1.582a1.4,1.4 0,0 0,0.237 0.075,0.694 0.694,0 0,0 0.808,-0.549c0.011,-0.065 0.022,-0.172 0.022,-0.248L10.462,5.161c0.011,-0.667 -0.205,-1.679 -0.398,-2.239 0,-0.011 -0.011,-0.022 -0.011,-0.032A11.979,11.979 0,0 0,8.824 0.36L8.781,0.285a0.697,0.697 0,0 0,-0.958 -0.162c-0.054,0.032 -0.086,0.075 -0.129,0.119L7.608,0.36a4.743,4.743 0,0 0,-0.786 3.412,8.212 8.212,0 0,0 -0.775,0.463c-0.043,0.032 -0.42,0.291 -0.71,0.56A4.803,4.803 0,0 0,1.87 4.3c-0.043,0.011 -0.097,0.021 -0.14,0.032 -0.054,0.022 -0.107,0.043 -0.151,0.076a0.702,0.702 0,0 0,-0.193 0.958l0.043,0.075zM8.222,1.07c0.366,0.614 0.678,1.249 0.925,1.917 -0.495,0.086 -0.98,0.215 -1.453,0.388a3.918,3.918 0,0 1,0.528 -2.305zM4.658,5.463a7.467,7.467 0,0 0,-0.893 1.216,11.68 11.68,0 0,1 -1.453,-1.55 3.825,3.825 0,0 1,2.346 0.334zM17.706,5.161a7.673,7.673 0,0 0,-2.347 -0.474,7.583 7.583,0 0,0 -3.811,0.818l-0.215,0.108v3.918c0,0.054 0,0.258 -0.032,0.431a1.535,1.535 0,0 1,-0.646 0.98,1.545 1.545,0 0,1 -1.152,0.247 2.618,2.618 0,0 1,-0.409 -0.118,747.6 747.6,0 0,1 -3.402,-1.313 8.9,8.9 0,0 0,-0.323 -0.129,30.597 30.597,0 0,0 -3.822,3.832l-0.075,0.086a0.698,0.698 0,0 0,0.538 1.098,0.676 0.676,0 0,0 0.42,-0.118c0.011,-0.011 0.022,-0.022 0.043,-0.032 1.313,-0.947 2.756,-1.712 4.284,-2.325a0.7,0.7 0,0 1,0.818 0.13,0.704 0.704,0 0,1 0.054,0.915l-0.237,0.388a20.277,20.277 0,0 0,-1.97 4.306l-0.032,0.129a0.646,0.646 0,0 0,0.108 0.538,0.713 0.713,0 0,0 0.549,0.301 0.657,0.657 0,0 0,0.42 -0.118c0.054,-0.043 0.108,-0.086 0.151,-0.14l0.043,-0.065a18.95,18.95 0,0 1,1.765 -2.153,20.156 20.156,0 0,1 10.797,-6.018c0.032,-0.011 0.065,-0.011 0.097,-0.011 0.237,0.011 0.42,0.215 0.409,0.452a0.424,0.424 0,0 1,-0.344 0.398c-3.908,0.829 -10.948,5.469 -8.483,12.208 0.043,0.108 0.075,0.172 0.129,0.269a0.71,0.71 0,0 0,0.538 0.301,0.742 0.742,0 0,0 0.657,-0.398c0.398,-0.754 1.152,-1.593 3.326,-2.497 6.061,-2.508 7.062,-6.093 7.17,-8.364v-0.129a7.716,7.716 0,0 0,-5.016 -7.451zM11.623,22.923c-0.56,-1.669 -0.506,-3.283 0.151,-4.823 1.26,2.035 3.456,2.207 3.456,2.207 -2.25,0.937 -3.133,1.863 -3.607,2.616z"
|
||||
android:fillColor="#000000"/>
|
||||
</vector>
|
||||
130
app/src/main/res/layout/activity_kitsu_auth.xml
Normal file
130
app/src/main/res/layout/activity_kitsu_auth.xml
Normal file
@@ -0,0 +1,130 @@
|
||||
<?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="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:padding="@dimen/screen_padding">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:drawablePadding="16dp"
|
||||
android:gravity="center_horizontal"
|
||||
android:text="@string/kitsu"
|
||||
android:textAppearance="?textAppearanceHeadline5"
|
||||
app:drawableTint="?colorPrimary"
|
||||
app:drawableTopCompat="@drawable/ic_kitsu"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView_subtitle"
|
||||
android:layout_width="0dp"
|
||||
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
|
||||
android:id="@+id/layout_email"
|
||||
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/textView_subtitle"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentEnd="true"
|
||||
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:autofillHints="emailAddress"
|
||||
android:imeOptions="actionDone"
|
||||
android:inputType="textEmailAddress"
|
||||
android:singleLine="true"
|
||||
android:textSize="16sp"
|
||||
tools:hint="Email" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/layout_password"
|
||||
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/layout_email"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_marginTop="8dp"
|
||||
app:endIconMode="password_toggle"
|
||||
app:errorIconDrawable="@null"
|
||||
app:hintEnabled="false">
|
||||
|
||||
<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:imeOptions="actionDone"
|
||||
android:inputType="textPassword"
|
||||
android:maxLength="24"
|
||||
android:singleLine="true"
|
||||
android:textSize="16sp"
|
||||
tools:hint="Password" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<Button
|
||||
android:id="@+id/button_cancel"
|
||||
style="@style/Widget.Material3.Button.OutlinedButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:text="@android:string/cancel" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/button_done"
|
||||
style="@style/Widget.Material3.Button.TonalButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:enabled="false"
|
||||
android:text="@string/done"
|
||||
tools:ignore="RelativeOverlap" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/layout_progress"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="gone">
|
||||
|
||||
<com.google.android.material.progressindicator.CircularProgressIndicator
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:indeterminate="true" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -12,6 +12,8 @@
|
||||
<string name="anilist_clientId" translatable="false">9887</string>
|
||||
<string name="anilist_clientSecret" translatable="false">wrMqFosItQWsmB8dtAHfIFPDt15FfQi2ZGiKkJoW</string>
|
||||
<string name="mal_clientId" translatable="false">6cd8e6349e9a36bc1fc1ab97703c9fd1</string>
|
||||
<string name="kitsu_clientId" translatable="false">dd031b32d2f56c990b1425efe6c42ad847e7fe3ab46bf1299f05ecd856bdb7dd</string>
|
||||
<string name="kitsu_clientSecret" translatable="false">54d7307928f63414defd96399fc31ba847961ceaecef3a5fd93144e960c0e151</string>
|
||||
<string name="acra_login" translatable="false">zPALLBPdpn5mnCB4</string>
|
||||
<string name="acra_password" translatable="false">kgpuhoNJpSsQDCwu</string>
|
||||
<string name="sync_authority_history" translatable="false">org.koitharu.kotatsu.history</string>
|
||||
|
||||
@@ -420,6 +420,8 @@
|
||||
<string name="port">Port</string>
|
||||
<string name="proxy">Proxy</string>
|
||||
<string name="invalid_value_message">Invalid value</string>
|
||||
<string name="kitsu" translatable="false">Kitsu</string>
|
||||
<string name="email_password_enter_hint">Enter your email and password to continue</string>
|
||||
<string name="manga_branch_title_template">%1$s (%2$s)</string>
|
||||
<string name="downloaded">Downloaded</string>
|
||||
<string name="images_proxy_title">Images optimization proxy</string>
|
||||
|
||||
@@ -30,21 +30,26 @@
|
||||
|
||||
<PreferenceCategory android:title="@string/tracking">
|
||||
|
||||
<Preference
|
||||
android:key="shikimori"
|
||||
android:title="@string/shikimori"
|
||||
app:icon="@drawable/ic_shikimori" />
|
||||
|
||||
<Preference
|
||||
android:key="anilist"
|
||||
android:title="@string/anilist"
|
||||
app:icon="@drawable/ic_anilist" />
|
||||
|
||||
<Preference
|
||||
android:key="kitsu"
|
||||
android:title="@string/kitsu"
|
||||
app:icon="@drawable/ic_kitsu" />
|
||||
|
||||
<Preference
|
||||
android:key="mal"
|
||||
android:title="@string/mal"
|
||||
app:icon="@drawable/ic_mal" />
|
||||
|
||||
<Preference
|
||||
android:key="shikimori"
|
||||
android:title="@string/shikimori"
|
||||
app:icon="@drawable/ic_shikimori" />
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
</PreferenceScreen>
|
||||
|
||||
Reference in New Issue
Block a user