Преглед на файлове

Update last chapter read in MAL when reaching the last page

inorichi преди 9 години
родител
ревизия
9db81b1832

+ 3 - 0
app/src/main/AndroidManifest.xml

@@ -46,6 +46,9 @@
         <service android:name=".data.download.DownloadService"
             android:exported="false"/>
 
+        <service android:name=".data.chaptersync.UpdateChapterSyncService"
+            android:exported="false"/>
+
         <receiver
             android:name=".data.sync.LibraryUpdateService$SyncOnConnectionAvailable"
             android:enabled="false">

+ 5 - 0
app/src/main/java/eu/kanade/mangafeed/data/chaptersync/BaseChapterSync.java

@@ -1,5 +1,8 @@
 package eu.kanade.mangafeed.data.chaptersync;
 
+import com.squareup.okhttp.Response;
+
+import eu.kanade.mangafeed.data.database.models.ChapterSync;
 import rx.Observable;
 
 public abstract class BaseChapterSync {
@@ -13,4 +16,6 @@ public abstract class BaseChapterSync {
     public abstract Observable<Boolean> login(String username, String password);
 
     public abstract boolean isLogged();
+
+    public abstract Observable<Response> update(ChapterSync chapter);
 }

+ 8 - 0
app/src/main/java/eu/kanade/mangafeed/data/chaptersync/ChapterSyncManager.java

@@ -26,4 +26,12 @@ public class ChapterSyncManager {
         return services;
     }
 
+    public BaseChapterSync getSyncService(int id) {
+        switch (id) {
+            case MYANIMELIST:
+                return myAnimeList;
+        }
+        return null;
+    }
+
 }

+ 2 - 2
app/src/main/java/eu/kanade/mangafeed/data/chaptersync/MyAnimeList.java

