Rewrote Theme
@ -100,7 +100,7 @@ apt {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
final SUPPORT_LIBRARY_VERSION = '23.2.0'
|
||||
final SUPPORT_LIBRARY_VERSION = '23.2.1'
|
||||
final DAGGER_VERSION = '2.0.2'
|
||||
final OKHTTP_VERSION = '3.2.0'
|
||||
final RETROFIT_VERSION = '2.0.0-beta4'
|
||||
|
@ -15,10 +15,10 @@
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:hardwareAccelerated="true"
|
||||
android:theme="@style/AppTheme" >
|
||||
android:theme="@style/Theme.Tachiyomi" >
|
||||
<activity
|
||||
android:name=".ui.main.MainActivity"
|
||||
android:theme="@style/AppTheme.BrandedLaunch">
|
||||
android:theme="@style/Theme.BrandedLaunch">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
|
@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.data.preference
|
||||
import android.content.Context
|
||||
import android.os.Environment
|
||||
import android.preference.PreferenceManager
|
||||
import android.support.annotation.StyleRes
|
||||
import com.f2prateek.rx.preferences.Preference
|
||||
import com.f2prateek.rx.preferences.RxSharedPreferences
|
||||
import eu.kanade.tachiyomi.R
|
||||
@ -188,6 +189,10 @@ class PreferencesHelper(private val context: Context) {
|
||||
return rxPrefs.getInteger(getKey(R.string.pref_library_update_interval_key), 0)
|
||||
}
|
||||
|
||||
fun getTheme(): Preference<Int> {
|
||||
return rxPrefs.getInteger(getKey(R.string.pref_theme_key), R.style.Theme_Tachiyomi)
|
||||
}
|
||||
|
||||
fun filterDownloaded(): Preference<Boolean> {
|
||||
return rxPrefs.getBoolean(getKey(R.string.pref_filter_downloaded), false)
|
||||
}
|
||||
|
@ -66,14 +66,14 @@ open class BaseActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
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 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)) {
|
||||
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() })
|
||||
|
@ -20,7 +20,7 @@ import eu.kanade.tachiyomi.ui.base.decoration.DividerItemDecoration
|
||||
import eu.kanade.tachiyomi.ui.base.fragment.BaseRxFragment
|
||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
||||
import eu.kanade.tachiyomi.ui.manga.MangaActivity
|
||||
import eu.kanade.tachiyomi.util.ToastUtil
|
||||
import eu.kanade.tachiyomi.util.toast
|
||||
import eu.kanade.tachiyomi.widget.EndlessGridScrollListener
|
||||
import eu.kanade.tachiyomi.widget.EndlessListScrollListener
|
||||
import kotlinx.android.synthetic.main.fragment_catalogue.*
|
||||
@ -179,7 +179,7 @@ class CatalogueFragment : BaseRxFragment<CataloguePresenter>(), FlexibleViewHold
|
||||
// Set previous selection if it's not a valid source and notify the user
|
||||
if (!presenter.isValidSource(source)) {
|
||||
spinner.setSelection(presenter.findFirstValidSource())
|
||||
ToastUtil.showShort(activity, R.string.source_requires_login)
|
||||
context.toast(R.string.source_requires_login)
|
||||
} else {
|
||||
selectedIndex = position
|
||||
presenter.setEnabledSource(selectedIndex)
|
||||
@ -431,7 +431,7 @@ class CatalogueFragment : BaseRxFragment<CataloguePresenter>(), FlexibleViewHold
|
||||
val selectedManga = adapter.getItem(position)
|
||||
|
||||
val intent = MangaActivity.newIntent(activity, selectedManga)
|
||||
intent.putExtra(MangaActivity.MANGA_ONLINE, true)
|
||||
intent.putExtra(MangaActivity.FROM_CATALOGUE, true)
|
||||
startActivity(intent)
|
||||
return false
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import android.support.v4.content.ContextCompat
|
||||
import android.view.View
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.util.getResourceColor
|
||||
import kotlinx.android.synthetic.main.item_catalogue_list.view.*
|
||||
|
||||
/**
|
||||
@ -18,8 +19,8 @@ import kotlinx.android.synthetic.main.item_catalogue_list.view.*
|
||||
class CatalogueListHolder(private val view: View, adapter: CatalogueAdapter, listener: OnListItemClickListener) :
|
||||
CatalogueHolder(view, adapter, listener) {
|
||||
|
||||
private val favoriteColor = ContextCompat.getColor(view.context, R.color.hint_text)
|
||||
private val unfavoriteColor = ContextCompat.getColor(view.context, R.color.primary_text)
|
||||
private val favoriteColor = view.context.theme.getResourceColor(android.R.attr.textColorHint)
|
||||
private val unfavoriteColor = view.context.theme.getResourceColor(android.R.attr.textColorPrimary)
|
||||
|
||||
/**
|
||||
* Method called from [CatalogueAdapter.onBindViewHolder]. It updates the data for this
|
||||
|
@ -12,7 +12,9 @@ import android.view.MenuItem
|
||||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.util.setTheme
|
||||
import eu.kanade.tachiyomi.data.database.models.Category
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.ui.base.activity.BaseRxActivity
|
||||
import eu.kanade.tachiyomi.ui.base.adapter.FlexibleViewHolder
|
||||
import eu.kanade.tachiyomi.ui.base.adapter.OnStartDragListener
|
||||
@ -57,6 +59,7 @@ class CategoryActivity : BaseRxActivity<CategoryPresenter>(), ActionMode.Callbac
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
this.setTheme()
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
// Inflate activity_edit_categories.xml.
|
||||
|
@ -8,7 +8,7 @@ import eu.kanade.tachiyomi.data.download.DownloadService
|
||||
import eu.kanade.tachiyomi.data.download.model.Download
|
||||
import eu.kanade.tachiyomi.ui.base.fragment.BaseRxFragment
|
||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
||||
import eu.kanade.tachiyomi.util.setInformationDrawable
|
||||
import eu.kanade.tachiyomi.util.setDrawableCompat
|
||||
import kotlinx.android.synthetic.main.activity_main.*
|
||||
import kotlinx.android.synthetic.main.fragment_download_queue.*
|
||||
import nucleus.factory.RequiresPresenter
|
||||
@ -191,7 +191,7 @@ class DownloadFragment : BaseRxFragment<DownloadPresenter>() {
|
||||
*/
|
||||
private fun setInformationView() {
|
||||
if (presenter.downloadQueue.isEmpty()) {
|
||||
( activity as MainActivity).image_view.setInformationDrawable(R.drawable.ic_file_download_grey_128dp)
|
||||
( activity as MainActivity).image_view.setDrawableCompat(R.drawable.ic_file_download_grey_128dp)
|
||||
( activity as MainActivity).text_label.text = getString(R.string.information_no_downloads)
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,6 @@ import android.support.design.widget.TabLayout
|
||||
import android.support.v7.view.ActionMode
|
||||
import android.support.v7.widget.SearchView
|
||||
import android.view.*
|
||||
import butterknife.ButterKnife
|
||||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.kanade.tachiyomi.R
|
||||
@ -20,9 +19,8 @@ import eu.kanade.tachiyomi.event.LibraryMangasEvent
|
||||
import eu.kanade.tachiyomi.ui.base.fragment.BaseRxFragment
|
||||
import eu.kanade.tachiyomi.ui.category.CategoryActivity
|
||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
||||
import eu.kanade.tachiyomi.util.ToastUtil
|
||||
import eu.kanade.tachiyomi.util.inflate
|
||||
import eu.kanade.tachiyomi.util.setInformationDrawable
|
||||
import eu.kanade.tachiyomi.util.setDrawableCompat
|
||||
import eu.kanade.tachiyomi.util.toast
|
||||
import kotlinx.android.synthetic.main.activity_main.*
|
||||
import kotlinx.android.synthetic.main.fragment_library.*
|
||||
@ -127,7 +125,6 @@ class LibraryFragment : BaseRxFragment<LibraryPresenter>(), ActionMode.Callback
|
||||
|
||||
override fun onViewCreated(view: View, savedState: Bundle?) {
|
||||
setToolbarTitle(getString(R.string.label_library))
|
||||
ButterKnife.bind(this, view)
|
||||
|
||||
appBar = (activity as MainActivity).appbar
|
||||
tabs = appBar.inflate(R.layout.library_tab_layout) as TabLayout
|
||||
@ -264,10 +261,10 @@ class LibraryFragment : BaseRxFragment<LibraryPresenter>(), ActionMode.Callback
|
||||
fun onNextLibraryUpdate(categories: List<Category>, mangaMap: Map<Int, List<Manga>>) {
|
||||
// Check if library is empty and update information accordingly.
|
||||
if (mangaMap.isEmpty()) {
|
||||
(activity as MainActivity).image_view.setInformationDrawable(R.drawable.ic_book_grey_128dp)
|
||||
(activity as MainActivity).image_view.setDrawableCompat(R.drawable.ic_book_grey_128dp)
|
||||
(activity as MainActivity).text_label.text = getString(R.string.information_empty_library)
|
||||
} else {
|
||||
( activity as MainActivity).image_view.setInformationDrawable(null)
|
||||
( activity as MainActivity).image_view.setDrawableCompat(null)
|
||||
( activity as MainActivity).text_label.text = ""
|
||||
}
|
||||
|
||||
@ -380,7 +377,7 @@ class LibraryFragment : BaseRxFragment<LibraryPresenter>(), ActionMode.Callback
|
||||
startActivityForResult(Intent.createChooser(intent,
|
||||
getString(R.string.file_select_cover)), REQUEST_IMAGE_OPEN)
|
||||
} else {
|
||||
ToastUtil.showShort(context, R.string.notification_first_add_to_library)
|
||||
context.toast(R.string.notification_first_add_to_library)
|
||||
}
|
||||
|
||||
}
|
||||
@ -430,8 +427,8 @@ class LibraryFragment : BaseRxFragment<LibraryPresenter>(), ActionMode.Callback
|
||||
destroyActionModeIfNeeded()
|
||||
true
|
||||
}
|
||||
.positiveText(R.string.button_ok)
|
||||
.negativeText(R.string.button_cancel)
|
||||
.positiveText(android.R.string.ok)
|
||||
.negativeText(android.R.string.cancel)
|
||||
.show()
|
||||
}
|
||||
|
||||
|
@ -6,13 +6,15 @@ import android.support.v4.app.Fragment
|
||||
import android.support.v4.view.GravityCompat
|
||||
import android.view.MenuItem
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.ui.base.activity.BaseActivity
|
||||
import eu.kanade.tachiyomi.ui.catalogue.CatalogueFragment
|
||||
import eu.kanade.tachiyomi.ui.download.DownloadFragment
|
||||
import eu.kanade.tachiyomi.ui.library.LibraryFragment
|
||||
import eu.kanade.tachiyomi.ui.recent.RecentChaptersFragment
|
||||
import eu.kanade.tachiyomi.ui.setting.SettingsActivity
|
||||
import eu.kanade.tachiyomi.util.setInformationDrawable
|
||||
import eu.kanade.tachiyomi.util.setDrawableCompat
|
||||
import eu.kanade.tachiyomi.util.setTheme
|
||||
import kotlinx.android.synthetic.main.activity_main.*
|
||||
import kotlinx.android.synthetic.main.toolbar.*
|
||||
import nucleus.view.ViewWithPresenter
|
||||
@ -22,7 +24,7 @@ class MainActivity : BaseActivity() {
|
||||
|
||||
|
||||
override fun onCreate(savedState: Bundle?) {
|
||||
setTheme(R.style.AppTheme);
|
||||
this.setTheme()
|
||||
super.onCreate(savedState)
|
||||
|
||||
// Do not let the launcher create a new activity
|
||||
@ -48,7 +50,7 @@ class MainActivity : BaseActivity() {
|
||||
nav_view.setNavigationItemSelectedListener(
|
||||
{ menuItem ->
|
||||
// Make information view invisible
|
||||
image_view.setInformationDrawable(null)
|
||||
image_view.setDrawableCompat(null)
|
||||
text_label.text = ""
|
||||
|
||||
when (menuItem.itemId) {
|
||||
@ -84,6 +86,8 @@ class MainActivity : BaseActivity() {
|
||||
setFragment(LibraryFragment.newInstance())
|
||||
}
|
||||
|
||||
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
android.R.id.home -> {
|
||||
|
@ -1,164 +0,0 @@
|
||||
package eu.kanade.tachiyomi.ui.manga;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.design.widget.TabLayout;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.support.v4.app.FragmentPagerAdapter;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.support.v4.view.ViewPager;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import butterknife.Bind;
|
||||
import butterknife.ButterKnife;
|
||||
import eu.kanade.tachiyomi.App;
|
||||
import eu.kanade.tachiyomi.R;
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga;
|
||||
import eu.kanade.tachiyomi.data.mangasync.MangaSyncManager;
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper;
|
||||
import eu.kanade.tachiyomi.ui.base.activity.BaseRxActivity;
|
||||
import eu.kanade.tachiyomi.ui.manga.chapter.ChaptersFragment;
|
||||
import eu.kanade.tachiyomi.ui.manga.info.MangaInfoFragment;
|
||||
import eu.kanade.tachiyomi.ui.manga.myanimelist.MyAnimeListFragment;
|
||||
import nucleus.factory.RequiresPresenter;
|
||||
|
||||
@RequiresPresenter(MangaPresenter.class)
|
||||
public class MangaActivity extends BaseRxActivity<MangaPresenter> {
|
||||
|
||||
@Bind(R.id.toolbar) Toolbar toolbar;
|
||||
@Bind(R.id.tabs) TabLayout tabs;
|
||||
@Bind(R.id.view_pager) ViewPager viewPager;
|
||||
|
||||
@Inject PreferencesHelper preferences;
|
||||
@Inject MangaSyncManager mangaSyncManager;
|
||||
|
||||
private MangaDetailAdapter adapter;
|
||||
private boolean isOnline;
|
||||
|
||||
public final static String MANGA_ONLINE = "manga_online";
|
||||
|
||||
public static Intent newIntent(Context context, Manga manga) {
|
||||
Intent intent = new Intent(context, MangaActivity.class);
|
||||
if (manga != null) {
|
||||
EventBus.getDefault().postSticky(manga);
|
||||
}
|
||||
return intent;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedState) {
|
||||
super.onCreate(savedState);
|
||||
App.get(this).getComponent().inject(this);
|
||||
setContentView(R.layout.activity_manga);
|
||||
ButterKnife.bind(this);
|
||||
|
||||
setupToolbar(toolbar);
|
||||
|
||||
Intent intent = getIntent();
|
||||
|
||||
isOnline = intent.getBooleanExtra(MANGA_ONLINE, false);
|
||||
|
||||
setupViewPager();
|
||||
|
||||
requestPermissionsOnMarshmallow();
|
||||
}
|
||||
|
||||
private void setupViewPager() {
|
||||
adapter = new MangaDetailAdapter(getSupportFragmentManager(), this);
|
||||
|
||||
viewPager.setAdapter(adapter);
|
||||
|
||||
// Workaround to prevent: Tab belongs to a different TabLayout.
|
||||
// Internal bug in Support library v23.2.0.
|
||||
// See https://code.google.com/p/android/issues/detail?id=201827
|
||||
for (int j = 0; j < 17; j++)
|
||||
tabs.newTab();
|
||||
|
||||
tabs.setupWithViewPager(viewPager);
|
||||
|
||||
if (!isOnline)
|
||||
viewPager.setCurrentItem(MangaDetailAdapter.CHAPTERS_FRAGMENT);
|
||||
}
|
||||
|
||||
public void setManga(Manga manga) {
|
||||
setToolbarTitle(manga.title);
|
||||
}
|
||||
|
||||
public boolean isCatalogueManga() {
|
||||
return isOnline;
|
||||
}
|
||||
|
||||
private void requestPermissionsOnMarshmallow() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
if (ContextCompat.checkSelfPermission(this,
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
|
||||
ActivityCompat.requestPermissions(this,
|
||||
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE,
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE},
|
||||
1);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MangaDetailAdapter extends FragmentPagerAdapter {
|
||||
|
||||
private int pageCount;
|
||||
private String tabTitles[];
|
||||
|
||||
final static int INFO_FRAGMENT = 0;
|
||||
final static int CHAPTERS_FRAGMENT = 1;
|
||||
final static int MYANIMELIST_FRAGMENT = 2;
|
||||
|
||||
public MangaDetailAdapter(FragmentManager fm, Context context) {
|
||||
super(fm);
|
||||
tabTitles = new String[]{
|
||||
context.getString(R.string.manga_detail_tab),
|
||||
context.getString(R.string.manga_chapters_tab),
|
||||
"MAL"
|
||||
};
|
||||
|
||||
pageCount = 2;
|
||||
if (!isOnline && mangaSyncManager.getMyAnimeList().isLogged())
|
||||
pageCount++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return pageCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fragment getItem(int position) {
|
||||
switch (position) {
|
||||
case INFO_FRAGMENT:
|
||||
return MangaInfoFragment.newInstance();
|
||||
case CHAPTERS_FRAGMENT:
|
||||
return ChaptersFragment.newInstance();
|
||||
case MYANIMELIST_FRAGMENT:
|
||||
return MyAnimeListFragment.newInstance();
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getPageTitle(int position) {
|
||||
// Generate title based on item position
|
||||
return tabTitles[position];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
118
app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaActivity.kt
Normal file
@ -0,0 +1,118 @@
|
||||
package eu.kanade.tachiyomi.ui.manga
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.support.v4.app.ActivityCompat
|
||||
import android.support.v4.app.Fragment
|
||||
import android.support.v4.app.FragmentManager
|
||||
import android.support.v4.app.FragmentPagerAdapter
|
||||
import android.support.v4.content.ContextCompat
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.ui.base.activity.BaseRxActivity
|
||||
import eu.kanade.tachiyomi.ui.manga.chapter.ChaptersFragment
|
||||
import eu.kanade.tachiyomi.ui.manga.info.MangaInfoFragment
|
||||
import eu.kanade.tachiyomi.ui.manga.myanimelist.MyAnimeListFragment
|
||||
import kotlinx.android.synthetic.main.activity_manga.*
|
||||
import kotlinx.android.synthetic.main.tab_layout.*
|
||||
import kotlinx.android.synthetic.main.toolbar.*
|
||||
import nucleus.factory.RequiresPresenter
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
|
||||
@RequiresPresenter(MangaPresenter::class)
|
||||
class MangaActivity : BaseRxActivity<MangaPresenter>() {
|
||||
|
||||
companion object {
|
||||
|
||||
val FROM_CATALOGUE = "from_catalogue"
|
||||
val INFO_FRAGMENT = 0
|
||||
val CHAPTERS_FRAGMENT = 1
|
||||
val MYANIMELIST_FRAGMENT = 2
|
||||
|
||||
fun newIntent(context: Context, manga: Manga?): Intent {
|
||||
val intent = Intent(context, MangaActivity::class.java)
|
||||
if (manga != null) {
|
||||
EventBus.getDefault().postSticky(manga)
|
||||
}
|
||||
return intent
|
||||
}
|
||||
}
|
||||
|
||||
private lateinit var adapter: MangaDetailAdapter
|
||||
|
||||
var isCatalogueManga: Boolean = false
|
||||
private set
|
||||
|
||||
override fun onCreate(savedState: Bundle?) {
|
||||
super.onCreate(savedState)
|
||||
setContentView(R.layout.activity_manga)
|
||||
|
||||
setupToolbar(toolbar)
|
||||
|
||||
isCatalogueManga = intent.getBooleanExtra(FROM_CATALOGUE, false)
|
||||
|
||||
adapter = MangaDetailAdapter(supportFragmentManager, this)
|
||||
view_pager.adapter = adapter
|
||||
|
||||
tabs.setupWithViewPager(view_pager)
|
||||
|
||||
if (!isCatalogueManga)
|
||||
view_pager.currentItem = CHAPTERS_FRAGMENT
|
||||
|
||||
requestPermissionsOnMarshmallow()
|
||||
}
|
||||
|
||||
fun onSetManga(manga: Manga) {
|
||||
setToolbarTitle(manga.title)
|
||||
}
|
||||
|
||||
private fun requestPermissionsOnMarshmallow() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
if (ContextCompat.checkSelfPermission(this,
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
|
||||
|
||||
ActivityCompat.requestPermissions(this,
|
||||
arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE),
|
||||
1)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class MangaDetailAdapter(fm: FragmentManager, activity: MangaActivity) : FragmentPagerAdapter(fm) {
|
||||
|
||||
private var pageCount: Int = 0
|
||||
private val tabTitles = arrayOf(activity.getString(R.string.manga_detail_tab),
|
||||
activity.getString(R.string.manga_chapters_tab), "MAL")
|
||||
|
||||
init {
|
||||
pageCount = 2
|
||||
if (!activity.isCatalogueManga && activity.presenter.syncManager.myAnimeList.isLogged)
|
||||
pageCount++
|
||||
}
|
||||
|
||||
override fun getCount(): Int {
|
||||
return pageCount
|
||||
}
|
||||
|
||||
override fun getItem(position: Int): Fragment? {
|
||||
when (position) {
|
||||
INFO_FRAGMENT -> return MangaInfoFragment.newInstance()
|
||||
CHAPTERS_FRAGMENT -> return ChaptersFragment.newInstance()
|
||||
MYANIMELIST_FRAGMENT -> return MyAnimeListFragment.newInstance()
|
||||
else -> return null
|
||||
}
|
||||
}
|
||||
|
||||
override fun getPageTitle(position: Int): CharSequence {
|
||||
// Generate title based on item position
|
||||
return tabTitles[position]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
package eu.kanade.tachiyomi.ui.manga;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.greenrobot.eventbus.Subscribe;
|
||||
import org.greenrobot.eventbus.ThreadMode;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper;
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga;
|
||||
import eu.kanade.tachiyomi.event.MangaEvent;
|
||||
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter;
|
||||
import icepick.State;
|
||||
import rx.Observable;
|
||||
|
||||
public class MangaPresenter extends BasePresenter<MangaActivity> {
|
||||
|
||||
@Inject DatabaseHelper db;
|
||||
|
||||
@State Manga manga;
|
||||
|
||||
private static final int GET_MANGA = 1;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedState) {
|
||||
super.onCreate(savedState);
|
||||
|
||||
restartableLatestCache(GET_MANGA, this::getMangaObservable, MangaActivity::setManga);
|
||||
|
||||
if (savedState == null)
|
||||
registerForEvents();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
// Avoid new instances receiving wrong manga
|
||||
EventBus.getDefault().removeStickyEvent(MangaEvent.class);
|
||||
}
|
||||
|
||||
private Observable<Manga> getMangaObservable() {
|
||||
return Observable.just(manga)
|
||||
.doOnNext(manga -> EventBus.getDefault().postSticky(new MangaEvent(manga)));
|
||||
}
|
||||
|
||||
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
|
||||
public void onEvent(Manga manga) {
|
||||
EventBus.getDefault().removeStickyEvent(manga);
|
||||
unregisterForEvents();
|
||||
this.manga = manga;
|
||||
start(GET_MANGA);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
package eu.kanade.tachiyomi.ui.manga
|
||||
|
||||
import android.os.Bundle
|
||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.data.mangasync.MangaSyncManager
|
||||
import eu.kanade.tachiyomi.event.MangaEvent
|
||||
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
import rx.Observable
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Presenter of [MangaActivity].
|
||||
*/
|
||||
class MangaPresenter : BasePresenter<MangaActivity>() {
|
||||
|
||||
/**
|
||||
* Database helper.
|
||||
*/
|
||||
@Inject lateinit var db: DatabaseHelper
|
||||
|
||||
/**
|
||||
* Manga sync manager.
|
||||
*/
|
||||
@Inject lateinit var syncManager: MangaSyncManager
|
||||
|
||||
/**
|
||||
* Manga associated with this instance.
|
||||
*/
|
||||
lateinit var manga: Manga
|
||||
|
||||
/**
|
||||
* Key to save and restore [manga] from a bundle.
|
||||
*/
|
||||
private val MANGA_KEY = "manga_key"
|
||||
|
||||
/**
|
||||
* Id of the restartable that notifies the view of a manga.
|
||||
*/
|
||||
private val GET_MANGA = 1
|
||||
|
||||
override fun onCreate(savedState: Bundle?) {
|
||||
super.onCreate(savedState)
|
||||
|
||||
if (savedState != null) {
|
||||
manga = savedState.getSerializable(MANGA_KEY) as Manga
|
||||
}
|
||||
|
||||
restartableLatestCache(GET_MANGA,
|
||||
{ Observable.just(manga)
|
||||
.doOnNext { EventBus.getDefault().postSticky(MangaEvent(it)) } },
|
||||
{ view, manga -> view.onSetManga(manga) })
|
||||
|
||||
if (savedState == null) {
|
||||
registerForEvents()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
// Avoid new instances receiving wrong manga
|
||||
EventBus.getDefault().removeStickyEvent(MangaEvent::class.java)
|
||||
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
override fun onSave(state: Bundle) {
|
||||
state.putSerializable(MANGA_KEY, manga)
|
||||
super.onSave(state)
|
||||
}
|
||||
|
||||
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
|
||||
fun onEvent(manga: Manga) {
|
||||
EventBus.getDefault().removeStickyEvent(manga)
|
||||
unregisterForEvents()
|
||||
this.manga = manga
|
||||
start(GET_MANGA)
|
||||
}
|
||||
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
package eu.kanade.tachiyomi.ui.manga.chapter;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter;
|
||||
import eu.kanade.tachiyomi.R;
|
||||
import eu.kanade.tachiyomi.data.database.models.Chapter;
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga;
|
||||
|
||||
public class ChaptersAdapter extends FlexibleAdapter<ChaptersHolder, Chapter> {
|
||||
|
||||
private ChaptersFragment fragment;
|
||||
|
||||
public ChaptersAdapter(ChaptersFragment fragment) {
|
||||
this.fragment = fragment;
|
||||
mItems = new ArrayList<>();
|
||||
setHasStableIds(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateDataSet(String param) {}
|
||||
|
||||
@Override
|
||||
public ChaptersHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
View v = LayoutInflater.from(fragment.getActivity()).inflate(R.layout.item_chapter, parent, false);
|
||||
return new ChaptersHolder(v, this, fragment);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(ChaptersHolder holder, int position) {
|
||||
final Chapter chapter = getItem(position);
|
||||
final Manga manga = fragment.getPresenter().getManga();
|
||||
holder.onSetValues(chapter, manga);
|
||||
|
||||
//When user scrolls this bind the correct selection status
|
||||
holder.itemView.setActivated(isSelected(position));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return mItems.get(position).id;
|
||||
}
|
||||
|
||||
public void setItems(List<Chapter> chapters) {
|
||||
mItems = chapters;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public ChaptersFragment getFragment() {
|
||||
return fragment;
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package eu.kanade.tachiyomi.ui.manga.chapter
|
||||
|
||||
import android.view.ViewGroup
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.Chapter
|
||||
import eu.kanade.tachiyomi.util.inflate
|
||||
|
||||
class ChaptersAdapter(val fragment: ChaptersFragment) : FlexibleAdapter<ChaptersHolder, Chapter>() {
|
||||
|
||||
init {
|
||||
setHasStableIds(true)
|
||||
}
|
||||
|
||||
override fun updateDataSet(param: String) {
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ChaptersHolder {
|
||||
val v = parent.inflate(R.layout.item_chapter)
|
||||
return ChaptersHolder(v, this, fragment)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ChaptersHolder, position: Int) {
|
||||
val chapter = getItem(position)
|
||||
val manga = fragment.presenter.manga
|
||||
holder.onSetValues(chapter, manga)
|
||||
|
||||
//When user scrolls this bind the correct selection status
|
||||
holder.itemView.isActivated = isSelected(position)
|
||||
}
|
||||
|
||||
override fun getItemId(position: Int): Long {
|
||||
return mItems[position].id
|
||||
}
|
||||
|
||||
fun setItems(chapters: List<Chapter>) {
|
||||
mItems = chapters
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
@ -1,439 +0,0 @@
|
||||
package eu.kanade.tachiyomi.ui.manga.chapter;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.support.v4.widget.SwipeRefreshLayout;
|
||||
import android.support.v7.view.ActionMode;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import com.afollestad.materialdialogs.MaterialDialog;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import butterknife.Bind;
|
||||
import butterknife.ButterKnife;
|
||||
import eu.kanade.tachiyomi.R;
|
||||
import eu.kanade.tachiyomi.data.database.models.Chapter;
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga;
|
||||
import eu.kanade.tachiyomi.data.download.DownloadService;
|
||||
import eu.kanade.tachiyomi.data.download.model.Download;
|
||||
import eu.kanade.tachiyomi.ui.base.adapter.FlexibleViewHolder;
|
||||
import eu.kanade.tachiyomi.ui.base.decoration.DividerItemDecoration;
|
||||
import eu.kanade.tachiyomi.ui.base.fragment.BaseRxFragment;
|
||||
import eu.kanade.tachiyomi.ui.manga.MangaActivity;
|
||||
import eu.kanade.tachiyomi.ui.reader.ReaderActivity;
|
||||
import eu.kanade.tachiyomi.util.ToastUtil;
|
||||
import nucleus.factory.RequiresPresenter;
|
||||
import rx.Observable;
|
||||
import rx.Subscription;
|
||||
import rx.android.schedulers.AndroidSchedulers;
|
||||
import rx.schedulers.Schedulers;
|
||||
|
||||
@RequiresPresenter(ChaptersPresenter.class)
|
||||
public class ChaptersFragment extends BaseRxFragment<ChaptersPresenter> implements
|
||||
ActionMode.Callback, FlexibleViewHolder.OnListItemClickListener {
|
||||
|
||||
@Bind(R.id.chapter_list) RecyclerView recyclerView;
|
||||
@Bind(R.id.swipe_refresh) SwipeRefreshLayout swipeRefresh;
|
||||
@Bind(R.id.toolbar_bottom) ViewGroup toolbarBottom;
|
||||
|
||||
@Bind(R.id.action_sort) ImageView sortBtn;
|
||||
@Bind(R.id.action_next_unread) ImageView nextUnreadBtn;
|
||||
@Bind(R.id.action_show_unread) CheckBox readCb;
|
||||
@Bind(R.id.action_show_downloaded) CheckBox downloadedCb;
|
||||
|
||||
private ChaptersAdapter adapter;
|
||||
private LinearLayoutManager linearLayout;
|
||||
private ActionMode actionMode;
|
||||
|
||||
private Subscription downloadProgressSubscription;
|
||||
|
||||
public static ChaptersFragment newInstance() {
|
||||
return new ChaptersFragment();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle bundle) {
|
||||
super.onCreate(bundle);
|
||||
setHasOptionsMenu(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
// Inflate the layout for this fragment
|
||||
View view = inflater.inflate(R.layout.fragment_manga_chapters, container, false);
|
||||
ButterKnife.bind(this, view);
|
||||
|
||||
// Init RecyclerView and adapter
|
||||
linearLayout = new LinearLayoutManager(getActivity());
|
||||
recyclerView.setLayoutManager(linearLayout);
|
||||
recyclerView.addItemDecoration(new DividerItemDecoration(
|
||||
ContextCompat.getDrawable(getContext(), R.drawable.line_divider)));
|
||||
recyclerView.setHasFixedSize(true);
|
||||
adapter = new ChaptersAdapter(this);
|
||||
recyclerView.setAdapter(adapter);
|
||||
|
||||
swipeRefresh.setOnRefreshListener(this::fetchChapters);
|
||||
|
||||
nextUnreadBtn.setOnClickListener(v -> {
|
||||
Chapter chapter = getPresenter().getNextUnreadChapter();
|
||||
if (chapter != null) {
|
||||
openChapter(chapter);
|
||||
} else {
|
||||
ToastUtil.showShort(getContext(), R.string.no_next_chapter);
|
||||
}
|
||||
});
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
// Stop recycler's scrolling when onPause is called. If the activity is finishing
|
||||
// the presenter will be destroyed, and it could cause NPE
|
||||
// https://github.com/inorichi/tachiyomi/issues/159
|
||||
recyclerView.stopScroll();
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
inflater.inflate(R.menu.chapters, menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.action_display_mode:
|
||||
showDisplayModeDialog();
|
||||
return true;
|
||||
case R.id.manga_download:
|
||||
showDownloadDialog();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void onNextManga(Manga manga) {
|
||||
// Remove listeners before setting the values
|
||||
readCb.setOnCheckedChangeListener(null);
|
||||
downloadedCb.setOnCheckedChangeListener(null);
|
||||
sortBtn.setOnClickListener(null);
|
||||
|
||||
// Set initial values
|
||||
setReadFilter();
|
||||
setDownloadedFilter();
|
||||
setSortIcon();
|
||||
|
||||
// Init listeners
|
||||
readCb.setOnCheckedChangeListener((arg, isChecked) ->
|
||||
getPresenter().setReadFilter(isChecked));
|
||||
downloadedCb.setOnCheckedChangeListener((v, isChecked) ->
|
||||
getPresenter().setDownloadedFilter(isChecked));
|
||||
sortBtn.setOnClickListener(v -> {
|
||||
getPresenter().revertSortOrder();
|
||||
setSortIcon();
|
||||
});
|
||||
}
|
||||
|
||||
public void onNextChapters(List<Chapter> chapters) {
|
||||
// If the list is empty, fetch chapters from source if the conditions are met
|
||||
// We use presenter chapters instead because they are always unfiltered
|
||||
if (getPresenter().getChapters().isEmpty())
|
||||
initialFetchChapters();
|
||||
|
||||
destroyActionModeIfNeeded();
|
||||
adapter.setItems(chapters);
|
||||
}
|
||||
|
||||
private void initialFetchChapters() {
|
||||
// Only fetch if this view is from the catalog and it hasn't requested previously
|
||||
if (isCatalogueManga() && !getPresenter().hasRequested()) {
|
||||
fetchChapters();
|
||||
}
|
||||
}
|
||||
|
||||
public void fetchChapters() {
|
||||
if (getPresenter().getManga() != null) {
|
||||
swipeRefresh.setRefreshing(true);
|
||||
getPresenter().fetchChaptersFromSource();
|
||||
}
|
||||
}
|
||||
|
||||
public void onFetchChaptersDone() {
|
||||
swipeRefresh.setRefreshing(false);
|
||||
}
|
||||
|
||||
public void onFetchChaptersError(Throwable error) {
|
||||
swipeRefresh.setRefreshing(false);
|
||||
ToastUtil.showShort(getContext(), error.getMessage());
|
||||
}
|
||||
|
||||
public boolean isCatalogueManga() {
|
||||
return ((MangaActivity) getActivity()).isCatalogueManga();
|
||||
}
|
||||
|
||||
protected void openChapter(Chapter chapter) {
|
||||
getPresenter().onOpenChapter(chapter);
|
||||
Intent intent = ReaderActivity.newIntent(getActivity());
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
private void showDisplayModeDialog() {
|
||||
final Manga manga = getPresenter().getManga();
|
||||
if (manga == null)
|
||||
return;
|
||||
|
||||
// Get available modes, ids and the selected mode
|
||||
String[] modes = {getString(R.string.show_title), getString(R.string.show_chapter_number)};
|
||||
int[] ids = {Manga.DISPLAY_NAME, Manga.DISPLAY_NUMBER};
|
||||
int selectedIndex = manga.getDisplayMode() == Manga.DISPLAY_NAME ? 0 : 1;
|
||||
|
||||
new MaterialDialog.Builder(getActivity())
|
||||
.title(R.string.action_display_mode)
|
||||
.items(modes)
|
||||
.itemsIds(ids)
|
||||
.itemsCallbackSingleChoice(selectedIndex, (dialog, itemView, which, text) -> {
|
||||
// Save the new display mode
|
||||
getPresenter().setDisplayMode(itemView.getId());
|
||||
// Refresh ui
|
||||
adapter.notifyDataSetChanged();
|
||||
return true;
|
||||
})
|
||||
.show();
|
||||
}
|
||||
|
||||
private void showDownloadDialog() {
|
||||
|
||||
// Get available modes
|
||||
String[] modes = {getString(R.string.download_all), getString(R.string.download_unread)};
|
||||
|
||||
new MaterialDialog.Builder(getActivity())
|
||||
.title(R.string.manga_download)
|
||||
.items(modes)
|
||||
.itemsCallback((dialog, view, i, charSequence) -> {
|
||||
List<Chapter> chapters = new ArrayList<>();
|
||||
|
||||
for(Chapter chapter : getPresenter().getChapters()) {
|
||||
if(!chapter.isDownloaded()) {
|
||||
if(i == 0 || (i == 1 && !chapter.read)) {
|
||||
chapters.add(chapter);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(chapters.size() > 0) {
|
||||
onDownload(Observable.from(chapters));
|
||||
}
|
||||
})
|
||||
.negativeText(R.string.button_cancel)
|
||||
.show();
|
||||
}
|
||||
|
||||
private void observeChapterDownloadProgress() {
|
||||
downloadProgressSubscription = getPresenter().getDownloadProgressObs()
|
||||
.subscribe(this::onDownloadProgressChange,
|
||||
error -> { /* TODO getting a NPE sometimes on 'manga' from presenter */ });
|
||||
}
|
||||
|
||||
private void unsubscribeChapterDownloadProgress() {
|
||||
if (downloadProgressSubscription != null)
|
||||
downloadProgressSubscription.unsubscribe();
|
||||
}
|
||||
|
||||
private void onDownloadProgressChange(Download download) {
|
||||
ChaptersHolder holder = getHolder(download.chapter);
|
||||
if (holder != null)
|
||||
holder.onProgressChange(getContext(), download.downloadedImages, download.pages.size());
|
||||
}
|
||||
|
||||
public void onChapterStatusChange(Download download) {
|
||||
ChaptersHolder holder = getHolder(download.chapter);
|
||||
if (holder != null)
|
||||
holder.onStatusChange(download.getStatus());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private ChaptersHolder getHolder(Chapter chapter) {
|
||||
return (ChaptersHolder) recyclerView.findViewHolderForItemId(chapter.id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
|
||||
mode.getMenuInflater().inflate(R.menu.chapter_selection, menu);
|
||||
adapter.setMode(ChaptersAdapter.MODE_MULTI);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.action_select_all:
|
||||
return onSelectAll();
|
||||
case R.id.action_mark_as_read:
|
||||
return onMarkAsRead(getSelectedChapters());
|
||||
case R.id.action_mark_as_unread:
|
||||
return onMarkAsUnread(getSelectedChapters());
|
||||
case R.id.action_download:
|
||||
return onDownload(getSelectedChapters());
|
||||
case R.id.action_delete:
|
||||
return onDelete(getSelectedChapters());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyActionMode(ActionMode mode) {
|
||||
adapter.setMode(ChaptersAdapter.MODE_SINGLE);
|
||||
adapter.clearSelection();
|
||||
actionMode = null;
|
||||
}
|
||||
|
||||
private Observable<Chapter> getSelectedChapters() {
|
||||
// Create a blocking copy of the selected chapters.
|
||||
// When the action mode is closed the list is cleared. If we use background
|
||||
// threads with this observable, some emissions could be lost.
|
||||
List<Chapter> chapters = Observable.from(adapter.getSelectedItems())
|
||||
.map(adapter::getItem).toList().toBlocking().single();
|
||||
|
||||
return Observable.from(chapters);
|
||||
}
|
||||
|
||||
public void destroyActionModeIfNeeded() {
|
||||
if (actionMode != null) {
|
||||
actionMode.finish();
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean onSelectAll() {
|
||||
adapter.selectAll();
|
||||
setContextTitle(adapter.getSelectedItemCount());
|
||||
return true;
|
||||
}
|
||||
|
||||
protected boolean onMarkAsRead(Observable<Chapter> chapters) {
|
||||
getPresenter().markChaptersRead(chapters, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected boolean onMarkAsUnread(Observable<Chapter> chapters) {
|
||||
getPresenter().markChaptersRead(chapters, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean onMarkPreviousAsRead(Chapter chapter) {
|
||||
getPresenter().markPreviousChaptersAsRead(chapter);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected boolean onDownload(Observable<Chapter> chapters) {
|
||||
DownloadService.start(getActivity());
|
||||
|
||||
Observable<Chapter> observable = chapters
|
||||
.doOnCompleted(adapter::notifyDataSetChanged);
|
||||
|
||||
getPresenter().downloadChapters(observable);
|
||||
destroyActionModeIfNeeded();
|
||||
return true;
|
||||
}
|
||||
|
||||
protected boolean onDelete(Observable<Chapter> chapters) {
|
||||
int size = adapter.getSelectedItemCount();
|
||||
|
||||
MaterialDialog dialog = new MaterialDialog.Builder(getActivity())
|
||||
.title(R.string.deleting)
|
||||
.progress(false, size, true)
|
||||
.cancelable(false)
|
||||
.show();
|
||||
|
||||
Observable<Chapter> observable = chapters
|
||||
.concatMap(chapter -> {
|
||||
getPresenter().deleteChapter(chapter);
|
||||
return Observable.just(chapter);
|
||||
})
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.doOnNext(chapter -> {
|
||||
dialog.incrementProgress(1);
|
||||
chapter.status = Download.NOT_DOWNLOADED;
|
||||
})
|
||||
.doOnCompleted(adapter::notifyDataSetChanged)
|
||||
.finallyDo(dialog::dismiss);
|
||||
|
||||
getPresenter().deleteChapters(observable);
|
||||
destroyActionModeIfNeeded();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onListItemClick(int position) {
|
||||
if (actionMode != null && adapter.getMode() == ChaptersAdapter.MODE_MULTI) {
|
||||
toggleSelection(position);
|
||||
return true;
|
||||
} else {
|
||||
openChapter(adapter.getItem(position));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onListItemLongClick(int position) {
|
||||
if (actionMode == null)
|
||||
actionMode = getBaseActivity().startSupportActionMode(this);
|
||||
|
||||
toggleSelection(position);
|
||||
}
|
||||
|
||||
private void toggleSelection(int position) {
|
||||
adapter.toggleSelection(position, false);
|
||||
|
||||
int count = adapter.getSelectedItemCount();
|
||||
if (count == 0) {
|
||||
actionMode.finish();
|
||||
} else {
|
||||
setContextTitle(count);
|
||||
actionMode.invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
private void setContextTitle(int count) {
|
||||
actionMode.setTitle(getString(R.string.label_selected, count));
|
||||
}
|
||||
|
||||
public void setSortIcon() {
|
||||
if (sortBtn != null) {
|
||||
boolean aToZ = getPresenter().getSortOrder();
|
||||
sortBtn.setImageResource(!aToZ ? R.drawable.ic_expand_less_white_36dp : R.drawable.ic_expand_more_white_36dp);
|
||||
}
|
||||
}
|
||||
|
||||
public void setReadFilter() {
|
||||
if (readCb != null) {
|
||||
readCb.setChecked(getPresenter().onlyUnread());
|
||||
}
|
||||
}
|
||||
|
||||
public void setDownloadedFilter() {
|
||||
if (downloadedCb != null) {
|
||||
downloadedCb.setChecked(getPresenter().onlyDownloaded());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,362 @@
|
||||
package eu.kanade.tachiyomi.ui.manga.chapter
|
||||
|
||||
import android.os.Bundle
|
||||
import android.support.v4.content.ContextCompat
|
||||
import android.support.v7.view.ActionMode
|
||||
import android.support.v7.widget.LinearLayoutManager
|
||||
import android.view.*
|
||||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.Chapter
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.data.download.DownloadService
|
||||
import eu.kanade.tachiyomi.data.download.model.Download
|
||||
import eu.kanade.tachiyomi.ui.base.adapter.FlexibleViewHolder
|
||||
import eu.kanade.tachiyomi.ui.base.decoration.DividerItemDecoration
|
||||
import eu.kanade.tachiyomi.ui.base.fragment.BaseRxFragment
|
||||
import eu.kanade.tachiyomi.ui.manga.MangaActivity
|
||||
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
|
||||
import eu.kanade.tachiyomi.util.toast
|
||||
import kotlinx.android.synthetic.main.fragment_manga_chapters.*
|
||||
import nucleus.factory.RequiresPresenter
|
||||
import rx.Observable
|
||||
import rx.android.schedulers.AndroidSchedulers
|
||||
import rx.schedulers.Schedulers
|
||||
import java.util.*
|
||||
|
||||
@RequiresPresenter(ChaptersPresenter::class)
|
||||
class ChaptersFragment : BaseRxFragment<ChaptersPresenter>(), ActionMode.Callback, FlexibleViewHolder.OnListItemClickListener {
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Creates a new instance of this fragment.
|
||||
*
|
||||
* @return a new instance of [ChaptersFragment].
|
||||
*/
|
||||
fun newInstance(): ChaptersFragment {
|
||||
return ChaptersFragment()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapter containing a list of chapters.
|
||||
*/
|
||||
private lateinit var adapter: ChaptersAdapter
|
||||
|
||||
/**
|
||||
* Action mode for multiple selection.
|
||||
*/
|
||||
private var actionMode: ActionMode? = null
|
||||
|
||||
override fun onCreate(savedState: Bundle?) {
|
||||
super.onCreate(savedState)
|
||||
setHasOptionsMenu(true)
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedState: Bundle?): View? {
|
||||
return inflater.inflate(R.layout.fragment_manga_chapters, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
// Init RecyclerView and adapter
|
||||
adapter = ChaptersAdapter(this)
|
||||
|
||||
recycler.adapter = adapter
|
||||
recycler.layoutManager = LinearLayoutManager(activity)
|
||||
recycler.addItemDecoration(DividerItemDecoration(
|
||||
ContextCompat.getDrawable(context, R.drawable.line_divider)))
|
||||
recycler.setHasFixedSize(true)
|
||||
|
||||
swipe_refresh.setOnRefreshListener { fetchChapters() }
|
||||
|
||||
next_unread_btn.setOnClickListener { v ->
|
||||
val chapter = presenter.getNextUnreadChapter()
|
||||
if (chapter != null) {
|
||||
openChapter(chapter)
|
||||
} else {
|
||||
context.toast(R.string.no_next_chapter)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
// Stop recycler's scrolling when onPause is called. If the activity is finishing
|
||||
// the presenter will be destroyed, and it could cause NPE
|
||||
// https://github.com/inorichi/tachiyomi/issues/159
|
||||
recycler.stopScroll()
|
||||
|
||||
super.onPause()
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
inflater.inflate(R.menu.chapters, menu)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
R.id.action_display_mode -> showDisplayModeDialog()
|
||||
R.id.manga_download -> showDownloadDialog()
|
||||
else -> return super.onOptionsItemSelected(item)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
fun onNextManga(manga: Manga) {
|
||||
// Remove listeners before setting the values
|
||||
show_unread.setOnCheckedChangeListener(null)
|
||||
show_downloaded.setOnCheckedChangeListener(null)
|
||||
sort_btn.setOnClickListener(null)
|
||||
|
||||
// Set initial values
|
||||
setReadFilter()
|
||||
setDownloadedFilter()
|
||||
setSortIcon()
|
||||
|
||||
// Init listeners
|
||||
show_unread.setOnCheckedChangeListener { arg, isChecked -> presenter.setReadFilter(isChecked) }
|
||||
show_downloaded.setOnCheckedChangeListener { v, isChecked -> presenter.setDownloadedFilter(isChecked) }
|
||||
sort_btn.setOnClickListener {
|
||||
presenter.revertSortOrder()
|
||||
setSortIcon()
|
||||
}
|
||||
}
|
||||
|
||||
fun onNextChapters(chapters: List<Chapter>) {
|
||||
// If the list is empty, fetch chapters from source if the conditions are met
|
||||
// We use presenter chapters instead because they are always unfiltered
|
||||
if (presenter.chapters.isEmpty())
|
||||
initialFetchChapters()
|
||||
|
||||
destroyActionModeIfNeeded()
|
||||
adapter.setItems(chapters)
|
||||
}
|
||||
|
||||
private fun initialFetchChapters() {
|
||||
// Only fetch if this view is from the catalog and it hasn't requested previously
|
||||
if (isCatalogueManga && !presenter.hasRequested) {
|
||||
fetchChapters()
|
||||
}
|
||||
}
|
||||
|
||||
fun fetchChapters() {
|
||||
swipe_refresh.isRefreshing = true
|
||||
presenter.fetchChaptersFromSource()
|
||||
}
|
||||
|
||||
fun onFetchChaptersDone() {
|
||||
swipe_refresh.isRefreshing = false
|
||||
}
|
||||
|
||||
fun onFetchChaptersError(error: Throwable) {
|
||||
swipe_refresh.isRefreshing = false
|
||||
context.toast(error.message)
|
||||
}
|
||||
|
||||
val isCatalogueManga: Boolean
|
||||
get() = (activity as MangaActivity).isCatalogueManga
|
||||
|
||||
protected fun openChapter(chapter: Chapter) {
|
||||
presenter.onOpenChapter(chapter)
|
||||
val intent = ReaderActivity.newIntent(activity)
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
private fun showDisplayModeDialog() {
|
||||
|
||||
// Get available modes, ids and the selected mode
|
||||
val modes = listOf(getString(R.string.show_title), getString(R.string.show_chapter_number))
|
||||
val ids = intArrayOf(Manga.DISPLAY_NAME, Manga.DISPLAY_NUMBER)
|
||||
val selectedIndex = if (presenter.manga.displayMode == Manga.DISPLAY_NAME) 0 else 1
|
||||
|
||||
MaterialDialog.Builder(activity)
|
||||
.title(R.string.action_display_mode)
|
||||
.items(modes)
|
||||
.itemsIds(ids)
|
||||
.itemsCallbackSingleChoice(selectedIndex) { dialog, itemView, which, text ->
|
||||
// Save the new display mode
|
||||
presenter.setDisplayMode(itemView.id)
|
||||
// Refresh ui
|
||||
adapter.notifyDataSetChanged()
|
||||
true
|
||||
}
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun showDownloadDialog() {
|
||||
// Get available modes
|
||||
val modes = listOf(getString(R.string.download_all), getString(R.string.download_unread))
|
||||
|
||||
MaterialDialog.Builder(activity)
|
||||
.title(R.string.manga_download)
|
||||
.negativeText(android.R.string.cancel)
|
||||
.items(modes)
|
||||
.itemsCallback { dialog, view, i, charSequence ->
|
||||
val chapters = ArrayList<Chapter>()
|
||||
|
||||
for (chapter in presenter.chapters) {
|
||||
if (!chapter.isDownloaded) {
|
||||
if (i == 0 || (i == 1 && !chapter.read)) {
|
||||
chapters.add(chapter)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (chapters.size > 0) {
|
||||
onDownload(Observable.from(chapters))
|
||||
}
|
||||
}
|
||||
.show()
|
||||
}
|
||||
|
||||
fun onChapterStatusChange(download: Download) {
|
||||
getHolder(download.chapter)?.notifyStatus(download.status)
|
||||
}
|
||||
|
||||
private fun getHolder(chapter: Chapter): ChaptersHolder? {
|
||||
return recycler.findViewHolderForItemId(chapter.id) as? ChaptersHolder
|
||||
}
|
||||
|
||||
override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
|
||||
mode.menuInflater.inflate(R.menu.chapter_selection, menu)
|
||||
adapter.mode = FlexibleAdapter.MODE_MULTI
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
R.id.action_select_all -> onSelectAll()
|
||||
R.id.action_mark_as_read -> onMarkAsRead(getSelectedChapters())
|
||||
R.id.action_mark_as_unread -> onMarkAsUnread(getSelectedChapters())
|
||||
R.id.action_download -> onDownload(getSelectedChapters())
|
||||
R.id.action_delete -> onDelete(getSelectedChapters())
|
||||
else -> return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onDestroyActionMode(mode: ActionMode) {
|
||||
adapter.mode = FlexibleAdapter.MODE_SINGLE
|
||||
adapter.clearSelection()
|
||||
actionMode = null
|
||||
}
|
||||
|
||||
fun getSelectedChapters(): Observable<Chapter> {
|
||||
val chapters = adapter.selectedItems.map { adapter.getItem(it) }
|
||||
return Observable.from(chapters)
|
||||
}
|
||||
|
||||
fun destroyActionModeIfNeeded() {
|
||||
actionMode?.finish()
|
||||
}
|
||||
|
||||
protected fun onSelectAll() {
|
||||
adapter.selectAll()
|
||||
setContextTitle(adapter.selectedItemCount)
|
||||
}
|
||||
|
||||
fun onMarkAsRead(chapters: Observable<Chapter>) {
|
||||
presenter.markChaptersRead(chapters, true)
|
||||
}
|
||||
|
||||
fun onMarkAsUnread(chapters: Observable<Chapter>) {
|
||||
presenter.markChaptersRead(chapters, false)
|
||||
}
|
||||
|
||||
fun onMarkPreviousAsRead(chapter: Chapter) {
|
||||
presenter.markPreviousChaptersAsRead(chapter)
|
||||
}
|
||||
|
||||
fun onDownload(chapters: Observable<Chapter>) {
|
||||
DownloadService.start(activity)
|
||||
|
||||
val observable = chapters.doOnCompleted { adapter.notifyDataSetChanged() }
|
||||
|
||||
presenter.downloadChapters(observable)
|
||||
destroyActionModeIfNeeded()
|
||||
}
|
||||
|
||||
fun onDelete(chapters: Observable<Chapter>) {
|
||||
val size = adapter.selectedItemCount
|
||||
|
||||
val dialog = MaterialDialog.Builder(activity)
|
||||
.title(R.string.deleting)
|
||||
.progress(false, size, true)
|
||||
.cancelable(false)
|
||||
.show()
|
||||
|
||||
val observable = chapters
|
||||
.concatMap { chapter ->
|
||||
presenter.deleteChapter(chapter)
|
||||
Observable.just(chapter)
|
||||
}
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.doOnNext { chapter ->
|
||||
dialog.incrementProgress(1)
|
||||
chapter.status = Download.NOT_DOWNLOADED
|
||||
}
|
||||
.doOnCompleted { adapter.notifyDataSetChanged() }
|
||||
.doAfterTerminate { dialog.dismiss() }
|
||||
|
||||
presenter.deleteChapters(observable)
|
||||
destroyActionModeIfNeeded()
|
||||
}
|
||||
|
||||
override fun onListItemClick(position: Int): Boolean {
|
||||
if (actionMode != null && adapter.mode == FlexibleAdapter.MODE_MULTI) {
|
||||
toggleSelection(position)
|
||||
return true
|
||||
} else {
|
||||
openChapter(adapter.getItem(position))
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
override fun onListItemLongClick(position: Int) {
|
||||
if (actionMode == null)
|
||||
actionMode = baseActivity.startSupportActionMode(this)
|
||||
|
||||
toggleSelection(position)
|
||||
}
|
||||
|
||||
private fun toggleSelection(position: Int) {
|
||||
adapter.toggleSelection(position, false)
|
||||
|
||||
val count = adapter.selectedItemCount
|
||||
if (count == 0) {
|
||||
actionMode?.finish()
|
||||
} else {
|
||||
setContextTitle(count)
|
||||
actionMode?.invalidate()
|
||||
}
|
||||
}
|
||||
|
||||
private fun setContextTitle(count: Int) {
|
||||
actionMode?.title = getString(R.string.label_selected, count)
|
||||
}
|
||||
|
||||
fun setSortIcon() {
|
||||
sort_btn?.let {
|
||||
val aToZ = presenter.sortOrder()
|
||||
it.setImageResource(if (!aToZ) R.drawable.ic_expand_less_white_36dp else R.drawable.ic_expand_more_white_36dp)
|
||||
}
|
||||
}
|
||||
|
||||
fun setReadFilter() {
|
||||
show_unread?.let {
|
||||
it.isChecked = presenter.onlyUnread()
|
||||
}
|
||||
}
|
||||
|
||||
fun setDownloadedFilter() {
|
||||
show_downloaded?.let {
|
||||
it.isChecked = presenter.onlyDownloaded()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,150 +0,0 @@
|
||||
package eu.kanade.tachiyomi.ui.manga.chapter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.view.Menu;
|
||||
import android.view.View;
|
||||
import android.widget.PopupMenu;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.DecimalFormatSymbols;
|
||||
import java.util.Date;
|
||||
|
||||
import butterknife.Bind;
|
||||
import butterknife.ButterKnife;
|
||||
import eu.kanade.tachiyomi.R;
|
||||
import eu.kanade.tachiyomi.data.database.models.Chapter;
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga;
|
||||
import eu.kanade.tachiyomi.data.download.model.Download;
|
||||
import eu.kanade.tachiyomi.ui.base.adapter.FlexibleViewHolder;
|
||||
import rx.Observable;
|
||||
|
||||
public class ChaptersHolder extends FlexibleViewHolder {
|
||||
|
||||
private final ChaptersAdapter adapter;
|
||||
private final int readColor;
|
||||
private final int unreadColor;
|
||||
private final DecimalFormat decimalFormat;
|
||||
private final DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT);
|
||||
@Bind(R.id.chapter_title) TextView title;
|
||||
@Bind(R.id.download_text) TextView downloadText;
|
||||
@Bind(R.id.chapter_menu) RelativeLayout chapterMenu;
|
||||
@Bind(R.id.chapter_pages) TextView pages;
|
||||
@Bind(R.id.chapter_date) TextView date;
|
||||
private Context context;
|
||||
private Chapter item;
|
||||
|
||||
public ChaptersHolder(View view, ChaptersAdapter adapter, OnListItemClickListener listener) {
|
||||
super(view, adapter, listener);
|
||||
this.adapter = adapter;
|
||||
context = view.getContext();
|
||||
ButterKnife.bind(this, view);
|
||||
|
||||
readColor = ContextCompat.getColor(view.getContext(), R.color.hint_text);
|
||||
unreadColor = ContextCompat.getColor(view.getContext(), R.color.primary_text);
|
||||
|
||||
DecimalFormatSymbols symbols = new DecimalFormatSymbols();
|
||||
symbols.setDecimalSeparator('.');
|
||||
decimalFormat = new DecimalFormat("#.###", symbols);
|
||||
|
||||
chapterMenu.setOnClickListener(v -> v.post(() -> showPopupMenu(v)));
|
||||
}
|
||||
|
||||
public void onSetValues(Chapter chapter, Manga manga) {
|
||||
this.item = chapter;
|
||||
String name;
|
||||
switch (manga.getDisplayMode()) {
|
||||
case Manga.DISPLAY_NAME:
|
||||
default:
|
||||
name = chapter.name;
|
||||
break;
|
||||
case Manga.DISPLAY_NUMBER:
|
||||
String formattedNumber = decimalFormat.format(chapter.chapter_number);
|
||||
name = context.getString(R.string.display_mode_chapter, formattedNumber);
|
||||
break;
|
||||
}
|
||||
title.setText(name);
|
||||
title.setTextColor(chapter.read ? readColor : unreadColor);
|
||||
date.setTextColor(chapter.read ? readColor : unreadColor);
|
||||
|
||||
if (!chapter.read && chapter.last_page_read > 0) {
|
||||
pages.setText(context.getString(R.string.chapter_progress, chapter.last_page_read + 1));
|
||||
} else {
|
||||
pages.setText("");
|
||||
}
|
||||
|
||||
onStatusChange(chapter.status);
|
||||
date.setText(df.format(new Date(chapter.date_upload)));
|
||||
}
|
||||
|
||||
public void onStatusChange(int status) {
|
||||
switch (status) {
|
||||
case Download.QUEUE:
|
||||
downloadText.setText(R.string.chapter_queued); break;
|
||||
case Download.DOWNLOADING:
|
||||
downloadText.setText(R.string.chapter_downloading); break;
|
||||
case Download.DOWNLOADED:
|
||||
downloadText.setText(R.string.chapter_downloaded); break;
|
||||
case Download.ERROR:
|
||||
downloadText.setText(R.string.chapter_error); break;
|
||||
default:
|
||||
downloadText.setText(""); break;
|
||||
}
|
||||
}
|
||||
|
||||
public void onProgressChange(Context context, int downloaded, int total) {
|
||||
downloadText.setText(context.getString(
|
||||
R.string.chapter_downloading_progress, downloaded, total));
|
||||
}
|
||||
|
||||
private void showPopupMenu(View view) {
|
||||
// Create a PopupMenu, giving it the clicked view for an anchor
|
||||
PopupMenu popup = new PopupMenu(adapter.getFragment().getActivity(), view);
|
||||
|
||||
// Inflate our menu resource into the PopupMenu's Menu
|
||||
popup.getMenuInflater().inflate(R.menu.chapter_single, popup.getMenu());
|
||||
|
||||
// Hide download and show delete if the chapter is downloaded and
|
||||
if(item.isDownloaded()) {
|
||||
Menu menu = popup.getMenu();
|
||||
menu.findItem(R.id.action_download).setVisible(false);
|
||||
menu.findItem(R.id.action_delete).setVisible(true);
|
||||
}
|
||||
|
||||
// Hide mark as unread when the chapter is unread
|
||||
if(!item.read && item.last_page_read == 0) {
|
||||
popup.getMenu().findItem(R.id.action_mark_as_unread).setVisible(false);
|
||||
}
|
||||
|
||||
// Hide mark as read when the chapter is read
|
||||
if(item.read) {
|
||||
popup.getMenu().findItem(R.id.action_mark_as_read).setVisible(false);
|
||||
}
|
||||
|
||||
// Set a listener so we are notified if a menu item is clicked
|
||||
popup.setOnMenuItemClickListener(menuItem -> {
|
||||
Observable<Chapter> chapter = Observable.just(item);
|
||||
|
||||
switch (menuItem.getItemId()) {
|
||||
case R.id.action_download:
|
||||
return adapter.getFragment().onDownload(chapter);
|
||||
case R.id.action_delete:
|
||||
return adapter.getFragment().onDelete(chapter);
|
||||
case R.id.action_mark_as_read:
|
||||
return adapter.getFragment().onMarkAsRead(chapter);
|
||||
case R.id.action_mark_as_unread:
|
||||
return adapter.getFragment().onMarkAsUnread(chapter);
|
||||
case R.id.action_mark_previous_as_read:
|
||||
return adapter.getFragment().onMarkPreviousAsRead(item);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
// Finally show the PopupMenu
|
||||
popup.show();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,116 @@
|
||||
package eu.kanade.tachiyomi.ui.manga.chapter
|
||||
|
||||
import android.content.Context
|
||||
import android.support.v4.content.ContextCompat
|
||||
import android.view.View
|
||||
import android.widget.PopupMenu
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.Chapter
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.data.download.model.Download
|
||||
import eu.kanade.tachiyomi.ui.base.adapter.FlexibleViewHolder
|
||||
import kotlinx.android.synthetic.main.item_chapter.view.*
|
||||
import rx.Observable
|
||||
import java.text.DateFormat
|
||||
import java.text.DecimalFormat
|
||||
import java.text.DecimalFormatSymbols
|
||||
import java.util.*
|
||||
|
||||
class ChaptersHolder(private val view: View, private val adapter: ChaptersAdapter, listener: FlexibleViewHolder.OnListItemClickListener) :
|
||||
FlexibleViewHolder(view, adapter, listener) {
|
||||
|
||||
private val readColor = ContextCompat.getColor(view.context, R.color.hint_text)
|
||||
private val unreadColor = ContextCompat.getColor(view.context, R.color.primary_text)
|
||||
private val decimalFormat = DecimalFormat("#.###", DecimalFormatSymbols().apply { decimalSeparator = '.' })
|
||||
private val df = DateFormat.getDateInstance(DateFormat.SHORT)
|
||||
|
||||
private var item: Chapter? = null
|
||||
|
||||
init {
|
||||
view.chapter_menu.setOnClickListener { v -> v.post { showPopupMenu(v) } }
|
||||
}
|
||||
|
||||
fun onSetValues(chapter: Chapter, manga: Manga?) = with(view) {
|
||||
item = chapter
|
||||
|
||||
val name: String
|
||||
when (manga?.displayMode) {
|
||||
Manga.DISPLAY_NUMBER -> {
|
||||
val formattedNumber = decimalFormat.format(chapter.chapter_number.toDouble())
|
||||
name = context.getString(R.string.display_mode_chapter, formattedNumber)
|
||||
}
|
||||
else -> name = chapter.name
|
||||
}
|
||||
|
||||
chapter_title.text = name
|
||||
chapter_title.setTextColor(if (chapter.read) readColor else unreadColor)
|
||||
|
||||
chapter_date.text = df.format(Date(chapter.date_upload))
|
||||
chapter_date.setTextColor(if (chapter.read) readColor else unreadColor)
|
||||
|
||||
if (!chapter.read && chapter.last_page_read > 0) {
|
||||
chapter_pages.text = context.getString(R.string.chapter_progress, chapter.last_page_read + 1)
|
||||
} else {
|
||||
chapter_pages.text = ""
|
||||
}
|
||||
|
||||
notifyStatus(chapter.status)
|
||||
}
|
||||
|
||||
fun notifyStatus(status: Int) = with(view) {
|
||||
when (status) {
|
||||
Download.QUEUE -> download_text.setText(R.string.chapter_queued)
|
||||
Download.DOWNLOADING -> download_text.setText(R.string.chapter_downloading)
|
||||
Download.DOWNLOADED -> download_text.setText(R.string.chapter_downloaded)
|
||||
Download.ERROR -> download_text.setText(R.string.chapter_error)
|
||||
else -> download_text.text = ""
|
||||
}
|
||||
}
|
||||
|
||||
fun onProgressChange(context: Context, downloaded: Int, total: Int) {
|
||||
view.download_text.text = context.getString(
|
||||
R.string.chapter_downloading_progress, downloaded, total)
|
||||
}
|
||||
|
||||
private fun showPopupMenu(view: View) = item?.let { item ->
|
||||
// Create a PopupMenu, giving it the clicked view for an anchor
|
||||
val popup = PopupMenu(view.context, view)
|
||||
|
||||
// Inflate our menu resource into the PopupMenu's Menu
|
||||
popup.menuInflater.inflate(R.menu.chapter_single, popup.menu)
|
||||
|
||||
// Hide download and show delete if the chapter is downloaded
|
||||
if (item.isDownloaded) {
|
||||
popup.menu.findItem(R.id.action_download).isVisible = false
|
||||
popup.menu.findItem(R.id.action_delete).isVisible = true
|
||||
}
|
||||
|
||||
// Hide mark as unread when the chapter is unread
|
||||
if (!item.read && item.last_page_read == 0) {
|
||||
popup.menu.findItem(R.id.action_mark_as_unread).isVisible = false
|
||||
}
|
||||
|
||||
// Hide mark as read when the chapter is read
|
||||
if (item.read) {
|
||||
popup.menu.findItem(R.id.action_mark_as_read).isVisible = false
|
||||
}
|
||||
|
||||
// Set a listener so we are notified if a menu item is clicked
|
||||
popup.setOnMenuItemClickListener { menuItem ->
|
||||
val chapter = Observable.just(item)
|
||||
|
||||
when (menuItem.itemId) {
|
||||
R.id.action_download -> adapter.fragment.onDownload(chapter)
|
||||
R.id.action_delete -> adapter.fragment.onDelete(chapter)
|
||||
R.id.action_mark_as_read -> adapter.fragment.onMarkAsRead(chapter)
|
||||
R.id.action_mark_as_unread -> adapter.fragment.onMarkAsUnread(chapter)
|
||||
R.id.action_mark_previous_as_read -> adapter.fragment.onMarkPreviousAsRead(item)
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
// Finally show the PopupMenu
|
||||
popup.show()
|
||||
}
|
||||
|
||||
}
|
@ -1,286 +0,0 @@
|
||||
package eu.kanade.tachiyomi.ui.manga.chapter;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.util.Pair;
|
||||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.greenrobot.eventbus.Subscribe;
|
||||
import org.greenrobot.eventbus.ThreadMode;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper;
|
||||
import eu.kanade.tachiyomi.data.database.models.Chapter;
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga;
|
||||
import eu.kanade.tachiyomi.data.download.DownloadManager;
|
||||
import eu.kanade.tachiyomi.data.download.model.Download;
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper;
|
||||
import eu.kanade.tachiyomi.data.source.SourceManager;
|
||||
import eu.kanade.tachiyomi.data.source.base.Source;
|
||||
import eu.kanade.tachiyomi.event.ChapterCountEvent;
|
||||
import eu.kanade.tachiyomi.event.DownloadChaptersEvent;
|
||||
import eu.kanade.tachiyomi.event.MangaEvent;
|
||||
import eu.kanade.tachiyomi.event.ReaderEvent;
|
||||
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter;
|
||||
import icepick.State;
|
||||
import rx.Observable;
|
||||
import rx.android.schedulers.AndroidSchedulers;
|
||||
import rx.schedulers.Schedulers;
|
||||
import rx.subjects.PublishSubject;
|
||||
import timber.log.Timber;
|
||||
|
||||
public class ChaptersPresenter extends BasePresenter<ChaptersFragment> {
|
||||
|
||||
@Inject DatabaseHelper db;
|
||||
@Inject SourceManager sourceManager;
|
||||
@Inject PreferencesHelper preferences;
|
||||
@Inject DownloadManager downloadManager;
|
||||
|
||||
private Manga manga;
|
||||
private Source source;
|
||||
private List<Chapter> chapters;
|
||||
@State boolean hasRequested;
|
||||
|
||||
private PublishSubject<List<Chapter>> chaptersSubject;
|
||||
|
||||
private static final int GET_MANGA = 1;
|
||||
private static final int DB_CHAPTERS = 2;
|
||||
private static final int FETCH_CHAPTERS = 3;
|
||||
private static final int CHAPTER_STATUS_CHANGES = 4;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedState) {
|
||||
super.onCreate(savedState);
|
||||
|
||||
chaptersSubject = PublishSubject.create();
|
||||
|
||||
startableLatestCache(GET_MANGA,
|
||||
() -> Observable.just(manga),
|
||||
ChaptersFragment::onNextManga);
|
||||
|
||||
startableLatestCache(DB_CHAPTERS,
|
||||
this::getDbChaptersObs,
|
||||
ChaptersFragment::onNextChapters);
|
||||
|
||||
startableFirst(FETCH_CHAPTERS,
|
||||
this::getOnlineChaptersObs,
|
||||
(view, result) -> view.onFetchChaptersDone(),
|
||||
(view, error) -> view.onFetchChaptersError(error));
|
||||
|
||||
startableLatestCache(CHAPTER_STATUS_CHANGES,
|
||||
this::getChapterStatusObs,
|
||||
(view, download) -> view.onChapterStatusChange(download),
|
||||
(view, error) -> Timber.e(error.getCause(), error.getMessage()));
|
||||
|
||||
registerForEvents();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
unregisterForEvents();
|
||||
EventBus.getDefault().removeStickyEvent(ChapterCountEvent.class);
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
|
||||
public void onEvent(MangaEvent event) {
|
||||
this.manga = event.manga;
|
||||
start(GET_MANGA);
|
||||
|
||||
if (isUnsubscribed(DB_CHAPTERS)) {
|
||||
source = sourceManager.get(manga.source);
|
||||
start(DB_CHAPTERS);
|
||||
|
||||
add(db.getChapters(manga).asRxObservable()
|
||||
.subscribeOn(Schedulers.io())
|
||||
.doOnNext(chapters -> {
|
||||
this.chapters = chapters;
|
||||
EventBus.getDefault().postSticky(new ChapterCountEvent(chapters.size()));
|
||||
for (Chapter chapter : chapters) {
|
||||
setChapterStatus(chapter);
|
||||
}
|
||||
start(CHAPTER_STATUS_CHANGES);
|
||||
})
|
||||
.subscribe(chaptersSubject::onNext));
|
||||
}
|
||||
}
|
||||
|
||||
public void fetchChaptersFromSource() {
|
||||
hasRequested = true;
|
||||
start(FETCH_CHAPTERS);
|
||||
}
|
||||
|
||||
private void refreshChapters() {
|
||||
chaptersSubject.onNext(chapters);
|
||||
}
|
||||
|
||||
private Observable<Pair<Integer, Integer>> getOnlineChaptersObs() {
|
||||
return source.pullChaptersFromNetwork(manga.url)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.flatMap(chapters -> db.insertOrRemoveChapters(manga, chapters, source))
|
||||
.observeOn(AndroidSchedulers.mainThread());
|
||||
}
|
||||
|
||||
private Observable<List<Chapter>> getDbChaptersObs() {
|
||||
return chaptersSubject.flatMap(this::applyChapterFilters)
|
||||
.observeOn(AndroidSchedulers.mainThread());
|
||||
}
|
||||
|
||||
private Observable<List<Chapter>> applyChapterFilters(List<Chapter> chapters) {
|
||||
Observable<Chapter> observable = Observable.from(chapters)
|
||||
.subscribeOn(Schedulers.io());
|
||||
if (onlyUnread()) {
|
||||
observable = observable.filter(chapter -> !chapter.read);
|
||||
}
|
||||
if (onlyDownloaded()) {
|
||||
observable = observable.filter(chapter -> chapter.status == Download.DOWNLOADED);
|
||||
}
|
||||
return observable.toSortedList((chapter, chapter2) -> getSortOrder() ?
|
||||
Float.compare(chapter2.chapter_number, chapter.chapter_number) :
|
||||
Float.compare(chapter.chapter_number, chapter2.chapter_number));
|
||||
}
|
||||
|
||||
private void setChapterStatus(Chapter chapter) {
|
||||
for (Download download : downloadManager.getQueue()) {
|
||||
if (chapter.id.equals(download.chapter.id)) {
|
||||
chapter.status = download.getStatus();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (downloadManager.isChapterDownloaded(source, manga, chapter)) {
|
||||
chapter.status = Download.DOWNLOADED;
|
||||
} else {
|
||||
chapter.status = Download.NOT_DOWNLOADED;
|
||||
}
|
||||
}
|
||||
|
||||
private Observable<Download> getChapterStatusObs() {
|
||||
return downloadManager.getQueue().getStatusObservable()
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.filter(download -> download.manga.id.equals(manga.id))
|
||||
.doOnNext(this::updateChapterStatus);
|
||||
}
|
||||
|
||||
public void updateChapterStatus(Download download) {
|
||||
for (Chapter chapter : chapters) {
|
||||
if (download.chapter.id.equals(chapter.id)) {
|
||||
chapter.status = download.getStatus();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (onlyDownloaded() && download.getStatus() == Download.DOWNLOADED)
|
||||
refreshChapters();
|
||||
}
|
||||
|
||||
public Observable<Download> getDownloadProgressObs() {
|
||||
return downloadManager.getQueue().getProgressObservable()
|
||||
.filter(download -> download.manga.id.equals(manga.id))
|
||||
.observeOn(AndroidSchedulers.mainThread());
|
||||
}
|
||||
|
||||
public void onOpenChapter(Chapter chapter) {
|
||||
EventBus.getDefault().postSticky(new ReaderEvent(source, manga, chapter));
|
||||
}
|
||||
|
||||
public Chapter getNextUnreadChapter() {
|
||||
return db.getNextUnreadChapter(manga).executeAsBlocking();
|
||||
}
|
||||
|
||||
public void markChaptersRead(Observable<Chapter> selectedChapters, boolean read) {
|
||||
add(selectedChapters
|
||||
.subscribeOn(Schedulers.io())
|
||||
.map(chapter -> {
|
||||
chapter.read = read;
|
||||
if (!read) chapter.last_page_read = 0;
|
||||
return chapter;
|
||||
})
|
||||
.toList()
|
||||
.flatMap(chapters -> db.insertChapters(chapters).asRxObservable())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe());
|
||||
}
|
||||
|
||||
public void markPreviousChaptersAsRead(Chapter selected) {
|
||||
Observable.from(chapters)
|
||||
.filter(c -> c.chapter_number > -1 && c.chapter_number < selected.chapter_number)
|
||||
.doOnNext(c -> c.read = true)
|
||||
.toList()
|
||||
.flatMap(chapters -> db.insertChapters(chapters).asRxObservable())
|
||||
.subscribe();
|
||||
}
|
||||
|
||||
public void downloadChapters(Observable<Chapter> selectedChapters) {
|
||||
add(selectedChapters
|
||||
.toList()
|
||||
.subscribe(chapters -> {
|
||||
EventBus.getDefault().postSticky(new DownloadChaptersEvent(manga, chapters));
|
||||
}));
|
||||
}
|
||||
|
||||
public void deleteChapters(Observable<Chapter> selectedChapters) {
|
||||
add(selectedChapters
|
||||
.subscribe(chapter -> {
|
||||
downloadManager.getQueue().remove(chapter);
|
||||
}, error -> {
|
||||
Timber.e(error.getMessage());
|
||||
}, () -> {
|
||||
if (onlyDownloaded())
|
||||
refreshChapters();
|
||||
}));
|
||||
}
|
||||
|
||||
public void deleteChapter(Chapter chapter) {
|
||||
downloadManager.deleteChapter(source, manga, chapter);
|
||||
}
|
||||
|
||||
public void revertSortOrder() {
|
||||
manga.setChapterOrder(getSortOrder() ? Manga.SORT_ZA : Manga.SORT_AZ);
|
||||
db.insertManga(manga).executeAsBlocking();
|
||||
refreshChapters();
|
||||
}
|
||||
|
||||
public void setReadFilter(boolean onlyUnread) {
|
||||
manga.setReadFilter(onlyUnread ? Manga.SHOW_UNREAD : Manga.SHOW_ALL);
|
||||
db.insertManga(manga).executeAsBlocking();
|
||||
refreshChapters();
|
||||
}
|
||||
|
||||
public void setDownloadedFilter(boolean onlyDownloaded) {
|
||||
manga.setDownloadedFilter(onlyDownloaded ? Manga.SHOW_DOWNLOADED : Manga.SHOW_ALL);
|
||||
db.insertManga(manga).executeAsBlocking();
|
||||
refreshChapters();
|
||||
}
|
||||
|
||||
public void setDisplayMode(int mode) {
|
||||
manga.setDisplayMode(mode);
|
||||
db.insertManga(manga).executeAsBlocking();
|
||||
}
|
||||
|
||||
public boolean onlyDownloaded() {
|
||||
return manga.getDownloadedFilter() == Manga.SHOW_DOWNLOADED;
|
||||
}
|
||||
|
||||
public boolean onlyUnread() {
|
||||
return manga.getReadFilter() == Manga.SHOW_UNREAD;
|
||||
}
|
||||
|
||||
public boolean getSortOrder() {
|
||||
return manga.sortChaptersAZ();
|
||||
}
|
||||
|
||||
public Manga getManga() {
|
||||
return manga;
|
||||
}
|
||||
|
||||
public List<Chapter> getChapters() {
|
||||
return chapters;
|
||||
}
|
||||
|
||||
public boolean hasRequested() {
|
||||
return hasRequested;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,264 @@
|
||||
package eu.kanade.tachiyomi.ui.manga.chapter
|
||||
|
||||
import android.os.Bundle
|
||||
import android.util.Pair
|
||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||
import eu.kanade.tachiyomi.data.database.models.Chapter
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.data.download.DownloadManager
|
||||
import eu.kanade.tachiyomi.data.download.model.Download
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.source.SourceManager
|
||||
import eu.kanade.tachiyomi.data.source.base.Source
|
||||
import eu.kanade.tachiyomi.event.ChapterCountEvent
|
||||
import eu.kanade.tachiyomi.event.DownloadChaptersEvent
|
||||
import eu.kanade.tachiyomi.event.MangaEvent
|
||||
import eu.kanade.tachiyomi.event.ReaderEvent
|
||||
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
import rx.Observable
|
||||
import rx.android.schedulers.AndroidSchedulers
|
||||
import rx.schedulers.Schedulers
|
||||
import rx.subjects.PublishSubject
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
class ChaptersPresenter : BasePresenter<ChaptersFragment>() {
|
||||
|
||||
@Inject lateinit var db: DatabaseHelper
|
||||
@Inject lateinit var sourceManager: SourceManager
|
||||
@Inject lateinit var preferences: PreferencesHelper
|
||||
@Inject lateinit var downloadManager: DownloadManager
|
||||
|
||||
lateinit var manga: Manga
|
||||
private set
|
||||
|
||||
lateinit var source: Source
|
||||
private set
|
||||
|
||||
lateinit var chapters: List<Chapter>
|
||||
private set
|
||||
|
||||
lateinit var chaptersSubject: PublishSubject<List<Chapter>>
|
||||
private set
|
||||
|
||||
var hasRequested: Boolean = false
|
||||
private set
|
||||
|
||||
private val GET_MANGA = 1
|
||||
private val DB_CHAPTERS = 2
|
||||
private val FETCH_CHAPTERS = 3
|
||||
private val CHAPTER_STATUS_CHANGES = 4
|
||||
|
||||
override fun onCreate(savedState: Bundle?) {
|
||||
super.onCreate(savedState)
|
||||
|
||||
chaptersSubject = PublishSubject.create()
|
||||
|
||||
startableLatestCache(GET_MANGA,
|
||||
{ Observable.just(manga) },
|
||||
{ view, manga -> view.onNextManga(manga) })
|
||||
|
||||
startableLatestCache(DB_CHAPTERS,
|
||||
{ getDbChaptersObs() },
|
||||
{ view, chapters -> view.onNextChapters(chapters) })
|
||||
|
||||
startableFirst(FETCH_CHAPTERS,
|
||||
{ getOnlineChaptersObs() },
|
||||
{ view, result -> view.onFetchChaptersDone() },
|
||||
{ view, error -> view.onFetchChaptersError(error) })
|
||||
|
||||
startableLatestCache(CHAPTER_STATUS_CHANGES,
|
||||
{ getChapterStatusObs() },
|
||||
{ view, download -> view.onChapterStatusChange(download) },
|
||||
{ view, error -> Timber.e(error.cause, error.message) })
|
||||
|
||||
registerForEvents()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
unregisterForEvents()
|
||||
EventBus.getDefault().removeStickyEvent(ChapterCountEvent::class.java)
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
|
||||
fun onEvent(event: MangaEvent) {
|
||||
this.manga = event.manga
|
||||
start(GET_MANGA)
|
||||
|
||||
if (isUnsubscribed(DB_CHAPTERS)) {
|
||||
source = sourceManager.get(manga.source)!!
|
||||
start(DB_CHAPTERS)
|
||||
|
||||
add(db.getChapters(manga).asRxObservable()
|
||||
.subscribeOn(Schedulers.io())
|
||||
.doOnNext { chapters ->
|
||||
this.chapters = chapters
|
||||
EventBus.getDefault().postSticky(ChapterCountEvent(chapters.size))
|
||||
for (chapter in chapters) {
|
||||
setChapterStatus(chapter)
|
||||
}
|
||||
start(CHAPTER_STATUS_CHANGES)
|
||||
}
|
||||
.subscribe{ chaptersSubject.onNext(it) })
|
||||
}
|
||||
}
|
||||
|
||||
fun fetchChaptersFromSource() {
|
||||
hasRequested = true
|
||||
start(FETCH_CHAPTERS)
|
||||
}
|
||||
|
||||
private fun refreshChapters() {
|
||||
chaptersSubject.onNext(chapters)
|
||||
}
|
||||
|
||||
fun getOnlineChaptersObs(): Observable<Pair<Int, Int>> {
|
||||
return source.pullChaptersFromNetwork(manga.url)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.flatMap { chapters -> db.insertOrRemoveChapters(manga, chapters, source) }
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
}
|
||||
|
||||
fun getDbChaptersObs(): Observable<List<Chapter>> {
|
||||
return chaptersSubject
|
||||
.flatMap { applyChapterFilters(it) }
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
}
|
||||
|
||||
fun getChapterStatusObs(): Observable<Download> {
|
||||
return downloadManager.queue.statusObservable
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.filter { download -> download.manga.id == manga.id }
|
||||
.doOnNext { updateChapterStatus(it) }
|
||||
}
|
||||
|
||||
private fun applyChapterFilters(chapters: List<Chapter>): Observable<List<Chapter>> {
|
||||
var observable = Observable.from(chapters).subscribeOn(Schedulers.io())
|
||||
if (onlyUnread()) {
|
||||
observable = observable.filter { chapter -> !chapter.read }
|
||||
}
|
||||
if (onlyDownloaded()) {
|
||||
observable = observable.filter { chapter -> chapter.status == Download.DOWNLOADED }
|
||||
}
|
||||
return observable.toSortedList { chapter, chapter2 ->
|
||||
if (sortOrder())
|
||||
chapter2.chapter_number.compareTo(chapter.chapter_number)
|
||||
else
|
||||
chapter.chapter_number.compareTo(chapter2.chapter_number)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setChapterStatus(chapter: Chapter) {
|
||||
for (download in downloadManager.queue) {
|
||||
if (chapter.id == download.chapter.id) {
|
||||
chapter.status = download.status
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (downloadManager.isChapterDownloaded(source, manga, chapter)) {
|
||||
chapter.status = Download.DOWNLOADED
|
||||
} else {
|
||||
chapter.status = Download.NOT_DOWNLOADED
|
||||
}
|
||||
}
|
||||
|
||||
fun updateChapterStatus(download: Download) {
|
||||
for (chapter in chapters) {
|
||||
if (download.chapter.id == chapter.id) {
|
||||
chapter.status = download.status
|
||||
break
|
||||
}
|
||||
}
|
||||
if (onlyDownloaded() && download.status == Download.DOWNLOADED)
|
||||
refreshChapters()
|
||||
}
|
||||
|
||||
fun onOpenChapter(chapter: Chapter) {
|
||||
EventBus.getDefault().postSticky(ReaderEvent(source, manga, chapter))
|
||||
}
|
||||
|
||||
fun getNextUnreadChapter(): Chapter? {
|
||||
return db.getNextUnreadChapter(manga).executeAsBlocking()
|
||||
}
|
||||
|
||||
fun markChaptersRead(selectedChapters: Observable<Chapter>, read: Boolean) {
|
||||
add(selectedChapters.subscribeOn(Schedulers.io())
|
||||
.doOnNext { chapter ->
|
||||
chapter.read = read
|
||||
if (!read) chapter.last_page_read = 0
|
||||
}
|
||||
.toList()
|
||||
.flatMap { chapters -> db.insertChapters(chapters).asRxObservable() }
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe())
|
||||
}
|
||||
|
||||
fun markPreviousChaptersAsRead(selected: Chapter) {
|
||||
Observable.from(chapters)
|
||||
.filter { c -> c.chapter_number > -1 && c.chapter_number < selected.chapter_number }
|
||||
.doOnNext { c -> c.read = true }
|
||||
.toList()
|
||||
.flatMap { chapters -> db.insertChapters(chapters).asRxObservable() }
|
||||
.subscribe()
|
||||
}
|
||||
|
||||
fun downloadChapters(selectedChapters: Observable<Chapter>) {
|
||||
add(selectedChapters.toList()
|
||||
.subscribe { chapters -> EventBus.getDefault().postSticky(DownloadChaptersEvent(manga, chapters)) })
|
||||
}
|
||||
|
||||
fun deleteChapters(selectedChapters: Observable<Chapter>) {
|
||||
add(selectedChapters.subscribe(
|
||||
{ chapter -> downloadManager.queue.remove(chapter) },
|
||||
{ error -> Timber.e(error.message) },
|
||||
{
|
||||
if (onlyDownloaded())
|
||||
refreshChapters()
|
||||
}))
|
||||
}
|
||||
|
||||
fun deleteChapter(chapter: Chapter) {
|
||||
downloadManager.deleteChapter(source, manga, chapter)
|
||||
}
|
||||
|
||||
fun revertSortOrder() {
|
||||
manga.setChapterOrder(if (sortOrder()) Manga.SORT_ZA else Manga.SORT_AZ)
|
||||
db.insertManga(manga).executeAsBlocking()
|
||||
refreshChapters()
|
||||
}
|
||||
|
||||
fun setReadFilter(onlyUnread: Boolean) {
|
||||
manga.readFilter = if (onlyUnread) Manga.SHOW_UNREAD else Manga.SHOW_ALL
|
||||
db.insertManga(manga).executeAsBlocking()
|
||||
refreshChapters()
|
||||
}
|
||||
|
||||
fun setDownloadedFilter(onlyDownloaded: Boolean) {
|
||||
manga.downloadedFilter = if (onlyDownloaded) Manga.SHOW_DOWNLOADED else Manga.SHOW_ALL
|
||||
db.insertManga(manga).executeAsBlocking()
|
||||
refreshChapters()
|
||||
}
|
||||
|
||||
fun setDisplayMode(mode: Int) {
|
||||
manga.displayMode = mode
|
||||
db.insertManga(manga).executeAsBlocking()
|
||||
}
|
||||
|
||||
fun onlyDownloaded(): Boolean {
|
||||
return manga.downloadedFilter == Manga.SHOW_DOWNLOADED
|
||||
}
|
||||
|
||||
fun onlyUnread(): Boolean {
|
||||
return manga.readFilter == Manga.SHOW_UNREAD
|
||||
}
|
||||
|
||||
fun sortOrder(): Boolean {
|
||||
return manga.sortChaptersAZ()
|
||||
}
|
||||
|
||||
}
|
@ -1,244 +0,0 @@
|
||||
package eu.kanade.tachiyomi.ui.manga.info;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.design.widget.FloatingActionButton;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.support.v4.widget.SwipeRefreshLayout;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.bumptech.glide.load.model.LazyHeaders;
|
||||
|
||||
import butterknife.Bind;
|
||||
import butterknife.ButterKnife;
|
||||
import eu.kanade.tachiyomi.R;
|
||||
import eu.kanade.tachiyomi.data.cache.CoverCache;
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga;
|
||||
import eu.kanade.tachiyomi.data.source.base.Source;
|
||||
import eu.kanade.tachiyomi.ui.base.fragment.BaseRxFragment;
|
||||
import nucleus.factory.RequiresPresenter;
|
||||
|
||||
/**
|
||||
* Fragment that shows manga information.
|
||||
* Uses R.layout.fragment_manga_info.
|
||||
* UI related actions should be called from here.
|
||||
*/
|
||||
@RequiresPresenter(MangaInfoPresenter.class)
|
||||
public class MangaInfoFragment extends BaseRxFragment<MangaInfoPresenter> {
|
||||
/**
|
||||
* SwipeRefreshLayout showing refresh status
|
||||
*/
|
||||
@Bind(R.id.swipe_refresh) SwipeRefreshLayout swipeRefresh;
|
||||
|
||||
/**
|
||||
* TextView containing artist information.
|
||||
*/
|
||||
@Bind(R.id.manga_artist) TextView artist;
|
||||
|
||||
/**
|
||||
* TextView containing author information.
|
||||
*/
|
||||
@Bind(R.id.manga_author) TextView author;
|
||||
|
||||
/**
|
||||
* TextView containing chapter count.
|
||||
*/
|
||||
@Bind(R.id.manga_chapters) TextView chapterCount;
|
||||
|
||||
/**
|
||||
* TextView containing genres.
|
||||
*/
|
||||
@Bind(R.id.manga_genres) TextView genres;
|
||||
|
||||
/**
|
||||
* TextView containing status (ongoing, finished).
|
||||
*/
|
||||
@Bind(R.id.manga_status) TextView status;
|
||||
|
||||
/**
|
||||
* TextView containing source.
|
||||
*/
|
||||
@Bind(R.id.manga_source) TextView source;
|
||||
|
||||
/**
|
||||
* TextView containing manga summary.
|
||||
*/
|
||||
@Bind(R.id.manga_summary) TextView description;
|
||||
|
||||
/**
|
||||
* ImageView of cover.
|
||||
*/
|
||||
@Bind(R.id.manga_cover) ImageView cover;
|
||||
|
||||
/**
|
||||
* ImageView containing manga cover shown as blurred backdrop.
|
||||
*/
|
||||
@Bind(R.id.backdrop) ImageView backdrop;
|
||||
|
||||
/**
|
||||
* FAB anchored to bottom of top view used to (add / remove) manga (to / from) library.
|
||||
*/
|
||||
@Bind(R.id.fab_favorite) FloatingActionButton fabFavorite;
|
||||
|
||||
/**
|
||||
* Create new instance of MangaInfoFragment.
|
||||
*
|
||||
* @return MangaInfoFragment.
|
||||
*/
|
||||
public static MangaInfoFragment newInstance() {
|
||||
return new MangaInfoFragment();
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
// Inflate the layout for this fragment.
|
||||
View view = inflater.inflate(R.layout.fragment_manga_info, container, false);
|
||||
|
||||
// Bind layout objects.
|
||||
ButterKnife.bind(this, view);
|
||||
|
||||
// Set onclickListener to toggle favorite when FAB clicked.
|
||||
fabFavorite.setOnClickListener(v -> getPresenter().toggleFavorite());
|
||||
|
||||
// Set SwipeRefresh to refresh manga data.
|
||||
swipeRefresh.setOnRefreshListener(this::fetchMangaFromSource);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if manga is initialized.
|
||||
* If true update view with manga information,
|
||||
* if false fetch manga information
|
||||
*
|
||||
* @param manga manga object containing information about manga.
|
||||
* @param source the source of the manga.
|
||||
*/
|
||||
public void onNextManga(Manga manga, Source source) {
|
||||
if (manga.initialized) {
|
||||
// Update view.
|
||||
setMangaInfo(manga, source);
|
||||
} else {
|
||||
// Initialize manga.
|
||||
fetchMangaFromSource();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the view with manga information.
|
||||
*
|
||||
* @param manga manga object containing information about manga.
|
||||
* @param mangaSource the source of the manga.
|
||||
*/
|
||||
private void setMangaInfo(Manga manga, Source mangaSource) {
|
||||
// Update artist TextView.
|
||||
artist.setText(manga.artist);
|
||||
|
||||
// Update author TextView.
|
||||
author.setText(manga.author);
|
||||
|
||||
// If manga source is known update source TextView.
|
||||
if (mangaSource != null) {
|
||||
source.setText(mangaSource.getVisibleName());
|
||||
}
|
||||
|
||||
// Update genres TextView.
|
||||
genres.setText(manga.genre);
|
||||
|
||||
// Update status TextView.
|
||||
status.setText(manga.getStatus(getActivity()));
|
||||
|
||||
// Update description TextView.
|
||||
description.setText(manga.description);
|
||||
|
||||
// Set the favorite drawable to the correct one.
|
||||
setFavoriteDrawable(manga.favorite);
|
||||
|
||||
// Initialize CoverCache and Glide headers to retrieve cover information.
|
||||
CoverCache coverCache = getPresenter().coverCache;
|
||||
LazyHeaders headers = getPresenter().source.getGlideHeaders();
|
||||
|
||||
// Check if thumbnail_url is given.
|
||||
if (manga.thumbnail_url != null) {
|
||||
// Check if cover is already drawn.
|
||||
if (cover.getDrawable() == null) {
|
||||
// If manga is in library then (download / save) (from / to) local cache if available,
|
||||
// else download from network.
|
||||
if (manga.favorite) {
|
||||
coverCache.saveOrLoadFromCache(cover, manga.thumbnail_url, headers);
|
||||
} else {
|
||||
coverCache.loadFromNetwork(cover, manga.thumbnail_url, headers);
|
||||
}
|
||||
}
|
||||
// Check if backdrop is already drawn.
|
||||
if (backdrop.getDrawable() == null) {
|
||||
// If manga is in library then (download / save) (from / to) local cache if available,
|
||||
// else download from network.
|
||||
if (manga.favorite) {
|
||||
coverCache.saveOrLoadFromCache(backdrop, manga.thumbnail_url, headers);
|
||||
} else {
|
||||
coverCache.loadFromNetwork(backdrop, manga.thumbnail_url, headers);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update chapter count TextView.
|
||||
*
|
||||
* @param count number of chapters.
|
||||
*/
|
||||
public void setChapterCount(int count) {
|
||||
chapterCount.setText(String.valueOf(count));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update FAB with correct drawable.
|
||||
*
|
||||
* @param isFavorite determines if manga is favorite or not.
|
||||
*/
|
||||
private void setFavoriteDrawable(boolean isFavorite) {
|
||||
// Set the Favorite drawable to the correct one.
|
||||
// Border drawable if false, filled drawable if true.
|
||||
fabFavorite.setImageDrawable(ContextCompat.getDrawable(getContext(), isFavorite ?
|
||||
R.drawable.ic_bookmark_white_24dp :
|
||||
R.drawable.ic_bookmark_border_white_24dp));
|
||||
}
|
||||
|
||||
/**
|
||||
* Start fetching manga information from source.
|
||||
*/
|
||||
private void fetchMangaFromSource() {
|
||||
setRefreshing(true);
|
||||
// Call presenter and start fetching manga information
|
||||
getPresenter().fetchMangaFromSource();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Update swipeRefresh to stop showing refresh in progress spinner.
|
||||
*/
|
||||
public void onFetchMangaDone() {
|
||||
setRefreshing(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update swipeRefresh to start showing refresh in progress spinner.
|
||||
*/
|
||||
public void onFetchMangaError() {
|
||||
setRefreshing(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set swipeRefresh status.
|
||||
*
|
||||
* @param value status of manga fetch.
|
||||
*/
|
||||
private void setRefreshing(boolean value) {
|
||||
swipeRefresh.setRefreshing(value);
|
||||
}
|
||||
}
|
@ -0,0 +1,179 @@
|
||||
package eu.kanade.tachiyomi.ui.manga.info
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.data.source.base.Source
|
||||
import eu.kanade.tachiyomi.ui.base.fragment.BaseRxFragment
|
||||
import eu.kanade.tachiyomi.util.setDrawableCompat
|
||||
import kotlinx.android.synthetic.main.fragment_manga_info.*
|
||||
import nucleus.factory.RequiresPresenter
|
||||
|
||||
/**
|
||||
* Fragment that shows manga information.
|
||||
* Uses R.layout.fragment_manga_info.
|
||||
* UI related actions should be called from here.
|
||||
*/
|
||||
@RequiresPresenter(MangaInfoPresenter::class)
|
||||
class MangaInfoFragment : BaseRxFragment<MangaInfoPresenter>() {
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Create new instance of MangaInfoFragment.
|
||||
*
|
||||
* @return MangaInfoFragment.
|
||||
*/
|
||||
fun newInstance(): MangaInfoFragment {
|
||||
return MangaInfoFragment()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedState: Bundle?): View? {
|
||||
return inflater.inflate(R.layout.fragment_manga_info, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View?, savedState: Bundle?) {
|
||||
// Set onclickListener to toggle favorite when FAB clicked.
|
||||
fab_favorite.setOnClickListener { presenter.toggleFavorite() }
|
||||
|
||||
// Set SwipeRefresh to refresh manga data.
|
||||
swipe_refresh.setOnRefreshListener { fetchMangaFromSource() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if manga is initialized.
|
||||
* If true update view with manga information,
|
||||
* if false fetch manga information
|
||||
*
|
||||
* @param manga manga object containing information about manga.
|
||||
* @param source the source of the manga.
|
||||
*/
|
||||
fun onNextManga(manga: Manga, source: Source) {
|
||||
if (manga.initialized) {
|
||||
// Update view.
|
||||
setMangaInfo(manga, source)
|
||||
} else {
|
||||
// Initialize manga.
|
||||
fetchMangaFromSource()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the view with manga information.
|
||||
*
|
||||
* @param manga manga object containing information about manga.
|
||||
* @param source the source of the manga.
|
||||
*/
|
||||
private fun setMangaInfo(manga: Manga, source: Source?) {
|
||||
// Update artist TextView.
|
||||
manga_artist.text = manga.artist
|
||||
|
||||
// Update author TextView.
|
||||
manga_author.text = manga.author
|
||||
|
||||
// If manga source is known update source TextView.
|
||||
if (source != null) {
|
||||
manga_source.text = source.visibleName
|
||||
}
|
||||
|
||||
// Update genres TextView.
|
||||
manga_genres.text = manga.genre
|
||||
|
||||
// Update status TextView.
|
||||
manga_status.text = manga.getStatus(activity)
|
||||
|
||||
// Update description TextView.
|
||||
manga_summary.text = manga.description
|
||||
|
||||
// Set the favorite drawable to the correct one.
|
||||
setFavoriteDrawable(manga.favorite)
|
||||
|
||||
// Initialize CoverCache and Glide headers to retrieve cover information.
|
||||
val coverCache = presenter.coverCache
|
||||
val headers = presenter.source.glideHeaders
|
||||
|
||||
// Check if thumbnail_url is given.
|
||||
if (manga.thumbnail_url != null) {
|
||||
// Check if cover is already drawn.
|
||||
if (manga_cover.drawable == null) {
|
||||
// If manga is in library then (download / save) (from / to) local cache if available,
|
||||
// else download from network.
|
||||
if (manga.favorite) {
|
||||
coverCache.saveOrLoadFromCache(manga_cover, manga.thumbnail_url, headers)
|
||||
} else {
|
||||
coverCache.loadFromNetwork(manga_cover, manga.thumbnail_url, headers)
|
||||
}
|
||||
}
|
||||
// Check if backdrop is already drawn.
|
||||
if (backdrop.drawable == null) {
|
||||
// If manga is in library then (download / save) (from / to) local cache if available,
|
||||
// else download from network.
|
||||
if (manga.favorite) {
|
||||
coverCache.saveOrLoadFromCache(backdrop, manga.thumbnail_url, headers)
|
||||
} else {
|
||||
coverCache.loadFromNetwork(backdrop, manga.thumbnail_url, headers)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update chapter count TextView.
|
||||
*
|
||||
* @param count number of chapters.
|
||||
*/
|
||||
fun setChapterCount(count: Int) {
|
||||
manga_chapters.text = count.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* Update FAB with correct drawable.
|
||||
*
|
||||
* @param isFavorite determines if manga is favorite or not.
|
||||
*/
|
||||
private fun setFavoriteDrawable(isFavorite: Boolean) {
|
||||
// Set the Favorite drawable to the correct one.
|
||||
// Border drawable if false, filled drawable if true.
|
||||
fab_favorite.setDrawableCompat(if (isFavorite)
|
||||
R.drawable.ic_bookmark_white_24dp
|
||||
else
|
||||
R.drawable.ic_bookmark_border_white_24dp)
|
||||
}
|
||||
|
||||
/**
|
||||
* Start fetching manga information from source.
|
||||
*/
|
||||
private fun fetchMangaFromSource() {
|
||||
setRefreshing(true)
|
||||
// Call presenter and start fetching manga information
|
||||
presenter.fetchMangaFromSource()
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Update swipe refresh to stop showing refresh in progress spinner.
|
||||
*/
|
||||
fun onFetchMangaDone() {
|
||||
setRefreshing(false)
|
||||
}
|
||||
|
||||
/**
|
||||
* Update swipe refresh to start showing refresh in progress spinner.
|
||||
*/
|
||||
fun onFetchMangaError() {
|
||||
setRefreshing(false)
|
||||
}
|
||||
|
||||
/**
|
||||
* Set swipe refresh status.
|
||||
*
|
||||
* @param value whether it should be refreshing or not.
|
||||
*/
|
||||
private fun setRefreshing(value: Boolean) {
|
||||
swipe_refresh.isRefreshing = value
|
||||
}
|
||||
|
||||
}
|
@ -1,177 +0,0 @@
|
||||
package eu.kanade.tachiyomi.ui.manga.info;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import org.greenrobot.eventbus.Subscribe;
|
||||
import org.greenrobot.eventbus.ThreadMode;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
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.source.SourceManager;
|
||||
import eu.kanade.tachiyomi.data.source.base.Source;
|
||||
import eu.kanade.tachiyomi.event.ChapterCountEvent;
|
||||
import eu.kanade.tachiyomi.event.MangaEvent;
|
||||
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter;
|
||||
import rx.Observable;
|
||||
import rx.android.schedulers.AndroidSchedulers;
|
||||
import rx.schedulers.Schedulers;
|
||||
|
||||
/**
|
||||
* Presenter of MangaInfoFragment.
|
||||
* Contains information and data for fragment.
|
||||
* Observable updates should be called from here.
|
||||
*/
|
||||
public class MangaInfoPresenter extends BasePresenter<MangaInfoFragment> {
|
||||
|
||||
/**
|
||||
* The id of the restartable.
|
||||
*/
|
||||
private static final int GET_MANGA = 1;
|
||||
|
||||
/**
|
||||
* The id of the restartable.
|
||||
*/
|
||||
private static final int GET_CHAPTER_COUNT = 2;
|
||||
|
||||
/**
|
||||
* The id of the restartable.
|
||||
*/
|
||||
private static final int FETCH_MANGA_INFO = 3;
|
||||
|
||||
/**
|
||||
* Source information.
|
||||
*/
|
||||
protected Source source;
|
||||
|
||||
/**
|
||||
* Used to connect to database.
|
||||
*/
|
||||
@Inject DatabaseHelper db;
|
||||
|
||||
/**
|
||||
* Used to connect to different manga sources.
|
||||
*/
|
||||
@Inject SourceManager sourceManager;
|
||||
|
||||
/**
|
||||
* Used to connect to cache.
|
||||
*/
|
||||
@Inject CoverCache coverCache;
|
||||
|
||||
/**
|
||||
* Selected manga information.
|
||||
*/
|
||||
private Manga manga;
|
||||
|
||||
/**
|
||||
* Count of chapters.
|
||||
*/
|
||||
private int count = -1;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedState) {
|
||||
super.onCreate(savedState);
|
||||
|
||||
// Notify the view a manga is available or has changed.
|
||||
startableLatestCache(GET_MANGA,
|
||||
() -> Observable.just(manga),
|
||||
(view, manga) -> view.onNextManga(manga, source));
|
||||
|
||||
// Update chapter count.
|
||||
startableLatestCache(GET_CHAPTER_COUNT,
|
||||
() -> Observable.just(count),
|
||||
MangaInfoFragment::setChapterCount);
|
||||
|
||||
// Fetch manga info from source.
|
||||
startableFirst(FETCH_MANGA_INFO,
|
||||
this::fetchMangaObs,
|
||||
(view, manga) -> view.onFetchMangaDone(),
|
||||
(view, error) -> view.onFetchMangaError());
|
||||
|
||||
// Listen for events.
|
||||
registerForEvents();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
unregisterForEvents();
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
|
||||
public void onEvent(MangaEvent event) {
|
||||
this.manga = event.manga;
|
||||
source = sourceManager.get(manga.source);
|
||||
refreshManga();
|
||||
}
|
||||
|
||||
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
|
||||
public void onEvent(ChapterCountEvent event) {
|
||||
if (count != event.getCount()) {
|
||||
count = event.getCount();
|
||||
// Update chapter count
|
||||
start(GET_CHAPTER_COUNT);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch manga information from source.
|
||||
*/
|
||||
public void fetchMangaFromSource() {
|
||||
if (isUnsubscribed(FETCH_MANGA_INFO)) {
|
||||
start(FETCH_MANGA_INFO);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch manga information from source.
|
||||
*
|
||||
* @return manga information.
|
||||
*/
|
||||
private Observable<Manga> fetchMangaObs() {
|
||||
return source.pullMangaFromNetwork(manga.url)
|
||||
.flatMap(networkManga -> {
|
||||
manga.copyFrom(networkManga);
|
||||
db.insertManga(manga).executeAsBlocking();
|
||||
return Observable.just(manga);
|
||||
})
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.doOnNext(manga -> refreshManga());
|
||||
}
|
||||
|
||||
/**
|
||||
* Update favorite status of manga, (removes / adds) manga (to / from) library.
|
||||
*/
|
||||
public void toggleFavorite() {
|
||||
manga.favorite = !manga.favorite;
|
||||
onMangaFavoriteChange(manga.favorite);
|
||||
db.insertManga(manga).executeAsBlocking();
|
||||
refreshManga();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* (Removes / Saves) cover depending on favorite status.
|
||||
*
|
||||
* @param isFavorite determines if manga is favorite or not.
|
||||
*/
|
||||
private void onMangaFavoriteChange(boolean isFavorite) {
|
||||
if (isFavorite) {
|
||||
coverCache.save(manga.thumbnail_url, source.getGlideHeaders());
|
||||
} else {
|
||||
coverCache.deleteCoverFromCache(manga.thumbnail_url);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh MangaInfo view.
|
||||
*/
|
||||
private void refreshManga() {
|
||||
start(GET_MANGA);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,173 @@
|
||||
package eu.kanade.tachiyomi.ui.manga.info
|
||||
|
||||
import android.os.Bundle
|
||||
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.source.SourceManager
|
||||
import eu.kanade.tachiyomi.data.source.base.Source
|
||||
import eu.kanade.tachiyomi.event.ChapterCountEvent
|
||||
import eu.kanade.tachiyomi.event.MangaEvent
|
||||
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
import rx.Observable
|
||||
import rx.android.schedulers.AndroidSchedulers
|
||||
import rx.schedulers.Schedulers
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Presenter of MangaInfoFragment.
|
||||
* Contains information and data for fragment.
|
||||
* Observable updates should be called from here.
|
||||
*/
|
||||
class MangaInfoPresenter : BasePresenter<MangaInfoFragment>() {
|
||||
|
||||
/**
|
||||
* Active manga.
|
||||
*/
|
||||
lateinit var manga: Manga
|
||||
private set
|
||||
|
||||
/**
|
||||
* Source of the manga.
|
||||
*/
|
||||
lateinit var source: Source
|
||||
private set
|
||||
|
||||
/**
|
||||
* Used to connect to database.
|
||||
*/
|
||||
@Inject lateinit var db: DatabaseHelper
|
||||
|
||||
/**
|
||||
* Used to connect to different manga sources.
|
||||
*/
|
||||
@Inject lateinit var sourceManager: SourceManager
|
||||
|
||||
/**
|
||||
* Used to connect to cache.
|
||||
*/
|
||||
@Inject lateinit var coverCache: CoverCache
|
||||
|
||||
/**
|
||||
* Count of chapters.
|
||||
*/
|
||||
private var count = -1
|
||||
|
||||
/**
|
||||
* The id of the restartable.
|
||||
*/
|
||||
private val GET_MANGA = 1
|
||||
|
||||
/**
|
||||
* The id of the restartable.
|
||||
*/
|
||||
private val GET_CHAPTER_COUNT = 2
|
||||
|
||||
/**
|
||||
* The id of the restartable.
|
||||
*/
|
||||
private val FETCH_MANGA_INFO = 3
|
||||
|
||||
override fun onCreate(savedState: Bundle?) {
|
||||
super.onCreate(savedState)
|
||||
|
||||
// Notify the view a manga is available or has changed.
|
||||
startableLatestCache(GET_MANGA,
|
||||
{ Observable.just(manga) },
|
||||
{ view, manga -> view.onNextManga(manga, source) })
|
||||
|
||||
// Update chapter count.
|
||||
startableLatestCache(GET_CHAPTER_COUNT,
|
||||
{ Observable.just(count) },
|
||||
{ view, count -> view.setChapterCount(count) })
|
||||
|
||||
// Fetch manga info from source.
|
||||
startableFirst(FETCH_MANGA_INFO,
|
||||
{ fetchMangaObs() },
|
||||
{ view, manga -> view.onFetchMangaDone() },
|
||||
{ view, error -> view.onFetchMangaError() })
|
||||
|
||||
// Listen for events.
|
||||
registerForEvents()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
unregisterForEvents()
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
|
||||
fun onEvent(event: MangaEvent) {
|
||||
manga = event.manga
|
||||
source = sourceManager.get(manga.source)!!
|
||||
refreshManga()
|
||||
}
|
||||
|
||||
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
|
||||
fun onEvent(event: ChapterCountEvent) {
|
||||
if (count != event.count) {
|
||||
count = event.count
|
||||
// Update chapter count
|
||||
start(GET_CHAPTER_COUNT)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch manga information from source.
|
||||
*/
|
||||
fun fetchMangaFromSource() {
|
||||
if (isUnsubscribed(FETCH_MANGA_INFO)) {
|
||||
start(FETCH_MANGA_INFO)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch manga information from source.
|
||||
*
|
||||
* @return manga information.
|
||||
*/
|
||||
private fun fetchMangaObs(): Observable<Manga> {
|
||||
return source.pullMangaFromNetwork(manga.url)
|
||||
.flatMap { networkManga ->
|
||||
manga.copyFrom(networkManga)
|
||||
db.insertManga(manga).executeAsBlocking()
|
||||
Observable.just<Manga>(manga)
|
||||
}
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.doOnNext { refreshManga() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Update favorite status of manga, (removes / adds) manga (to / from) library.
|
||||
*/
|
||||
fun toggleFavorite() {
|
||||
manga.favorite = !manga.favorite
|
||||
onMangaFavoriteChange(manga.favorite)
|
||||
db.insertManga(manga).executeAsBlocking()
|
||||
refreshManga()
|
||||
}
|
||||
|
||||
/**
|
||||
* (Removes / Saves) cover depending on favorite status.
|
||||
*
|
||||
* @param isFavorite determines if manga is favorite or not.
|
||||
*/
|
||||
private fun onMangaFavoriteChange(isFavorite: Boolean) {
|
||||
if (isFavorite) {
|
||||
coverCache.save(manga.thumbnail_url, source.glideHeaders)
|
||||
} else {
|
||||
coverCache.deleteCoverFromCache(manga.thumbnail_url)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh MangaInfo view.
|
||||
*/
|
||||
private fun refreshManga() {
|
||||
start(GET_MANGA)
|
||||
}
|
||||
|
||||
}
|
@ -1,151 +0,0 @@
|
||||
package eu.kanade.tachiyomi.ui.manga.myanimelist;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.text.Editable;
|
||||
import android.text.TextUtils;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.View;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ListView;
|
||||
import android.widget.ProgressBar;
|
||||
|
||||
import com.afollestad.materialdialogs.MaterialDialog;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import butterknife.Bind;
|
||||
import butterknife.ButterKnife;
|
||||
import eu.kanade.tachiyomi.R;
|
||||
import eu.kanade.tachiyomi.data.database.models.MangaSync;
|
||||
import rx.Subscription;
|
||||
import rx.android.schedulers.AndroidSchedulers;
|
||||
import rx.subjects.PublishSubject;
|
||||
|
||||
public class MyAnimeListDialogFragment extends DialogFragment {
|
||||
|
||||
@Bind(R.id.myanimelist_search_field) EditText searchText;
|
||||
@Bind(R.id.myanimelist_search_results) ListView searchResults;
|
||||
@Bind(R.id.progress) ProgressBar progressBar;
|
||||
|
||||
private MyAnimeListSearchAdapter adapter;
|
||||
private MangaSync selectedItem;
|
||||
|
||||
private Subscription searchSubscription;
|
||||
|
||||
public static MyAnimeListDialogFragment newInstance() {
|
||||
return new MyAnimeListDialogFragment();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedState) {
|
||||
MaterialDialog dialog = new MaterialDialog.Builder(getActivity())
|
||||
.customView(R.layout.dialog_myanimelist_search, false)
|
||||
.positiveText(R.string.button_ok)
|
||||
.negativeText(R.string.button_cancel)
|
||||
.onPositive((dialog1, which) -> onPositiveButtonClick())
|
||||
.build();
|
||||
|
||||
ButterKnife.bind(this, dialog.getView());
|
||||
|
||||
// Create adapter
|
||||
adapter = new MyAnimeListSearchAdapter(getActivity());
|
||||
searchResults.setAdapter(adapter);
|
||||
|
||||
// Set listeners
|
||||
searchResults.setOnItemClickListener((parent, viewList, position, id) ->
|
||||
selectedItem = adapter.getItem(position));
|
||||
|
||||
// Do an initial search based on the manga's title
|
||||
if (savedState == null) {
|
||||
String title = getPresenter().manga.title;
|
||||
searchText.append(title);
|
||||
search(title);
|
||||
}
|
||||
|
||||
return dialog;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
PublishSubject<String> querySubject = PublishSubject.create();
|
||||
searchText.addTextChangedListener(new SimpleTextChangeListener() {
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
querySubject.onNext(s.toString());
|
||||
}
|
||||
});
|
||||
|
||||
// Listen to text changes
|
||||
searchSubscription = querySubject.debounce(1, TimeUnit.SECONDS)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(this::search);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
if (searchSubscription != null) {
|
||||
searchSubscription.unsubscribe();
|
||||
}
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
private void onPositiveButtonClick() {
|
||||
if (adapter != null && selectedItem != null) {
|
||||
getPresenter().registerManga(selectedItem);
|
||||
}
|
||||
}
|
||||
|
||||
private void search(String query) {
|
||||
if (!TextUtils.isEmpty(query)) {
|
||||
searchResults.setVisibility(View.GONE);
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
getPresenter().searchManga(query);
|
||||
}
|
||||
}
|
||||
|
||||
public void onSearchResults(List<MangaSync> results) {
|
||||
selectedItem = null;
|
||||
progressBar.setVisibility(View.GONE);
|
||||
searchResults.setVisibility(View.VISIBLE);
|
||||
adapter.setItems(results);
|
||||
}
|
||||
|
||||
public void onSearchResultsError() {
|
||||
progressBar.setVisibility(View.GONE);
|
||||
searchResults.setVisibility(View.VISIBLE);
|
||||
adapter.clear();
|
||||
}
|
||||
|
||||
public MyAnimeListFragment getMALFragment() {
|
||||
return (MyAnimeListFragment) getParentFragment();
|
||||
}
|
||||
|
||||
public MyAnimeListPresenter getPresenter() {
|
||||
return getMALFragment().getPresenter();
|
||||
}
|
||||
|
||||
private static class SimpleTextChangeListener implements TextWatcher {
|
||||
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,126 @@
|
||||
package eu.kanade.tachiyomi.ui.manga.myanimelist
|
||||
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import android.support.v4.app.DialogFragment
|
||||
import android.view.View
|
||||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.MangaSync
|
||||
import eu.kanade.tachiyomi.widget.SimpleTextWatcher
|
||||
import kotlinx.android.synthetic.main.dialog_myanimelist_search.view.*
|
||||
import rx.Subscription
|
||||
import rx.android.schedulers.AndroidSchedulers
|
||||
import rx.subjects.PublishSubject
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class MyAnimeListDialogFragment : DialogFragment() {
|
||||
|
||||
companion object {
|
||||
|
||||
fun newInstance(): MyAnimeListDialogFragment {
|
||||
return MyAnimeListDialogFragment()
|
||||
}
|
||||
}
|
||||
|
||||
private lateinit var v: View
|
||||
|
||||
lateinit var adapter: MyAnimeListSearchAdapter
|
||||
private set
|
||||
|
||||
lateinit var querySubject: PublishSubject<String>
|
||||
private set
|
||||
|
||||
private var selectedItem: MangaSync? = null
|
||||
|
||||
private var searchSubscription: Subscription? = null
|
||||
|
||||
override fun onCreateDialog(savedState: Bundle?): Dialog {
|
||||
val dialog = MaterialDialog.Builder(activity)
|
||||
.customView(R.layout.dialog_myanimelist_search, false)
|
||||
.positiveText(android.R.string.ok)
|
||||
.negativeText(android.R.string.cancel)
|
||||
.onPositive { dialog1, which -> onPositiveButtonClick() }
|
||||
.build()
|
||||
|
||||
onViewCreated(dialog.view, savedState)
|
||||
|
||||
return dialog
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedState: Bundle?) {
|
||||
v = view
|
||||
|
||||
// Create adapter
|
||||
adapter = MyAnimeListSearchAdapter(activity)
|
||||
view.myanimelist_search_results.adapter = adapter
|
||||
|
||||
// Set listeners
|
||||
view.myanimelist_search_results.setOnItemClickListener { parent, viewList, position, id ->
|
||||
selectedItem = adapter.getItem(position)
|
||||
}
|
||||
|
||||
// Do an initial search based on the manga's title
|
||||
if (savedState == null) {
|
||||
val title = presenter.manga.title
|
||||
view.myanimelist_search_field.append(title)
|
||||
search(title)
|
||||
}
|
||||
|
||||
querySubject = PublishSubject.create<String>()
|
||||
|
||||
view.myanimelist_search_field.addTextChangedListener(object : SimpleTextWatcher() {
|
||||
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
|
||||
querySubject.onNext(s.toString())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
|
||||
// Listen to text changes
|
||||
searchSubscription = querySubject.debounce(1, TimeUnit.SECONDS)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe { search(it) }
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
searchSubscription?.unsubscribe()
|
||||
super.onPause()
|
||||
}
|
||||
|
||||
private fun onPositiveButtonClick() {
|
||||
selectedItem?.let {
|
||||
presenter.registerManga(it)
|
||||
}
|
||||
}
|
||||
|
||||
private fun search(query: String) {
|
||||
if (!query.isNullOrEmpty()) {
|
||||
v.myanimelist_search_results.visibility = View.GONE
|
||||
v.progress.visibility = View.VISIBLE
|
||||
presenter.searchManga(query)
|
||||
}
|
||||
}
|
||||
|
||||
fun onSearchResults(results: List<MangaSync>) {
|
||||
selectedItem = null
|
||||
v.progress.visibility = View.GONE
|
||||
v.myanimelist_search_results.visibility = View.VISIBLE
|
||||
adapter.setItems(results)
|
||||
}
|
||||
|
||||
fun onSearchResultsError() {
|
||||
v.progress.visibility = View.GONE
|
||||
v.myanimelist_search_results.visibility = View.VISIBLE
|
||||
adapter.clear()
|
||||
}
|
||||
|
||||
val malFragment: MyAnimeListFragment
|
||||
get() = parentFragment as MyAnimeListFragment
|
||||
|
||||
val presenter: MyAnimeListPresenter
|
||||
get() = malFragment.presenter
|
||||
|
||||
}
|
@ -1,181 +0,0 @@
|
||||
package eu.kanade.tachiyomi.ui.manga.myanimelist;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.widget.SwipeRefreshLayout;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.NumberPicker;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.afollestad.materialdialogs.MaterialDialog;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.List;
|
||||
|
||||
import butterknife.Bind;
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.OnClick;
|
||||
import eu.kanade.tachiyomi.R;
|
||||
import eu.kanade.tachiyomi.data.database.models.MangaSync;
|
||||
import eu.kanade.tachiyomi.ui.base.fragment.BaseRxFragment;
|
||||
import nucleus.factory.RequiresPresenter;
|
||||
|
||||
@RequiresPresenter(MyAnimeListPresenter.class)
|
||||
public class MyAnimeListFragment extends BaseRxFragment<MyAnimeListPresenter> {
|
||||
|
||||
@Bind(R.id.myanimelist_title) TextView title;
|
||||
@Bind(R.id.myanimelist_chapters) TextView chapters;
|
||||
@Bind(R.id.myanimelist_score) TextView score;
|
||||
@Bind(R.id.myanimelist_status) TextView status;
|
||||
@Bind(R.id.swipe_refresh) SwipeRefreshLayout swipeRefresh;
|
||||
|
||||
private MyAnimeListDialogFragment dialog;
|
||||
|
||||
private DecimalFormat decimalFormat = new DecimalFormat("#.##");
|
||||
|
||||
private final static String SEARCH_FRAGMENT_TAG = "mal_search";
|
||||
|
||||
public static MyAnimeListFragment newInstance() {
|
||||
return new MyAnimeListFragment();
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.fragment_myanimelist, container, false);
|
||||
ButterKnife.bind(this, view);
|
||||
|
||||
swipeRefresh.setEnabled(false);
|
||||
swipeRefresh.setOnRefreshListener(() -> getPresenter().refresh());
|
||||
return view;
|
||||
}
|
||||
|
||||
public void setMangaSync(MangaSync mangaSync) {
|
||||
swipeRefresh.setEnabled(mangaSync != null);
|
||||
if (mangaSync != null) {
|
||||
title.setText(mangaSync.title);
|
||||
chapters.setText(mangaSync.last_chapter_read + "/" +
|
||||
(mangaSync.total_chapters > 0 ? mangaSync.total_chapters : "-"));
|
||||
score.setText(mangaSync.score == 0 ? "-" : decimalFormat.format(mangaSync.score));
|
||||
status.setText(getPresenter().myAnimeList.getStatus(mangaSync.status));
|
||||
}
|
||||
}
|
||||
|
||||
public void onRefreshDone() {
|
||||
swipeRefresh.setRefreshing(false);
|
||||
}
|
||||
|
||||
public void onRefreshError() {
|
||||
swipeRefresh.setRefreshing(false);
|
||||
}
|
||||
|
||||
public void setSearchResults(List<MangaSync> results) {
|
||||
findSearchFragmentIfNeeded();
|
||||
|
||||
if (dialog != null) {
|
||||
dialog.onSearchResults(results);
|
||||
}
|
||||
}
|
||||
|
||||
public void setSearchResultsError() {
|
||||
findSearchFragmentIfNeeded();
|
||||
|
||||
if (dialog != null) {
|
||||
dialog.onSearchResultsError();
|
||||
}
|
||||
}
|
||||
|
||||
private void findSearchFragmentIfNeeded() {
|
||||
if (dialog == null) {
|
||||
dialog = (MyAnimeListDialogFragment) getChildFragmentManager()
|
||||
.findFragmentByTag(SEARCH_FRAGMENT_TAG);
|
||||
}
|
||||
}
|
||||
|
||||
@OnClick(R.id.myanimelist_title_layout)
|
||||
void onTitleClick() {
|
||||
if (dialog == null)
|
||||
dialog = MyAnimeListDialogFragment.newInstance();
|
||||
|
||||
getPresenter().restartSearch();
|
||||
dialog.show(getChildFragmentManager(), SEARCH_FRAGMENT_TAG);
|
||||
}
|
||||
|
||||
@OnClick(R.id.myanimelist_status_layout)
|
||||
void onStatusClick() {
|
||||
if (getPresenter().mangaSync == null)
|
||||
return;
|
||||
|
||||
Context ctx = getActivity();
|
||||
new MaterialDialog.Builder(ctx)
|
||||
.title(R.string.status)
|
||||
.items(getPresenter().getAllStatus(ctx))
|
||||
.itemsCallbackSingleChoice(getPresenter().getIndexFromStatus(),
|
||||
(materialDialog, view, i, charSequence) -> {
|
||||
getPresenter().setStatus(i);
|
||||
status.setText("...");
|
||||
return true;
|
||||
})
|
||||
.show();
|
||||
}
|
||||
|
||||
@OnClick(R.id.myanimelist_chapters_layout)
|
||||
void onChaptersClick() {
|
||||
if (getPresenter().mangaSync == null)
|
||||
return;
|
||||
|
||||
MaterialDialog dialog = new MaterialDialog.Builder(getActivity())
|
||||
.title(R.string.chapters)
|
||||
.customView(R.layout.dialog_myanimelist_chapters, false)
|
||||
.positiveText(R.string.button_ok)
|
||||
.negativeText(R.string.button_cancel)
|
||||
.onPositive((materialDialog, dialogAction) -> {
|
||||
View view = materialDialog.getCustomView();
|
||||
if (view != null) {
|
||||
NumberPicker np = (NumberPicker) view.findViewById(R.id.chapters_picker);
|
||||
getPresenter().setLastChapterRead(np.getValue());
|
||||
chapters.setText("...");
|
||||
}
|
||||
})
|
||||
.show();
|
||||
|
||||
View view = dialog.getCustomView();
|
||||
if (view != null) {
|
||||
NumberPicker np = (NumberPicker) view.findViewById(R.id.chapters_picker);
|
||||
// Set initial value
|
||||
np.setValue(getPresenter().mangaSync.last_chapter_read);
|
||||
// Don't allow to go from 0 to 9999
|
||||
np.setWrapSelectorWheel(false);
|
||||
}
|
||||
}
|
||||
|
||||
@OnClick(R.id.myanimelist_score_layout)
|
||||
void onScoreClick() {
|
||||
if (getPresenter().mangaSync == null)
|
||||
return;
|
||||
|
||||
MaterialDialog dialog = new MaterialDialog.Builder(getActivity())
|
||||
.title(R.string.score)
|
||||
.customView(R.layout.dialog_myanimelist_score, false)
|
||||
.positiveText(R.string.button_ok)
|
||||
.negativeText(R.string.button_cancel)
|
||||
.onPositive((materialDialog, dialogAction) -> {
|
||||
View view = materialDialog.getCustomView();
|
||||
if (view != null) {
|
||||
NumberPicker np = (NumberPicker) view.findViewById(R.id.score_picker);
|
||||
getPresenter().setScore(np.getValue());
|
||||
score.setText("...");
|
||||
}
|
||||
})
|
||||
.show();
|
||||
|
||||
View view = dialog.getCustomView();
|
||||
if (view != null) {
|
||||
NumberPicker np = (NumberPicker) view.findViewById(R.id.score_picker);
|
||||
// Set initial value
|
||||
np.setValue((int) getPresenter().mangaSync.score);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,168 @@
|
||||
package eu.kanade.tachiyomi.ui.manga.myanimelist
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.NumberPicker
|
||||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.MangaSync
|
||||
import eu.kanade.tachiyomi.ui.base.fragment.BaseRxFragment
|
||||
import eu.kanade.tachiyomi.util.toast
|
||||
import kotlinx.android.synthetic.main.card_myanimelist_personal.*
|
||||
import kotlinx.android.synthetic.main.fragment_myanimelist.*
|
||||
import nucleus.factory.RequiresPresenter
|
||||
import java.text.DecimalFormat
|
||||
|
||||
@RequiresPresenter(MyAnimeListPresenter::class)
|
||||
class MyAnimeListFragment : BaseRxFragment<MyAnimeListPresenter>() {
|
||||
|
||||
companion object {
|
||||
fun newInstance(): MyAnimeListFragment {
|
||||
return MyAnimeListFragment()
|
||||
}
|
||||
}
|
||||
|
||||
private var dialog: MyAnimeListDialogFragment? = null
|
||||
|
||||
private val decimalFormat = DecimalFormat("#.##")
|
||||
|
||||
private val SEARCH_FRAGMENT_TAG = "mal_search"
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedState: Bundle?): View? {
|
||||
return inflater.inflate(R.layout.fragment_myanimelist, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedState: Bundle?) {
|
||||
swipe_refresh.isEnabled = false
|
||||
swipe_refresh.setOnRefreshListener { presenter.refresh() }
|
||||
myanimelist_title_layout.setOnClickListener { onTitleClick() }
|
||||
myanimelist_status_layout.setOnClickListener { onStatusClick() }
|
||||
myanimelist_chapters_layout.setOnClickListener { onChaptersClick() }
|
||||
myanimelist_score_layout.setOnClickListener { onScoreClick() }
|
||||
}
|
||||
|
||||
fun setMangaSync(mangaSync: MangaSync?) {
|
||||
swipe_refresh.isEnabled = mangaSync != null
|
||||
mangaSync?.let {
|
||||
myanimelist_title.text = it.title
|
||||
val chaptersText = if (it.total_chapters > 0)
|
||||
"${it.last_chapter_read}/${it.total_chapters}" else "${it.last_chapter_read}/-"
|
||||
|
||||
myanimelist_chapters.text = chaptersText
|
||||
myanimelist_score.text = if (it.score == 0f) "-" else decimalFormat.format(it.score)
|
||||
myanimelist_status.text = presenter.myAnimeList.getStatus(it.status)
|
||||
}
|
||||
}
|
||||
|
||||
fun onRefreshDone() {
|
||||
swipe_refresh.isRefreshing = false
|
||||
}
|
||||
|
||||
fun onRefreshError() {
|
||||
swipe_refresh.isRefreshing = false
|
||||
}
|
||||
|
||||
fun setSearchResults(results: List<MangaSync>) {
|
||||
findSearchFragmentIfNeeded()
|
||||
|
||||
dialog?.onSearchResults(results)
|
||||
}
|
||||
|
||||
fun setSearchResultsError(error: Throwable) {
|
||||
findSearchFragmentIfNeeded()
|
||||
context.toast(error.message)
|
||||
|
||||
dialog?.onSearchResultsError()
|
||||
}
|
||||
|
||||
private fun findSearchFragmentIfNeeded() {
|
||||
if (dialog == null) {
|
||||
dialog = childFragmentManager.findFragmentByTag(SEARCH_FRAGMENT_TAG) as MyAnimeListDialogFragment
|
||||
}
|
||||
}
|
||||
|
||||
fun onTitleClick() {
|
||||
if (dialog == null) {
|
||||
dialog = MyAnimeListDialogFragment.newInstance()
|
||||
}
|
||||
|
||||
presenter.restartSearch()
|
||||
dialog?.show(childFragmentManager, SEARCH_FRAGMENT_TAG)
|
||||
}
|
||||
|
||||
fun onStatusClick() {
|
||||
if (presenter.mangaSync == null)
|
||||
return
|
||||
|
||||
MaterialDialog.Builder(activity)
|
||||
.title(R.string.status)
|
||||
.items(presenter.getAllStatus())
|
||||
.itemsCallbackSingleChoice(presenter.getIndexFromStatus(), { dialog, view, i, charSequence ->
|
||||
presenter.setStatus(i)
|
||||
myanimelist_status.text = "..."
|
||||
true
|
||||
})
|
||||
.show()
|
||||
}
|
||||
|
||||
fun onChaptersClick() {
|
||||
if (presenter.mangaSync == null)
|
||||
return
|
||||
|
||||
val dialog = MaterialDialog.Builder(activity)
|
||||
.title(R.string.chapters)
|
||||
.customView(R.layout.dialog_myanimelist_chapters, false)
|
||||
.positiveText(android.R.string.ok)
|
||||
.negativeText(android.R.string.cancel)
|
||||
.onPositive { d, action ->
|
||||
val view = d.customView
|
||||
if (view != null) {
|
||||
val np = view.findViewById(R.id.chapters_picker) as NumberPicker
|
||||
np.clearFocus()
|
||||
presenter.setLastChapterRead(np.value)
|
||||
myanimelist_chapters.text = "..."
|
||||
}
|
||||
}
|
||||
.show()
|
||||
|
||||
val view = dialog.customView
|
||||
if (view != null) {
|
||||
val np = view.findViewById(R.id.chapters_picker) as NumberPicker
|
||||
// Set initial value
|
||||
np.value = presenter.mangaSync!!.last_chapter_read
|
||||
// Don't allow to go from 0 to 9999
|
||||
np.wrapSelectorWheel = false
|
||||
}
|
||||
}
|
||||
|
||||
fun onScoreClick() {
|
||||
if (presenter.mangaSync == null)
|
||||
return
|
||||
|
||||
val dialog = MaterialDialog.Builder(activity)
|
||||
.title(R.string.score)
|
||||
.customView(R.layout.dialog_myanimelist_score, false)
|
||||
.positiveText(android.R.string.ok)
|
||||
.negativeText(android.R.string.cancel)
|
||||
.onPositive { d, action ->
|
||||
val view = d.customView
|
||||
if (view != null) {
|
||||
val np = view.findViewById(R.id.score_picker) as NumberPicker
|
||||
np.clearFocus()
|
||||
presenter.setScore(np.value)
|
||||
myanimelist_score.text = "..."
|
||||
}
|
||||
}
|
||||
.show()
|
||||
|
||||
val view = dialog.customView
|
||||
if (view != null) {
|
||||
val np = view.findViewById(R.id.score_picker) as NumberPicker
|
||||
// Set initial value
|
||||
np.value = presenter.mangaSync!!.score.toInt()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,197 +0,0 @@
|
||||
package eu.kanade.tachiyomi.ui.manga.myanimelist;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import org.greenrobot.eventbus.Subscribe;
|
||||
import org.greenrobot.eventbus.ThreadMode;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import eu.kanade.tachiyomi.R;
|
||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper;
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga;
|
||||
import eu.kanade.tachiyomi.data.database.models.MangaSync;
|
||||
import eu.kanade.tachiyomi.data.mangasync.MangaSyncManager;
|
||||
import eu.kanade.tachiyomi.data.mangasync.services.MyAnimeList;
|
||||
import eu.kanade.tachiyomi.event.MangaEvent;
|
||||
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter;
|
||||
import eu.kanade.tachiyomi.util.ToastUtil;
|
||||
import rx.Observable;
|
||||
import rx.android.schedulers.AndroidSchedulers;
|
||||
import rx.schedulers.Schedulers;
|
||||
import timber.log.Timber;
|
||||
|
||||
public class MyAnimeListPresenter extends BasePresenter<MyAnimeListFragment> {
|
||||
|
||||
@Inject DatabaseHelper db;
|
||||
@Inject MangaSyncManager syncManager;
|
||||
|
||||
protected MyAnimeList myAnimeList;
|
||||
protected Manga manga;
|
||||
protected MangaSync mangaSync;
|
||||
|
||||
private String query;
|
||||
|
||||
private static final int GET_MANGA_SYNC = 1;
|
||||
private static final int GET_SEARCH_RESULTS = 2;
|
||||
private static final int REFRESH = 3;
|
||||
|
||||
private static final String PREFIX_MY = "my:";
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedState) {
|
||||
super.onCreate(savedState);
|
||||
|
||||
myAnimeList = syncManager.getMyAnimeList();
|
||||
|
||||
startableLatestCache(GET_MANGA_SYNC,
|
||||
() -> db.getMangaSync(manga, myAnimeList).asRxObservable()
|
||||
.doOnNext(mangaSync -> this.mangaSync = mangaSync)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread()),
|
||||
MyAnimeListFragment::setMangaSync);
|
||||
|
||||
startableLatestCache(GET_SEARCH_RESULTS,
|
||||
this::getSearchResultsObservable,
|
||||
(view, results) -> {
|
||||
view.setSearchResults(results);
|
||||
}, (view, error) -> {
|
||||
Timber.e(error.getMessage());
|
||||
view.setSearchResultsError();
|
||||
});
|
||||
|
||||
startableFirst(REFRESH,
|
||||
() -> myAnimeList.getList()
|
||||
.flatMap(myList -> {
|
||||
for (MangaSync myManga : myList) {
|
||||
if (myManga.remote_id == mangaSync.remote_id) {
|
||||
mangaSync.copyPersonalFrom(myManga);
|
||||
mangaSync.total_chapters = myManga.total_chapters;
|
||||
return Observable.just(mangaSync);
|
||||
}
|
||||
}
|
||||
return Observable.error(new Exception("Could not find manga"));
|
||||
})
|
||||
.flatMap(myManga -> db.insertMangaSync(myManga).asRxObservable())
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread()),
|
||||
(view, result) -> view.onRefreshDone(),
|
||||
(view, error) -> view.onRefreshError());
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onTakeView(MyAnimeListFragment view) {
|
||||
super.onTakeView(view);
|
||||
registerForEvents();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDropView() {
|
||||
unregisterForEvents();
|
||||
super.onDropView();
|
||||
}
|
||||
|
||||
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
|
||||
public void onEvent(MangaEvent event) {
|
||||
this.manga = event.manga;
|
||||
start(GET_MANGA_SYNC);
|
||||
}
|
||||
|
||||
private Observable<List<MangaSync>> getSearchResultsObservable() {
|
||||
Observable<List<MangaSync>> observable;
|
||||
if (query.startsWith(PREFIX_MY)) {
|
||||
String realQuery = query.substring(PREFIX_MY.length()).toLowerCase().trim();
|
||||
observable = myAnimeList.getList()
|
||||
.flatMap(Observable::from)
|
||||
.filter(manga -> manga.title.toLowerCase().contains(realQuery))
|
||||
.toList();
|
||||
} else {
|
||||
observable = myAnimeList.search(query);
|
||||
}
|
||||
return observable
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread());
|
||||
}
|
||||
|
||||
private void updateRemote() {
|
||||
add(myAnimeList.update(mangaSync)
|
||||
.flatMap(response -> db.insertMangaSync(mangaSync).asRxObservable())
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(next -> {},
|
||||
error -> {
|
||||
Timber.e(error.getMessage());
|
||||
// Restart on error to set old values
|
||||
start(GET_MANGA_SYNC);
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
public void searchManga(String query) {
|
||||
if (TextUtils.isEmpty(query) || query.equals(this.query))
|
||||
return;
|
||||
|
||||
this.query = query;
|
||||
start(GET_SEARCH_RESULTS);
|
||||
}
|
||||
|
||||
public void restartSearch() {
|
||||
this.query = null;
|
||||
stop(GET_SEARCH_RESULTS);
|
||||
}
|
||||
|
||||
public void registerManga(MangaSync manga) {
|
||||
manga.manga_id = this.manga.id;
|
||||
add(myAnimeList.bind(manga)
|
||||
.flatMap(response -> {
|
||||
if (response.isSuccessful()) {
|
||||
return db.insertMangaSync(manga).asRxObservable();
|
||||
}
|
||||
return Observable.error(new Exception("Could not bind manga"));
|
||||
})
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(manga2 -> {},
|
||||
error -> ToastUtil.showShort(getContext(), error.getMessage())));
|
||||
}
|
||||
|
||||
public String[] getAllStatus(Context context) {
|
||||
return new String[] {
|
||||
context.getString(R.string.reading),
|
||||
context.getString(R.string.completed),
|
||||
context.getString(R.string.on_hold),
|
||||
context.getString(R.string.dropped),
|
||||
context.getString(R.string.plan_to_read)
|
||||
};
|
||||
}
|
||||
|
||||
public int getIndexFromStatus() {
|
||||
return mangaSync.status == 6 ? 4 : mangaSync.status - 1;
|
||||
}
|
||||
|
||||
public void setStatus(int index) {
|
||||
mangaSync.status = index == 4 ? 6 : index + 1;
|
||||
updateRemote();
|
||||
}
|
||||
|
||||
public void setScore(int score) {
|
||||
mangaSync.score = score;
|
||||
updateRemote();
|
||||
}
|
||||
|
||||
public void setLastChapterRead(int chapterNumber) {
|
||||
mangaSync.last_chapter_read = chapterNumber;
|
||||
updateRemote();
|
||||
}
|
||||
|
||||
public void refresh() {
|
||||
if (mangaSync != null) {
|
||||
start(REFRESH);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,191 @@
|
||||
package eu.kanade.tachiyomi.ui.manga.myanimelist
|
||||
|
||||
import android.os.Bundle
|
||||
import com.pushtorefresh.storio.sqlite.operations.put.PutResult
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.data.database.models.MangaSync
|
||||
import eu.kanade.tachiyomi.data.mangasync.MangaSyncManager
|
||||
import eu.kanade.tachiyomi.event.MangaEvent
|
||||
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
|
||||
import eu.kanade.tachiyomi.util.toast
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
import rx.Observable
|
||||
import rx.android.schedulers.AndroidSchedulers
|
||||
import rx.schedulers.Schedulers
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
class MyAnimeListPresenter : BasePresenter<MyAnimeListFragment>() {
|
||||
|
||||
@Inject lateinit var db: DatabaseHelper
|
||||
@Inject lateinit var syncManager: MangaSyncManager
|
||||
|
||||
val myAnimeList by lazy { syncManager.myAnimeList }
|
||||
|
||||
lateinit var manga: Manga
|
||||
private set
|
||||
|
||||
var mangaSync: MangaSync? = null
|
||||
private set
|
||||
|
||||
private var query: String? = null
|
||||
|
||||
private val GET_MANGA_SYNC = 1
|
||||
private val GET_SEARCH_RESULTS = 2
|
||||
private val REFRESH = 3
|
||||
|
||||
private val PREFIX_MY = "my:"
|
||||
|
||||
override fun onCreate(savedState: Bundle?) {
|
||||
super.onCreate(savedState)
|
||||
|
||||
startableLatestCache(GET_MANGA_SYNC,
|
||||
{ db.getMangaSync(manga, myAnimeList).asRxObservable()
|
||||
.doOnNext { mangaSync = it }
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread()) },
|
||||
{ view, mangaSync -> view.setMangaSync(mangaSync) })
|
||||
|
||||
startableLatestCache(GET_SEARCH_RESULTS,
|
||||
{ getSearchResultsObservable() },
|
||||
{ view, results -> view.setSearchResults(results) },
|
||||
{ view, error -> view.setSearchResultsError(error) })
|
||||
|
||||
startableFirst(REFRESH,
|
||||
{ getRefreshObservable() },
|
||||
{ view, result -> view.onRefreshDone() },
|
||||
{ view, error -> view.onRefreshError() })
|
||||
|
||||
registerForEvents()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
unregisterForEvents()
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
|
||||
fun onEvent(event: MangaEvent) {
|
||||
manga = event.manga
|
||||
start(GET_MANGA_SYNC)
|
||||
}
|
||||
|
||||
fun getSearchResultsObservable(): Observable<List<MangaSync>> {
|
||||
return query?.let { query ->
|
||||
val observable: Observable<List<MangaSync>>
|
||||
if (query.startsWith(PREFIX_MY)) {
|
||||
val realQuery = query.substring(PREFIX_MY.length).toLowerCase().trim()
|
||||
observable = myAnimeList.getList()
|
||||
.flatMap { Observable.from(it) }
|
||||
.filter { it.title.toLowerCase().contains(realQuery) }
|
||||
.toList()
|
||||
} else {
|
||||
observable = myAnimeList.search(query)
|
||||
}
|
||||
observable.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
|
||||
} ?: Observable.error(Exception("Null query"))
|
||||
|
||||
}
|
||||
|
||||
fun getRefreshObservable(): Observable<PutResult> {
|
||||
return mangaSync?.let { mangaSync ->
|
||||
myAnimeList.getList()
|
||||
.flatMap { myList ->
|
||||
for (myManga in myList) {
|
||||
if (myManga.remote_id == mangaSync.remote_id) {
|
||||
mangaSync.copyPersonalFrom(myManga)
|
||||
mangaSync.total_chapters = myManga.total_chapters
|
||||
return@flatMap Observable.just(mangaSync)
|
||||
}
|
||||
}
|
||||
Observable.error<MangaSync>(Exception("Could not find manga"))
|
||||
}
|
||||
.flatMap { db.insertMangaSync(it).asRxObservable() }
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
} ?: Observable.error(Exception("Not found"))
|
||||
}
|
||||
|
||||
private fun updateRemote() {
|
||||
mangaSync?.let { mangaSync ->
|
||||
add(myAnimeList.update(mangaSync)
|
||||
.flatMap { response -> db.insertMangaSync(mangaSync).asRxObservable() }
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe({ next -> },
|
||||
{ error ->
|
||||
Timber.e(error.message)
|
||||
// Restart on error to set old values
|
||||
start(GET_MANGA_SYNC)
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
fun searchManga(query: String) {
|
||||
if (query.isNullOrEmpty() || query == this.query)
|
||||
return
|
||||
|
||||
this.query = query
|
||||
start(GET_SEARCH_RESULTS)
|
||||
}
|
||||
|
||||
fun restartSearch() {
|
||||
query = null
|
||||
stop(GET_SEARCH_RESULTS)
|
||||
}
|
||||
|
||||
fun registerManga(sync: MangaSync) {
|
||||
sync.manga_id = manga.id
|
||||
add(myAnimeList.bind(sync)
|
||||
.flatMap { response ->
|
||||
if (response.isSuccessful) {
|
||||
db.insertMangaSync(sync).asRxObservable()
|
||||
} else {
|
||||
Observable.error(Exception("Could not bind manga"))
|
||||
}
|
||||
}
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe({ },
|
||||
{ error -> context.toast(error.message) }))
|
||||
}
|
||||
|
||||
fun getAllStatus(): List<String> {
|
||||
return listOf(context.getString(R.string.reading),
|
||||
context.getString(R.string.completed),
|
||||
context.getString(R.string.on_hold),
|
||||
context.getString(R.string.dropped),
|
||||
context.getString(R.string.plan_to_read))
|
||||
}
|
||||
|
||||
fun getIndexFromStatus(): Int {
|
||||
return mangaSync?.let { mangaSync ->
|
||||
if (mangaSync.status == 6) 4 else mangaSync.status - 1
|
||||
} ?: 0
|
||||
}
|
||||
|
||||
fun setStatus(index: Int) {
|
||||
mangaSync?.status = if (index == 4) 6 else index + 1
|
||||
updateRemote()
|
||||
}
|
||||
|
||||
fun setScore(score: Int) {
|
||||
mangaSync?.score = score.toFloat()
|
||||
updateRemote()
|
||||
}
|
||||
|
||||
fun setLastChapterRead(chapterNumber: Int) {
|
||||
mangaSync?.last_chapter_read = chapterNumber
|
||||
updateRemote()
|
||||
}
|
||||
|
||||
fun refresh() {
|
||||
if (mangaSync != null) {
|
||||
start(REFRESH)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
package eu.kanade.tachiyomi.ui.manga.myanimelist;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import butterknife.Bind;
|
||||
import butterknife.ButterKnife;
|
||||
import eu.kanade.tachiyomi.R;
|
||||
import eu.kanade.tachiyomi.data.database.models.MangaSync;
|
||||
|
||||
public class MyAnimeListSearchAdapter extends ArrayAdapter<MangaSync> {
|
||||
|
||||
public MyAnimeListSearchAdapter(Context context) {
|
||||
super(context, R.layout.dialog_myanimelist_search_item, new ArrayList<>());
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View view, ViewGroup parent) {
|
||||
// Get the data item for this position
|
||||
MangaSync sync = getItem(position);
|
||||
// Check if an existing view is being reused, otherwise inflate the view
|
||||
SearchViewHolder holder; // view lookup cache stored in tag
|
||||
if (view == null) {
|
||||
LayoutInflater inflater = LayoutInflater.from(getContext());
|
||||
view = inflater.inflate(R.layout.dialog_myanimelist_search_item, parent, false);
|
||||
holder = new SearchViewHolder(view);
|
||||
view.setTag(holder);
|
||||
} else {
|
||||
holder = (SearchViewHolder) view.getTag();
|
||||
}
|
||||
holder.onSetValues(sync);
|
||||
return view;
|
||||
}
|
||||
|
||||
public void setItems(List<MangaSync> syncs) {
|
||||
setNotifyOnChange(false);
|
||||
clear();
|
||||
addAll(syncs);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public static class SearchViewHolder {
|
||||
|
||||
@Bind(R.id.myanimelist_result_title) TextView title;
|
||||
|
||||
public SearchViewHolder(View view) {
|
||||
ButterKnife.bind(this, view);
|
||||
}
|
||||
|
||||
public void onSetValues(MangaSync sync) {
|
||||
title.setText(sync.title);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
package eu.kanade.tachiyomi.ui.manga.myanimelist
|
||||
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ArrayAdapter
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.MangaSync
|
||||
import eu.kanade.tachiyomi.util.inflate
|
||||
import kotlinx.android.synthetic.main.dialog_myanimelist_search_item.view.*
|
||||
import java.util.*
|
||||
|
||||
class MyAnimeListSearchAdapter(context: Context) :
|
||||
ArrayAdapter<MangaSync>(context, R.layout.dialog_myanimelist_search_item, ArrayList<MangaSync>()) {
|
||||
|
||||
override fun getView(position: Int, view: View?, parent: ViewGroup): View {
|
||||
var v = view
|
||||
// Get the data item for this position
|
||||
val sync = getItem(position)
|
||||
// Check if an existing view is being reused, otherwise inflate the view
|
||||
val holder: SearchViewHolder // view lookup cache stored in tag
|
||||
if (v == null) {
|
||||
v = parent.inflate(R.layout.dialog_myanimelist_search_item)
|
||||
holder = SearchViewHolder(v)
|
||||
v.tag = holder
|
||||
} else {
|
||||
holder = v.tag as SearchViewHolder
|
||||
}
|
||||
holder.onSetValues(sync)
|
||||
return v
|
||||
}
|
||||
|
||||
fun setItems(syncs: List<MangaSync>) {
|
||||
setNotifyOnChange(false)
|
||||
clear()
|
||||
addAll(syncs)
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
class SearchViewHolder(private val view: View) {
|
||||
|
||||
fun onSetValues(sync: MangaSync) {
|
||||
view.myanimelist_result_title.text = sync.title
|
||||
}
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Color;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
@ -85,6 +86,7 @@ public class ReaderActivity extends BaseRxActivity<ReaderPresenter> {
|
||||
|
||||
initializeSettings();
|
||||
|
||||
|
||||
maxBitmapSize = GLUtil.getMaxTextureSize();
|
||||
}
|
||||
|
||||
@ -393,12 +395,12 @@ public class ReaderActivity extends BaseRxActivity<ReaderPresenter> {
|
||||
View rootView = getWindow().getDecorView().getRootView();
|
||||
if (theme == BLACK_THEME) {
|
||||
rootView.setBackgroundColor(Color.BLACK);
|
||||
pageNumber.setTextColor(ContextCompat.getColor(this, R.color.light_grey));
|
||||
pageNumber.setBackgroundColor(ContextCompat.getColor(this, R.color.page_number_background_black));
|
||||
pageNumber.setTextColor(ContextCompat.getColor(this, R.color.textColorPrimaryDark));
|
||||
pageNumber.setBackgroundColor(ContextCompat.getColor(this, R.color.backgroundDark));
|
||||
} else {
|
||||
rootView.setBackgroundColor(Color.WHITE);
|
||||
pageNumber.setTextColor(ContextCompat.getColor(this, R.color.primary_text));
|
||||
pageNumber.setBackgroundColor(ContextCompat.getColor(this, R.color.page_number_background));
|
||||
pageNumber.setTextColor(ContextCompat.getColor(this, R.color.textColorPrimaryLight));
|
||||
pageNumber.setBackgroundColor(ContextCompat.getColor(this, R.color.backgroundLight));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -17,8 +17,8 @@ import eu.kanade.tachiyomi.ui.reader.ReaderActivity
|
||||
|
||||
class PageDecodeErrorLayout(context: Context) : LinearLayout(context) {
|
||||
|
||||
private val lightGreyColor = ContextCompat.getColor(context, R.color.light_grey)
|
||||
private val blackColor = ContextCompat.getColor(context, R.color.primary_text)
|
||||
private val lightGreyColor = ContextCompat.getColor(context, android.R.attr.textColorHint)
|
||||
private val blackColor = ContextCompat.getColor(context, android.R.attr.textColorPrimary)
|
||||
|
||||
init {
|
||||
orientation = LinearLayout.VERTICAL
|
||||
|
@ -74,12 +74,12 @@ class PagerReaderFragment : BaseFragment() {
|
||||
/**
|
||||
* Text color for black theme.
|
||||
*/
|
||||
private val lightGreyColor by lazy { ContextCompat.getColor(context, R.color.light_grey) }
|
||||
private val lightGreyColor by lazy { ContextCompat.getColor(context, android.R.attr.textColorHint) }
|
||||
|
||||
/**
|
||||
* Text color for white theme.
|
||||
*/
|
||||
private val blackColor by lazy { ContextCompat.getColor(context, R.color.primary_text) }
|
||||
private val blackColor by lazy { ContextCompat.getColor(context, android.R.attr.textColorPrimary) }
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedState: Bundle?): View? {
|
||||
return inflater.inflate(R.layout.item_pager_reader, container, false)
|
||||
|
@ -17,7 +17,7 @@ import eu.kanade.tachiyomi.ui.base.decoration.DividerItemDecoration
|
||||
import eu.kanade.tachiyomi.ui.base.fragment.BaseRxFragment
|
||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
||||
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
|
||||
import eu.kanade.tachiyomi.util.setInformationDrawable
|
||||
import eu.kanade.tachiyomi.util.setDrawableCompat
|
||||
import kotlinx.android.synthetic.main.activity_main.*
|
||||
import kotlinx.android.synthetic.main.fragment_recent_chapters.*
|
||||
import nucleus.factory.RequiresPresenter
|
||||
@ -80,7 +80,7 @@ class RecentChaptersFragment : BaseRxFragment<RecentChaptersPresenter>(), Flexib
|
||||
setToolbarTitle(R.string.label_recent_updates)
|
||||
|
||||
// Check if recent chapters is empty and update information accordingly.
|
||||
(activity as MainActivity).image_view.setInformationDrawable(R.drawable.ic_history_grey_128dp)
|
||||
(activity as MainActivity).image_view.setDrawableCompat(R.drawable.ic_history_grey_128dp)
|
||||
(activity as MainActivity).text_label.text = getString(R.string.information_no_recent)
|
||||
(activity as MainActivity).information_layout.bringToFront()
|
||||
}
|
||||
@ -130,7 +130,7 @@ class RecentChaptersFragment : BaseRxFragment<RecentChaptersPresenter>(), Flexib
|
||||
*/
|
||||
fun onNextMangaChapters(chapters: List<Any>) {
|
||||
if (!chapters.isEmpty()) {
|
||||
( activity as MainActivity).image_view.setInformationDrawable(null)
|
||||
( activity as MainActivity).image_view.setDrawableCompat(null)
|
||||
( activity as MainActivity).text_label.text = ""
|
||||
}
|
||||
adapter.setItems(chapters)
|
||||
|
@ -1,6 +1,6 @@
|
||||
package eu.kanade.tachiyomi.ui.recent
|
||||
|
||||
import android.support.v4.content.ContextCompat
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
import android.widget.PopupMenu
|
||||
import eu.kanade.tachiyomi.R
|
||||
@ -8,6 +8,7 @@ import eu.kanade.tachiyomi.data.database.models.Chapter
|
||||
import eu.kanade.tachiyomi.data.database.models.MangaChapter
|
||||
import eu.kanade.tachiyomi.data.download.model.Download
|
||||
import eu.kanade.tachiyomi.ui.base.adapter.FlexibleViewHolder
|
||||
import eu.kanade.tachiyomi.util.getResourceColor
|
||||
import kotlinx.android.synthetic.main.item_recent_chapter.view.*
|
||||
import rx.Observable
|
||||
|
||||
@ -26,12 +27,12 @@ class RecentChaptersHolder(view: View, private val adapter: RecentChaptersAdapte
|
||||
/**
|
||||
* Color of read chapter
|
||||
*/
|
||||
private val readColor = ContextCompat.getColor(view.context, R.color.hint_text)
|
||||
private var readColor = view.context.theme.getResourceColor(android.R.attr.textColorHint)
|
||||
|
||||
/**
|
||||
* Color of unread chapter
|
||||
*/
|
||||
private val unreadColor = ContextCompat.getColor(view.context, R.color.primary_text)
|
||||
private var unreadColor = view.context.theme.getResourceColor(android.R.attr.textColorPrimary)
|
||||
|
||||
/**
|
||||
* Object containing chapter information
|
||||
@ -41,6 +42,7 @@ class RecentChaptersHolder(view: View, private val adapter: RecentChaptersAdapte
|
||||
init {
|
||||
//Set OnClickListener for download menu
|
||||
itemView.chapterMenu.setOnClickListener { v -> v.post({ showPopupMenu(v) }) }
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -7,7 +7,6 @@ import eu.kanade.tachiyomi.BuildConfig
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.updater.GithubUpdateChecker
|
||||
import eu.kanade.tachiyomi.data.updater.UpdateDownloader
|
||||
import eu.kanade.tachiyomi.util.ToastUtil
|
||||
import eu.kanade.tachiyomi.util.toast
|
||||
import rx.Subscription
|
||||
import rx.android.schedulers.AndroidSchedulers
|
||||
@ -109,7 +108,7 @@ class SettingsAboutFragment : SettingsNestedFragment() {
|
||||
UpdateDownloader(activity.applicationContext).execute(downloadLink)
|
||||
}.show()
|
||||
} else {
|
||||
ToastUtil.showShort(activity, getString(R.string.update_check_no_new_updates))
|
||||
context.toast(R.string.update_check_no_new_updates)
|
||||
}
|
||||
}, {
|
||||
it.printStackTrace()
|
||||
|
@ -1,5 +1,6 @@
|
||||
package eu.kanade.tachiyomi.ui.setting
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.support.v14.preference.PreferenceFragment
|
||||
import eu.kanade.tachiyomi.R
|
||||
@ -9,7 +10,9 @@ import eu.kanade.tachiyomi.data.mangasync.MangaSyncManager
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.source.SourceManager
|
||||
import eu.kanade.tachiyomi.ui.base.activity.BaseActivity
|
||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
||||
import kotlinx.android.synthetic.main.toolbar.*
|
||||
import eu.kanade.tachiyomi.util.setTheme
|
||||
import javax.inject.Inject
|
||||
|
||||
class SettingsActivity : BaseActivity() {
|
||||
@ -21,6 +24,7 @@ class SettingsActivity : BaseActivity() {
|
||||
@Inject lateinit var syncManager: MangaSyncManager
|
||||
|
||||
override fun onCreate(savedState: Bundle?) {
|
||||
setTheme()
|
||||
super.onCreate(savedState)
|
||||
setContentView(R.layout.activity_preferences)
|
||||
applicationComponent.inject(this)
|
||||
|
@ -7,7 +7,7 @@ import com.afollestad.materialdialogs.MaterialDialog
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.cache.ChapterCache
|
||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||
import eu.kanade.tachiyomi.util.ToastUtil
|
||||
import eu.kanade.tachiyomi.util.toast
|
||||
import rx.Observable
|
||||
import rx.Subscription
|
||||
import rx.android.schedulers.AndroidSchedulers
|
||||
@ -74,10 +74,10 @@ class SettingsAdvancedFragment : SettingsNestedFragment() {
|
||||
dialog.incrementProgress(1)
|
||||
}, {
|
||||
dialog.dismiss()
|
||||
ToastUtil.showShort(activity, getString(R.string.cache_delete_error))
|
||||
context.toast(R.string.cache_delete_error)
|
||||
}, {
|
||||
dialog.dismiss()
|
||||
ToastUtil.showShort(activity, getString(R.string.cache_deleted, deletedFiles.get()))
|
||||
context.toast(getString(R.string.cache_deleted, deletedFiles.get()))
|
||||
preference.summary = getString(R.string.used_cache, chapterCache.readableSize)
|
||||
})
|
||||
}
|
||||
|
@ -1,10 +1,15 @@
|
||||
package eu.kanade.tachiyomi.ui.setting
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.support.v4.app.TaskStackBuilder
|
||||
import android.support.v7.preference.Preference
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateAlarm
|
||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
||||
import eu.kanade.tachiyomi.util.ToastUtil
|
||||
import eu.kanade.tachiyomi.widget.preference.IntListPreference
|
||||
import eu.kanade.tachiyomi.widget.preference.LibraryColumnsDialog
|
||||
import eu.kanade.tachiyomi.widget.preference.SimpleDialogPreference
|
||||
@ -29,6 +34,10 @@ class SettingsGeneralFragment : SettingsNestedFragment() {
|
||||
findPreference(getString(R.string.pref_library_update_interval_key)) as IntListPreference
|
||||
}
|
||||
|
||||
val themePreference by lazy {
|
||||
findPreference(getString(R.string.pref_theme_key)) as IntListPreference
|
||||
}
|
||||
|
||||
var columnsSubscription: Subscription? = null
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
@ -36,6 +45,15 @@ class SettingsGeneralFragment : SettingsNestedFragment() {
|
||||
LibraryUpdateAlarm.startAlarm(activity, (newValue as String).toInt())
|
||||
true
|
||||
}
|
||||
|
||||
themePreference.setOnPreferenceChangeListener { preference, newValue ->
|
||||
// Rebuild activity's to apply themes.
|
||||
TaskStackBuilder.create(activity)
|
||||
.addNextIntent(Intent(activity, MainActivity::class.java))
|
||||
.addNextIntent(activity.intent)
|
||||
.startActivities()
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
|
@ -7,6 +7,8 @@ import android.content.Context
|
||||
import android.support.annotation.StringRes
|
||||
import android.support.v4.app.NotificationCompat
|
||||
import android.widget.Toast
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
|
||||
/**
|
||||
* Display a toast in this context.
|
||||
@ -17,6 +19,15 @@ fun Context.toast(@StringRes resource: Int, duration: Int = Toast.LENGTH_SHORT)
|
||||
Toast.makeText(this, resource, duration).show()
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a toast in this context.
|
||||
* @param text the text to display.
|
||||
* @param duration the duration of the toast. Defaults to short.
|
||||
*/
|
||||
fun Context.toast(text: String?, duration: Int = Toast.LENGTH_SHORT) {
|
||||
Toast.makeText(this, text, duration).show()
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to create a notification.
|
||||
* @param func the function that will execute inside the builder.
|
||||
@ -40,3 +51,12 @@ val Context.notificationManager : NotificationManager
|
||||
*/
|
||||
val Context.alarmManager: AlarmManager
|
||||
get() = getSystemService(Context.ALARM_SERVICE) as AlarmManager
|
||||
|
||||
fun Context.setTheme()
|
||||
{
|
||||
when (PreferencesHelper(this).getTheme().get())
|
||||
{
|
||||
1 -> this.setTheme(R.style.Theme_Tachiyomi)
|
||||
2 -> this.setTheme(R.style.Theme_Tachiyomi_Dark)
|
||||
}
|
||||
}
|
@ -1,19 +1,18 @@
|
||||
package eu.kanade.tachiyomi.util
|
||||
|
||||
import android.support.annotation.DrawableRes
|
||||
import android.support.v4.content.ContextCompat
|
||||
import android.widget.ImageView
|
||||
import org.jetbrains.annotations.Nullable
|
||||
|
||||
/**
|
||||
* Set information image
|
||||
* Set a drawable on a [ImageView] using [ContextCompat] for backwards compatibility.
|
||||
*
|
||||
* @param drawable id of image
|
||||
* @null makes image transparent
|
||||
* @param drawable id of drawable resource
|
||||
*/
|
||||
fun ImageView.setInformationDrawable(@Nullable drawable: Int?) {
|
||||
if (drawable == null) {
|
||||
setImageResource(android.R.color.transparent)
|
||||
} else {
|
||||
fun ImageView.setDrawableCompat(@DrawableRes drawable: Int?) {
|
||||
if (drawable != null) {
|
||||
setImageDrawable(ContextCompat.getDrawable(context, drawable))
|
||||
} else {
|
||||
setImageResource(android.R.color.transparent)
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package eu.kanade.tachiyomi.util
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.Resources
|
||||
import android.support.annotation.StringRes
|
||||
import android.support.annotation.StyleRes
|
||||
import android.support.v7.view.ContextThemeWrapper
|
||||
|
||||
fun Resources.Theme.getResourceColor(@StringRes resource: Int) : Int {
|
||||
val typedArray = this.obtainStyledAttributes(intArrayOf(resource))
|
||||
val attrValue = typedArray.getColor(0, 0)
|
||||
typedArray.recycle()
|
||||
return attrValue
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package eu.kanade.tachiyomi.widget
|
||||
|
||||
import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
|
||||
open class SimpleTextWatcher : TextWatcher {
|
||||
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
|
||||
|
||||
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
|
||||
|
||||
override fun afterTextChanged(s: Editable) {}
|
||||
}
|
@ -5,8 +5,6 @@ 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
|
||||
@ -14,6 +12,7 @@ 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 eu.kanade.tachiyomi.widget.SimpleTextWatcher
|
||||
import kotlinx.android.synthetic.main.pref_account_login.view.*
|
||||
import rx.Subscription
|
||||
|
||||
@ -54,11 +53,7 @@ abstract class LoginDialogPreference : DialogFragment() {
|
||||
|
||||
show_password.isEnabled = password.text.isNullOrEmpty()
|
||||
|
||||
password.addTextChangedListener(object : TextWatcher {
|
||||
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
|
||||
|
||||
override fun afterTextChanged(s: Editable) {}
|
||||
|
||||
password.addTextChangedListener(object : SimpleTextWatcher() {
|
||||
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
|
||||
if (s.length == 0) {
|
||||
show_password.isEnabled = true
|
||||
|
Before Width: | Height: | Size: 227 B |
Before Width: | Height: | Size: 249 B |
Before Width: | Height: | Size: 136 B |
Before Width: | Height: | Size: 153 B |
Before Width: | Height: | Size: 142 B |
Before Width: | Height: | Size: 148 B |
@ -4,10 +4,10 @@
|
||||
android:color="?android:attr/colorControlHighlight">
|
||||
|
||||
<item android:id="@android:id/mask"
|
||||
android:drawable="@color/list_choice_pressed_bg_light" />
|
||||
android:drawable="@color/dividerLight" />
|
||||
<item>
|
||||
<selector>
|
||||
<item android:state_activated="true" android:drawable="@color/list_choice_pressed_bg_light"/>
|
||||
<item android:state_activated="true" android:drawable="@color/dividerLight"/>
|
||||
</selector>
|
||||
</item>
|
||||
|
||||
|
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ripple
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="@color/colorAccent"
|
||||
>
|
||||
<item>
|
||||
<selector>
|
||||
<item android:state_selected="true">
|
||||
<color android:color="@color/selectorColorDark" />
|
||||
</item>
|
||||
|
||||
<item android:state_activated="true">
|
||||
<color android:color="@color/selectorColorDark" />
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<color android:color="@color/backgroundDark" />
|
||||
</item>
|
||||
</selector>
|
||||
</item>
|
||||
|
||||
|
||||
</ripple>
|
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ripple
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="@color/colorAccent"
|
||||
>
|
||||
<item>
|
||||
<selector>
|
||||
<item android:state_selected="true">
|
||||
<color android:color="@color/selectorColorLight" />
|
||||
</item>
|
||||
|
||||
<item android:state_activated="true">
|
||||
<color android:color="@color/selectorColorLight" />
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<color android:color="@color/backgroundLight" />
|
||||
</item>
|
||||
</selector>
|
||||
</item>
|
||||
|
||||
|
||||
</ripple>
|
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="@color/line_grey">
|
||||
<item android:drawable="@color/white"/>
|
||||
android:color="@color/dividerLight">
|
||||
<item android:drawable="@color/md_white_1000"/>
|
||||
</ripple>
|
Before Width: | Height: | Size: 366 B |
Before Width: | Height: | Size: 387 B |
Before Width: | Height: | Size: 434 B |
Before Width: | Height: | Size: 499 B |
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 1.6 KiB |
@ -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="#FFFFFFFF"
|
||||
android:pathData="M19,5v14H5V5h14m0,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2z"/>
|
||||
</vector>
|
@ -6,6 +6,6 @@
|
||||
android:width="1dp"
|
||||
android:height="1dp" />
|
||||
|
||||
<solid android:color="@color/divider" />
|
||||
<solid android:color="@color/dividerLight" />
|
||||
|
||||
</shape>
|
@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_checked="true" android:drawable="@drawable/reader_background_checkbox_selected" />
|
||||
<item android:drawable="@drawable/reader_background_checkbox_unselected" />
|
||||
</selector>
|
@ -2,9 +2,9 @@
|
||||
<selector android:exitFadeDuration="@android:integer/config_longAnimTime"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item android:state_focused="true" android:drawable="@color/list_choice_pressed_bg_light"/>
|
||||
<item android:state_pressed="true" android:drawable="@color/list_choice_pressed_bg_light"/>
|
||||
<item android:state_activated="true" android:drawable="@color/list_choice_pressed_bg_light"/>
|
||||
<item android:state_focused="true" android:drawable="@color/dividerLight"/>
|
||||
<item android:state_pressed="true" android:drawable="@color/dividerLight"/>
|
||||
<item android:state_activated="true" android:drawable="@color/dividerLight"/>
|
||||
<item android:drawable="@android:color/transparent"/>
|
||||
|
||||
</selector>
|
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector android:exitFadeDuration="@android:integer/config_longAnimTime"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item android:state_focused="true" android:drawable="@color/selectorColorDark"/>
|
||||
<item android:state_pressed="true" android:drawable="@color/selectorColorDark"/>
|
||||
<item android:state_activated="true" android:drawable="@color/selectorColorDark"/>
|
||||
<item android:drawable="@android:color/background_dark"/>
|
||||
|
||||
</selector>
|
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--<selector android:exitFadeDuration="@android:integer/config_longAnimTime"-->
|
||||
<!--xmlns:android="http://schemas.android.com/apk/res/android">-->
|
||||
|
||||
<!--<item android:state_focused="true" android:drawable="?attr/colorAccent"/>-->
|
||||
<!--<item android:state_pressed="true" android:drawable="?attr/colorAccent"/>-->
|
||||
<!--<item android:state_activated="true" android:drawable="?attr/colorAccent"/>-->
|
||||
<!--<item android:drawable="?android:attr/colorBackground"/>-->
|
||||
<!--</selector>-->
|
||||
|
||||
<selector android:exitFadeDuration="@android:integer/config_longAnimTime"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item android:state_focused="true" android:drawable="@color/selectorColorLight"/>
|
||||
<item android:state_pressed="true" android:drawable="@color/selectorColorLight"/>
|
||||
<item android:state_activated="true" android:drawable="@color/selectorColorLight"/>
|
||||
<item android:drawable="@color/backgroundLight"/>
|
||||
|
||||
</selector>
|
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@color/super_light_grey" android:state_pressed="true"/>
|
||||
<item android:drawable="@color/super_light_grey" android:state_focused="true"/>
|
||||
<item android:drawable="@color/white"/>
|
||||
<item android:drawable="@color/dividerLight" android:state_pressed="true"/>
|
||||
<item android:drawable="@color/dividerLight" android:state_focused="true"/>
|
||||
<item android:drawable="@color/md_white_1000"/>
|
||||
</selector>
|
@ -15,8 +15,8 @@
|
||||
android:layout_marginTop="?attr/actionBarSize"
|
||||
android:id="@+id/recycler"
|
||||
android:choiceMode="multipleChoice"
|
||||
android:listSelector="@color/list_choice_pressed_bg_light"
|
||||
tools:listitem="@layout/item_edit_categories"/>
|
||||
tools:listitem="@layout/item_edit_categories"
|
||||
/>
|
||||
|
||||
<android.support.design.widget.FloatingActionButton
|
||||
android:id="@+id/fab"
|
||||
@ -26,7 +26,6 @@
|
||||
android:layout_margin="@dimen/fab_margin"
|
||||
android:scaleType="fitCenter"
|
||||
app:srcCompat="@drawable/ic_add_white_24dp"
|
||||
app:backgroundTint="@color/colorPrimary"
|
||||
app:layout_anchor="@id/recycler"
|
||||
app:layout_anchorGravity="bottom|right|end"
|
||||
app:layout_behavior="eu.kanade.tachiyomi.ui.base.fab.FABAnimationUpDown"/>
|
||||
|
@ -57,7 +57,7 @@
|
||||
|
||||
<android.support.design.widget.NavigationView
|
||||
android:id="@+id/nav_view"
|
||||
android:layout_width="wrap_content"
|
||||
style="@style/Theme.NavigationDrawer"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="start"
|
||||
android:fitsSystemWindows="true"
|
||||
|
@ -14,8 +14,8 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="4dp"
|
||||
android:layout_gravity="bottom|left"
|
||||
android:background="@color/page_number_background"
|
||||
android:textColor="@color/primary_text"
|
||||
android:background="?android:attr/colorBackground"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:textSize="12sp"
|
||||
android:id="@+id/page_number"/>
|
||||
|
||||
|
@ -35,7 +35,7 @@
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_below="@id/myanimelist_title_layout"
|
||||
android:background="@color/list_choice_pressed_bg_light" />
|
||||
android:background="@color/dividerLight" />
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/myanimelist_status_layout"
|
||||
@ -68,7 +68,7 @@
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_below="@id/myanimelist_status_layout"
|
||||
android:background="@color/list_choice_pressed_bg_light" />
|
||||
android:background="@color/dividerLight" />
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/myanimelist_chapters_layout"
|
||||
@ -101,7 +101,7 @@
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_below="@id/myanimelist_chapters_layout"
|
||||
android:background="@color/list_choice_pressed_bg_light" />
|
||||
android:background="@color/dividerLight" />
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/myanimelist_score_layout"
|
||||
|
@ -48,7 +48,7 @@
|
||||
android:dividerHeight="0dp"
|
||||
android:clipToPadding="false"
|
||||
android:choiceMode="singleChoice"
|
||||
android:listSelector="@color/list_choice_pressed_bg_light"
|
||||
android:listSelector="@color/dividerLight"
|
||||
android:visibility="gone"/>
|
||||
|
||||
</LinearLayout>
|
@ -23,7 +23,7 @@
|
||||
|
||||
<eu.kanade.tachiyomi.widget.AutofitRecyclerView
|
||||
android:id="@+id/catalogue_grid"
|
||||
style="@style/AppTheme.GridView"
|
||||
style="@style/Theme.GridView"
|
||||
android:columnWidth="140dp"
|
||||
tools:listitem="@layout/item_catalogue_grid" />
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
<eu.kanade.tachiyomi.widget.AutofitRecyclerView
|
||||
android:id="@+id/recycler"
|
||||
style="@style/AppTheme.GridView"
|
||||
style="@style/Theme.GridView"
|
||||
android:columnWidth="140dp"
|
||||
tools:listitem="@layout/item_catalogue_grid" />
|
||||
|
||||
|
@ -14,7 +14,7 @@
|
||||
android:orientation="vertical">
|
||||
|
||||
<android.support.v7.widget.RecyclerView
|
||||
android:id="@+id/chapter_list"
|
||||
android:id="@+id/recycler"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginLeft="16dp"
|
||||
@ -41,7 +41,7 @@
|
||||
android:theme="@style/AppTheme.Popup">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/action_sort"
|
||||
android:id="@+id/sort_btn"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center"
|
||||
@ -52,9 +52,9 @@
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_toEndOf="@+id/action_sort"
|
||||
android:layout_toLeftOf="@+id/action_next_unread"
|
||||
android:layout_toRightOf="@+id/action_sort"
|
||||
android:layout_toEndOf="@+id/sort_btn"
|
||||
android:layout_toLeftOf="@+id/next_unread_btn"
|
||||
android:layout_toRightOf="@+id/sort_btn"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<View
|
||||
@ -64,7 +64,7 @@
|
||||
android:background="@color/white"/>
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/action_show_unread"
|
||||
android:id="@+id/show_unread"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
@ -73,7 +73,7 @@
|
||||
android:title="@string/action_show_unread"/>
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/action_show_downloaded"
|
||||
android:id="@+id/show_downloaded"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
@ -90,7 +90,7 @@
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/action_next_unread"
|
||||
android:id="@+id/next_unread_btn"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_alignParentRight="true"
|
||||
|
@ -10,7 +10,6 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:descendantFocusability="blocksDescendants"
|
||||
android:background="@color/white"
|
||||
tools:listitem="@layout/item_recent_chapter">
|
||||
|
||||
</android.support.v7.widget.RecyclerView>
|
||||
|
@ -5,7 +5,7 @@
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/selector_chapter_light">
|
||||
android:background="?selectable_list_drawable">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
@ -21,7 +21,7 @@
|
||||
android:id="@+id/thumbnail"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/white"
|
||||
android:background="@color/md_white_1000"
|
||||
tools:background="@color/md_red_100"
|
||||
tools:src="@mipmap/ic_launcher"/>
|
||||
|
||||
@ -39,12 +39,12 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:background="@color/manga_unread_bg"
|
||||
android:background="?android:attr/colorBackground"
|
||||
android:paddingBottom="1dp"
|
||||
android:paddingLeft="3dp"
|
||||
android:paddingRight="3dp"
|
||||
android:paddingTop="1dp"
|
||||
android:textColor="@color/white"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:textSize="12sp"
|
||||
android:visibility="gone"/>
|
||||
|
||||
@ -71,11 +71,11 @@
|
||||
android:lineSpacingExtra="-4dp"
|
||||
android:maxLines="2"
|
||||
android:padding="8dp"
|
||||
android:shadowColor="@color/primary_text"
|
||||
android:shadowColor="?android:attr/textColorPrimary"
|
||||
android:shadowDx="0"
|
||||
android:shadowDy="0"
|
||||
android:shadowRadius="4"
|
||||
android:textColor="@color/white"
|
||||
android:textColor="@color/md_white_1000"
|
||||
android:textSize="14sp"
|
||||
app:typeface="ptsansNarrowBold"
|
||||
tools:text="Sample name"/>
|
||||
|
@ -51,7 +51,7 @@
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:textAllCaps="true"
|
||||
android:textColor="@color/accent_text"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:textSize="12sp"/>
|
||||
</RelativeLayout>
|
||||
|
||||
|
@ -1,13 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?android:attr/listPreferredItemHeightLarge"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:background="@drawable/selector_chapter_light">
|
||||
|
||||
android:background="?attr/selectable_list_drawable"
|
||||
>
|
||||
<ImageView
|
||||
android:id="@+id/image"
|
||||
android:layout_width="50dp"
|
||||
@ -21,7 +21,7 @@
|
||||
android:layout_marginRight="@dimen/margin_right"
|
||||
android:layout_marginEnd="@dimen/margin_right"/>
|
||||
|
||||
<ImageView
|
||||
<android.support.v7.widget.AppCompatImageView
|
||||
android:id="@+id/reorder"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="50dp"
|
||||
@ -33,7 +33,8 @@
|
||||
android:layout_centerInParent="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_alignParentEnd="true"
|
||||
app:srcCompat="@drawable/ic_reorder_grey_24dp"/>
|
||||
app:srcCompat="@drawable/ic_reorder_grey_24dp"
|
||||
android:tint="?android:attr/textColorPrimary"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
@ -47,7 +48,7 @@
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Subhead"
|
||||
android:textColor="@color/primary_text"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
tools:text="Title"/>
|
||||
|
||||
</RelativeLayout>
|
@ -31,7 +31,6 @@
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:textAllCaps="true"
|
||||
android:textColor="@color/accent_text"
|
||||
android:textSize="12sp"/>
|
||||
</RelativeLayout>
|
||||
|
||||
@ -51,7 +50,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
|
||||
android:textAppearance="@style/TextAppearance.Medium"
|
||||
tools:text="My manga"/>
|
||||
|
||||
<TextView
|
||||
@ -60,7 +59,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Small"
|
||||
android:textAppearance="@style/TextAppearance.Small"
|
||||
tools:text="Title"/>
|
||||
|
||||
</LinearLayout>
|
||||
@ -82,7 +81,7 @@
|
||||
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
||||
android:paddingLeft="?android:attr/listPreferredItemPaddingLeft">
|
||||
|
||||
<ImageView
|
||||
<android.support.v7.widget.AppCompatImageView
|
||||
android:id="@+id/chapterMenu"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
|
@ -15,7 +15,7 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="true"
|
||||
android:textColor="@color/white"
|
||||
android:textColor="@color/md_white_1000"
|
||||
android:textSize="16sp"
|
||||
android:id="@+id/section_text"
|
||||
android:layout_gravity="center_vertical"
|
||||
|
@ -5,10 +5,10 @@
|
||||
android:id="@+id/tabs"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:theme="@style/AppTheme.Overlay.Dark"
|
||||
android:background="@color/colorPrimary"
|
||||
android:visibility="gone"
|
||||
android:theme="@style/AppTheme.Tab"
|
||||
app:tabIndicatorColor="@android:color/white"
|
||||
app:tabGravity="center"
|
||||
app:tabMode="scrollable"
|
||||
app:tabMinWidth="75dp"
|
||||
app:tabIndicatorColor="@color/white"/>
|
||||
app:tabMinWidth="75dp"/>
|
@ -9,7 +9,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:scaleType="centerCrop"
|
||||
android:background="@drawable/header" />
|
||||
android:src="@drawable/header" />
|
||||
|
||||
<ImageView
|
||||
android:layout_marginLeft="16dp"
|
||||
@ -17,7 +17,7 @@
|
||||
android:layout_width="64dp"
|
||||
android:layout_height="64dp"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@drawable/test" />
|
||||
android:src="@drawable/test"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
@ -34,16 +34,18 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:gravity="center_vertical"
|
||||
android:text="Desarrollador Android"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Body2" />
|
||||
android:text="John Doe"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Body2"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:gravity="center_vertical"
|
||||
android:text="thedahnark@gmail.com"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Body1" />
|
||||
android:text="email@email.com"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
|
||||
android:visibility="gone"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
@ -17,7 +17,7 @@
|
||||
<View android:id="@+id/titleDivider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="@color/line_grey"
|
||||
android:background="@color/dividerLight"
|
||||
android:layout_marginTop="6dp"
|
||||
android:layout_marginBottom="24dp"/>
|
||||
|
||||
|
@ -12,16 +12,15 @@
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="@color/reader_menu_background"
|
||||
android:theme="@style/AppTheme.Overlay.Dark"
|
||||
app:popupTheme="@style/AppTheme.Popup"
|
||||
android:elevation="4dp" />
|
||||
android:background="?colorPrimary"
|
||||
android:elevation="4dp"
|
||||
android:theme="?attr/actionBarTheme"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/reader_menu_bottom"
|
||||
android:background="@color/reader_menu_background"
|
||||
android:background="?colorPrimary"
|
||||
android:orientation="vertical"
|
||||
android:layout_alignParentBottom="true">
|
||||
|
||||
@ -35,7 +34,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:textColor="@color/light_grey"
|
||||
android:textColor="?android:attr/textColorHint"
|
||||
android:textSize="15sp"
|
||||
android:gravity="center_horizontal"
|
||||
android:layout_marginTop="12dp"
|
||||
@ -47,7 +46,6 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:theme="@style/AppTheme.Overlay.Dark"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
@ -56,7 +54,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:textColor="@color/light_grey"
|
||||
android:textColor="?android:attr/textColorHint"
|
||||
android:textSize="15sp"
|
||||
android:gravity="center_horizontal" />
|
||||
|
||||
@ -70,7 +68,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize">
|
||||
|
||||
<ImageButton
|
||||
<android.support.v7.widget.AppCompatImageButton
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
@ -79,7 +77,7 @@
|
||||
android:layout_gravity="center_vertical"
|
||||
android:background="?android:selectableItemBackground" />
|
||||
|
||||
<ImageButton
|
||||
<android.support.v7.widget.AppCompatImageButton
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
@ -88,7 +86,7 @@
|
||||
android:layout_gravity="center_vertical"
|
||||
android:background="?android:selectableItemBackground"/>
|
||||
|
||||
<ImageButton
|
||||
<android.support.v7.widget.AppCompatImageButton
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
@ -97,7 +95,7 @@
|
||||
android:layout_gravity="center_vertical"
|
||||
android:background="?android:selectableItemBackground" />
|
||||
|
||||
<ImageButton
|
||||
<android.support.v7.widget.AppCompatImageButton
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
@ -106,7 +104,7 @@
|
||||
android:layout_gravity="center_vertical"
|
||||
android:background="?android:selectableItemBackground" />
|
||||
|
||||
<ImageButton
|
||||
<android.support.v7.widget.AppCompatImageButton
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
|
@ -1,69 +1,88 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical" android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/reader_menu_background"
|
||||
android:paddingRight="10dp"
|
||||
android:paddingLeft="5dp"
|
||||
android:paddingTop="5dp"
|
||||
android:paddingBottom="5dp">
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?colorPrimary"
|
||||
android:orientation="vertical"
|
||||
android:paddingBottom="5dp"
|
||||
android:paddingLeft="5dp"
|
||||
android:paddingRight="10dp"
|
||||
android:paddingTop="5dp">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/image_decoder_container"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/image_decoder_container">
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/image_decoder_initial"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="16sp"
|
||||
android:gravity="center"
|
||||
android:textColor="@color/colorAccent"/>
|
||||
android:textColor="@color/colorAccent"
|
||||
android:textSize="16sp"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/image_decoder"
|
||||
style="@style/reader_menu_settings_item"
|
||||
android:text="@string/pref_image_decoder"/>
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="36dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/pref_image_decoder"
|
||||
style="@style/TextAppearance.Light"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<CheckBox
|
||||
<android.support.v7.widget.AppCompatCheckBox
|
||||
android:id="@+id/reader_theme"
|
||||
style="@style/reader_menu_settings_item"
|
||||
android:button="@drawable/reader_background_checkbox"
|
||||
android:text="@string/pref_reader_theme"/>
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="36dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/pref_reader_theme"
|
||||
style="@style/AppTheme.CheckBox.Light"/>
|
||||
|
||||
<CheckBox
|
||||
<android.support.v7.widget.AppCompatCheckBox
|
||||
android:id="@+id/enable_transitions"
|
||||
style="@style/reader_menu_settings_item"
|
||||
android:text="@string/pref_enable_transitions"/>
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="36dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/pref_enable_transitions"
|
||||
style="@style/AppTheme.CheckBox.Light"/>
|
||||
|
||||
<CheckBox
|
||||
<android.support.v7.widget.AppCompatCheckBox
|
||||
android:id="@+id/show_page_number"
|
||||
style="@style/reader_menu_settings_item"
|
||||
android:text="@string/pref_show_page_number"/>
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="36dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/pref_show_page_number"
|
||||
style="@style/AppTheme.CheckBox.Light"/>
|
||||
|
||||
<CheckBox
|
||||
<android.support.v7.widget.AppCompatCheckBox
|
||||
android:id="@+id/hide_status_bar"
|
||||
style="@style/reader_menu_settings_item"
|
||||
android:text="@string/pref_hide_status_bar"/>
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="36dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/pref_hide_status_bar"
|
||||
style="@style/AppTheme.CheckBox.Light"/>
|
||||
|
||||
<CheckBox
|
||||
<android.support.v7.widget.AppCompatCheckBox
|
||||
android:id="@+id/keep_screen_on"
|
||||
style="@style/reader_menu_settings_item"
|
||||
android:text="@string/pref_keep_screen_on"/>
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="36dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/pref_keep_screen_on"
|
||||
style="@style/AppTheme.CheckBox.Light"/>
|
||||
|
||||
<CheckBox
|
||||
<android.support.v7.widget.AppCompatCheckBox
|
||||
android:id="@+id/custom_brightness"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/reader_menu_settings_item"
|
||||
android:gravity="center"
|
||||
android:text="@string/pref_custom_brightness"
|
||||
android:id="@+id/custom_brightness" />
|
||||
style="@style/AppTheme.CheckBox.Light"/>
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/brightness_seekbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/brightness_seekbar" />
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
</LinearLayout>
|
@ -5,7 +5,7 @@
|
||||
android:id="@+id/tabs"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:theme="@style/AppTheme.Overlay.Dark"
|
||||
app:tabGravity="fill"
|
||||
android:background="@color/colorPrimary"
|
||||
app:tabIndicatorColor="@color/white"/>
|
||||
android:theme="@style/AppTheme.Tab"
|
||||
app:tabIndicatorColor="@android:color/white"/>
|
@ -1,7 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.v7.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<android.support.v7.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="@color/colorPrimary"
|
||||
android:theme="@style/AppTheme.ActionBar"/>
|
||||
android:theme="?attr/actionBarTheme"/>
|
7
app/src/main/res/values-sw376dp/styles.xml
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<style name="Theme.NavigationDrawer">
|
||||
<item name="android:layout_width">320dp</item>
|
||||
</style>
|
||||
</resources>
|
7
app/src/main/res/values-sw600dp/styles.xml
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<style name="Theme.NavigationDrawer">
|
||||
<item name="android:layout_width">400dp</item>
|
||||
</style>
|
||||
</resources>
|
@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<style name="AppTheme" parent="AppTheme.Base">
|
||||
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
|
||||
<item name="android:statusBarColor">@android:color/transparent</item>
|
||||
</style>
|
||||
</resources>
|
24
app/src/main/res/values-v21/themes.xml
Normal file
@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<style name="Theme.Tachiyomi" parent="Theme.AppCompat.Light.NoActionBar">
|
||||
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
|
||||
<item name="android:statusBarColor">@color/md_statusbar_translucent</item>
|
||||
|
||||
<item name="android:windowContentTransitions">true</item>
|
||||
<item name="android:windowAllowEnterTransitionOverlap">true</item>
|
||||
<item name="android:windowAllowReturnTransitionOverlap">true</item>
|
||||
<item name="android:windowSharedElementEnterTransition">@android:transition/move</item>
|
||||
<item name="android:windowSharedElementExitTransition">@android:transition/move</item>
|
||||
</style>
|
||||
|
||||
<style name="Theme.Tachiyomi.Dark" parent="Theme.AppCompat.NoActionBar">
|
||||
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
|
||||
<item name="android:statusBarColor">@color/md_statusbar_translucent</item>
|
||||
|
||||
<item name="android:windowContentTransitions">true</item>
|
||||
<item name="android:windowAllowEnterTransitionOverlap">true</item>
|
||||
<item name="android:windowAllowReturnTransitionOverlap">true</item>
|
||||
<item name="android:windowSharedElementEnterTransition">@android:transition/move</item>
|
||||
<item name="android:windowSharedElementExitTransition">@android:transition/move</item>
|
||||
</style>
|
||||
</resources>
|
@ -22,6 +22,16 @@
|
||||
<item>@string/webtoon_viewer</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="themes_values">
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="themes">
|
||||
<item>"Light Theme"</item>
|
||||
<item>"Dark Theme"</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="download_slots">
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
|
@ -16,4 +16,7 @@
|
||||
<attr name="min" format="integer"/>
|
||||
<attr name="max" format="integer"/>
|
||||
</declare-styleable>
|
||||
|
||||
|
||||
<attr name="selectable_list_drawable" format="reference|integer" />
|
||||
</resources>
|
@ -1,718 +1,62 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="colorAccent">@color/md_blue_A400</color>
|
||||
|
||||
<!-- Application Colors -->
|
||||
<color name="colorPrimary">#54759e</color>
|
||||
<color name="colorPrimaryDark">#435e7e</color>
|
||||
<color name="colorPrimarySuperDark">@color/md_blue_grey_900</color>
|
||||
<color name="colorPrimaryLight">@color/md_blue_grey_100</color>
|
||||
<color name="colorAccent">@color/md_blue_400</color>
|
||||
|
||||
<color name="colorBackgroundLight">@color/md_blue_grey_50</color>
|
||||
<!-- Light Theme -->
|
||||
<color name="textColorPrimaryLight">@color/md_black_1000_87</color>
|
||||
<color name="textColorSecondaryLight">@color/md_black_1000_54</color>
|
||||
<color name="textColorHintLight">@color/md_black_1000_38</color>
|
||||
<color name="dividerLight">@color/md_white_1000_12</color>
|
||||
|
||||
<color name="primary">@color/colorPrimary</color>
|
||||
<color name="primary_dark">@color/colorPrimaryDark</color>
|
||||
<color name="primary_light">@color/colorPrimaryLight</color>
|
||||
<color name="color_ripple">#E9F1FF</color>
|
||||
<color name="statusBarLight">@color/md_grey_300</color>
|
||||
<color name="appBarLight">@color/md_grey_100</color>
|
||||
<color name="backgroundLight">@color/md_grey_50</color>
|
||||
<color name="dialogLight">@color/md_white_1000</color>
|
||||
|
||||
<color name="divider">@color/md_light_dividers</color>
|
||||
<color name="selectorColorLight">@color/md_blue_400_38</color>
|
||||
|
||||
<color name="white">@color/md_white_1000</color>
|
||||
<color name="primary_text">#000000</color>
|
||||
<color name="secondary_text">#000000</color>
|
||||
<color name="hint_text">#000000</color>
|
||||
<color name="accent_text">@color/md_green_900</color>
|
||||
<!-- Dark Theme -->
|
||||
<color name="textColorPrimaryDark">@color/md_white_1000</color>
|
||||
<color name="textColorSecondaryDark">@color/md_white_1000_70</color>
|
||||
<color name="textColorHintDark">@color/md_white_1000_50</color>
|
||||
<color name="dividerDark">@color/md_black_1000_12</color>
|
||||
|
||||
<color name="list_choice_pressed_bg_light">@color/colorPrimaryLight</color>
|
||||
<color name="statusBarDark">@color/md_black_1000</color>
|
||||
<color name="appBarDark">@color/md_grey_900</color>
|
||||
<color name="backgroundDark">#303030</color>
|
||||
<color name="dialogDark">@color/md_grey_800</color>
|
||||
|
||||
<color name="manga_unread_bg">@color/colorAccent</color>
|
||||
<color name="manga_cover_title_background">#55333333</color>
|
||||
<color name="selectorColorDark">@color/md_blue_400_50</color>
|
||||
|
||||
<color name="super_light_grey">@color/md_grey_50</color>
|
||||
<color name="line_grey">@color/md_light_dividers</color>
|
||||
<color name="light_grey">@color/md_grey_300</color>
|
||||
<color name="page_number_background">#AAE9E9E9</color>
|
||||
<color name="page_number_background_black">#99252525</color>
|
||||
<color name="reader_menu_background">@color/colorPrimarySuperDark</color>
|
||||
<!-- Text Colors -->
|
||||
<color name="md_black_1000_87">#DE000000</color>
|
||||
<color name="md_black_1000_54">#8A000000</color>
|
||||
<color name="md_black_1000_38">#61000000</color>
|
||||
<color name="md_black_1000_12">#1F000000</color>
|
||||
|
||||
<color name="md_light_dividers">@color/md_blue_400</color>
|
||||
<color name="md_white_1000">#FFFFFFFF</color>
|
||||
<color name="md_white_1000_70">#B3FFFFFF</color>
|
||||
<color name="md_white_1000_50">#80FFFFFF</color>
|
||||
<color name="md_white_1000_12">#1FFFFFFF</color>
|
||||
|
||||
<!-- google's material design colours from
|
||||
http://www.google.com/design/spec/style/color.html#color-ui-color-palette -->
|
||||
<!--reds-->
|
||||
<array name="reds">
|
||||
<item>@color/md_red_500</item>
|
||||
<item>@color/md_red_50</item>
|
||||
<item>@color/md_red_100</item>
|
||||
<item>@color/md_red_200</item>
|
||||
<item>@color/md_red_300</item>
|
||||
<item>@color/md_red_400</item>
|
||||
<item>@color/md_red_600</item>
|
||||
<item>@color/md_red_700</item>
|
||||
<item>@color/md_red_800</item>
|
||||
<item>@color/md_red_900</item>
|
||||
<item>@color/md_red_A100</item>
|
||||
<item>@color/md_red_A200</item>
|
||||
<item>@color/md_red_A400</item>
|
||||
<item>@color/md_red_A700</item>
|
||||
</array>
|
||||
<color name="md_red_50">#ffebee</color>
|
||||
<color name="md_red_100">#ffcdd2</color>
|
||||
<color name="md_red_200">#ef9a9a</color>
|
||||
<color name="md_red_300">#e57373</color>
|
||||
<color name="md_red_400">#ef5350</color>
|
||||
<color name="md_red_500">#f44336</color>
|
||||
<color name="md_red_600">#e53935</color>
|
||||
<color name="md_red_700">#d32f2f</color>
|
||||
<color name="md_red_800">#c62828</color>
|
||||
<color name="md_red_900">#b71c1c</color>
|
||||
<color name="md_red_A100">#ff8a80</color>
|
||||
<color name="md_red_A200">#ff5252</color>
|
||||
<color name="md_red_A400">#ff1744</color>
|
||||
<color name="md_red_A700">#d50000</color>
|
||||
<color name="md_red_500_25">#40f44336</color>
|
||||
<color name="md_red_500_50">#80f44336</color>
|
||||
<color name="md_red_500_75">#c0f44336</color>
|
||||
<!-- pinks -->
|
||||
<array name="pinks">
|
||||
<item>@color/md_pink_500</item>
|
||||
<item>@color/md_pink_50</item>
|
||||
<item>@color/md_pink_100</item>
|
||||
<item>@color/md_pink_200</item>
|
||||
<item>@color/md_pink_300</item>
|
||||
<item>@color/md_pink_400</item>
|
||||
<item>@color/md_pink_600</item>
|
||||
<item>@color/md_pink_700</item>
|
||||
<item>@color/md_pink_800</item>
|
||||
<item>@color/md_pink_900</item>
|
||||
<item>@color/md_pink_A100</item>
|
||||
<item>@color/md_pink_A200</item>
|
||||
<item>@color/md_pink_A400</item>
|
||||
<item>@color/md_pink_A700</item>
|
||||
</array>
|
||||
<color name="md_pink_50">#fce4ec</color>
|
||||
<color name="md_pink_100">#f8bbd0</color>
|
||||
<color name="md_pink_200">#f48fb1</color>
|
||||
<color name="md_pink_300">#f06292</color>
|
||||
<color name="md_pink_400">#ec407a</color>
|
||||
<color name="md_pink_500">#e91e63</color>
|
||||
<color name="md_pink_600">#d81b60</color>
|
||||
<color name="md_pink_700">#c2185b</color>
|
||||
<color name="md_pink_800">#ad1457</color>
|
||||
<color name="md_pink_900">#880e4f</color>
|
||||
<color name="md_pink_A100">#ff80ab</color>
|
||||
<color name="md_pink_A200">#ff4081</color>
|
||||
<color name="md_pink_A400">#f50057</color>
|
||||
<color name="md_pink_A700">#c51162</color>
|
||||
<color name="md_pink_500_25">#40e91e63</color>
|
||||
<color name="md_pink_500_50">#80e91e63</color>
|
||||
<color name="md_pink_500_75">#c0e91e63</color>
|
||||
<!-- purples -->
|
||||
<array name="purples">
|
||||
<item>@color/md_purple_500</item>
|
||||
<item>@color/md_purple_50</item>
|
||||
<item>@color/md_purple_100</item>
|
||||
<item>@color/md_purple_200</item>
|
||||
<item>@color/md_purple_300</item>
|
||||
<item>@color/md_purple_400</item>
|
||||
<item>@color/md_purple_600</item>
|
||||
<item>@color/md_purple_700</item>
|
||||
<item>@color/md_purple_800</item>
|
||||
<item>@color/md_purple_900</item>
|
||||
<item>@color/md_purple_A100</item>
|
||||
<item>@color/md_purple_A200</item>
|
||||
<item>@color/md_purple_A400</item>
|
||||
<item>@color/md_purple_A700</item>
|
||||
</array>
|
||||
<color name="md_purple_50">#f3e5f5</color>
|
||||
<color name="md_purple_100">#e1bee7</color>
|
||||
<color name="md_purple_200">#ce93d8</color>
|
||||
<color name="md_purple_300">#ba68c8</color>
|
||||
<color name="md_purple_400">#ab47bc</color>
|
||||
<color name="md_purple_500">#9c27b0</color>
|
||||
<color name="md_purple_600">#8e24aa</color>
|
||||
<color name="md_purple_700">#7b1fa2</color>
|
||||
<color name="md_purple_800">#6a1b9a</color>
|
||||
<color name="md_purple_900">#4a148c</color>
|
||||
<color name="md_purple_A100">#ea80fc</color>
|
||||
<color name="md_purple_A200">#e040fb</color>
|
||||
<color name="md_purple_A400">#d500f9</color>
|
||||
<color name="md_purple_A700">#aa00ff</color>
|
||||
<color name="md_purple_500_25">#409c27b0</color>
|
||||
<color name="md_purple_500_50">#809c27b0</color>
|
||||
<color name="md_purple_500_75">#c09c27b0</color>
|
||||
<!-- deep purples -->
|
||||
<array name="deep_purples">
|
||||
<item>@color/md_deep_purple_500</item>
|
||||
<item>@color/md_deep_purple_50</item>
|
||||
<item>@color/md_deep_purple_100</item>
|
||||
<item>@color/md_deep_purple_200</item>
|
||||
<item>@color/md_deep_purple_300</item>
|
||||
<item>@color/md_deep_purple_400</item>
|
||||
<item>@color/md_deep_purple_600</item>
|
||||
<item>@color/md_deep_purple_700</item>
|
||||
<item>@color/md_deep_purple_800</item>
|
||||
<item>@color/md_deep_purple_900</item>
|
||||
<item>@color/md_deep_purple_A100</item>
|
||||
<item>@color/md_deep_purple_A200</item>
|
||||
<item>@color/md_deep_purple_A400</item>
|
||||
<item>@color/md_deep_purple_A700</item>
|
||||
</array>
|
||||
<color name="md_deep_purple_50">#ede7f6</color>
|
||||
<color name="md_deep_purple_100">#d1c4e9</color>
|
||||
<color name="md_deep_purple_200">#b39ddb</color>
|
||||
<color name="md_deep_purple_300">#9575cd</color>
|
||||
<color name="md_deep_purple_400">#7e57c2</color>
|
||||
<color name="md_deep_purple_500">#673ab7</color>
|
||||
<color name="md_deep_purple_600">#5e35b1</color>
|
||||
<color name="md_deep_purple_700">#512da8</color>
|
||||
<color name="md_deep_purple_800">#4527a0</color>
|
||||
<color name="md_deep_purple_900">#311b92</color>
|
||||
<color name="md_deep_purple_A100">#b388ff</color>
|
||||
<color name="md_deep_purple_A200">#7c4dff</color>
|
||||
<color name="md_deep_purple_A400">#651fff</color>
|
||||
<color name="md_deep_purple_A700">#6200ea</color>
|
||||
<color name="md_deep_purple_500_25">#40673ab7</color>
|
||||
<color name="md_deep_purple_500_50">#80673ab7</color>
|
||||
<color name="md_deep_purple_500_75">#c0673ab7</color>
|
||||
<!-- indigos -->
|
||||
<array name="indigos">
|
||||
<item>@color/md_indigo_500</item>
|
||||
<item>@color/md_indigo_50</item>
|
||||
<item>@color/md_indigo_100</item>
|
||||
<item>@color/md_indigo_200</item>
|
||||
<item>@color/md_indigo_300</item>
|
||||
<item>@color/md_indigo_400</item>
|
||||
<item>@color/md_indigo_600</item>
|
||||
<item>@color/md_indigo_700</item>
|
||||
<item>@color/md_indigo_800</item>
|
||||
<item>@color/md_indigo_900</item>
|
||||
<item>@color/md_indigo_A100</item>
|
||||
<item>@color/md_indigo_A200</item>
|
||||
<item>@color/md_indigo_A400</item>
|
||||
<item>@color/md_indigo_A700</item>
|
||||
</array>
|
||||
<color name="md_indigo_50">#e8eaf6</color>
|
||||
<color name="md_indigo_100">#c5cae9</color>
|
||||
<color name="md_indigo_200">#9fa8da</color>
|
||||
<color name="md_indigo_300">#7986cb</color>
|
||||
<color name="md_indigo_400">#5c6bc0</color>
|
||||
<color name="md_indigo_500">#3f51b5</color>
|
||||
<color name="md_indigo_600">#3949ab</color>
|
||||
<color name="md_indigo_700">#303f9f</color>
|
||||
<color name="md_indigo_800">#283593</color>
|
||||
<color name="md_indigo_900">#1a237e</color>
|
||||
<color name="md_indigo_A100">#8c9eff</color>
|
||||
<color name="md_indigo_A200">#536dfe</color>
|
||||
<color name="md_indigo_A400">#3d5afe</color>
|
||||
<color name="md_indigo_A700">#304ffe</color>
|
||||
<color name="md_indigo_500_25">#403f51b5</color>
|
||||
<color name="md_indigo_500_50">#803f51b5</color>
|
||||
<color name="md_indigo_500_75">#c03f51b5</color>
|
||||
<!--blues-->
|
||||
<array name="blues">
|
||||
<item>@color/md_blue_500</item>
|
||||
<item>@color/md_blue_50</item>
|
||||
<item>@color/md_blue_100</item>
|
||||
<item>@color/md_blue_200</item>
|
||||
<item>@color/md_blue_300</item>
|
||||
<item>@color/md_blue_400</item>
|
||||
<item>@color/md_blue_600</item>
|
||||
<item>@color/md_blue_700</item>
|
||||
<item>@color/md_blue_800</item>
|
||||
<item>@color/md_blue_900</item>
|
||||
<item>@color/md_blue_A100</item>
|
||||
<item>@color/md_blue_A200</item>
|
||||
<item>@color/md_blue_A400</item>
|
||||
<item>@color/md_blue_A700</item>
|
||||
</array>
|
||||
<color name="md_blue_50">#e3f2fd</color>
|
||||
<color name="md_blue_100">#bbdefb</color>
|
||||
<color name="md_blue_200">#90caf9</color>
|
||||
<color name="md_blue_300">#64b5f6</color>
|
||||
<color name="md_blue_400">#42a5f5</color>
|
||||
<color name="md_blue_500">#2196f3</color>
|
||||
<color name="md_blue_600">#1e88e5</color>
|
||||
<color name="md_blue_700">#1976d2</color>
|
||||
<color name="md_blue_800">#1565c0</color>
|
||||
<color name="md_blue_900">#0d47a1</color>
|
||||
<color name="md_blue_A100">#82b1ff</color>
|
||||
<color name="md_blue_A200">#448aff</color>
|
||||
<color name="md_blue_A400">#2979ff</color>
|
||||
<color name="md_blue_A700">#2962ff</color>
|
||||
<color name="md_blue_500_25">#402196f3</color>
|
||||
<color name="md_blue_500_50">#802196f3</color>
|
||||
<color name="md_blue_500_75">#c02196f3</color>
|
||||
<!-- light blues-->
|
||||
<array name="light_blues">
|
||||
<item>@color/md_light_blue_500</item>
|
||||
<item>@color/md_light_blue_50</item>
|
||||
<item>@color/md_light_blue_100</item>
|
||||
<item>@color/md_light_blue_200</item>
|
||||
<item>@color/md_light_blue_300</item>
|
||||
<item>@color/md_light_blue_400</item>
|
||||
<item>@color/md_light_blue_600</item>
|
||||
<item>@color/md_light_blue_700</item>
|
||||
<item>@color/md_light_blue_800</item>
|
||||
<item>@color/md_light_blue_900</item>
|
||||
<item>@color/md_light_blue_A100</item>
|
||||
<item>@color/md_light_blue_A200</item>
|
||||
<item>@color/md_light_blue_A400</item>
|
||||
<item>@color/md_light_blue_A700</item>
|
||||
</array>
|
||||
<color name="md_light_blue_50">#e1f5fe</color>
|
||||
<color name="md_light_blue_100">#b3e5fc</color>
|
||||
<color name="md_light_blue_200">#81d4fa</color>
|
||||
<color name="md_light_blue_300">#4fc3f7</color>
|
||||
<color name="md_light_blue_400">#29b6f6</color>
|
||||
<color name="md_light_blue_500">#03a9f4</color>
|
||||
<color name="md_light_blue_600">#039be5</color>
|
||||
<color name="md_light_blue_700">#0288d1</color>
|
||||
<color name="md_light_blue_800">#0277bd</color>
|
||||
<color name="md_light_blue_900">#01579b</color>
|
||||
<color name="md_light_blue_A100">#80d8ff</color>
|
||||
<color name="md_light_blue_A200">#40c4ff</color>
|
||||
<color name="md_light_blue_A400">#00b0ff</color>
|
||||
<color name="md_light_blue_A700">#0091ea</color>
|
||||
<color name="md_light_blue_500_25">#4003a9f4</color>
|
||||
<color name="md_light_blue_500_50">#8003a9f4</color>
|
||||
<color name="md_light_blue_500_75">#c003a9f4</color>
|
||||
<!-- cyans -->
|
||||
<array name="cyans">
|
||||
<item>@color/md_cyan_500</item>
|
||||
<item>@color/md_cyan_50</item>
|
||||
<item>@color/md_cyan_100</item>
|
||||
<item>@color/md_cyan_200</item>
|
||||
<item>@color/md_cyan_300</item>
|
||||
<item>@color/md_cyan_400</item>
|
||||
<item>@color/md_cyan_600</item>
|
||||
<item>@color/md_cyan_700</item>
|
||||
<item>@color/md_cyan_800</item>
|
||||
<item>@color/md_cyan_900</item>
|
||||
<item>@color/md_cyan_A100</item>
|
||||
<item>@color/md_cyan_A200</item>
|
||||
<item>@color/md_cyan_A400</item>
|
||||
<item>@color/md_cyan_A700</item>
|
||||
</array>
|
||||
<color name="md_cyan_50">#e0f7fa</color>
|
||||
<color name="md_cyan_100">#b2ebf2</color>
|
||||
<color name="md_cyan_200">#80deea</color>
|
||||
<color name="md_cyan_300">#4dd0e1</color>
|
||||
<color name="md_cyan_400">#26c6da</color>
|
||||
<color name="md_cyan_500">#00bcd4</color>
|
||||
<color name="md_cyan_600">#00acc1</color>
|
||||
<color name="md_cyan_700">#0097a7</color>
|
||||
<color name="md_cyan_800">#00838f</color>
|
||||
<color name="md_cyan_900">#006064</color>
|
||||
<color name="md_cyan_A100">#84ffff</color>
|
||||
<color name="md_cyan_A200">#18ffff</color>
|
||||
<color name="md_cyan_A400">#00e5ff</color>
|
||||
<color name="md_cyan_A700">#00b8d4</color>
|
||||
<color name="md_cyan_500_25">#4000bcd4</color>
|
||||
<color name="md_cyan_500_50">#8000bcd4</color>
|
||||
<color name="md_cyan_500_75">#c000bcd4</color>
|
||||
<!-- teals -->
|
||||
<array name="teals">
|
||||
<item>@color/md_teal_500</item>
|
||||
<item>@color/md_teal_50</item>
|
||||
<item>@color/md_teal_100</item>
|
||||
<item>@color/md_teal_200</item>
|
||||
<item>@color/md_teal_300</item>
|
||||
<item>@color/md_teal_400</item>
|
||||
<item>@color/md_teal_600</item>
|
||||
<item>@color/md_teal_700</item>
|
||||
<item>@color/md_teal_800</item>
|
||||
<item>@color/md_teal_900</item>
|
||||
<item>@color/md_teal_A100</item>
|
||||
<item>@color/md_teal_A200</item>
|
||||
<item>@color/md_teal_A400</item>
|
||||
<item>@color/md_teal_A700</item>
|
||||
</array>
|
||||
<color name="md_teal_50">#e0f2f1</color>
|
||||
<color name="md_teal_100">#b2dfdb</color>
|
||||
<color name="md_teal_200">#80cbc4</color>
|
||||
<color name="md_teal_300">#4db6ac</color>
|
||||
<color name="md_teal_400">#26a69a</color>
|
||||
<color name="md_teal_500">#009688</color>
|
||||
<color name="md_teal_600">#00897b</color>
|
||||
<color name="md_teal_700">#00796b</color>
|
||||
<color name="md_teal_800">#00695c</color>
|
||||
<color name="md_teal_900">#004d40</color>
|
||||
<color name="md_teal_A100">#a7ffeb</color>
|
||||
<color name="md_teal_A200">#64ffda</color>
|
||||
<color name="md_teal_A400">#1de9b6</color>
|
||||
<color name="md_teal_A700">#00bfa5</color>
|
||||
<color name="md_teal_500_25">#40009688</color>
|
||||
<color name="md_teal_500_50">#80009688</color>
|
||||
<color name="md_teal_500_75">#c8009688</color>
|
||||
<!-- greens -->
|
||||
<array name="greens">
|
||||
<item>@color/md_green_500</item>
|
||||
<item>@color/md_green_50</item>
|
||||
<item>@color/md_green_100</item>
|
||||
<item>@color/md_green_200</item>
|
||||
<item>@color/md_green_300</item>
|
||||
<item>@color/md_green_400</item>
|
||||
<item>@color/md_green_600</item>
|
||||
<item>@color/md_green_700</item>
|
||||
<item>@color/md_green_800</item>
|
||||
<item>@color/md_green_900</item>
|
||||
<item>@color/md_green_A100</item>
|
||||
<item>@color/md_green_A200</item>
|
||||
<item>@color/md_green_A400</item>
|
||||
<item>@color/md_green_A700</item>
|
||||
</array>
|
||||
<color name="md_green_50">#e8f5e9</color>
|
||||
<color name="md_green_100">#c8e6c9</color>
|
||||
<color name="md_green_200">#a5d6a7</color>
|
||||
<color name="md_green_300">#81c784</color>
|
||||
<color name="md_green_400">#66bb6a</color>
|
||||
<color name="md_green_500">#4caf50</color>
|
||||
<color name="md_green_600">#43a047</color>
|
||||
<color name="md_green_700">#388e3c</color>
|
||||
<color name="md_green_800">#2e7d32</color>
|
||||
<color name="md_green_900">#1b5e20</color>
|
||||
<color name="md_green_A100">#b9f6ca</color>
|
||||
<color name="md_green_A200">#69f0ae</color>
|
||||
<color name="md_green_A400">#00e676</color>
|
||||
<color name="md_green_A700">#00c853</color>
|
||||
<color name="md_green_500_25">#404caf50</color>
|
||||
<color name="md_green_500_50">#804caf50</color>
|
||||
<color name="md_green_500_75">#c04caf50</color>
|
||||
<!--light greens-->
|
||||
<array name="light_greens">
|
||||
<item>@color/md_light_green_500</item>
|
||||
<item>@color/md_light_green_50</item>
|
||||
<item>@color/md_light_green_100</item>
|
||||
<item>@color/md_light_green_200</item>
|
||||
<item>@color/md_light_green_300</item>
|
||||
<item>@color/md_light_green_400</item>
|
||||
<item>@color/md_light_green_600</item>
|
||||
<item>@color/md_light_green_700</item>
|
||||
<item>@color/md_light_green_800</item>
|
||||
<item>@color/md_light_green_900</item>
|
||||
<item>@color/md_light_green_A100</item>
|
||||
<item>@color/md_light_green_A200</item>
|
||||
<item>@color/md_light_green_A400</item>
|
||||
<item>@color/md_light_green_A700</item>
|
||||
</array>
|
||||
<color name="md_light_green_50">#f1f8e9</color>
|
||||
<color name="md_light_green_100">#dcedc8</color>
|
||||
<color name="md_light_green_200">#c5e1a5</color>
|
||||
<color name="md_light_green_300">#aed581</color>
|
||||
<color name="md_light_green_400">#9ccc65</color>
|
||||
<color name="md_light_green_500">#8bc34a</color>
|
||||
<color name="md_light_green_600">#7cb342</color>
|
||||
<color name="md_light_green_700">#689f38</color>
|
||||
<color name="md_light_green_800">#558b2f</color>
|
||||
<color name="md_light_green_900">#33691e</color>
|
||||
<color name="md_light_green_A100">#ccff90</color>
|
||||
<color name="md_light_green_A200">#b2ff59</color>
|
||||
<color name="md_light_green_A400">#76ff03</color>
|
||||
<color name="md_light_green_A700">#64dd17</color>
|
||||
<color name="md_light_green_500_25">#408bc34a</color>
|
||||
<color name="md_light_green_500_50">#808bc34a</color>
|
||||
<color name="md_light_green_500_75">#c88bc34a</color>
|
||||
<!-- limes -->
|
||||
<array name="limes">
|
||||
<item>@color/md_lime_500</item>
|
||||
<item>@color/md_lime_50</item>
|
||||
<item>@color/md_lime_100</item>
|
||||
<item>@color/md_lime_200</item>
|
||||
<item>@color/md_lime_300</item>
|
||||
<item>@color/md_lime_400</item>
|
||||
<item>@color/md_lime_600</item>
|
||||
<item>@color/md_lime_700</item>
|
||||
<item>@color/md_lime_800</item>
|
||||
<item>@color/md_lime_900</item>
|
||||
<item>@color/md_lime_A100</item>
|
||||
<item>@color/md_lime_A200</item>
|
||||
<item>@color/md_lime_A400</item>
|
||||
<item>@color/md_lime_A700</item>
|
||||
</array>
|
||||
<color name="md_lime_50">#f9fbe7</color>
|
||||
<color name="md_lime_100">#f0f4c3</color>
|
||||
<color name="md_lime_200">#e6ee9c</color>
|
||||
<color name="md_lime_300">#dce775</color>
|
||||
<color name="md_lime_400">#d4e157</color>
|
||||
<color name="md_lime_500">#cddc39</color>
|
||||
<color name="md_lime_600">#c0ca33</color>
|
||||
<color name="md_lime_700">#afb42b</color>
|
||||
<color name="md_lime_800">#9e9d24</color>
|
||||
<color name="md_lime_900">#827717</color>
|
||||
<color name="md_lime_A100">#f4ff81</color>
|
||||
<color name="md_lime_A200">#eeff41</color>
|
||||
<color name="md_lime_A400">#c6ff00</color>
|
||||
<color name="md_lime_A700">#aeea00</color>
|
||||
<color name="md_lime_500_25">#40cddc39</color>
|
||||
<color name="md_lime_500_50">#80cddc39</color>
|
||||
<color name="md_lime_500_75">#c0cddc39</color>
|
||||
<!--yellows -->
|
||||
<array name="yellows">
|
||||
<item>@color/md_yellow_500</item>
|
||||
<item>@color/md_yellow_50</item>
|
||||
<item>@color/md_yellow_100</item>
|
||||
<item>@color/md_yellow_200</item>
|
||||
<item>@color/md_yellow_300</item>
|
||||
<item>@color/md_yellow_400</item>
|
||||
<item>@color/md_yellow_600</item>
|
||||
<item>@color/md_yellow_700</item>
|
||||
<item>@color/md_yellow_800</item>
|
||||
<item>@color/md_yellow_900</item>
|
||||
<item>@color/md_yellow_A100</item>
|
||||
<item>@color/md_yellow_A200</item>
|
||||
<item>@color/md_yellow_A400</item>
|
||||
<item>@color/md_yellow_A700</item>
|
||||
</array>
|
||||
<color name="md_yellow_50">#fffde7</color>
|
||||
<color name="md_yellow_100">#fff9c4</color>
|
||||
<color name="md_yellow_200">#fff59d</color>
|
||||
<color name="md_yellow_300">#fff176</color>
|
||||
<color name="md_yellow_400">#ffee58</color>
|
||||
<color name="md_yellow_500">#ffeb3b</color>
|
||||
<color name="md_yellow_600">#fdd835</color>
|
||||
<color name="md_yellow_700">#fbc02d</color>
|
||||
<color name="md_yellow_800">#f9a825</color>
|
||||
<color name="md_yellow_900">#f57f17</color>
|
||||
<color name="md_yellow_A100">#ffff8d</color>
|
||||
<color name="md_yellow_A200">#ffff00</color>
|
||||
<color name="md_yellow_A400">#ffea00</color>
|
||||
<color name="md_yellow_A700">#ffd600</color>
|
||||
<color name="md_yellow_500_25">#40ffeb3b</color>
|
||||
<color name="md_yellow_500_50">#80ffeb3b</color>
|
||||
<color name="md_yellow_500_75">#c0ffeb3b</color>
|
||||
<!-- ambers -->
|
||||
<array name="ambers">
|
||||
<item>@color/md_amber_500</item>
|
||||
<item>@color/md_amber_50</item>
|
||||
<item>@color/md_amber_100</item>
|
||||
<item>@color/md_amber_200</item>
|
||||
<item>@color/md_amber_300</item>
|
||||
<item>@color/md_amber_400</item>
|
||||
<item>@color/md_amber_600</item>
|
||||
<item>@color/md_amber_700</item>
|
||||
<item>@color/md_amber_800</item>
|
||||
<item>@color/md_amber_900</item>
|
||||
<item>@color/md_amber_A100</item>
|
||||
<item>@color/md_amber_A200</item>
|
||||
<item>@color/md_amber_A400</item>
|
||||
<item>@color/md_amber_A700</item>
|
||||
</array>
|
||||
<color name="md_amber_50">#fff8e1</color>
|
||||
<color name="md_amber_100">#ffecb3</color>
|
||||
<color name="md_amber_200">#ffe082</color>
|
||||
<color name="md_amber_300">#ffd54f</color>
|
||||
<color name="md_amber_400">#ffca28</color>
|
||||
<color name="md_amber_500">#ffc107</color>
|
||||
<color name="md_amber_600">#ffb300</color>
|
||||
<color name="md_amber_700">#ffa000</color>
|
||||
<color name="md_amber_800">#ff8f00</color>
|
||||
<color name="md_amber_900">#ff6f00</color>
|
||||
<color name="md_amber_A100">#ffe57f</color>
|
||||
<color name="md_amber_A200">#ffd740</color>
|
||||
<color name="md_amber_A400">#ffc400</color>
|
||||
<color name="md_amber_A700">#ffab00</color>
|
||||
<color name="md_amber_500_25">#40ffc107</color>
|
||||
<color name="md_amber_500_50">#80ffc107</color>
|
||||
<color name="md_amber_500_75">#c0ffc107</color>
|
||||
<!-- oranges -->
|
||||
<array name="oranges">
|
||||
<item>@color/md_orange_500</item>
|
||||
<item>@color/md_orange_50</item>
|
||||
<item>@color/md_orange_100</item>
|
||||
<item>@color/md_orange_200</item>
|
||||
<item>@color/md_orange_300</item>
|
||||
<item>@color/md_orange_400</item>
|
||||
<item>@color/md_orange_600</item>
|
||||
<item>@color/md_orange_700</item>
|
||||
<item>@color/md_orange_800</item>
|
||||
<item>@color/md_orange_900</item>
|
||||
<item>@color/md_orange_A100</item>
|
||||
<item>@color/md_orange_A200</item>
|
||||
<item>@color/md_orange_A400</item>
|
||||
<item>@color/md_orange_A700</item>
|
||||
</array>
|
||||
<color name="md_orange_50">#fff3e0</color>
|
||||
<color name="md_orange_100">#ffe0b2</color>
|
||||
<color name="md_orange_200">#ffcc80</color>
|
||||
<color name="md_orange_300">#ffb74d</color>
|
||||
<color name="md_orange_400">#ffa726</color>
|
||||
<color name="md_orange_500">#ff9800</color>
|
||||
<color name="md_orange_600">#fb8c00</color>
|
||||
<color name="md_orange_700">#f57c00</color>
|
||||
<color name="md_orange_800">#ef6c00</color>
|
||||
<color name="md_orange_900">#e65100</color>
|
||||
<color name="md_orange_A100">#ffd180</color>
|
||||
<color name="md_orange_A200">#ffab40</color>
|
||||
<color name="md_orange_A400">#ff9100</color>
|
||||
<color name="md_orange_A700">#ff6d00</color>
|
||||
<color name="md_orange_500_25">#40ff9800</color>
|
||||
<color name="md_orange_500_50">#80ff9800</color>
|
||||
<color name="md_orange_500_75">#c0ff9800</color>
|
||||
<!-- deep oranges -->
|
||||
<array name="deep_oranges">
|
||||
<item>@color/md_deep_orange_500</item>
|
||||
<item>@color/md_deep_orange_50</item>
|
||||
<item>@color/md_deep_orange_100</item>
|
||||
<item>@color/md_deep_orange_200</item>
|
||||
<item>@color/md_deep_orange_300</item>
|
||||
<item>@color/md_deep_orange_400</item>
|
||||
<item>@color/md_deep_orange_600</item>
|
||||
<item>@color/md_deep_orange_700</item>
|
||||
<item>@color/md_deep_orange_800</item>
|
||||
<item>@color/md_deep_orange_900</item>
|
||||
<item>@color/md_deep_orange_A100</item>
|
||||
<item>@color/md_deep_orange_A200</item>
|
||||
<item>@color/md_deep_orange_A400</item>
|
||||
<item>@color/md_deep_orange_A700</item>
|
||||
</array>
|
||||
<color name="md_deep_orange_50">#fbe9e7</color>
|
||||
<color name="md_deep_orange_100">#ffccbc</color>
|
||||
<color name="md_deep_orange_200">#ffab91</color>
|
||||
<color name="md_deep_orange_300">#ff8a65</color>
|
||||
<color name="md_deep_orange_400">#ff7043</color>
|
||||
<color name="md_deep_orange_500">#ff5722</color>
|
||||
<color name="md_deep_orange_600">#f4511e</color>
|
||||
<color name="md_deep_orange_700">#e64a19</color>
|
||||
<color name="md_deep_orange_800">#d84315</color>
|
||||
<color name="md_deep_orange_900">#bf360c</color>
|
||||
<color name="md_deep_orange_A100">#ff9e80</color>
|
||||
<color name="md_deep_orange_A200">#ff6e40</color>
|
||||
<color name="md_deep_orange_A400">#ff3d00</color>
|
||||
<color name="md_deep_orange_A700">#dd2c00</color>
|
||||
<color name="md_deep_orange_500_25">#40ff5722</color>
|
||||
<color name="md_deep_orange_500_50">#80ff5722</color>
|
||||
<color name="md_deep_orange_500_75">#c0ff5722</color>
|
||||
<!-- browns -->
|
||||
<array name="browns">
|
||||
<item>@color/md_brown_500</item>
|
||||
<item>@color/md_brown_50</item>
|
||||
<item>@color/md_brown_100</item>
|
||||
<item>@color/md_brown_200</item>
|
||||
<item>@color/md_brown_300</item>
|
||||
<item>@color/md_brown_400</item>
|
||||
<item>@color/md_brown_600</item>
|
||||
<item>@color/md_brown_700</item>
|
||||
<item>@color/md_brown_800</item>
|
||||
<item>@color/md_brown_900</item>
|
||||
</array>
|
||||
<color name="md_brown_50">#efebe9</color>
|
||||
<color name="md_brown_100">#d7ccc8</color>
|
||||
<color name="md_brown_200">#bcaaa4</color>
|
||||
<color name="md_brown_300">#a1887f</color>
|
||||
<color name="md_brown_400">#8d6e63</color>
|
||||
<color name="md_brown_500">#795548</color>
|
||||
<color name="md_brown_600">#6d4c41</color>
|
||||
<color name="md_brown_700">#5d4037</color>
|
||||
<color name="md_brown_800">#4e342e</color>
|
||||
<color name="md_brown_900">#3e2723</color>
|
||||
<color name="md_brown_500_25">#40795548</color>
|
||||
<color name="md_brown_500_50">#80795548</color>
|
||||
<color name="md_brown_500_75">#c0795548</color>
|
||||
<!--grey-->
|
||||
<array name="greys">
|
||||
<item>@color/md_grey_500</item>
|
||||
<item>@color/md_grey_50</item>
|
||||
<item>@color/md_grey_100</item>
|
||||
<item>@color/md_grey_200</item>
|
||||
<item>@color/md_grey_300</item>
|
||||
<item>@color/md_grey_400</item>
|
||||
<item>@color/md_grey_600</item>
|
||||
<item>@color/md_grey_700</item>
|
||||
<item>@color/md_grey_800</item>
|
||||
<item>@color/md_grey_900</item>
|
||||
<item>@color/md_black_1000</item>
|
||||
<item>@color/md_white_1000</item>
|
||||
</array>
|
||||
<color name="md_grey_50">#fafafa</color>
|
||||
<color name="md_grey_100">#f5f5f5</color>
|
||||
<color name="md_grey_200">#eeeeee</color>
|
||||
<color name="md_grey_300">#e0e0e0</color>
|
||||
<color name="md_grey_400">#bdbdbd</color>
|
||||
<color name="md_grey_500">#9e9e9e</color>
|
||||
<color name="md_grey_600">#757575</color>
|
||||
<color name="md_grey_700">#616161</color>
|
||||
<!-- Material Design Colors -->
|
||||
<color name="md_black_1000">#000000</color>
|
||||
|
||||
<color name="md_grey_50">#FAFAFA</color>
|
||||
<color name="md_grey_100">#F5F5F5</color>
|
||||
<color name="md_grey_300">#E0E0E0</color>
|
||||
<color name="md_grey_800">#424242</color>
|
||||
<color name="md_grey_900">#212121</color>
|
||||
<color name="md_grey_500_25">#409e9e9e</color>
|
||||
<color name="md_grey_500_50">#809e9e9e</color>
|
||||
<color name="md_grey_500_75">#c09e9e9e</color>
|
||||
<color name="md_white_1000">#ffffff</color>
|
||||
<color name="md_white_1000_10">#1affffff</color>
|
||||
<color name="md_white_1000_15">#22ffffff</color>
|
||||
<color name="md_white_1000_20">#33ffffff</color>
|
||||
<color name="md_white_1000_25">#40ffffff</color>
|
||||
<color name="md_white_1000_50">#80ffffff</color>
|
||||
<color name="md_white_1000_60">#99ffffff</color>
|
||||
<color name="md_white_1000_75">#c0ffffff</color>
|
||||
<color name="md_black_1000_10">#1a000000</color>
|
||||
<color name="md_black_1000_15">#26000000</color>
|
||||
<color name="md_black_1000_20">#33000000</color>
|
||||
<color name="md_black_1000_25">#40000000</color>
|
||||
<color name="md_black_1000_50">#80000000</color>
|
||||
<color name="md_black_1000_75">#c0000000</color>
|
||||
<color name="md_black_1000">#000000</color>
|
||||
<!--blue grey-->
|
||||
<array name="blue_greys">
|
||||
<item>@color/md_blue_grey_500</item>
|
||||
<item>@color/md_blue_grey_50</item>
|
||||
<item>@color/md_blue_grey_100</item>
|
||||
<item>@color/md_blue_grey_200</item>
|
||||
<item>@color/md_blue_grey_300</item>
|
||||
<item>@color/md_blue_grey_400</item>
|
||||
<item>@color/md_blue_grey_600</item>
|
||||
<item>@color/md_blue_grey_700</item>
|
||||
<item>@color/md_blue_grey_800</item>
|
||||
<item>@color/md_blue_grey_900</item>
|
||||
</array>
|
||||
<color name="md_blue_grey_50">#eceff1</color>
|
||||
<color name="md_blue_grey_100">#cfd8dc</color>
|
||||
<color name="md_blue_grey_200">#b0bec5</color>
|
||||
<color name="md_blue_grey_300">#90a4ae</color>
|
||||
<color name="md_blue_grey_400">#78909c</color>
|
||||
<color name="md_blue_grey_500">#607d8b</color>
|
||||
<color name="md_blue_grey_600">#546e7a</color>
|
||||
<color name="md_blue_grey_700">#455a64</color>
|
||||
<color name="md_blue_grey_800">#37474f</color>
|
||||
<color name="md_blue_grey_900">#263238</color>
|
||||
<color name="md_blue_grey_500_25">#40607d8b</color>
|
||||
<color name="md_blue_grey_500_50">#80607d8b</color>
|
||||
<color name="md_blue_grey_500_75">#c0607d8b</color>
|
||||
<!--Texts-->
|
||||
<array name="texts_white">
|
||||
<item>@color/md_text_white</item>
|
||||
<item>@color/md_text_white_87</item>
|
||||
<item>@color/md_secondary_text_icons_white</item>
|
||||
<item>@color/md_disabled_hint_text_white</item>
|
||||
<item>@color/md_divider_white</item>
|
||||
</array>
|
||||
<color name="md_text_white">#ffffffff</color>
|
||||
<color name="md_text_white_87">#dfffffff</color>
|
||||
<color name="md_secondary_text_icons_white">#b3ffffff</color>
|
||||
<color name="md_disabled_hint_text_white">#4dffffff</color>
|
||||
<color name="md_divider_white">#1fffffff</color>
|
||||
<array name="texts_black">
|
||||
<item>@color/md_text</item>
|
||||
<item>@color/md_secondary_text_icons</item>
|
||||
<item>@color/md_disabled_hint_text</item>
|
||||
<item>@color/md_divider</item>
|
||||
</array>
|
||||
<color name="md_text">#df000000</color>
|
||||
<color name="md_secondary_text_icons">#8a000000</color>
|
||||
<color name="md_disabled_hint_text">#4c000000</color>
|
||||
<color name="md_divider">#1f000000</color>
|
||||
<!--Falcon-->
|
||||
<array name="falcon">
|
||||
<item>@color/md_falcon_400</item>
|
||||
<item>@color/md_falcon_500</item>
|
||||
<item>@color/md_falcon_700</item>
|
||||
</array>
|
||||
<color name="md_falcon_400">#ff38628b</color>
|
||||
<color name="md_falcon_500">#384e77</color>
|
||||
<color name="md_falcon_700">#2b3e5f</color>
|
||||
<color name="md_falcon_A400">#598bae</color>
|
||||
<color name="md_falcon_500_25">#40384e77</color>
|
||||
<color name="md_falcon_500_50">#80384e77</color>
|
||||
<color name="md_falcon_500_75">#c0384e77</color>
|
||||
<color name="md_falcon_A400_25">#40598bae</color>
|
||||
|
||||
<color name="md_blue_400">#42A5F5</color>
|
||||
<color name="md_blue_400_50">#8042A5F5</color>
|
||||
<color name="md_blue_400_38">#6142A5F5</color>
|
||||
|
||||
<color name="md_red_100">#FFCDD2</color>
|
||||
|
||||
<color name="md_statusbar_translucent">#4000</color>
|
||||
|
||||
|
@ -1,16 +1,16 @@
|
||||
<resources>
|
||||
<!-- Default screen margins, per the Android Design guidelines. -->
|
||||
<dimen name="activity_horizontal_margin">0dp</dimen>
|
||||
<dimen name="activity_vertical_margin">0dp</dimen>
|
||||
<dimen name="margin_top">0dp</dimen>
|
||||
<dimen name="margin_bottom">0dp</dimen>
|
||||
<dimen name="margin_left">0dp</dimen>
|
||||
<dimen name="margin_right">0dp</dimen>
|
||||
<dimen name="fab_margin">0dp</dimen>
|
||||
<dimen name="activity_horizontal_margin">16dp</dimen>
|
||||
<dimen name="activity_vertical_margin">16dp</dimen>
|
||||
<dimen name="margin_top">16dp</dimen>
|
||||
<dimen name="margin_bottom">16dp</dimen>
|
||||
<dimen name="margin_left">16dp</dimen>
|
||||
<dimen name="margin_right">16dp</dimen>
|
||||
<dimen name="fab_margin">16dp</dimen>
|
||||
<dimen name="fab_size">56dp</dimen>
|
||||
|
||||
<dimen name="dialog_content_padding">0dp</dimen>
|
||||
<dimen name="dialog_margin_top_content">0dp</dimen>
|
||||
<dimen name="dialog_content_padding">24dp</dimen>
|
||||
<dimen name="dialog_margin_top_content">20dp</dimen>
|
||||
|
||||
<dimen name="text_headline">24sp</dimen>
|
||||
<dimen name="text_large_title">22sp</dimen>
|
||||
|
@ -15,6 +15,7 @@
|
||||
<string name="pref_update_only_non_completed_key">pref_update_only_non_completed_key</string>
|
||||
<string name="pref_auto_update_manga_sync_key">pref_auto_update_manga_sync_key</string>
|
||||
<string name="pref_ask_update_manga_sync_key">pref_ask_update_manga_sync_key</string>
|
||||
<string name="pref_theme_key">pref_theme_key</string>
|
||||
|
||||
<string name="pref_default_viewer_key">pref_default_viewer_key</string>
|
||||
<string name="pref_image_scale_type_key">pref_image_scale_type_key</string>
|
||||
|