mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-11-03 23:58:55 +01:00 
			
		
		
		
	Manga in Kotlin. Expect some errors yet
This commit is contained in:
		@@ -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'
 | 
			
		||||
 
 | 
			
		||||
@@ -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.*
 | 
			
		||||
@@ -178,7 +178,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)
 | 
			
		||||
@@ -430,7 +430,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
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -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,7 +19,6 @@ 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.toast
 | 
			
		||||
import kotlinx.android.synthetic.main.fragment_library.*
 | 
			
		||||
@@ -125,7 +123,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
 | 
			
		||||
@@ -369,7 +366,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)
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
@@ -419,8 +416,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()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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
									
								
							
							
						
						
									
										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
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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()
 | 
			
		||||
 
 | 
			
		||||
@@ -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)
 | 
			
		||||
                })
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -17,6 +17,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.
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,18 @@
 | 
			
		||||
package eu.kanade.tachiyomi.util
 | 
			
		||||
 | 
			
		||||
import android.support.annotation.DrawableRes
 | 
			
		||||
import android.support.v4.content.ContextCompat
 | 
			
		||||
import android.widget.ImageView
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Set a drawable on a [ImageView] using [ContextCompat] for backwards compatibility.
 | 
			
		||||
 *
 | 
			
		||||
 * @param drawable id of drawable resource
 | 
			
		||||
 */
 | 
			
		||||
fun ImageView.setDrawableCompat(@DrawableRes drawable: Int?) {
 | 
			
		||||
    if (drawable != null) {
 | 
			
		||||
        setImageDrawable(ContextCompat.getDrawable(context, drawable))
 | 
			
		||||
    } else {
 | 
			
		||||
        setImageResource(android.R.color.transparent)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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"
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,7 @@ buildscript {
 | 
			
		||||
        jcenter()
 | 
			
		||||
    }
 | 
			
		||||
    dependencies {
 | 
			
		||||
        classpath 'com.android.tools.build:gradle:2.0.0-beta6'
 | 
			
		||||
        classpath 'com.android.tools.build:gradle:2.1.0-alpha1'
 | 
			
		||||
        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
 | 
			
		||||
        classpath 'me.tatarka:gradle-retrolambda:3.2.4'
 | 
			
		||||
        classpath 'com.github.ben-manes:gradle-versions-plugin:0.12.0'
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user