diff --git a/app/build.gradle.kts b/app/build.gradle.kts index ef8dc37deb..81174e1aef 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -196,7 +196,7 @@ dependencies { // UI implementation("com.dmitrymalkovich.android:material-design-dimens:1.4") - implementation("com.github.dmytrodanylyk.android-process-button:library:1.0.4") + implementation("br.com.simplepass:loading-button-android:2.2.0") implementation("com.mikepenz:fastadapter:${Versions.FASTADAPTER}") implementation("com.mikepenz:fastadapter-extensions-binding:${Versions.FASTADAPTER}") implementation("eu.davidea:flexible-adapter:5.1.0") diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/TrackService.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/TrackService.kt index 912873c924..99ca915512 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/TrackService.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/TrackService.kt @@ -13,7 +13,7 @@ abstract class TrackService(val id: Int) { val preferences: PreferencesHelper by injectLazy() val networkService: NetworkHelper by injectLazy() - + open fun canRemoveFromService() = false open val client: OkHttpClient get() = networkService.client @@ -51,6 +51,8 @@ abstract class TrackService(val id: Int) { abstract suspend fun login(username: String, password: String): Boolean + open suspend fun removeFromService(track: Track): Boolean = false + @CallSuper open fun logout() { preferences.setTrackCredentials(this, "", "") diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/Anilist.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/Anilist.kt index 843fd3a7e5..f4f76962ec 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/Anilist.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/Anilist.kt @@ -150,6 +150,12 @@ class Anilist(private val context: Context, id: Int) : TrackService(id) { } } + override fun canRemoveFromService(): Boolean = true + + override suspend fun removeFromService(track: Track): Boolean { + return api.remove(track) + } + override suspend fun search(query: String) = api.search(query) override suspend fun refresh(track: Track): Track { diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/AnilistApi.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/AnilistApi.kt index bc4598fd07..96c9f3a609 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/AnilistApi.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/AnilistApi.kt @@ -19,6 +19,7 @@ import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.Response +import timber.log.Timber import java.util.Calendar import java.util.concurrent.TimeUnit @@ -128,6 +129,29 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) { } } + suspend fun remove(track: Track): Boolean { + return withContext(Dispatchers.IO) { + try { + + val variables = jsonObject( + "listId" to track.library_id + ) + val payload = jsonObject( + "query" to deleteFromLibraryQuery(), + "variables" to variables + ) + + val body = payload.toString().toRequestBody(MediaType.jsonType()) + val request = Request.Builder().url(apiUrl).post(body).build() + val result = authClient.newCall(request).execute() + return@withContext true + } catch (e: Exception) { + Timber.w(e) + } + return@withContext false + } + } + fun createOAuth(token: String): OAuth { return OAuth( token, @@ -222,6 +246,14 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) { |} |""".trimMargin() + fun deleteFromLibraryQuery() = """ + |mutation DeleteManga(${'$'}listId: Int) { + |DeleteMediaListEntry (id: ${'$'}listId) { + |deleted + + |} + |}""".trimMargin() + fun updateInLibraryQuery() = """ |mutation UpdateManga(${'$'}listId: Int, ${'$'}progress: Int, ${'$'}status: MediaListStatus, ${'$'}score: Int) { |SaveMediaListEntry (id: ${'$'}listId, progress: ${'$'}progress, status: ${'$'}status, scoreRaw: ${'$'}score) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/kitsu/Kitsu.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/kitsu/Kitsu.kt index 542e0c2538..5cfde9e2ed 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/kitsu/Kitsu.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/kitsu/Kitsu.kt @@ -103,6 +103,12 @@ class Kitsu(private val context: Context, id: Int) : TrackService(id) { } } + override fun canRemoveFromService() = true + + override suspend fun removeFromService(track: Track): Boolean { + return api.remove(track) + } + override suspend fun search(query: String): List { return api.search(query) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/kitsu/KitsuApi.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/kitsu/KitsuApi.kt index 76aad53386..c0f5bacc3e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/kitsu/KitsuApi.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/kitsu/KitsuApi.kt @@ -16,6 +16,7 @@ import okhttp3.OkHttpClient import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory import retrofit2.http.Body +import retrofit2.http.DELETE import retrofit2.http.Field import retrofit2.http.FormUrlEncoded import retrofit2.http.GET @@ -25,6 +26,7 @@ import retrofit2.http.PATCH import retrofit2.http.POST import retrofit2.http.Path import retrofit2.http.Query +import timber.log.Timber class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor) { @@ -97,6 +99,16 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor) return track } + suspend fun remove(track: Track): Boolean { + try { + rest.deleteLibManga(track.media_id) + return true + } catch (e: Exception) { + Timber.w(e) + } + return false + } + suspend fun search(query: String): List { val key = searchRest.getKey()["media"].asJsonObject["key"].string return algoliaSearch(key, query) @@ -156,6 +168,12 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor) @Body data: JsonObject ): JsonObject + @Headers("Content-Type: application/vnd.api+json") + @DELETE("library-entries/{id}") + suspend fun deleteLibManga( + @Path("id") remoteId: Int + ): JsonObject + @Headers("Content-Type: application/vnd.api+json") @PATCH("library-entries/{id}") suspend fun updateLibManga( diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeList.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeList.kt index 7fc39d2fd5..10943d24a6 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeList.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeList.kt @@ -79,6 +79,12 @@ class MyAnimeList(private val context: Context, id: Int) : TrackService(id) { return track } + override fun canRemoveFromService(): Boolean = true + + override suspend fun removeFromService(track: Track): Boolean { + return api.remove(track) + } + override suspend fun search(query: String): List { return api.search(query) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListApi.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListApi.kt index af94d88eb6..3fd51a0454 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListApi.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListApi.kt @@ -23,6 +23,7 @@ import org.jsoup.Jsoup import org.jsoup.nodes.Document import org.jsoup.nodes.Element import org.jsoup.parser.Parser +import timber.log.Timber class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListInterceptor) { @@ -40,18 +41,18 @@ class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListI .select("tbody").select("tr").drop(1) matches.filter { row -> row.select(TD)[2].text() != "Novel" }.map { row -> - TrackSearch.create(TrackManager.MYANIMELIST).apply { - title = row.searchTitle() - media_id = row.searchMediaId() - total_chapters = row.searchTotalChapters() - summary = row.searchSummary() - cover_url = row.searchCoverUrl() - tracking_url = mangaUrl(media_id) - publishing_status = row.searchPublishingStatus() - publishing_type = row.searchPublishingType() - start_date = row.searchStartDate() - } - }.toList() + TrackSearch.create(TrackManager.MYANIMELIST).apply { + title = row.searchTitle() + media_id = row.searchMediaId() + total_chapters = row.searchTotalChapters() + summary = row.searchSummary() + cover_url = row.searchCoverUrl() + tracking_url = mangaUrl(media_id) + publishing_status = row.searchPublishingStatus() + publishing_type = row.searchPublishingType() + start_date = row.searchStartDate() + } + }.toList() } } } @@ -71,6 +72,16 @@ class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListI return track } + suspend fun remove(track: Track): Boolean { + try { + authClient.newCall(POST(url = removeUrl(track.media_id))).await() + return true + } catch (e: Exception) { + Timber.w(e) + } + return false + } + suspend fun findLibManga(track: Track): Track? { return withContext(Dispatchers.IO) { val response = authClient.newCall(GET(url = listEntryUrl(track.media_id))).await() @@ -133,16 +144,16 @@ class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListI val results = getListXml(getListUrl()).select("manga") return results.map { - TrackSearch.create(TrackManager.MYANIMELIST).apply { - title = it.selectText("manga_title")!! - media_id = it.selectInt("manga_mangadb_id") - last_chapter_read = it.selectInt("my_read_chapters") - status = getStatus(it.selectText("my_status")!!) - score = it.selectInt("my_score").toFloat() - total_chapters = it.selectInt("manga_chapters") - tracking_url = mangaUrl(media_id) - } - }.toList() + TrackSearch.create(TrackManager.MYANIMELIST).apply { + title = it.selectText("manga_title")!! + media_id = it.selectInt("manga_mangadb_id") + last_chapter_read = it.selectInt("my_read_chapters") + status = getStatus(it.selectText("my_status")!!) + score = it.selectInt("my_score").toFloat() + total_chapters = it.selectInt("manga_chapters") + tracking_url = mangaUrl(media_id) + } + }.toList() } private suspend fun getListUrl(): String { @@ -188,6 +199,9 @@ class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListI private fun updateUrl() = Uri.parse(baseModifyListUrl).buildUpon().appendPath("edit.json").toString() + private fun removeUrl(mediaId: Int) = Uri.parse(baseModifyListUrl).buildUpon().appendPath(mediaId.toString()) + .appendPath("delete").toString() + private fun addUrl() = Uri.parse(baseModifyListUrl).buildUpon().appendPath("add.json").toString() diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadButton.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadButton.kt index 971556a708..beecc88a4a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadButton.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadButton.kt @@ -24,7 +24,7 @@ class DownloadButton @JvmOverloads constructor(context: Context, attrs: Attribut private val downloadedColor = ContextCompat.getColor(context, R.color.download) private val errorColor = ContextCompat.getColor(context, - R.color.red_error) + R.color.material_red_500) private val filledCircle = ContextCompat.getDrawable(context, R.drawable.filled_circle)?.mutate() private val borderCircle = ContextCompat.getDrawable(context, diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsPresenter.kt index ec39de7ff3..8fcf37389f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsPresenter.kt @@ -850,13 +850,18 @@ class MangaDetailsPresenter( } fetchTracks() } - } else { - scope.launch { - withContext(Dispatchers.IO) { - db.deleteTrackForManga(manga, service).executeAsBlocking() + } + } + + fun removeTracker(trackItem: TrackItem, removeFromService: Boolean) { + scope.launch { + withContext(Dispatchers.IO) { + db.deleteTrackForManga(manga, trackItem.service).executeAsBlocking() + if (removeFromService && trackItem.service.canRemoveFromService()) { + trackItem.service.removeFromService(trackItem.track!!) } - fetchTracks() } + fetchTracks() } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackAdapter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackAdapter.kt index f4c6d54ff1..4d8ea28218 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackAdapter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackAdapter.kt @@ -45,5 +45,6 @@ class TrackAdapter(controller: OnClickListener) : RecyclerView.Adapter : DialogController + where T : TrackRemoveDialog.Listener { + + private val item: TrackItem + private lateinit var listener: Listener + + constructor(target: T, item: TrackItem) : super(Bundle().apply { + putSerializable(KEY_ITEM_TRACK, item.track) + }) { + listener = target + this.item = item + } + + @Suppress("unused") + constructor(bundle: Bundle) : super(bundle) { + val track = bundle.getSerializable(KEY_ITEM_TRACK) as Track + val service = Injekt.get().getService(track.sync_id)!! + item = TrackItem(track, service) + } + + override fun onCreateDialog(savedViewState: Bundle?): Dialog { + val item = item + + val dialog = MaterialDialog(activity!!) + .title(R.string.remove_tracking) + .negativeButton(android.R.string.cancel) + + if (item.service.canRemoveFromService()) { + dialog.checkBoxPrompt( + text = activity!!.getString( + R.string.remove_tracking_from_, item.service.name + ), onToggle = null + ).positiveButton(android.R.string.ok) { listener.removeTracker(item, it.isCheckPromptChecked()) } + dialog.getCheckBoxPrompt().textSize = 16f + } else { + dialog.positiveButton(android.R.string.ok) { listener.removeTracker(item, false) } + } + + return dialog + } + + interface Listener { + fun removeTracker(item: TrackItem, fromServiceAlso: Boolean) + } + + private companion object { + const val KEY_ITEM_TRACK = "TrackRemoveDialog.item.track" + } +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackSearchDialog.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackSearchDialog.kt index f418f48625..cd57e1fbad 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackSearchDialog.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackSearchDialog.kt @@ -40,6 +40,7 @@ class TrackSearchDialog : DialogController { private lateinit var bottomSheet: TrackingBottomSheet private var wasPreviouslyTracked: Boolean = false + private lateinit var presenter: MangaDetailsPresenter constructor(target: TrackingBottomSheet, service: TrackService, wasTracked: Boolean) : super(Bundle() @@ -61,9 +62,6 @@ class TrackSearchDialog : DialogController { val dialog = MaterialDialog(activity!!).apply { customView(viewRes = R.layout.track_search_dialog, scrollable = false) negativeButton(android.R.string.cancel) - if (wasPreviouslyTracked) { - positiveButton(R.string.clear) { onPositiveButtonClick() } - } } if (subscriptions.isUnsubscribed) { @@ -151,12 +149,6 @@ class TrackSearchDialog : DialogController { adapter?.setItems(emptyList()) } - private fun onPositiveButtonClick() { - bottomSheet.refreshTrack(service) - presenter.registerTracking(null, - service) - } - private companion object { const val KEY_SERVICE = "service_id" } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackingBottomSheet.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackingBottomSheet.kt index 7fc999d13e..4aadcb72a3 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackingBottomSheet.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackingBottomSheet.kt @@ -24,7 +24,8 @@ class TrackingBottomSheet(private val controller: MangaDetailsController) : Bott TrackAdapter.OnClickListener, SetTrackStatusDialog.Listener, SetTrackChaptersDialog.Listener, - SetTrackScoreDialog.Listener { + SetTrackScoreDialog.Listener, + TrackRemoveDialog.Listener { val activity = controller.activity!! @@ -145,6 +146,18 @@ class TrackingBottomSheet(private val controller: MangaDetailsController) : Bott SetTrackStatusDialog(this, item).showDialog(controller.router) } + override fun onRemoveClick(position: Int) { + val item = adapter?.getItem(position) ?: return + if (item.track == null) return + + if (controller.isNotOnline()) { + dismiss() + return + } + + TrackRemoveDialog(this, item).showDialog(controller.router) + } + override fun onChaptersClick(position: Int) { val item = adapter?.getItem(position) ?: return if (item.track == null) return @@ -197,6 +210,11 @@ class TrackingBottomSheet(private val controller: MangaDetailsController) : Bott refreshItem(item) } + override fun removeTracker(item: TrackItem, fromServiceAlso: Boolean) { + refreshTrack(item.service) + presenter.removeTracker(item, fromServiceAlso) + } + private companion object { const val TAG_SEARCH_CONTROLLER = "track_search_controller" } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsTrackingController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsTrackingController.kt index dbd5ea3e93..d44ecd5c4b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsTrackingController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsTrackingController.kt @@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.ui.setting import android.app.Activity import android.content.Intent +import android.net.Uri import androidx.browser.customtabs.CustomTabsIntent import androidx.preference.PreferenceScreen import eu.kanade.tachiyomi.R @@ -13,11 +14,12 @@ import eu.kanade.tachiyomi.data.track.shikimori.ShikimoriApi import eu.kanade.tachiyomi.util.system.getResourceColor import eu.kanade.tachiyomi.widget.preference.LoginPreference import eu.kanade.tachiyomi.widget.preference.TrackLoginDialog +import eu.kanade.tachiyomi.widget.preference.TrackLogoutDialog import uy.kohesive.injekt.injectLazy import eu.kanade.tachiyomi.data.preference.PreferenceKeys as Keys class SettingsTrackingController : SettingsController(), - TrackLoginDialog.Listener { + TrackLoginDialog.Listener, TrackLogoutDialog.Listener { private val trackManager: TrackManager by injectLazy() @@ -34,43 +36,27 @@ class SettingsTrackingController : SettingsController(), trackPreference(trackManager.myAnimeList) { onClick { - val dialog = TrackLoginDialog(trackManager.myAnimeList) - dialog.targetController = this@SettingsTrackingController - dialog.showDialog(router) + showDialog(trackManager.myAnimeList) } } trackPreference(trackManager.aniList) { onClick { - val tabsIntent = CustomTabsIntent.Builder() - .setToolbarColor(context.getResourceColor(R.attr.colorPrimaryVariant)) - .build() - tabsIntent.intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY) - tabsIntent.launchUrl(activity!!, AnilistApi.authUrl()) + showDialog(trackManager.aniList, AnilistApi.authUrl()) } } trackPreference(trackManager.kitsu) { onClick { - val dialog = TrackLoginDialog(trackManager.kitsu, context.getString(R.string.email)) - dialog.targetController = this@SettingsTrackingController - dialog.showDialog(router) + showDialog(trackManager.kitsu, userNameLabel = context.getString(R.string.email)) } } trackPreference(trackManager.shikimori) { onClick { - val tabsIntent = CustomTabsIntent.Builder() - .setToolbarColor(context.getResourceColor(R.attr.colorPrimaryVariant)) - .build() - tabsIntent.intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY) - tabsIntent.launchUrl(activity!!, ShikimoriApi.authUrl()) + showDialog(trackManager.shikimori, ShikimoriApi.authUrl()) } } trackPreference(trackManager.bangumi) { onClick { - val tabsIntent = CustomTabsIntent.Builder() - .setToolbarColor(context.getResourceColor(R.attr.colorPrimaryVariant)) - .build() - tabsIntent.intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY) - tabsIntent.launchUrl(activity!!, BangumiApi.authUrl()) + showDialog(trackManager.bangumi, BangumiApi.authUrl()) } } } @@ -91,6 +77,25 @@ class SettingsTrackingController : SettingsController(), // Manually refresh anilist holder updatePreference(trackManager.aniList.id) updatePreference(trackManager.shikimori.id) + updatePreference(trackManager.bangumi.id) + } + + private fun showDialog(trackService: TrackService, url: Uri? = null, userNameLabel: String? = null) { + if (trackService.isLogged) { + val dialog = TrackLogoutDialog(trackService) + dialog.targetController = this@SettingsTrackingController + dialog.showDialog(router) + } else if (url == null) { + val dialog = TrackLoginDialog(trackService, userNameLabel) + dialog.targetController = this@SettingsTrackingController + dialog.showDialog(router) + } else { + val tabsIntent = CustomTabsIntent.Builder() + .setToolbarColor(activity!!.getResourceColor(R.attr.colorPrimaryVariant)) + .build() + tabsIntent.intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY) + tabsIntent.launchUrl(activity!!, url) + } } private fun updatePreference(id: Int) { @@ -98,7 +103,11 @@ class SettingsTrackingController : SettingsController(), pref?.notifyChanged() } - override fun trackDialogClosed(service: TrackService) { + override fun trackLoginDialogClosed(service: TrackService) { + updatePreference(service.id) + } + + override fun trackLogoutDialogClosed(service: TrackService) { updatePreference(service.id) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/preference/LoginDialogPreference.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/preference/LoginDialogPreference.kt index 06b591673c..d4ab96e2c1 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/widget/preference/LoginDialogPreference.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/widget/preference/LoginDialogPreference.kt @@ -2,18 +2,14 @@ package eu.kanade.tachiyomi.widget.preference import android.app.Dialog import android.os.Bundle -import android.text.method.PasswordTransformationMethod import android.view.View import com.afollestad.materialdialogs.MaterialDialog import com.afollestad.materialdialogs.customview.customView import com.bluelinelabs.conductor.ControllerChangeHandler import com.bluelinelabs.conductor.ControllerChangeType -import com.dd.processbutton.iml.ActionProcessButton import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.ui.base.controller.DialogController -import eu.kanade.tachiyomi.util.view.visible -import eu.kanade.tachiyomi.widget.SimpleTextWatcher import kotlinx.android.synthetic.main.pref_account_login.view.* import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -42,7 +38,6 @@ abstract class LoginDialogPreference( override fun onCreateDialog(savedViewState: Bundle?): Dialog { val dialog = MaterialDialog(activity!!).apply { customView(R.layout.pref_account_login, scrollable = false) - positiveButton(android.R.string.cancel) } onViewCreated(dialog.view) @@ -50,40 +45,18 @@ abstract class LoginDialogPreference( return dialog } - open fun logout() {} - fun onViewCreated(view: View) { v = view.apply { - show_password.setOnCheckedChangeListener { _, isChecked -> - if (isChecked) - password.transformationMethod = null - else - password.transformationMethod = PasswordTransformationMethod() - } if (!usernameLabel.isNullOrEmpty()) { - username_label.text = usernameLabel + username_input.hint = usernameLabel } - login.setMode(ActionProcessButton.Mode.ENDLESS) - login.setOnClickListener { checkLogin() } + login.setOnClickListener { + checkLogin() + } setCredentialsOnView(this) - - if (canLogout && !username.text.isNullOrEmpty()) { - logout.visible() - logout.setOnClickListener { logout() } - } - - show_password.isEnabled = password.text.isNullOrEmpty() - - password.addTextChangedListener(object : SimpleTextWatcher() { - override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { - if (s.isEmpty()) { - show_password.isEnabled = true - } - } - }) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/preference/TrackLoginDialog.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/preference/TrackLoginDialog.kt index 8555d1f4eb..76734b3000 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/widget/preference/TrackLoginDialog.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/widget/preference/TrackLoginDialog.kt @@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.widget.preference import android.os.Bundle import android.view.View +import br.com.simplepass.loadingbutton.animatedDrawables.ProgressType import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.track.TrackManager import eu.kanade.tachiyomi.data.track.TrackService @@ -18,8 +19,6 @@ class TrackLoginDialog(usernameLabel: String? = null, bundle: Bundle? = null) : override var canLogout = true - constructor(service: TrackService) : this(service, null) - constructor(service: TrackService, usernameLabel: String?) : this(usernameLabel, Bundle().apply { putInt("key", service.id) }) @@ -32,13 +31,18 @@ class TrackLoginDialog(usernameLabel: String? = null, bundle: Bundle? = null) : override fun checkLogin() { v?.apply { - if (username.text.isEmpty() || password.text.isEmpty()) + login.apply { + progressType = ProgressType.INDETERMINATE + startAnimation() + } + if (username.text.isNullOrBlank() || password.text.isNullOrBlank()) { + errorResult() + context.toast(R.string.username_must_not_be_blank) return + } - login.progress = 1 val user = username.text.toString() val pass = password.text.toString() - scope.launch { try { val result = service.login(user, pass) @@ -58,24 +62,18 @@ class TrackLoginDialog(usernameLabel: String? = null, bundle: Bundle? = null) : private fun errorResult() { v?.apply { - login.progress = -1 - login.setText(R.string.unknown_error) - } - } - - override fun logout() { - if (service.isLogged) { - service.logout() - activity?.toast(R.string.successfully_logged_out) + login.revertAnimation { + login.text = activity!!.getText(R.string.unknown_error) + } } } override fun onDialogClosed() { super.onDialogClosed() - (targetController as? Listener)?.trackDialogClosed(service) + (targetController as? Listener)?.trackLoginDialogClosed(service) } interface Listener { - fun trackDialogClosed(service: TrackService) + fun trackLoginDialogClosed(service: TrackService) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/preference/TrackLogoutDialog.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/preference/TrackLogoutDialog.kt new file mode 100644 index 0000000000..5b99884bfa --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/widget/preference/TrackLogoutDialog.kt @@ -0,0 +1,34 @@ +package eu.kanade.tachiyomi.widget.preference + +import android.app.Dialog +import android.os.Bundle +import com.afollestad.materialdialogs.MaterialDialog +import eu.kanade.tachiyomi.R +import eu.kanade.tachiyomi.data.track.TrackManager +import eu.kanade.tachiyomi.data.track.TrackService +import eu.kanade.tachiyomi.ui.base.controller.DialogController +import eu.kanade.tachiyomi.util.system.toast +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get + +class TrackLogoutDialog(bundle: Bundle? = null) : DialogController(bundle) { + + private val service = Injekt.get().getService(args.getInt("key"))!! + + constructor(service: TrackService) : this(Bundle().apply { putInt("key", service.id) }) + + override fun onCreateDialog(savedViewState: Bundle?): Dialog { + return MaterialDialog(activity!!) + .title(text = activity!!.getString(R.string.logout_from_, service.name)) + .negativeButton(R.string.cancel) + .positiveButton(R.string.logout) { _ -> + service.logout() + (targetController as? Listener)?.trackLogoutDialogClosed(service) + activity!!.toast(R.string.successfully_logged_out) + } + } + + interface Listener { + fun trackLogoutDialogClosed(service: TrackService) + } +} diff --git a/app/src/main/res/drawable/bordered_list_selector.xml b/app/src/main/res/drawable/bordered_list_selector.xml index 7f30001a6c..3082754632 100644 --- a/app/src/main/res/drawable/bordered_list_selector.xml +++ b/app/src/main/res/drawable/bordered_list_selector.xml @@ -1,7 +1,7 @@ + tools:background="@color/material_red_500"> - + @@ -15,11 +15,11 @@ - - + + - + diff --git a/app/src/main/res/drawable/ic_close_circle_24dp.xml b/app/src/main/res/drawable/ic_close_circle_24dp.xml new file mode 100644 index 0000000000..d87d11ba5c --- /dev/null +++ b/app/src/main/res/drawable/ic_close_circle_24dp.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/unread_angled_badge.xml b/app/src/main/res/drawable/unread_angled_badge.xml index 4d5987186d..b505c96dd7 100644 --- a/app/src/main/res/drawable/unread_angled_badge.xml +++ b/app/src/main/res/drawable/unread_angled_badge.xml @@ -5,7 +5,7 @@ android:height="100dp" android:viewportHeight="10" android:viewportWidth="100" - tools:background="@color/red_error"> + tools:background="@color/material_red_500"> + android:background="@color/material_red_500"> + android:background="@color/material_red_500"> diff --git a/app/src/main/res/layout/pref_account_login.xml b/app/src/main/res/layout/pref_account_login.xml index 8df75f759e..9157f75fa7 100644 --- a/app/src/main/res/layout/pref_account_login.xml +++ b/app/src/main/res/layout/pref_account_login.xml @@ -1,77 +1,69 @@ + 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="24dp"> + tools:text="Log in to AniList" /> - - + android:layout_marginTop="15dp" + android:hint="@string/username" + app:boxStrokeColor="@color/colorAccent" + app:hintTextColor="@color/colorAccent"> - + + - + android:layout_marginTop="12dp" + android:hint="@string/password" + app:boxStrokeColor="@color/colorAccent" + app:endIconMode="password_toggle" + app:hintTextColor="@color/colorAccent"> - + + - - - - + android:textSize="16sp" + app:finalCornerAngle="50dp" + app:initialCornerAngle="2dp" + app:spinning_bar_color="@color/md_white_1000" + app:spinning_bar_padding="6dp" + app:spinning_bar_width="3dp" /> \ No newline at end of file diff --git a/app/src/main/res/layout/source_item.xml b/app/src/main/res/layout/source_item.xml index 5bb95bcf5f..cc93cc99c6 100644 --- a/app/src/main/res/layout/source_item.xml +++ b/app/src/main/res/layout/source_item.xml @@ -9,7 +9,7 @@ android:id="@+id/right_view" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@color/red_error" + android:background="@color/material_red_500" android:visibility="gone"> + android:background="@color/material_red_500"> + + + app:layout_constraintTop_toTopOf="parent" /> + app:layout_constraintTop_toTopOf="parent" /> - - + android:layout_marginEnd="16dp" + android:hint="@string/title" + app:boxStrokeColor="@color/colorAccent" + app:endIconMode="clear_text" + app:hintEnabled="false" + app:hintTextColor="@color/colorAccent"> + + + + + + android:layout_weight="1" + android:elevation="12dp" + > + tools:visibility="visible" /> + tools:visibility="visible" /> - diff --git a/app/src/main/res/layout/track_search_item.xml b/app/src/main/res/layout/track_search_item.xml index 95d32dc436..28cd11235e 100644 --- a/app/src/main/res/layout/track_search_item.xml +++ b/app/src/main/res/layout/track_search_item.xml @@ -1,5 +1,5 @@ - + tools:src="@mipmap/ic_launcher" /> + - + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 597da03042..90ea4db38f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -421,6 +421,8 @@ Manga URL not set, please click title and select manga again Refresh tracking Add tracking + Remove tracking from app + Also remove from %1$s Select sources @@ -582,7 +584,7 @@ Website Open source licenses - + Log in to %1$s Username Email address @@ -590,7 +592,11 @@ Show password Login Logout + Logout from %1$s? + Successfully logged in + Username or password cannot be blank + You are now logged out Could not log in diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index dbc54bb53b..8ba6e0f30d 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -5,8 +5,7 @@ - @@ -202,7 +201,7 @@ - - + diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index fc1bfdeb63..b88f91e46e 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -28,7 +28,7 @@ @color/divider @drawable/line_divider @style/CustomActionModeStyle - + true @style/ThemeOverlay.AppCompat.DayNight.ActionBar diff --git a/buildSrc/src/main/kotlin/Dependencies.kt b/buildSrc/src/main/kotlin/Dependencies.kt index 7afb39bef2..b974130e24 100644 --- a/buildSrc/src/main/kotlin/Dependencies.kt +++ b/buildSrc/src/main/kotlin/Dependencies.kt @@ -1,7 +1,7 @@ object Versions { const val ACRA = "4.9.2" const val CHUCKER = "3.2.0" - const val COIL = "0.10.1" + const val COIL = "0.11.0" const val COROUTINES = "1.3.5" const val FASTADAPTER = "5.0.0" const val HYPERION = "0.9.27"