Browse Source

Support for sources from different languages

len 9 years ago
parent
commit
689f2e7fbf
36 changed files with 407 additions and 173 deletions
  1. 2 2
      app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.java
  2. 5 3
      app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt
  3. 8 0
      app/src/main/java/eu/kanade/tachiyomi/data/source/Language.kt
  4. 2 2
      app/src/main/java/eu/kanade/tachiyomi/data/source/SourceManager.kt
  5. 9 1
      app/src/main/java/eu/kanade/tachiyomi/data/source/base/BaseSource.java
  6. 7 1
      app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Batoto.java
  7. 7 1
      app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Kissmanga.java
  8. 7 1
      app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Mangafox.java
  9. 7 1
      app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Mangahere.java
  10. 0 3
      app/src/main/java/eu/kanade/tachiyomi/injection/component/AppComponent.java
  11. 23 0
      app/src/main/java/eu/kanade/tachiyomi/ui/base/activity/BaseActivity.kt
  12. 6 2
      app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueFragment.kt
  13. 21 6
      app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CataloguePresenter.kt
  14. 1 1
      app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoFragment.java
  15. 0 76
      app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAccountsFragment.kt
  16. 12 8
      app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsActivity.kt
  17. 2 2
      app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsNestedFragment.kt
  18. 88 0
      app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsSourcesFragment.kt
  19. 52 0
      app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsSyncFragment.kt
  20. 2 2
      app/src/main/java/eu/kanade/tachiyomi/widget/preference/LibraryColumnsDialog.kt
  21. 25 9
      app/src/main/java/eu/kanade/tachiyomi/widget/preference/LoginDialogPreference.kt
  22. 34 0
      app/src/main/java/eu/kanade/tachiyomi/widget/preference/LoginPreference.kt
  23. 4 8
      app/src/main/java/eu/kanade/tachiyomi/widget/preference/MangaSyncLoginDialog.kt
  24. 4 8
      app/src/main/java/eu/kanade/tachiyomi/widget/preference/SourceLoginDialog.kt
  25. BIN
      app/src/main/res/drawable-xhdpi/bkg_shadow_img.9.png
  26. 9 0
      app/src/main/res/drawable/ic_done_green_24dp.xml
  27. 1 1
      app/src/main/res/layout/item_chapter.xml
  28. 2 2
      app/src/main/res/layout/pref_account_login.xml
  29. 7 0
      app/src/main/res/layout/preference_widget_imageview.xml
  30. 4 1
      app/src/main/res/values/keys.xml
  31. 11 3
      app/src/main/res/values/strings.xml
  32. 0 16
      app/src/main/res/xml/pref_accounts.xml
  33. 0 11
      app/src/main/res/xml/pref_general.xml
  34. 7 2
      app/src/main/res/xml/pref_main.xml
  35. 16 0
      app/src/main/res/xml/pref_sources.xml
  36. 22 0
      app/src/main/res/xml/pref_sync.xml

+ 2 - 2
app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.java

@@ -379,11 +379,11 @@ public class DownloadManager {
     }
 
     public File getAbsoluteMangaDirectory(Source source, Manga manga) {
-        String chapterRelativePath = source.getName() +
+        String mangaRelativePath = source.getVisibleName() +
                 File.separator +
                 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

+ 5 - 3
app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt

@@ -11,9 +11,7 @@ import eu.kanade.tachiyomi.data.source.base.Source
 import java.io.File
 import java.io.IOException
 
-fun <T> Preference<T>.getOrDefault(): T {
-    return get() ?: defaultValue()!!
-}
+fun <T> Preference<T>.getOrDefault(): T = get() ?: defaultValue()!!
 
 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)
     }
 
+    fun enabledLanguages(): Preference<MutableSet<String>> {
+        return rxPrefs.getStringSet(getKey(R.string.pref_source_languages), setOf("EN"))
+    }
+
     fun getSourceUsername(source: Source): String {
         return prefs.getString(SOURCE_ACCOUNT_USERNAME + source.id, "")
     }

