Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Hikka Tracker Integration #1386

Open
wants to merge 18 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@
<data android:host="bangumi-auth" />
<data android:host="myanimelist-auth" />
<data android:host="shikimori-auth" />
<data android:host="hikka-auth" />
</intent-filter>
</activity>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import eu.kanade.tachiyomi.data.track.Tracker
import eu.kanade.tachiyomi.data.track.TrackerManager
import eu.kanade.tachiyomi.data.track.anilist.AnilistApi
import eu.kanade.tachiyomi.data.track.bangumi.BangumiApi
import eu.kanade.tachiyomi.data.track.hikka.HikkaApi
import eu.kanade.tachiyomi.data.track.myanimelist.MyAnimeListApi
import eu.kanade.tachiyomi.data.track.shikimori.ShikimoriApi
import eu.kanade.tachiyomi.util.system.openInBrowser
Expand Down Expand Up @@ -164,6 +165,12 @@ object SettingsTrackingScreen : SearchableSettings {
login = { context.openInBrowser(BangumiApi.authUrl(), forceDefaultBrowser = true) },
logout = { dialog = LogoutDialog(trackerManager.bangumi) },
),
Preference.PreferenceItem.TrackerPreference(
title = trackerManager.hikka.name,
tracker = trackerManager.hikka,
login = { context.openInBrowser(HikkaApi.authUrl(), forceDefaultBrowser = true) },
logout = { dialog = LogoutDialog(trackerManager.hikka) },
),
Preference.PreferenceItem.InfoPreference(stringResource(MR.strings.tracking_info)),
),
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.data.track

import eu.kanade.tachiyomi.data.track.anilist.Anilist
import eu.kanade.tachiyomi.data.track.bangumi.Bangumi
import eu.kanade.tachiyomi.data.track.hikka.Hikka
import eu.kanade.tachiyomi.data.track.kavita.Kavita
import eu.kanade.tachiyomi.data.track.kitsu.Kitsu
import eu.kanade.tachiyomi.data.track.komga.Komga
Expand All @@ -28,8 +29,9 @@ class TrackerManager {
val mangaUpdates = MangaUpdates(7L)
val kavita = Kavita(KAVITA)
val suwayomi = Suwayomi(9L)
val hikka = Hikka(10L)

val trackers = listOf(myAnimeList, aniList, kitsu, shikimori, bangumi, komga, mangaUpdates, kavita, suwayomi)
val trackers = listOf(myAnimeList, aniList, kitsu, shikimori, bangumi, komga, mangaUpdates, kavita, suwayomi, hikka)

fun loggedInTrackers() = trackers.filter { it.isLoggedIn }

Expand Down
163 changes: 163 additions & 0 deletions app/src/main/java/eu/kanade/tachiyomi/data/track/hikka/Hikka.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
package eu.kanade.tachiyomi.data.track.hikka

import android.graphics.Color
import dev.icerock.moko.resources.StringResource
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.track.BaseTracker
import eu.kanade.tachiyomi.data.track.DeletableTracker
import eu.kanade.tachiyomi.data.track.hikka.dto.HKOAuth
import eu.kanade.tachiyomi.data.track.model.TrackSearch
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import tachiyomi.i18n.MR
import uy.kohesive.injekt.injectLazy
import tachiyomi.domain.track.model.Track as DomainTrack

class Hikka(id: Long) : BaseTracker(id, "Hikka"), DeletableTracker {

companion object {
const val READING = 0L
const val COMPLETED = 1L
const val ON_HOLD = 2L
const val DROPPED = 3L
const val PLAN_TO_READ = 4L
const val REREADING = 5L

private val SCORE_LIST = IntRange(0, 10)
.map(Int::toString)
.toImmutableList()
}

private val json: Json by injectLazy()

private val interceptor by lazy { HikkaInterceptor(this) }
private val api by lazy { HikkaApi(id, client, interceptor) }

override fun getLogoColor(): Int {
return Color.rgb(0, 0, 0)
}

override fun getLogo(): Int {
return R.drawable.ic_tracker_hikka
}

override fun getStatusList(): List<Long> {
return listOf(
READING,
COMPLETED,
ON_HOLD,
DROPPED,
PLAN_TO_READ,
REREADING,
)
}

override fun getStatus(status: Long): StringResource? = when (status) {
READING -> MR.strings.reading
PLAN_TO_READ -> MR.strings.plan_to_read
COMPLETED -> MR.strings.completed
ON_HOLD -> MR.strings.on_hold
DROPPED -> MR.strings.dropped
REREADING -> MR.strings.repeating
else -> null
}

override fun getReadingStatus(): Long {
return READING
}

override fun getRereadingStatus(): Long {
return REREADING
}

override fun getCompletionStatus(): Long {
return COMPLETED
}

override fun getScoreList(): ImmutableList<String> {
return SCORE_LIST
}

override fun displayScore(track: DomainTrack): String {
return track.score.toInt().toString()
}

override suspend fun update(
track: Track,
didReadChapter: Boolean,
): Track {
if (track.status != COMPLETED) {
if (didReadChapter) {
if (track.last_chapter_read.toLong() == track.total_chapters && track.total_chapters > 0) {
track.status = COMPLETED
} else if (track.status != REREADING) {
track.status = READING
}
}
}
return api.updateUserManga(track)
}

override suspend fun bind(track: Track, hasReadChapters: Boolean): Track {
val remoteTrack = api.getManga(track)

track.copyPersonalFrom(remoteTrack)
track.library_id = remoteTrack.library_id

if (track.status != COMPLETED) {
val isRereading = track.status == REREADING
track.status = if (!isRereading && hasReadChapters) READING else track.status
}

return update(track)
}

override suspend fun search(query: String): List<TrackSearch> {
return api.searchManga(query)
}

override suspend fun refresh(track: Track): Track {
val remoteTrack = api.updateUserManga(track)
track.copyPersonalFrom(remoteTrack)
track.total_chapters = remoteTrack.total_chapters
return track
}

override suspend fun login(username: String, password: String) = login(password)

suspend fun login(reference: String) {
try {
val oauth = api.accessToken(reference)
interceptor.setAuth(oauth)
val user = api.getCurrentUser()
saveCredentials(user.reference, oauth.accessToken)
} catch (e: Throwable) {
logout()
}
}

override suspend fun delete(track: DomainTrack) {
api.deleteUserManga(track)
}

override fun logout() {
super.logout()
trackPreferences.trackToken(this).delete()
interceptor.setAuth(null)
}

fun saveOAuth(oAuth: HKOAuth?) {
trackPreferences.trackToken(this).set(json.encodeToString(oAuth))
}

fun loadOAuth(): HKOAuth? {
return try {
json.decodeFromString<HKOAuth>(trackPreferences.trackToken(this).get())
} catch (e: Exception) {
null
}
}
}
Loading