@@ -89,7 +89,7 @@ public class MyAnimeList extends BaseChapterSync {
                 .map(entry -> {
                     ChapterSync chapter = ChapterSync.create(this);
                     chapter.title = entry.select("title").first().text();
-                    chapter.remote_id = Long.parseLong(entry.select("id").first().text());
+                    chapter.remote_id = Integer.parseInt(entry.select("id").first().text());
                     return chapter;
                 })
                 .toList();
@@ -111,7 +111,7 @@ public class MyAnimeList extends BaseChapterSync {
                 .map(entry -> {
                     ChapterSync chapter = ChapterSync.create(this);
                     chapter.title = entry.select("series_title").first().text();
-                    chapter.remote_id = Long.parseLong(
+                    chapter.remote_id = Integer.parseInt(
                             entry.select("series_mangadb_id").first().text());
                     chapter.last_chapter_read = Integer.parseInt(
                             entry.select("my_read_chapters").first().text());

+ 77 - 0
app/src/main/java/eu/kanade/mangafeed/data/chaptersync/UpdateChapterSyncService.java

@@ -0,0 +1,77 @@
+package eu.kanade.mangafeed.data.chaptersync;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.IBinder;
+
+import javax.inject.Inject;
+
+import de.greenrobot.event.EventBus;
+import eu.kanade.mangafeed.App;
+import eu.kanade.mangafeed.data.database.DatabaseHelper;
+import eu.kanade.mangafeed.data.database.models.ChapterSync;
+import eu.kanade.mangafeed.data.network.NetworkHelper;
+import eu.kanade.mangafeed.event.UpdateChapterSyncEvent;
+import eu.kanade.mangafeed.util.EventBusHook;
+import rx.android.schedulers.AndroidSchedulers;
+import rx.schedulers.Schedulers;
+import rx.subscriptions.CompositeSubscription;
+
+public class UpdateChapterSyncService extends Service {
+
+    @Inject ChapterSyncManager syncManager;
+    @Inject NetworkHelper networkManager;
+    @Inject DatabaseHelper db;
+
+    private CompositeSubscription subscriptions;
+
+    public static void start(Context context) {
+        context.startService(new Intent(context, UpdateChapterSyncService.class));
+    }
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        App.get(this).getComponent().inject(this);
+        subscriptions = new CompositeSubscription();
+        EventBus.getDefault().registerSticky(this);
+    }
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        return START_STICKY;
+    }
+
+    @Override
+    public void onDestroy() {
+        EventBus.getDefault().unregister(this);
+        subscriptions.unsubscribe();
+        super.onDestroy();
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return null;
+    }
+
+    @EventBusHook
+    public void onEventMainThread(UpdateChapterSyncEvent event) {
+        updateLastChapteRead(event.getChapterSync());
+    }
+
+    private void updateLastChapteRead(ChapterSync chapterSync) {
+        BaseChapterSync sync = syncManager.getSyncService(chapterSync.sync_id);
+
+        subscriptions.add(sync.update(chapterSync)
+                .flatMap(response -> db.insertChapterSync(chapterSync).createObservable())
+                .subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe(result -> {
+                    stopSelf();
+                }, error -> {
+                    stopSelf();
+                }));
+    }
+
+}

+ 2 - 2
app/src/main/java/eu/kanade/mangafeed/data/database/models/ChapterSync.java

@@ -16,10 +16,10 @@ public class ChapterSync {
     public long manga_id;
 
     @StorIOSQLiteColumn(name = ChapterSyncTable.COLUMN_SYNC_ID)
-    public long sync_id;
+    public int sync_id;
 
     @StorIOSQLiteColumn(name = ChapterSyncTable.COLUMN_REMOTE_ID)
-    public long remote_id;
+    public int remote_id;
 
     @StorIOSQLiteColumn(name = ChapterSyncTable.COLUMN_TITLE)
     public String title;

+ 17 - 0
app/src/main/java/eu/kanade/mangafeed/event/UpdateChapterSyncEvent.java

@@ -0,0 +1,17 @@
+package eu.kanade.mangafeed.event;
+
+import eu.kanade.mangafeed.data.database.models.ChapterSync;
+
+public class UpdateChapterSyncEvent {
+
+    private ChapterSync chapterSync;
+
+    public UpdateChapterSyncEvent(ChapterSync chapterSync) {
+        this.chapterSync = chapterSync;
+    }
+
+    public ChapterSync getChapterSync() {
+        return chapterSync;
+    }
+
+}

+ 2 - 0
app/src/main/java/eu/kanade/mangafeed/injection/component/AppComponent.java

@@ -6,6 +6,7 @@ import javax.inject.Singleton;
 
 import dagger.Component;
 import eu.kanade.mangafeed.data.chaptersync.MyAnimeList;
+import eu.kanade.mangafeed.data.chaptersync.UpdateChapterSyncService;
 import eu.kanade.mangafeed.data.download.DownloadService;
 import eu.kanade.mangafeed.data.sync.LibraryUpdateService;
 import eu.kanade.mangafeed.injection.module.AppModule;
@@ -55,6 +56,7 @@ public interface AppComponent {
 
     void inject(LibraryUpdateService libraryUpdateService);
     void inject(DownloadService downloadService);
+    void inject(UpdateChapterSyncService updateChapterSyncService);
 
     Application application();
 

+ 4 - 2
app/src/main/java/eu/kanade/mangafeed/ui/base/activity/BaseRxActivity.java

@@ -4,7 +4,7 @@ import android.os.Bundle;
 import android.support.annotation.NonNull;
 
 import eu.kanade.mangafeed.App;
-import eu.kanade.mangafeed.ui.base.activity.BaseActivity;
+import eu.kanade.mangafeed.ui.base.presenter.BasePresenter;
 import nucleus.factory.PresenterFactory;
 import nucleus.factory.ReflectionPresenterFactory;
 import nucleus.presenter.Presenter;
@@ -60,7 +60,9 @@ public abstract class BaseRxActivity<P extends Presenter> extends BaseActivity i
         final PresenterFactory<P> superFactory = getPresenterFactory();
         setPresenterFactory(() -> {
             P presenter = superFactory.createPresenter();
-            ((App)getApplication()).getComponentReflection().inject(presenter);
+            App app = (App) getApplication();
+            app.getComponentReflection().inject(presenter);
+            ((BasePresenter)presenter).setContext(app.getApplicationContext());
             return presenter;
         });
 

+ 4 - 2
app/src/main/java/eu/kanade/mangafeed/ui/base/fragment/BaseRxFragment.java

@@ -3,7 +3,7 @@ package eu.kanade.mangafeed.ui.base.fragment;
 import android.os.Bundle;
 
 import eu.kanade.mangafeed.App;
-import eu.kanade.mangafeed.ui.base.fragment.BaseFragment;
+import eu.kanade.mangafeed.ui.base.presenter.BasePresenter;
 import nucleus.factory.PresenterFactory;
 import nucleus.factory.ReflectionPresenterFactory;
 import nucleus.presenter.Presenter;
@@ -57,7 +57,9 @@ public abstract class BaseRxFragment<P extends Presenter> extends BaseFragment i
         final PresenterFactory<P> superFactory = getPresenterFactory();
         setPresenterFactory(() -> {
             P presenter = superFactory.createPresenter();
-            ((App)getActivity().getApplication()).getComponentReflection().inject(presenter);
+            App app = (App) getActivity().getApplication();
+            app.getComponentReflection().inject(presenter);
+            ((BasePresenter)presenter).setContext(app.getApplicationContext());
             return presenter;
         });
 

+ 12 - 0
app/src/main/java/eu/kanade/mangafeed/ui/base/presenter/BasePresenter.java

@@ -1,5 +1,6 @@
 package eu.kanade.mangafeed.ui.base.presenter;
 
+import android.content.Context;
 import android.os.Bundle;
 import android.support.annotation.NonNull;
 
@@ -10,6 +11,8 @@ import nucleus.view.ViewWithPresenter;
 
 public class BasePresenter<V extends ViewWithPresenter> extends RxPresenter<V> {
 
+    private Context context;
+
     @Override
     protected void onCreate(Bundle savedState) {
         super.onCreate(savedState);
@@ -33,4 +36,13 @@ public class BasePresenter<V extends ViewWithPresenter> extends RxPresenter<V> {
     public void unregisterForEvents() {
         EventBus.getDefault().unregister(this);
     }
+
+    public void setContext(Context applicationContext) {
+        context = applicationContext;
+    }
+
+    public Context getContext() {
+        return context;
+    }
+
 }

+ 47 - 12
app/src/main/java/eu/kanade/mangafeed/ui/reader/ReaderPresenter.java

@@ -8,8 +8,12 @@ import java.util.List;
 import javax.inject.Inject;
 
 import de.greenrobot.event.EventBus;
+import eu.kanade.mangafeed.data.chaptersync.ChapterSyncManager;
+import eu.kanade.mangafeed.data.chaptersync.MyAnimeList;
+import eu.kanade.mangafeed.data.chaptersync.UpdateChapterSyncService;
 import eu.kanade.mangafeed.data.database.DatabaseHelper;
 import eu.kanade.mangafeed.data.database.models.Chapter;
+import eu.kanade.mangafeed.data.database.models.ChapterSync;
 import eu.kanade.mangafeed.data.database.models.Manga;
 import eu.kanade.mangafeed.data.download.DownloadManager;
 import eu.kanade.mangafeed.data.preference.PreferencesHelper;
@@ -17,6 +21,7 @@ import eu.kanade.mangafeed.data.source.base.Source;
 import eu.kanade.mangafeed.data.source.model.Page;
 import eu.kanade.mangafeed.event.RetryPageEvent;
 import eu.kanade.mangafeed.event.SourceMangaChapterEvent;
+import eu.kanade.mangafeed.event.UpdateChapterSyncEvent;
 import eu.kanade.mangafeed.ui.base.presenter.BasePresenter;
 import eu.kanade.mangafeed.util.EventBusHook;
 import icepick.State;
@@ -32,6 +37,7 @@ public class ReaderPresenter extends BasePresenter<ReaderActivity> {
     @Inject PreferencesHelper prefs;
     @Inject DatabaseHelper db;
     @Inject DownloadManager downloadManager;
+    @Inject ChapterSyncManager syncManager;
 
     private Source source;
     private Manga manga;
@@ -135,10 +141,47 @@ public class ReaderPresenter extends BasePresenter<ReaderActivity> {
     }
 
     private void onChapterChange() {
-        if (pageList != null) {
-            if (!isDownloaded)
-                source.savePageList(chapter.url, pageList);
-            saveChapterProgress();
+        if (pageList == null)
+            return;
+
+        // Cache page list for online chapters to allow a faster reopen
+        if (!isDownloaded)
+            source.savePageList(chapter.url, pageList);
+
+        // Save current progress of the chapter. Mark as read if the chapter is finished
+        // and update progress in remote services (like MyAnimeList)
+        chapter.last_page_read = currentPage;
+        if (isChapterFinished()) {
+            chapter.read = true;
+            updateChapterSyncLastChapterRead();
+        }
+        db.insertChapter(chapter).executeAsBlocking();
+    }
+
+    private boolean isChapterFinished() {
+        return !chapter.read && currentPage == pageList.size() - 1;
+    }
+
+    private void updateChapterSyncLastChapterRead() {
+        // TODO don't use MAL methods for possible alternatives to MAL
+        MyAnimeList mal = syncManager.getMyAnimeList();
+
+        if (!mal.isLogged())
+            return;
+
+        List<ChapterSync> result = db.getChapterSync(manga, mal).executeAsBlocking();
+        if (result.isEmpty())
+            return;
+
+        ChapterSync chapterSync = result.get(0);
+
+        int lastChapterReadLocal = (int) Math.floor(chapter.chapter_number);
+        int lastChapterReadRemote = chapterSync.last_chapter_read;
+
+        if (lastChapterReadLocal > lastChapterReadRemote) {
+            chapterSync.last_chapter_read = lastChapterReadLocal;
+            EventBus.getDefault().postSticky(new UpdateChapterSyncEvent(chapterSync));
+            UpdateChapterSyncService.start(getContext());
         }
     }
 
@@ -186,14 +229,6 @@ public class ReaderPresenter extends BasePresenter<ReaderActivity> {
         this.currentPage = currentPage;
     }
 
-    private void saveChapterProgress() {
-        chapter.last_page_read = currentPage;
-        if (currentPage == pageList.size() - 1) {
-            chapter.read = true;
-        }
-        db.insertChapter(chapter).executeAsBlocking();
-    }
-
     private void getAdjacentChapters() {
         if (nextChapterSubscription != null)
             remove(nextChapterSubscription);