Merge pull request #209 from inorichi/source-languages

Support for sources from different languages
This commit is contained in:
inorichi 2016-03-10 16:23:09 +01:00
commit dc742f4b8d
36 changed files with 407 additions and 173 deletions

View File

@ -379,11 +379,11 @@ public class DownloadManager {
} }
public File getAbsoluteMangaDirectory(Source source, Manga manga) { public File getAbsoluteMangaDirectory(Source source, Manga manga) {
String chapterRelativePath = source.getName() + String mangaRelativePath = source.getVisibleName() +
File.separator + File.separator +
manga.title.replaceAll("[^\\sa-zA-Z0-9.-]", "_"); manga.title.replaceAll("[^\\sa-zA-Z0-9.-]", "_");
return new File(preferences.getDownloadsDirectory(), chapterRelativePath); return new File(preferences.getDownloadsDirectory(), mangaRelativePath);
} }
// Get the absolute path to the chapter directory // Get the absolute path to the chapter directory

View File

@ -11,9 +11,7 @@ import eu.kanade.tachiyomi.data.source.base.Source
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
fun <T> Preference<T>.getOrDefault(): T { fun <T> Preference<T>.getOrDefault(): T = get() ?: defaultValue()!!
return get() ?: defaultValue()!!
}
class PreferencesHelper(private val context: Context) { class PreferencesHelper(private val context: Context) {
@ -140,6 +138,10 @@ class PreferencesHelper(private val context: Context) {
return rxPrefs.getBoolean(getKey(R.string.pref_display_catalogue_as_list), false) return rxPrefs.getBoolean(getKey(R.string.pref_display_catalogue_as_list), false)
} }
fun enabledLanguages(): Preference<MutableSet<String>> {
return rxPrefs.getStringSet(getKey(R.string.pref_source_languages), setOf("EN"))
}
fun getSourceUsername(source: Source): String { fun getSourceUsername(source: Source): String {
return prefs.getString(SOURCE_ACCOUNT_USERNAME + source.id, "") return prefs.getString(SOURCE_ACCOUNT_USERNAME + source.id, "")
} }

View File

@ -0,0 +1,8 @@
package eu.kanade.tachiyomi.data.source
class Language(val lang: String, val code: String)
val EN = Language("English", "EN")
val RU = Language("Russian", "RU")
fun getLanguages(): List<Language> = listOf(EN, RU)

View File

@ -11,7 +11,6 @@ import java.util.*
open class SourceManager(private val context: Context) { open class SourceManager(private val context: Context) {
val sourcesMap: HashMap<Int, Source> val sourcesMap: HashMap<Int, Source>
val sources: List<Source>
val BATOTO = 1 val BATOTO = 1
val MANGAHERE = 2 val MANGAHERE = 2
@ -22,7 +21,6 @@ open class SourceManager(private val context: Context) {
init { init {
sourcesMap = createSourcesMap() sourcesMap = createSourcesMap()
sources = ArrayList(sourcesMap.values).sortedBy { it.name }
} }
open fun get(sourceKey: Int): Source? { open fun get(sourceKey: Int): Source? {
@ -49,4 +47,6 @@ open class SourceManager(private val context: Context) {
return map return map
} }
fun getSources(): List<Source> = ArrayList(sourcesMap.values)
} }

View File

@ -6,6 +6,7 @@ import java.util.List;
import eu.kanade.tachiyomi.data.database.models.Chapter; import eu.kanade.tachiyomi.data.database.models.Chapter;
import eu.kanade.tachiyomi.data.database.models.Manga; import eu.kanade.tachiyomi.data.database.models.Manga;
import eu.kanade.tachiyomi.data.source.Language;
import eu.kanade.tachiyomi.data.source.model.MangasPage; import eu.kanade.tachiyomi.data.source.model.MangasPage;
import okhttp3.Headers; import okhttp3.Headers;
import okhttp3.Response; import okhttp3.Response;
@ -24,9 +25,16 @@ public abstract class BaseSource {
this.id = id; this.id = id;
} }
public abstract Language getLang();
// Name of the source to display // Name of the source to display
public abstract String getName(); public abstract String getName();
// Name of the source to display with the language
public String getVisibleName() {
return getName() + " (" + getLang().getCode() + ")";
}
// Base url of the source, like: http://example.com // Base url of the source, like: http://example.com
public abstract String getBaseUrl(); public abstract String getBaseUrl();
@ -86,6 +94,6 @@ public abstract class BaseSource {
@Override @Override
public String toString() { public String toString() {
return getName(); return getVisibleName();
} }
} }

View File

@ -28,6 +28,8 @@ import java.util.regex.Pattern;
import eu.kanade.tachiyomi.data.database.models.Chapter; import eu.kanade.tachiyomi.data.database.models.Chapter;
import eu.kanade.tachiyomi.data.database.models.Manga; import eu.kanade.tachiyomi.data.database.models.Manga;
import eu.kanade.tachiyomi.data.network.ReqKt; import eu.kanade.tachiyomi.data.network.ReqKt;
import eu.kanade.tachiyomi.data.source.Language;
import eu.kanade.tachiyomi.data.source.LanguageKt;
import eu.kanade.tachiyomi.data.source.base.LoginSource; import eu.kanade.tachiyomi.data.source.base.LoginSource;
import eu.kanade.tachiyomi.data.source.model.MangasPage; import eu.kanade.tachiyomi.data.source.model.MangasPage;
import eu.kanade.tachiyomi.data.source.model.Page; import eu.kanade.tachiyomi.data.source.model.Page;
@ -40,7 +42,7 @@ import rx.Observable;
public class Batoto extends LoginSource { public class Batoto extends LoginSource {
public static final String NAME = "Batoto (EN)"; public static final String NAME = "Batoto";
public static final String BASE_URL = "http://bato.to"; public static final String BASE_URL = "http://bato.to";
public static final String POPULAR_MANGAS_URL = BASE_URL + "/search_ajax?order_cond=views&order=desc&p=%s"; public static final String POPULAR_MANGAS_URL = BASE_URL + "/search_ajax?order_cond=views&order=desc&p=%s";
public static final String SEARCH_URL = BASE_URL + "/search_ajax?name=%s&p=%s"; public static final String SEARCH_URL = BASE_URL + "/search_ajax?name=%s&p=%s";
@ -79,6 +81,10 @@ public class Batoto extends LoginSource {
return BASE_URL; return BASE_URL;
} }
public Language getLang() {
return LanguageKt.getEN();
}
@Override @Override
protected Headers.Builder headersBuilder() { protected Headers.Builder headersBuilder() {
Headers.Builder builder = super.headersBuilder(); Headers.Builder builder = super.headersBuilder();

View File

@ -18,6 +18,8 @@ import java.util.regex.Pattern;
import eu.kanade.tachiyomi.data.database.models.Chapter; import eu.kanade.tachiyomi.data.database.models.Chapter;
import eu.kanade.tachiyomi.data.database.models.Manga; import eu.kanade.tachiyomi.data.database.models.Manga;
import eu.kanade.tachiyomi.data.network.ReqKt; import eu.kanade.tachiyomi.data.network.ReqKt;
import eu.kanade.tachiyomi.data.source.Language;
import eu.kanade.tachiyomi.data.source.LanguageKt;
import eu.kanade.tachiyomi.data.source.base.Source; import eu.kanade.tachiyomi.data.source.base.Source;
import eu.kanade.tachiyomi.data.source.model.MangasPage; import eu.kanade.tachiyomi.data.source.model.MangasPage;
import eu.kanade.tachiyomi.data.source.model.Page; import eu.kanade.tachiyomi.data.source.model.Page;
@ -28,7 +30,7 @@ import okhttp3.Request;
public class Kissmanga extends Source { public class Kissmanga extends Source {
public static final String NAME = "Kissmanga (EN)"; public static final String NAME = "Kissmanga";
public static final String HOST = "kissmanga.com"; public static final String HOST = "kissmanga.com";
public static final String IP = "93.174.95.110"; public static final String IP = "93.174.95.110";
public static final String BASE_URL = "http://" + IP; public static final String BASE_URL = "http://" + IP;
@ -56,6 +58,10 @@ public class Kissmanga extends Source {
return BASE_URL; return BASE_URL;
} }
public Language getLang() {
return LanguageKt.getEN();
}
@Override @Override
protected String getInitialPopularMangasUrl() { protected String getInitialPopularMangasUrl() {
return String.format(POPULAR_MANGAS_URL, 1); return String.format(POPULAR_MANGAS_URL, 1);

View File

@ -18,13 +18,15 @@ import java.util.Locale;
import eu.kanade.tachiyomi.data.database.models.Chapter; import eu.kanade.tachiyomi.data.database.models.Chapter;
import eu.kanade.tachiyomi.data.database.models.Manga; import eu.kanade.tachiyomi.data.database.models.Manga;
import eu.kanade.tachiyomi.data.source.Language;
import eu.kanade.tachiyomi.data.source.LanguageKt;
import eu.kanade.tachiyomi.data.source.base.Source; import eu.kanade.tachiyomi.data.source.base.Source;
import eu.kanade.tachiyomi.data.source.model.MangasPage; import eu.kanade.tachiyomi.data.source.model.MangasPage;
import eu.kanade.tachiyomi.util.Parser; import eu.kanade.tachiyomi.util.Parser;
public class Mangafox extends Source { public class Mangafox extends Source {
public static final String NAME = "Mangafox (EN)"; public static final String NAME = "Mangafox";
public static final String BASE_URL = "http://mangafox.me"; public static final String BASE_URL = "http://mangafox.me";
public static final String POPULAR_MANGAS_URL = BASE_URL + "/directory/%s"; public static final String POPULAR_MANGAS_URL = BASE_URL + "/directory/%s";
public static final String SEARCH_URL = public static final String SEARCH_URL =
@ -44,6 +46,10 @@ public class Mangafox extends Source {
return BASE_URL; return BASE_URL;
} }
public Language getLang() {
return LanguageKt.getEN();
}
@Override @Override
protected String getInitialPopularMangasUrl() { protected String getInitialPopularMangasUrl() {
return String.format(POPULAR_MANGAS_URL, ""); return String.format(POPULAR_MANGAS_URL, "");

View File

@ -18,13 +18,15 @@ import java.util.Locale;
import eu.kanade.tachiyomi.data.database.models.Chapter; import eu.kanade.tachiyomi.data.database.models.Chapter;
import eu.kanade.tachiyomi.data.database.models.Manga; import eu.kanade.tachiyomi.data.database.models.Manga;
import eu.kanade.tachiyomi.data.source.Language;
import eu.kanade.tachiyomi.data.source.LanguageKt;
import eu.kanade.tachiyomi.data.source.base.Source; import eu.kanade.tachiyomi.data.source.base.Source;
import eu.kanade.tachiyomi.data.source.model.MangasPage; import eu.kanade.tachiyomi.data.source.model.MangasPage;
import eu.kanade.tachiyomi.util.Parser; import eu.kanade.tachiyomi.util.Parser;
public class Mangahere extends Source { public class Mangahere extends Source {
public static final String NAME = "Mangahere (EN)"; public static final String NAME = "Mangahere";
public static final String BASE_URL = "http://www.mangahere.co"; public static final String BASE_URL = "http://www.mangahere.co";
public static final String POPULAR_MANGAS_URL = BASE_URL + "/directory/%s"; public static final String POPULAR_MANGAS_URL = BASE_URL + "/directory/%s";
public static final String SEARCH_URL = BASE_URL + "/search.php?name=%s&page=%s&sort=views&order=za"; public static final String SEARCH_URL = BASE_URL + "/search.php?name=%s&page=%s&sort=views&order=za";
@ -43,6 +45,10 @@ public class Mangahere extends Source {
return BASE_URL; return BASE_URL;
} }
public Language getLang() {
return LanguageKt.getEN();
}
@Override @Override
protected String getInitialPopularMangasUrl() { protected String getInitialPopularMangasUrl() {
return String.format(POPULAR_MANGAS_URL, ""); return String.format(POPULAR_MANGAS_URL, "");

View File

@ -24,7 +24,6 @@ import eu.kanade.tachiyomi.ui.manga.info.MangaInfoPresenter;
import eu.kanade.tachiyomi.ui.manga.myanimelist.MyAnimeListPresenter; import eu.kanade.tachiyomi.ui.manga.myanimelist.MyAnimeListPresenter;
import eu.kanade.tachiyomi.ui.reader.ReaderPresenter; import eu.kanade.tachiyomi.ui.reader.ReaderPresenter;
import eu.kanade.tachiyomi.ui.recent.RecentChaptersPresenter; import eu.kanade.tachiyomi.ui.recent.RecentChaptersPresenter;
import eu.kanade.tachiyomi.ui.setting.SettingsAccountsFragment;
import eu.kanade.tachiyomi.ui.setting.SettingsActivity; import eu.kanade.tachiyomi.ui.setting.SettingsActivity;
@Singleton @Singleton
@ -48,8 +47,6 @@ public interface AppComponent {
void inject(RecentChaptersPresenter recentChaptersPresenter); void inject(RecentChaptersPresenter recentChaptersPresenter);
void inject(MangaActivity mangaActivity); void inject(MangaActivity mangaActivity);
void inject(SettingsAccountsFragment settingsAccountsFragment);
void inject(SettingsActivity settingsActivity); void inject(SettingsActivity settingsActivity);
void inject(Source source); void inject(Source source);

View File

@ -1,10 +1,15 @@
package eu.kanade.tachiyomi.ui.base.activity package eu.kanade.tachiyomi.ui.base.activity
import android.graphics.Color
import android.os.Bundle import android.os.Bundle
import android.support.design.widget.Snackbar
import android.support.v7.app.AppCompatActivity import android.support.v7.app.AppCompatActivity
import android.support.v7.widget.Toolbar import android.support.v7.widget.Toolbar
import android.view.MenuItem import android.view.MenuItem
import android.view.View
import android.widget.TextView
import eu.kanade.tachiyomi.App import eu.kanade.tachiyomi.App
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.injection.component.AppComponent import eu.kanade.tachiyomi.injection.component.AppComponent
import icepick.Icepick import icepick.Icepick
import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.EventBus
@ -60,6 +65,24 @@ open class BaseActivity : AppCompatActivity() {
EventBus.getDefault().unregister(this) EventBus.getDefault().unregister(this)
} }
fun snack(text: String?, duration: Int = Snackbar.LENGTH_LONG) {
val snack = Snackbar.make(findViewById(android.R.id.content), text ?: getString(R.string.unknown_error), duration)
val textView = snack.view.findViewById(android.support.design.R.id.snackbar_text) as TextView
textView.setTextColor(Color.WHITE)
snack.show()
}
fun snack(text: String?, actionRes: Int, actionFunc: () -> Unit,
duration: Int = Snackbar.LENGTH_LONG, view: View = findViewById(android.R.id.content)) {
val snack = Snackbar.make(view, text ?: getString(R.string.unknown_error), duration)
.setAction(actionRes, { actionFunc() })
val textView = snack.view.findViewById(android.support.design.R.id.snackbar_text) as TextView
textView.setTextColor(Color.WHITE)
snack.show()
}
protected val applicationComponent: AppComponent protected val applicationComponent: AppComponent
get() = App.get(this).component get() = App.get(this).component

View File

@ -168,7 +168,7 @@ class CatalogueFragment : BaseRxFragment<CataloguePresenter>(), FlexibleViewHold
val themedContext = baseActivity.supportActionBar?.themedContext ?: activity val themedContext = baseActivity.supportActionBar?.themedContext ?: activity
val spinnerAdapter = ArrayAdapter(themedContext, val spinnerAdapter = ArrayAdapter(themedContext,
android.R.layout.simple_spinner_item, presenter.getEnabledSources()) android.R.layout.simple_spinner_item, presenter.sources)
spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
val onItemSelected = object : AdapterView.OnItemSelectedListener { val onItemSelected = object : AdapterView.OnItemSelectedListener {
@ -353,8 +353,12 @@ class CatalogueFragment : BaseRxFragment<CataloguePresenter>(), FlexibleViewHold
*/ */
fun onAddPageError(error: Throwable) { fun onAddPageError(error: Throwable) {
hideProgressBar() hideProgressBar()
ToastUtil.showShort(context, error.message)
Timber.e(error, error.message) Timber.e(error, error.message)
baseActivity.snack(error.message, R.string.action_retry, {
showProgressBar()
presenter.retryRequest()
})
} }
/** /**

View File

@ -5,6 +5,7 @@ import eu.kanade.tachiyomi.data.cache.CoverCache
import eu.kanade.tachiyomi.data.database.DatabaseHelper import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault
import eu.kanade.tachiyomi.data.source.SourceManager import eu.kanade.tachiyomi.data.source.SourceManager
import eu.kanade.tachiyomi.data.source.base.Source import eu.kanade.tachiyomi.data.source.base.Source
import eu.kanade.tachiyomi.data.source.model.MangasPage import eu.kanade.tachiyomi.data.source.model.MangasPage
@ -45,7 +46,7 @@ class CataloguePresenter : BasePresenter<CatalogueFragment>() {
/** /**
* Enabled sources. * Enabled sources.
*/ */
private val sources by lazy { sourceManager.sources } val sources by lazy { getEnabledSources() }
/** /**
* Active source. * Active source.
@ -188,6 +189,13 @@ class CataloguePresenter : BasePresenter<CatalogueFragment>() {
} }
} }
/**
* Retry a failed request.
*/
fun retryRequest() {
start(GET_MANGA_PAGE)
}
/** /**
* Returns the observable of the network request for a page. * Returns the observable of the network request for a page.
* *
@ -308,12 +316,19 @@ class CataloguePresenter : BasePresenter<CatalogueFragment>() {
} }
/** /**
* Returns a list of enabled sources. * Returns a list of enabled sources ordered by language and name.
*
* TODO filter by enabled sources.
*/ */
fun getEnabledSources(): List<Source> { private fun getEnabledSources(): List<Source> {
return sourceManager.sources val languages = prefs.enabledLanguages().getOrDefault()
// Ensure at least one language
if (languages.isEmpty()) {
languages.add("EN")
}
return sourceManager.getSources()
.filter { it.lang.code in languages }
.sortedBy { "(${it.lang.code}) ${it.name}" }
} }
/** /**

View File

@ -143,7 +143,7 @@ public class MangaInfoFragment extends BaseRxFragment<MangaInfoPresenter> {
// If manga source is known update source TextView. // If manga source is known update source TextView.
if (mangaSource != null) { if (mangaSource != null) {
source.setText(mangaSource.getName()); source.setText(mangaSource.getVisibleName());
} }
// Update genres TextView. // Update genres TextView.

View File

@ -1,76 +0,0 @@
package eu.kanade.tachiyomi.ui.setting
import android.content.Context
import android.os.Bundle
import android.support.v7.preference.DialogPreference
import android.support.v7.preference.Preference
import android.support.v7.preference.PreferenceCategory
import android.view.View
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.source.base.Source
import eu.kanade.tachiyomi.widget.preference.MangaSyncLoginDialog
import eu.kanade.tachiyomi.widget.preference.SourceLoginDialog
class SettingsAccountsFragment : SettingsNestedFragment() {
companion object {
fun newInstance(resourcePreference: Int, resourceTitle: Int): SettingsNestedFragment {
val fragment = SettingsAccountsFragment()
fragment.setArgs(resourcePreference, resourceTitle)
return fragment
}
}
val sourceCategory by lazy { findPreference("pref_category_source_accounts") as PreferenceCategory }
val syncCategory by lazy { findPreference("pref_category_manga_sync_accounts") as PreferenceCategory }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val themedContext = preferenceManager.context
for (source in getSourcesWithLogin()) {
val pref = SourcePreference(themedContext).apply {
isPersistent = false
title = source.name
key = source.id.toString()
dialogLayoutResource = R.layout.pref_account_login
}
sourceCategory.addPreference(pref)
}
for (sync in settingsActivity.syncManager.services) {
val pref = SyncPreference(themedContext).apply {
isPersistent = false
title = sync.name
key = sync.id.toString()
dialogLayoutResource = R.layout.pref_account_login
}
syncCategory.addPreference(pref)
}
}
fun getSourcesWithLogin(): List<Source> {
return settingsActivity.sourceManager.sources.filter { it.isLoginRequired }
}
override fun onDisplayPreferenceDialog(preference: Preference) {
if (preference is SourcePreference) {
val fragment = SourceLoginDialog.newInstance(preference)
fragment.setTargetFragment(this, 0)
fragment.show(childFragmentManager, null)
} else if (preference is SyncPreference) {
val fragment = MangaSyncLoginDialog.newInstance(preference)
fragment.setTargetFragment(this, 0)
fragment.show(childFragmentManager, null)
} else {
super.onDisplayPreferenceDialog(preference)
}
}
class SourcePreference(context: Context) : DialogPreference(context) {}
class SyncPreference(context: Context) : DialogPreference(context) {}
}

View File

@ -1,7 +1,7 @@
package eu.kanade.tachiyomi.ui.setting package eu.kanade.tachiyomi.ui.setting
import android.os.Bundle import android.os.Bundle
import android.support.v7.preference.PreferenceFragmentCompat import android.support.v14.preference.PreferenceFragment
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.cache.ChapterCache import eu.kanade.tachiyomi.data.cache.ChapterCache
import eu.kanade.tachiyomi.data.database.DatabaseHelper import eu.kanade.tachiyomi.data.database.DatabaseHelper
@ -28,19 +28,19 @@ class SettingsActivity : BaseActivity() {
setupToolbar(toolbar) setupToolbar(toolbar)
if (savedState == null) { if (savedState == null) {
supportFragmentManager.beginTransaction() fragmentManager.beginTransaction()
.replace(R.id.settings_content,SettingsMainFragment()) .replace(R.id.settings_content, SettingsMainFragment())
.commit() .commit()
} }
} }
override fun onBackPressed() { override fun onBackPressed() {
if (!supportFragmentManager.popBackStackImmediate()) { if (!fragmentManager.popBackStackImmediate()) {
super.onBackPressed() super.onBackPressed()
} }
} }
class SettingsMainFragment : PreferenceFragmentCompat() { class SettingsMainFragment : PreferenceFragment() {
override fun onCreatePreferences(savedState: Bundle?, s: String?) { override fun onCreatePreferences(savedState: Bundle?, s: String?) {
addPreferencesFromResource(R.xml.pref_main) addPreferencesFromResource(R.xml.pref_main)
@ -57,8 +57,12 @@ class SettingsActivity : BaseActivity() {
SettingsDownloadsFragment.newInstance(R.xml.pref_downloads, R.string.pref_category_downloads) SettingsDownloadsFragment.newInstance(R.xml.pref_downloads, R.string.pref_category_downloads)
} }
registerSubpreference(R.string.pref_category_accounts_key) { registerSubpreference(R.string.pref_category_sources_key) {
SettingsAccountsFragment.newInstance(R.xml.pref_accounts, R.string.pref_category_accounts) SettingsSourcesFragment.newInstance(R.xml.pref_sources, R.string.pref_category_sources)
}
registerSubpreference(R.string.pref_category_sync_key) {
SettingsSyncFragment.newInstance(R.xml.pref_sync, R.string.pref_category_sync)
} }
registerSubpreference(R.string.pref_category_advanced_key) { registerSubpreference(R.string.pref_category_advanced_key) {
@ -75,7 +79,7 @@ class SettingsActivity : BaseActivity() {
(activity as BaseActivity).setToolbarTitle(getString(R.string.label_settings)) (activity as BaseActivity).setToolbarTitle(getString(R.string.label_settings))
} }
private fun registerSubpreference(preferenceResource: Int, func: () -> PreferenceFragmentCompat) { private fun registerSubpreference(preferenceResource: Int, func: () -> PreferenceFragment) {
findPreference(getString(preferenceResource)).setOnPreferenceClickListener { findPreference(getString(preferenceResource)).setOnPreferenceClickListener {
val fragment = func() val fragment = func()
fragmentManager.beginTransaction() fragmentManager.beginTransaction()

View File

@ -1,10 +1,10 @@
package eu.kanade.tachiyomi.ui.setting package eu.kanade.tachiyomi.ui.setting
import android.os.Bundle import android.os.Bundle
import android.support.v7.preference.PreferenceFragmentCompat import android.support.v14.preference.PreferenceFragment
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
open class SettingsNestedFragment : PreferenceFragmentCompat() { open class SettingsNestedFragment : PreferenceFragment() {
companion object { companion object {

View File

@ -0,0 +1,88 @@
package eu.kanade.tachiyomi.ui.setting
import android.content.Intent
import android.os.Bundle
import android.support.v14.preference.MultiSelectListPreference
import android.support.v7.preference.Preference
import android.support.v7.preference.PreferenceGroup
import android.view.View
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault
import eu.kanade.tachiyomi.data.source.base.Source
import eu.kanade.tachiyomi.data.source.getLanguages
import eu.kanade.tachiyomi.widget.preference.LoginPreference
import eu.kanade.tachiyomi.widget.preference.SourceLoginDialog
import rx.Subscription
class SettingsSourcesFragment : SettingsNestedFragment() {
companion object {
const val SOURCE_CHANGE_REQUEST = 120
fun newInstance(resourcePreference: Int, resourceTitle: Int): SettingsNestedFragment {
val fragment = SettingsSourcesFragment()
fragment.setArgs(resourcePreference, resourceTitle)
return fragment
}
}
val languagesPref by lazy { findPreference("pref_source_languages") as MultiSelectListPreference }
val sourcesPref by lazy { findPreference("pref_sources") as PreferenceGroup }
var languagesSubscription: Subscription? = null
override fun onViewCreated(view: View, savedState: Bundle?) {
val langs = getLanguages()
val entryKeys = langs.map { it.code }
languagesPref.entries = langs.map { it.lang }.toTypedArray()
languagesPref.entryValues = entryKeys.toTypedArray()
languagesPref.values = preferences.enabledLanguages().getOrDefault()
languagesSubscription = preferences.enabledLanguages().asObservable()
.subscribe { languages ->
sourcesPref.removeAll()
val enabledSources = settingsActivity.sourceManager.getSources()
.filter { it.lang.code in languages }
for (source in enabledSources) {
if (source.isLoginRequired) {
val pref = createSource(source)
sourcesPref.addPreference(pref)
}
}
// Hide category if it doesn't have any child
sourcesPref.isVisible = sourcesPref.preferenceCount > 0
}
}
override fun onDestroyView() {
languagesSubscription?.unsubscribe()
super.onDestroyView()
}
fun createSource(source: Source): Preference {
return LoginPreference(preferenceManager.context).apply {
key = PreferencesHelper.SOURCE_ACCOUNT_USERNAME + source.id
title = source.visibleName
setOnPreferenceClickListener {
val fragment = SourceLoginDialog.newInstance(source)
fragment.setTargetFragment(this@SettingsSourcesFragment, SOURCE_CHANGE_REQUEST)
fragment.show(childFragmentManager, null)
true
}
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == SOURCE_CHANGE_REQUEST) {
val pref = findPreference(PreferencesHelper.SOURCE_ACCOUNT_USERNAME + resultCode) as? LoginPreference
pref?.notifyChanged()
}
}
}

View File

@ -0,0 +1,52 @@
package eu.kanade.tachiyomi.ui.setting
import android.content.Intent
import android.os.Bundle
import android.support.v7.preference.PreferenceCategory
import android.view.View
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.widget.preference.LoginPreference
import eu.kanade.tachiyomi.widget.preference.MangaSyncLoginDialog
class SettingsSyncFragment : SettingsNestedFragment() {
companion object {
const val SYNC_CHANGE_REQUEST = 121
fun newInstance(resourcePreference: Int, resourceTitle: Int): SettingsNestedFragment {
val fragment = SettingsSyncFragment()
fragment.setArgs(resourcePreference, resourceTitle)
return fragment
}
}
val syncCategory by lazy { findPreference("pref_category_manga_sync_accounts") as PreferenceCategory }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val themedContext = preferenceManager.context
for (sync in settingsActivity.syncManager.services) {
val pref = LoginPreference(themedContext).apply {
key = PreferencesHelper.MANGASYNC_ACCOUNT_USERNAME + sync.id
title = sync.name
setOnPreferenceClickListener {
val fragment = MangaSyncLoginDialog.newInstance(sync)
fragment.setTargetFragment(this@SettingsSyncFragment, SYNC_CHANGE_REQUEST)
fragment.show(childFragmentManager, null)
true
}
}
syncCategory.addPreference(pref)
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == SYNC_CHANGE_REQUEST) {
val pref = findPreference(PreferencesHelper.MANGASYNC_ACCOUNT_USERNAME + resultCode) as? LoginPreference
pref?.notifyChanged()
}
}
}

View File

@ -1,15 +1,15 @@
package eu.kanade.tachiyomi.widget.preference package eu.kanade.tachiyomi.widget.preference
import android.os.Bundle import android.os.Bundle
import android.support.v14.preference.PreferenceDialogFragment
import android.support.v7.preference.Preference import android.support.v7.preference.Preference
import android.support.v7.preference.PreferenceDialogFragmentCompat
import android.view.View import android.view.View
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault import eu.kanade.tachiyomi.data.preference.getOrDefault
import eu.kanade.tachiyomi.ui.setting.SettingsActivity import eu.kanade.tachiyomi.ui.setting.SettingsActivity
import kotlinx.android.synthetic.main.pref_library_columns.view.* import kotlinx.android.synthetic.main.pref_library_columns.view.*
class LibraryColumnsDialog : PreferenceDialogFragmentCompat() { class LibraryColumnsDialog : PreferenceDialogFragment() {
companion object { companion object {

View File

@ -1,18 +1,23 @@
package eu.kanade.tachiyomi.widget.preference package eu.kanade.tachiyomi.widget.preference
import android.support.v7.app.AlertDialog import android.app.Dialog
import android.support.v7.preference.PreferenceDialogFragmentCompat import android.app.DialogFragment
import android.content.DialogInterface
import android.content.Intent
import android.os.Bundle
import android.text.Editable import android.text.Editable
import android.text.TextWatcher import android.text.TextWatcher
import android.text.method.PasswordTransformationMethod import android.text.method.PasswordTransformationMethod
import android.view.View import android.view.View
import com.afollestad.materialdialogs.MaterialDialog
import com.dd.processbutton.iml.ActionProcessButton import com.dd.processbutton.iml.ActionProcessButton
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.ui.setting.SettingsActivity import eu.kanade.tachiyomi.ui.setting.SettingsActivity
import kotlinx.android.synthetic.main.pref_account_login.view.* import kotlinx.android.synthetic.main.pref_account_login.view.*
import rx.Subscription import rx.Subscription
abstract class LoginDialogPreference : PreferenceDialogFragmentCompat() { abstract class LoginDialogPreference : DialogFragment() {
var v: View? = null var v: View? = null
private set private set
@ -22,13 +27,18 @@ abstract class LoginDialogPreference : PreferenceDialogFragmentCompat() {
var requestSubscription: Subscription? = null var requestSubscription: Subscription? = null
override fun onPrepareDialogBuilder(builder: AlertDialog.Builder) { override fun onCreateDialog(savedState: Bundle?): Dialog {
// Hide positive button val dialog = MaterialDialog.Builder(activity)
builder.setPositiveButton("", this) .customView(R.layout.pref_account_login, false)
.negativeText(android.R.string.cancel)
.build();
onViewCreated(dialog.view, savedState)
return dialog
} }
override fun onBindDialogView(view: View) { override fun onViewCreated(view: View, savedState: Bundle?) {
super.onBindDialogView(view)
v = view.apply { v = view.apply {
show_password.setOnCheckedChangeListener { v, isChecked -> show_password.setOnCheckedChangeListener { v, isChecked ->
if (isChecked) if (isChecked)
@ -59,10 +69,16 @@ abstract class LoginDialogPreference : PreferenceDialogFragmentCompat() {
} }
override fun onDialogClosed(positiveResult: Boolean) { override fun onPause() {
super.onPause()
requestSubscription?.unsubscribe() requestSubscription?.unsubscribe()
} }
override fun onDismiss(dialog: DialogInterface) {
super.onDismiss(dialog)
targetFragment?.onActivityResult(targetRequestCode, arguments.getInt("key"), Intent())
}
protected abstract fun checkLogin() protected abstract fun checkLogin()
protected abstract fun setCredentialsOnView(view: View) protected abstract fun setCredentialsOnView(view: View)

View File

@ -0,0 +1,34 @@
package eu.kanade.tachiyomi.widget.preference
import android.content.Context
import android.support.v4.content.ContextCompat
import android.support.v7.preference.Preference
import android.support.v7.preference.PreferenceViewHolder
import android.util.AttributeSet
import eu.kanade.tachiyomi.R
import kotlinx.android.synthetic.main.preference_widget_imageview.view.*
class LoginPreference @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
Preference(context, attrs) {
init {
widgetLayoutResource = R.layout.preference_widget_imageview
}
override fun onBindViewHolder(holder: PreferenceViewHolder) {
super.onBindViewHolder(holder)
with(holder.itemView.image_view) {
if (getPersistedString("").isNullOrEmpty()) {
setImageResource(android.R.color.transparent)
} else {
setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_done_green_24dp))
}
}
}
override public fun notifyChanged() {
super.notifyChanged()
}
}

View File

@ -1,8 +1,6 @@
package eu.kanade.tachiyomi.widget.preference package eu.kanade.tachiyomi.widget.preference
import android.content.DialogInterface
import android.os.Bundle import android.os.Bundle
import android.support.v7.preference.Preference
import android.view.View import android.view.View
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.mangasync.base.MangaSyncService import eu.kanade.tachiyomi.data.mangasync.base.MangaSyncService
@ -16,10 +14,10 @@ class MangaSyncLoginDialog : LoginDialogPreference() {
companion object { companion object {
fun newInstance(preference: Preference): LoginDialogPreference { fun newInstance(sync: MangaSyncService): LoginDialogPreference {
val fragment = MangaSyncLoginDialog() val fragment = MangaSyncLoginDialog()
val bundle = Bundle(1) val bundle = Bundle(1)
bundle.putString("key", preference.key) bundle.putInt("key", sync.id)
fragment.arguments = bundle fragment.arguments = bundle
return fragment return fragment
} }
@ -30,12 +28,12 @@ class MangaSyncLoginDialog : LoginDialogPreference() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
val syncId = Integer.parseInt(arguments.getString("key")) val syncId = arguments.getInt("key")
sync = (activity as SettingsActivity).syncManager.getService(syncId) sync = (activity as SettingsActivity).syncManager.getService(syncId)
} }
override fun setCredentialsOnView(view: View) = with(view) { override fun setCredentialsOnView(view: View) = with(view) {
accounts_login.text = getString(R.string.accounts_login_title, sync.name) title.text = getString(R.string.login_title, sync.name)
username.setText(preferences.getMangaSyncUsername(sync)) username.setText(preferences.getMangaSyncUsername(sync))
password.setText(preferences.getMangaSyncPassword(sync)) password.setText(preferences.getMangaSyncPassword(sync))
} }
@ -58,8 +56,6 @@ class MangaSyncLoginDialog : LoginDialogPreference() {
username.text.toString(), username.text.toString(),
password.text.toString()) password.text.toString())
// Simulate a positive button click and dismiss the dialog
onClick(dialog, DialogInterface.BUTTON_POSITIVE)
dialog.dismiss() dialog.dismiss()
context.toast(R.string.login_success) context.toast(R.string.login_success)
} else { } else {

View File

@ -1,8 +1,6 @@
package eu.kanade.tachiyomi.widget.preference package eu.kanade.tachiyomi.widget.preference
import android.content.DialogInterface
import android.os.Bundle import android.os.Bundle
import android.support.v7.preference.Preference
import android.view.View import android.view.View
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.source.base.Source import eu.kanade.tachiyomi.data.source.base.Source
@ -16,10 +14,10 @@ class SourceLoginDialog : LoginDialogPreference() {
companion object { companion object {
fun newInstance(preference: Preference): LoginDialogPreference { fun newInstance(source: Source): LoginDialogPreference {
val fragment = SourceLoginDialog() val fragment = SourceLoginDialog()
val bundle = Bundle(1) val bundle = Bundle(1)
bundle.putString("key", preference.key) bundle.putInt("key", source.id)
fragment.arguments = bundle fragment.arguments = bundle
return fragment return fragment
} }
@ -30,12 +28,12 @@ class SourceLoginDialog : LoginDialogPreference() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
val sourceId = Integer.parseInt(arguments.getString("key")) val sourceId = arguments.getInt("key")
source = (activity as SettingsActivity).sourceManager.get(sourceId)!! source = (activity as SettingsActivity).sourceManager.get(sourceId)!!
} }
override fun setCredentialsOnView(view: View) = with(view) { override fun setCredentialsOnView(view: View) = with(view) {
accounts_login.text = getString(R.string.accounts_login_title, source.name) title.text = getString(R.string.login_title, source.visibleName)
username.setText(preferences.getSourceUsername(source)) username.setText(preferences.getSourceUsername(source))
password.setText(preferences.getSourcePassword(source)) password.setText(preferences.getSourcePassword(source))
} }
@ -58,8 +56,6 @@ class SourceLoginDialog : LoginDialogPreference() {
username.text.toString(), username.text.toString(),
password.text.toString()) password.text.toString())
// Simulate a positive button click and dismiss the dialog
onClick(dialog, DialogInterface.BUTTON_POSITIVE)
dialog.dismiss() dialog.dismiss()
context.toast(R.string.login_success) context.toast(R.string.login_success)
} else { } else {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF4CAF50"
android:pathData="M9,16.2L4.8,12l-1.4,1.4L9,19 21,7l-1.4,-1.4L9,16.2z"/>
</vector>

View File

@ -9,8 +9,8 @@
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/accounts_login_title" android:text="@string/login_title"
android:id="@+id/accounts_login" android:id="@+id/title"
android:textStyle="bold" android:textStyle="bold"
android:textSize="16sp" android:textSize="16sp"
android:layout_gravity="center_horizontal" /> android:layout_gravity="center_horizontal" />

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/image_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</ImageView>

View File

@ -2,10 +2,11 @@
<resources> <resources>
<string name="pref_category_general_key">pref_category_general_key</string> <string name="pref_category_general_key">pref_category_general_key</string>
<string name="pref_category_reader_key">pref_category_reader_key</string> <string name="pref_category_reader_key">pref_category_reader_key</string>
<string name="pref_category_accounts_key">pref_category_accounts_key</string> <string name="pref_category_sync_key">pref_category_sync_key</string>
<string name="pref_category_downloads_key">pref_category_downloads_key</string> <string name="pref_category_downloads_key">pref_category_downloads_key</string>
<string name="pref_category_advanced_key">pref_category_advanced_key</string> <string name="pref_category_advanced_key">pref_category_advanced_key</string>
<string name="pref_category_about_key">pref_category_about_key</string> <string name="pref_category_about_key">pref_category_about_key</string>
<string name="pref_category_sources_key">pref_category_sources_key</string>
<string name="pref_library_columns_dialog_key">pref_library_columns_dialog_key</string> <string name="pref_library_columns_dialog_key">pref_library_columns_dialog_key</string>
<string name="pref_library_columns_portrait_key">pref_library_columns_portrait_key</string> <string name="pref_library_columns_portrait_key">pref_library_columns_portrait_key</string>
@ -35,6 +36,8 @@
<string name="pref_download_slots_key">pref_download_slots_key</string> <string name="pref_download_slots_key">pref_download_slots_key</string>
<string name="pref_download_only_over_wifi_key">pref_download_only_over_wifi_key</string> <string name="pref_download_only_over_wifi_key">pref_download_only_over_wifi_key</string>
<string name="pref_source_languages">pref_source_languages</string>
<string name="pref_clear_chapter_cache_key">pref_clear_chapter_cache_key</string> <string name="pref_clear_chapter_cache_key">pref_clear_chapter_cache_key</string>
<string name="pref_clear_database_key">pref_clear_database_key</string> <string name="pref_clear_database_key">pref_clear_database_key</string>

View File

@ -63,8 +63,9 @@
<!-- Subsections --> <!-- Subsections -->
<string name="pref_category_general">General</string> <string name="pref_category_general">General</string>
<string name="pref_category_reader">Reader</string> <string name="pref_category_reader">Reader</string>
<string name="pref_category_accounts">Accounts</string>
<string name="pref_category_downloads">Downloads</string> <string name="pref_category_downloads">Downloads</string>
<string name="pref_category_sources">Sources</string>
<string name="pref_category_sync">Sync</string>
<string name="pref_category_advanced">Advanced</string> <string name="pref_category_advanced">Advanced</string>
<string name="pref_category_about">About</string> <string name="pref_category_about">About</string>
@ -124,13 +125,20 @@
<string name="rotation_force_portrait">Force portrait</string> <string name="rotation_force_portrait">Force portrait</string>
<string name="rotation_force_landscape">Force landscape</string> <string name="rotation_force_landscape">Force landscape</string>
<!-- Downloads section --> <!-- Downloads section -->
<string name="pref_download_directory">Downloads directory</string> <string name="pref_download_directory">Downloads directory</string>
<string name="pref_download_slots">Simultaneous downloads</string> <string name="pref_download_slots">Simultaneous downloads</string>
<string name="pref_download_only_over_wifi">Only download over Wi-Fi</string> <string name="pref_download_only_over_wifi">Only download over Wi-Fi</string>
<string name="custom_dir">Custom directory</string> <string name="custom_dir">Custom directory</string>
<!-- Sources section -->
<string name="languages">Languages</string>
<string name="languages_summary">Select the languages to show sources from</string>
<string name="accounts">Accounts</string>
<!-- Sync section -->
<string name="services">Services</string>
<!-- Advanced section --> <!-- Advanced section -->
<string name="pref_clear_chapter_cache">Clear chapter cache</string> <string name="pref_clear_chapter_cache">Clear chapter cache</string>
<string name="used_cache">Used: %1$s</string> <string name="used_cache">Used: %1$s</string>
@ -150,7 +158,7 @@
<!-- Login dialog --> <!-- Login dialog -->
<string name="accounts_login_title">Login for %1$s</string> <string name="login_title">Login for %1$s</string>
<string name="username">Username</string> <string name="username">Username</string>
<string name="password">Password</string> <string name="password">Password</string>
<string name="show_password">Show password</string> <string name="show_password">Show password</string>

View File

@ -1,16 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.preference.PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
android:orderingFromXml="true">
<PreferenceCategory
android:key="pref_category_source_accounts"
android:title="Sources"
android:persistent="false"/>
<PreferenceCategory
android:key="pref_category_manga_sync_accounts"
android:title="Sync"
android:persistent="false"/>
</android.support.v7.preference.PreferenceScreen>

View File

@ -21,15 +21,4 @@
android:title="@string/pref_update_only_non_completed" android:title="@string/pref_update_only_non_completed"
android:defaultValue="false"/> android:defaultValue="false"/>
<SwitchPreferenceCompat
android:key="@string/pref_auto_update_manga_sync_key"
android:title="@string/pref_auto_update_manga_sync"
android:defaultValue="true"/>
<SwitchPreferenceCompat
android:key="@string/pref_ask_update_manga_sync_key"
android:title="@string/pref_ask_update_manga_sync"
android:defaultValue="false"
android:dependency="@string/pref_auto_update_manga_sync_key"/>
</android.support.v7.preference.PreferenceScreen> </android.support.v7.preference.PreferenceScreen>

View File

@ -18,9 +18,14 @@
android:title="@string/pref_category_downloads" /> android:title="@string/pref_category_downloads" />
<Preference <Preference
android:key="@string/pref_category_accounts_key" android:key="@string/pref_category_sources_key"
android:persistent="false" android:persistent="false"
android:title="@string/pref_category_accounts" /> android:title="@string/pref_category_sources" />
<Preference
android:key="@string/pref_category_sync_key"
android:persistent="false"
android:title="@string/pref_category_sync" />
<Preference <Preference
android:key="@string/pref_category_advanced_key" android:key="@string/pref_category_advanced_key"

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.preference.PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
android:orderingFromXml="true">
<MultiSelectListPreference
android:key="@string/pref_source_languages"
android:title="@string/languages"
android:summary="@string/languages_summary"/>
<PreferenceCategory
android:key="pref_sources"
android:persistent="false"
android:title="@string/accounts"/>
</android.support.v7.preference.PreferenceScreen>

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.preference.PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
android:orderingFromXml="true">
<SwitchPreferenceCompat
android:key="@string/pref_auto_update_manga_sync_key"
android:title="@string/pref_auto_update_manga_sync"
android:defaultValue="true"/>
<SwitchPreferenceCompat
android:key="@string/pref_ask_update_manga_sync_key"
android:title="@string/pref_ask_update_manga_sync"
android:defaultValue="false"
android:dependency="@string/pref_auto_update_manga_sync_key"/>
<PreferenceCategory
android:key="pref_category_manga_sync_accounts"
android:title="@string/services"
android:persistent="false"/>
</android.support.v7.preference.PreferenceScreen>