+ 8 - 0
app/src/main/java/eu/kanade/tachiyomi/data/source/Language.kt

@@ -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)

+ 2 - 2
app/src/main/java/eu/kanade/tachiyomi/data/source/SourceManager.kt

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

+ 9 - 1
app/src/main/java/eu/kanade/tachiyomi/data/source/base/BaseSource.java

@@ -6,6 +6,7 @@ import java.util.List;
 
 import eu.kanade.tachiyomi.data.database.models.Chapter;
 import eu.kanade.tachiyomi.data.database.models.Manga;
+import eu.kanade.tachiyomi.data.source.Language;
 import eu.kanade.tachiyomi.data.source.model.MangasPage;
 import okhttp3.Headers;
 import okhttp3.Response;
@@ -24,9 +25,16 @@ public abstract class BaseSource {
         this.id = id;
     }
 
+    public abstract Language getLang();
+
     // Name of the source to display
     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
     public abstract String getBaseUrl();
 
@@ -86,6 +94,6 @@ public abstract class BaseSource {
 
     @Override
     public String toString() {
-        return getName();
+        return getVisibleName();
     }
 }

+ 7 - 1
app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Batoto.java

@@ -28,6 +28,8 @@ import java.util.regex.Pattern;
 import eu.kanade.tachiyomi.data.database.models.Chapter;
 import eu.kanade.tachiyomi.data.database.models.Manga;
 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.model.MangasPage;
 import eu.kanade.tachiyomi.data.source.model.Page;
@@ -40,7 +42,7 @@ import rx.Observable;
 
 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 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";
@@ -79,6 +81,10 @@ public class Batoto extends LoginSource {
         return BASE_URL;
     }
 
