More crash fixes

This commit is contained in:
len 2016-04-06 02:18:04 +02:00
parent d8ac35d259
commit a598ebf72f
13 changed files with 147 additions and 164 deletions

View file

@ -32,11 +32,7 @@
</activity>
<activity
android:name=".ui.reader.ReaderActivity"
android:parentActivityName=".ui.manga.MangaActivity"
android:theme="@style/Theme.Reader">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".ui.manga.MangaActivity" />
</activity>
<activity
android:name=".ui.setting.SettingsActivity"

View file

@ -1,3 +1,16 @@
package eu.kanade.tachiyomi.event
class ChapterCountEvent(val count: Int)
import rx.Observable
import rx.subjects.BehaviorSubject
class ChapterCountEvent() {
private val subject = BehaviorSubject.create<Int>()
val observable: Observable<Int>
get() = subject
fun emit(count: Int) {
subject.onNext(count)
}
}

View file

@ -12,7 +12,7 @@ import nucleus.view.PresenterLifecycleDelegate;
import nucleus.view.ViewWithPresenter;
/**
* This class is an example of how an activity could controls it's presenter.
* This view is an example of how a view should control it's presenter.
* You can inherit from this class or copy/paste this class's code to
* create your own view implementation.
*
@ -87,12 +87,11 @@ public abstract class BaseRxFragment<P extends Presenter> extends BaseFragment i
@Override
public void onPause() {
super.onPause();
presenterDelegate.onPause(getActivity().isFinishing() || shouldDestroyPresenter(this));
presenterDelegate.onPause(getActivity().isFinishing() || isRemoving(this));
}
private boolean shouldDestroyPresenter(Fragment fragment) {
if (fragment == null) return false;
else return fragment.isRemoving() || shouldDestroyPresenter(fragment.getParentFragment());
private static boolean isRemoving(Fragment fragment) {
Fragment parent = fragment.getParentFragment();
return fragment.isRemoving() || (parent != null && isRemoving(parent));
}
}

View file

@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.ui.base.presenter
import android.content.Context
import nucleus.view.ViewWithPresenter
import org.greenrobot.eventbus.EventBus
import rx.Observable
open class BasePresenter<V : ViewWithPresenter<*>> : RxPresenter<V>() {
@ -16,4 +17,13 @@ open class BasePresenter<V : ViewWithPresenter<*>> : RxPresenter<V>() {
EventBus.getDefault().unregister(this)
}
fun <T> Observable<T>.subscribeFirst(onNext: (V, T) -> Unit, onError: ((V, Throwable) -> Unit)? = null)
= compose(deliverFirst<T>()).subscribe(split(onNext, onError))
fun <T> Observable<T>.subscribeLatestCache(onNext: (V, T) -> Unit, onError: ((V, Throwable) -> Unit)? = null)
= compose(deliverLatestCache<T>()).subscribe(split(onNext, onError))
fun <T> Observable<T>.subscribeReplay(onNext: (V, T) -> Unit, onError: ((V, Throwable) -> Unit)? = null)
= compose(deliverReplay<T>()).subscribe(split(onNext, onError))
}

View file

@ -13,15 +13,16 @@ 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.event.MangaEvent
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 eu.kanade.tachiyomi.util.SharedData
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>() {
@ -33,11 +34,9 @@ class MangaActivity : BaseRxActivity<MangaPresenter>() {
val CHAPTERS_FRAGMENT = 1
val MYANIMELIST_FRAGMENT = 2
fun newIntent(context: Context, manga: Manga?): Intent {
fun newIntent(context: Context, manga: Manga): Intent {
val intent = Intent(context, MangaActivity::class.java)
if (manga != null) {
EventBus.getDefault().postSticky(manga)
}
SharedData.put(MangaEvent(manga))
return intent
}
}

View file

@ -4,11 +4,10 @@ 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.ChapterCountEvent
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 eu.kanade.tachiyomi.util.SharedData
import rx.Observable
import javax.inject.Inject
@ -37,32 +36,26 @@ class MangaPresenter : BasePresenter<MangaActivity>() {
*/
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()
manga = SharedData.get(MangaEvent::class.java)!!.manga
} else {
manga = savedState.getSerializable(MANGA_KEY) as Manga
SharedData.put(MangaEvent(manga))
}
// Prepare a subject to communicate the chapters and info presenters for the chapter count.
SharedData.put(ChapterCountEvent())
add(Observable.just(manga)
.subscribeLatestCache({ view, manga -> view.onSetManga(manga) }))
}
override fun onDestroy() {
// Avoid new instances receiving wrong manga
EventBus.getDefault().removeStickyEvent(MangaEvent::class.java)
SharedData.remove(MangaEvent::class.java)
SharedData.remove(ChapterCountEvent::class.java)
super.onDestroy()
}
@ -71,12 +64,4 @@ class MangaPresenter : BasePresenter<MangaActivity>() {
super.onSave(state)
}
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
fun onEvent(manga: Manga) {
EventBus.getDefault().removeStickyEvent(manga)
unregisterForEvents()
this.manga = manga
start(GET_MANGA)
}
}