+    public Language getLang() {
+        return LanguageKt.getEN();
+    }
+
     @Override
     protected Headers.Builder headersBuilder() {
         Headers.Builder builder = super.headersBuilder();

+ 7 - 1
app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Kissmanga.java

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

+ 7 - 1
app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Mangafox.java

@@ -18,13 +18,15 @@ import java.util.Locale;
 
 import eu.kanade.tachiyomi.data.database.models.Chapter;
 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.model.MangasPage;
 import eu.kanade.tachiyomi.util.Parser;
 
 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 POPULAR_MANGAS_URL = BASE_URL + "/directory/%s";
     public static final String SEARCH_URL =
@@ -44,6 +46,10 @@ public class Mangafox extends Source {
         return BASE_URL;
     }
 
+    public Language getLang() {
+        return LanguageKt.getEN();
+    }
+
     @Override
     protected String getInitialPopularMangasUrl() {
         return String.format(POPULAR_MANGAS_URL, "");

+ 7 - 1
app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Mangahere.java

@@ -18,13 +18,15 @@ import java.util.Locale;
 
 import eu.kanade.tachiyomi.data.database.models.Chapter;
 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.model.MangasPage;
 import eu.kanade.tachiyomi.util.Parser;
 
 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 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";
@@ -43,6 +45,10 @@ public class Mangahere extends Source {
         return BASE_URL;
     }
 
+    public Language getLang() {
+        return LanguageKt.getEN();
+    }
+
     @Override
     protected String getInitialPopularMangasUrl() {
         return String.format(POPULAR_MANGAS_URL, "");

+ 0 - 3
app/src/main/java/eu/kanade/tachiyomi/injection/component/AppComponent.java

@@ -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.reader.ReaderPresenter;
 import eu.kanade.tachiyomi.ui.recent.RecentChaptersPresenter;
-import eu.kanade.tachiyomi.ui.setting.SettingsAccountsFragment;
 import eu.kanade.tachiyomi.ui.setting.SettingsActivity;
 
 @Singleton
@@ -48,8 +47,6 @@ public interface AppComponent {
     void inject(RecentChaptersPresenter recentChaptersPresenter);
 
     void inject(MangaActivity mangaActivity);
-    void inject(SettingsAccountsFragment settingsAccountsFragment);
-
     void inject(SettingsActivity settingsActivity);
 
     void inject(Source source);

+ 23 - 0
app/src/main/java/eu/kanade/tachiyomi/ui/base/activity/BaseActivity.kt

@@ -1,10 +1,15 @@
 package eu.kanade.tachiyomi.ui.base.activity
 
+import android.graphics.Color
 import android.os.Bundle
+import android.support.design.widget.Snackbar
 import android.support.v7.app.AppCompatActivity
 import android.support.v7.widget.Toolbar
 import android.view.MenuItem
+import android.view.View
+import android.widget.TextView
 import eu.kanade.tachiyomi.App
+import eu.kanade.tachiyomi.R
 import eu.kanade.tachiyomi.injection.component.AppComponent
 import icepick.Icepick
 import org.greenrobot.eventbus.EventBus
@@ -60,6 +65,24 @@ open class BaseActivity : AppCompatActivity() {
         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
         get() = App.get(this).component
 

+ 6 - 2
app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueFragment.kt

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

+ 21 - 6
app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CataloguePresenter.kt

@@ -5,6 +5,7 @@ import eu.kanade.tachiyomi.data.cache.CoverCache
 import eu.kanade.tachiyomi.data.database.DatabaseHelper
 import eu.kanade.tachiyomi.data.database.models.Manga
 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.base.Source
 import eu.kanade.tachiyomi.data.source.model.MangasPage
@@ -45,7 +46,7 @@ class CataloguePresenter : BasePresenter<CatalogueFragment>() {
     /**
      * Enabled sources.
      */
-    private val sources by lazy { sourceManager.sources }
+    val sources by lazy { getEnabledSources() }
 
     /**
      * 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.
      *
@@ -308,12 +316,19 @@ class CataloguePresenter : BasePresenter<CatalogueFragment>() {
     }
 
     /**
-     * Returns a list of enabled sources.
-     *
-     * TODO filter by enabled sources.
+     * Returns a list of enabled sources ordered by language and name.
      */
-    fun getEnabledSources(): List<Source> {
-        return sourceManager.sources
+    private fun getEnabledSources(): List<Source> {
+        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}" }
     }
 
     /**

+ 1 - 1
app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoFragment.java

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

+ 0 - 76
app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAccountsFragment.kt

@@ -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) {}
-
-}

+ 12 - 8
app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsActivity.kt

@@ -1,7 +1,7 @@
 package eu.kanade.tachiyomi.ui.setting
 
 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.data.cache.ChapterCache
 import eu.kanade.tachiyomi.data.database.DatabaseHelper
@@ -28,19 +28,19 @@ class SettingsActivity : BaseActivity() {
         setupToolbar(toolbar)
 
         if (savedState == null) {
-            supportFragmentManager.beginTransaction()
-                    .replace(R.id.settings_content,SettingsMainFragment())
+            fragmentManager.beginTransaction()
+                    .replace(R.id.settings_content, SettingsMainFragment())
                     .commit()
         }
     }
 
     override fun onBackPressed() {
-        if (!supportFragmentManager.popBackStackImmediate()) {
+        if (!fragmentManager.popBackStackImmediate()) {
             super.onBackPressed()
         }
     }
 
-    class SettingsMainFragment : PreferenceFragmentCompat() {
+    class SettingsMainFragment : PreferenceFragment() {
 
         override fun onCreatePreferences(savedState: Bundle?, s: String?) {
             addPreferencesFromResource(R.xml.pref_main)
@@ -57,8 +57,12 @@ class SettingsActivity : BaseActivity() {
                 SettingsDownloadsFragment.newInstance(R.xml.pref_downloads, R.string.pref_category_downloads)
             }
 
-            registerSubpreference(R.string.pref_category_accounts_key) {
-                SettingsAccountsFragment.newInstance(R.xml.pref_accounts, R.string.pref_category_accounts)
+            registerSubpreference(R.string.pref_category_sources_key) {
+                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) {
@@ -75,7 +79,7 @@ class SettingsActivity : BaseActivity() {
             (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 {
                 val fragment = func()
                 fragmentManager.beginTransaction()

+ 2 - 2
app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsNestedFragment.kt

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

+ 88 - 0
app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsSourcesFragment.kt

@@ -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()
+        }
+    }
+
+}

+ 52 - 0
app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsSyncFragment.kt

@@ -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()
+        }
+    }
+
+}

+ 2 - 2
app/src/main/java/eu/kanade/tachiyomi/widget/preference/LibraryColumnsDialog.kt

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

+ 25 - 9
app/src/main/java/eu/kanade/tachiyomi/widget/preference/LoginDialogPreference.kt

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

+ 34 - 0
app/src/main/java/eu/kanade/tachiyomi/widget/preference/LoginPreference.kt

@@ -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()
+    }
+
+}

+ 4 - 8
app/src/main/java/eu/kanade/tachiyomi/widget/preference/MangaSyncLoginDialog.kt

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

+ 4 - 8
app/src/main/java/eu/kanade/tachiyomi/widget/preference/SourceLoginDialog.kt

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

BIN
app/src/main/res/drawable-xhdpi/bkg_shadow_img.9.png


+ 9 - 0
app/src/main/res/drawable/ic_done_green_24dp.xml

@@ -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>

+ 1 - 1
app/src/main/res/layout/item_chapter.xml

@@ -96,7 +96,7 @@
             android:layout_alignParentTop="true"
             android:background="?android:selectableItemBackground"
             app:srcCompat="@drawable/ic_more_horiz_black_24dp"/>
-        
+
     </RelativeLayout>
 
 

+ 2 - 2
app/src/main/res/layout/pref_account_login.xml

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

+ 7 - 0
app/src/main/res/layout/preference_widget_imageview.xml

@@ -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>

+ 4 - 1
app/src/main/res/values/keys.xml

@@ -2,10 +2,11 @@
 <resources>
     <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_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_advanced_key">pref_category_advanced_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_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_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_database_key">pref_clear_database_key</string>
 

+ 11 - 3
app/src/main/res/values/strings.xml

@@ -63,8 +63,9 @@
       <!-- Subsections -->
     <string name="pref_category_general">General</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_sources">Sources</string>
+    <string name="pref_category_sync">Sync</string>
     <string name="pref_category_advanced">Advanced</string>
     <string name="pref_category_about">About</string>
 
@@ -124,13 +125,20 @@
     <string name="rotation_force_portrait">Force portrait</string>
     <string name="rotation_force_landscape">Force landscape</string>
 
-
       <!-- Downloads section -->
     <string name="pref_download_directory">Downloads directory</string>
     <string name="pref_download_slots">Simultaneous downloads</string>
     <string name="pref_download_only_over_wifi">Only download over Wi-Fi</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 -->
     <string name="pref_clear_chapter_cache">Clear chapter cache</string>
     <string name="used_cache">Used: %1$s</string>
@@ -150,7 +158,7 @@
 
 
     <!-- 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="password">Password</string>
     <string name="show_password">Show password</string>

+ 0 - 16
app/src/main/res/xml/pref_accounts.xml

@@ -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>

+ 0 - 11
app/src/main/res/xml/pref_general.xml

@@ -21,15 +21,4 @@
         android:title="@string/pref_update_only_non_completed"
         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>

+ 7 - 2
app/src/main/res/xml/pref_main.xml

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

+ 16 - 0
app/src/main/res/xml/pref_sources.xml

@@ -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>

+ 22 - 0
app/src/main/res/xml/pref_sync.xml

@@ -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>