View file

@ -15,9 +15,8 @@ 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 eu.kanade.tachiyomi.util.SharedData
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
@ -47,20 +46,15 @@ class ChaptersPresenter : BasePresenter<ChaptersFragment>() {
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
private val DB_CHAPTERS = 1
private val FETCH_CHAPTERS = 2
private val CHAPTER_STATUS_CHANGES = 3
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) })
@ -75,21 +69,10 @@ class ChaptersPresenter : BasePresenter<ChaptersFragment>() {
{ view, download -> view.onChapterStatusChange(download) },
{ view, error -> Timber.e(error.cause, error.message) })
registerForEvents()
}
manga = SharedData.get(MangaEvent::class.java)!!.manga
add(Observable.just(manga)
.subscribeLatestCache({ view, manga -> view.onNextManga(manga) }))
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)
@ -97,7 +80,9 @@ class ChaptersPresenter : BasePresenter<ChaptersFragment>() {
.subscribeOn(Schedulers.io())
.doOnNext { chapters ->
this.chapters = chapters
EventBus.getDefault().postSticky(ChapterCountEvent(chapters.size))
SharedData.get(ChapterCountEvent::class.java)?.let {
it.emit(chapters.size)
}
for (chapter in chapters) {
setChapterStatus(chapter)
}
@ -105,7 +90,6 @@ class ChaptersPresenter : BasePresenter<ChaptersFragment>() {
}
.subscribe { chaptersSubject.onNext(it) })
}
}
fun fetchChaptersFromSource() {
hasRequested = true
@ -179,7 +163,7 @@ class ChaptersPresenter : BasePresenter<ChaptersFragment>() {
}
fun onOpenChapter(chapter: Chapter) {
EventBus.getDefault().postSticky(ReaderEvent(manga, chapter))
SharedData.put(ReaderEvent(manga, chapter))
}
fun getNextUnreadChapter(): Chapter? {

View file

@ -9,8 +9,7 @@ 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 eu.kanade.tachiyomi.util.SharedData
import rx.Observable
import rx.android.schedulers.AndroidSchedulers
import rx.schedulers.Schedulers
@ -50,11 +49,6 @@ class MangaInfoPresenter : BasePresenter<MangaInfoFragment>() {
*/
@Inject lateinit var coverCache: CoverCache
/**
* Count of chapters.
*/
private var count = -1
/**
* The id of the restartable.
*/
@ -63,12 +57,7 @@ class MangaInfoPresenter : BasePresenter<MangaInfoFragment>() {
/**
* The id of the restartable.
*/
private val GET_CHAPTER_COUNT = 2
/**
* The id of the restartable.
*/
private val FETCH_MANGA_INFO = 3
private val FETCH_MANGA_INFO = 2
override fun onCreate(savedState: Bundle?) {
super.onCreate(savedState)
@ -78,39 +67,19 @@ class MangaInfoPresenter : BasePresenter<MangaInfoFragment>() {
{ 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
manga = SharedData.get(MangaEvent::class.java)!!.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)
SharedData.get(ChapterCountEvent::class.java)?.let {
add(it.observable.subscribeLatestCache({ view, count -> view.setChapterCount(count) }))
}
}

View file

@ -9,9 +9,8 @@ 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.SharedData
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
@ -59,17 +58,7 @@ class MyAnimeListPresenter : BasePresenter<MyAnimeListFragment>() {
{ view, result -> view.onRefreshDone() },
{ view, error -> view.onRefreshError(error) })
registerForEvents()
}
override fun onDestroy() {
unregisterForEvents()
super.onDestroy()
}
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
fun onEvent(event: MangaEvent) {
manga = event.manga
manga = SharedData.get(MangaEvent::class.java)!!.manga
start(GET_MANGA_SYNC)
}

View file

@ -36,6 +36,7 @@ import kotlinx.android.synthetic.main.reader_menu.*
import nucleus.factory.RequiresPresenter
import rx.Subscription
import rx.subscriptions.CompositeSubscription
import timber.log.Timber
import java.text.DecimalFormat
@RequiresPresenter(ReaderPresenter::class)
@ -83,7 +84,7 @@ class ReaderActivity : BaseRxActivity<ReaderPresenter>() {
val preferences: PreferencesHelper
get() = presenter.prefs
public override fun onCreate(savedState: Bundle?) {
override fun onCreate(savedState: Bundle?) {
super.onCreate(savedState)
setContentView(R.layout.activity_reader)
@ -189,8 +190,9 @@ class ReaderActivity : BaseRxActivity<ReaderPresenter>() {
}
}
fun onChapterError() {
fun onChapterError(error: Throwable) {
finish()
Timber.e(error, error.message)
toast(R.string.page_list_error)
}

View file

@ -17,9 +17,7 @@ import eu.kanade.tachiyomi.data.source.base.Source
import eu.kanade.tachiyomi.data.source.model.Page
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 eu.kanade.tachiyomi.util.SharedData
import rx.Observable
import rx.Subscription
import rx.android.schedulers.AndroidSchedulers
@ -72,19 +70,26 @@ class ReaderPresenter : BasePresenter<ReaderActivity>() {
override fun onCreate(savedState: Bundle?) {
super.onCreate(savedState)
if (savedState != null) {
if (savedState == null) {
val event = SharedData.remove(ReaderEvent::class.java)!!
manga = event.manga
chapter = event.chapter
} else {
manga = savedState.getSerializable(MANGA_KEY) as Manga
source = sourceManager.get(manga.source)!!
chapter = savedState.getSerializable(CHAPTER_KEY) as Chapter
requestedPage = savedState.getInt(PAGE_KEY)
initializeSubjects()
}
source = sourceManager.get(manga.source)!!
initializeSubjects()
startableLatestCache(GET_ADJACENT_CHAPTERS,
{ getAdjacentChaptersObservable() },
{ view, pair -> view.onAdjacentChapters(pair.first, pair.second) })
startable(PRELOAD_NEXT_CHAPTER, { getPreloadNextChapterObservable() },
startable(PRELOAD_NEXT_CHAPTER,
{ getPreloadNextChapterObservable() },
{ },
{ error -> Timber.e("Error preloading chapter") })
@ -95,38 +100,24 @@ class ReaderPresenter : BasePresenter<ReaderActivity>() {
restartableLatestCache(GET_PAGE_LIST,
{ getPageListObservable(chapter) },
{ view, chapter -> view.onChapterReady(manga, chapter, currentPage) },
{ view, error -> view.onChapterError() })
{ view, error -> view.onChapterError(error) })
if (savedState == null) {
registerForEvents()
loadChapter(chapter)
if (prefs.autoUpdateMangaSync()) {
start(GET_MANGA_SYNC)
}
}
override fun onDestroy() {
unregisterForEvents()
super.onDestroy()
}
override fun onSave(state: Bundle) {
onChapterLeft()
state.putSerializable(MANGA_KEY, manga)
state.putSerializable(CHAPTER_KEY, chapter)
state.putSerializable(PAGE_KEY, requestedPage)
state.putSerializable(PAGE_KEY, currentPage?.pageNumber ?: 0)
super.onSave(state)
}
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
fun onEvent(event: ReaderEvent) {
EventBus.getDefault().removeStickyEvent(event)
manga = event.manga
source = sourceManager.get(manga.source)!!
initializeSubjects()
loadChapter(event.chapter)
if (prefs.autoUpdateMangaSync()) {
start(GET_MANGA_SYNC)
}
}
private fun initializeSubjects() {
// Listen for pages initialization events
pageInitializerSubject = PublishSubject.create<Chapter>()

View file

@ -12,6 +12,7 @@ import eu.kanade.tachiyomi.data.source.SourceManager
import eu.kanade.tachiyomi.event.DownloadChaptersEvent
import eu.kanade.tachiyomi.event.ReaderEvent
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
import eu.kanade.tachiyomi.util.SharedData
import org.greenrobot.eventbus.EventBus
import rx.Observable
import rx.android.schedulers.AndroidSchedulers
@ -256,7 +257,7 @@ class RecentChaptersPresenter : BasePresenter<RecentChaptersFragment>() {
* @param item chapter that is opened
*/
fun onOpenChapter(item: MangaChapter) {
EventBus.getDefault().postSticky(ReaderEvent(item.manga, item.chapter))
SharedData.put(ReaderEvent(item.manga, item.chapter))
}
/**

View file

@ -0,0 +1,45 @@
package eu.kanade.tachiyomi.util
import java.util.*
/**
* This singleton is used to share some objects within the application, useful to communicate
* different parts of the app.
*
* It stores the objects in a map using the type of the object as key, so that only one object per
* class is stored at once.
*/
object SharedData {
/**
* Map where the objects are saved.
*/
private val map = HashMap<Class<*>, Any>()
/**
* Publish an object to the shared data.
*
* @param data the object to put.
*/
fun <T : Any> put(data: T) {
map.put(data.javaClass, data)
}
/**
* Retrieves an object from the shared data.
*
* @param classType the class of the object to retrieve.
* @return an object of type T or null if it's not found.
*/
@Suppress("UNCHECKED_CAST")
fun <T : Any> get(classType: Class<T>) = map[classType] as? T
/**
* Removes an object from the shared data.
*
* @param classType the class of the object to remove.
* @return the object removed, null otherwise.
*/
fun <T : Any> remove(classType: Class<T>) = get(classType)?.apply { map.remove(classType) }
}