mirror of
https://github.com/mihonapp/mihon.git
synced 2024-11-21 20:47:03 -05:00
Refresh button in library is now looking for new chapters in sources and notifying the user
This commit is contained in:
parent
faef785fc3
commit
04dfdba0b7
12 changed files with 301 additions and 6 deletions
|
@ -42,6 +42,17 @@
|
|||
android:label="@string/title_activity_settings"
|
||||
android:parentActivityName=".ui.activity.MainActivity" >
|
||||
</activity>
|
||||
|
||||
<service android:name=".data.services.LibraryUpdateService"
|
||||
android:exported="false"/>
|
||||
|
||||
<receiver
|
||||
android:name=".data.services.LibraryUpdateService$SyncOnConnectionAvailable"
|
||||
android:enabled="false">
|
||||
<intent-filter>
|
||||
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
|
|
@ -103,6 +103,11 @@ public class DatabaseHelper implements MangaManager, ChapterManager {
|
|||
return mMangaManager.getMangasWithUnread();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Observable<List<Manga>> getFavoriteMangas() {
|
||||
return mMangaManager.getFavoriteMangas();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Observable<List<Manga>> getManga(String url) {
|
||||
return mMangaManager.getManga(url);
|
||||
|
|
|
@ -6,7 +6,6 @@ import java.util.ArrayList;
|
|||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import eu.kanade.mangafeed.data.caches.CacheManager;
|
||||
import eu.kanade.mangafeed.sources.Batoto;
|
||||
import eu.kanade.mangafeed.sources.MangaHere;
|
||||
import eu.kanade.mangafeed.sources.base.Source;
|
||||
|
@ -17,8 +16,6 @@ public class SourceManager {
|
|||
public static final int MANGAHERE = 2;
|
||||
|
||||
private HashMap<Integer, Source> mSourcesMap;
|
||||
private NetworkHelper mNetworkHelper;
|
||||
private CacheManager mCacheManager;
|
||||
private Context context;
|
||||
|
||||
public SourceManager(Context context) {
|
||||
|
|
|
@ -16,6 +16,8 @@ public interface MangaManager {
|
|||
|
||||
Observable<List<Manga>> getMangasWithUnread();
|
||||
|
||||
Observable<List<Manga>> getFavoriteMangas();
|
||||
|
||||
Observable<List<Manga>> getManga(String url);
|
||||
|
||||
Observable<List<Manga>> getManga(long id);
|
||||
|
|
|
@ -55,6 +55,19 @@ public class MangaManagerImpl extends BaseManager implements MangaManager {
|
|||
.createObservable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Observable<List<Manga>> getFavoriteMangas() {
|
||||
return db.get()
|
||||
.listOfObjects(Manga.class)
|
||||
.withQuery(Query.builder()
|
||||
.table(MangasTable.TABLE)
|
||||
.where(MangasTable.COLUMN_FAVORITE + "=?")
|
||||
.whereArgs(1)
|
||||
.build())
|
||||
.prepare()
|
||||
.createObservable();
|
||||
}
|
||||
|
||||
public Observable<List<Manga>> getManga(String url) {
|
||||
return db.get()
|
||||
.listOfObjects(Manga.class)
|
||||
|
|
|
@ -0,0 +1,168 @@
|
|||
package eu.kanade.mangafeed.data.services;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.IBinder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import eu.kanade.mangafeed.App;
|
||||
import eu.kanade.mangafeed.BuildConfig;
|
||||
import eu.kanade.mangafeed.R;
|
||||
import eu.kanade.mangafeed.data.helpers.DatabaseHelper;
|
||||
import eu.kanade.mangafeed.data.helpers.SourceManager;
|
||||
import eu.kanade.mangafeed.data.models.Manga;
|
||||
import eu.kanade.mangafeed.util.AndroidComponentUtil;
|
||||
import eu.kanade.mangafeed.util.NetworkUtil;
|
||||
import eu.kanade.mangafeed.util.NotificationUtil;
|
||||
import eu.kanade.mangafeed.util.PostResult;
|
||||
import rx.Observable;
|
||||
import rx.Subscription;
|
||||
import timber.log.Timber;
|
||||
|
||||
public class LibraryUpdateService extends Service {
|
||||
|
||||
@Inject DatabaseHelper db;
|
||||
@Inject SourceManager sourceManager;
|
||||
|
||||
private Subscription updateSubscription;
|
||||
private Subscription favoriteMangasSubscription;
|
||||
|
||||
public static final int UPDATE_NOTIFICATION_ID = 1;
|
||||
|
||||
public static Intent getStartIntent(Context context) {
|
||||
return new Intent(context, LibraryUpdateService.class);
|
||||
}
|
||||
|
||||
public static boolean isRunning(Context context) {
|
||||
return AndroidComponentUtil.isServiceRunning(context, LibraryUpdateService.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
App.get(this).getComponent().inject(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
if (updateSubscription != null)
|
||||
updateSubscription.unsubscribe();
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, final int startId) {
|
||||
Timber.i("Starting sync...");
|
||||
|
||||
if (!NetworkUtil.isNetworkConnected(this)) {
|
||||
Timber.i("Sync canceled, connection not available");
|
||||
AndroidComponentUtil.toggleComponent(this, SyncOnConnectionAvailable.class, true);
|
||||
stopSelf(startId);
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
if (favoriteMangasSubscription != null && !favoriteMangasSubscription.isUnsubscribed())
|
||||
favoriteMangasSubscription.unsubscribe();
|
||||
|
||||
favoriteMangasSubscription = db.getFavoriteMangas()
|
||||
.subscribe(mangas -> {
|
||||
// Don't receive further db updates
|
||||
favoriteMangasSubscription.unsubscribe();
|
||||
this.startUpdating(mangas, startId);
|
||||
});
|
||||
|
||||
return START_STICKY;
|
||||
}
|
||||
|
||||
private void startUpdating(final List<Manga> mangas, final int startId) {
|
||||
if (updateSubscription != null && !updateSubscription.isUnsubscribed())
|
||||
updateSubscription.unsubscribe();
|
||||
|
||||
final AtomicInteger count = new AtomicInteger(0);
|
||||
|
||||
List<MangaUpdate> updates = new ArrayList<>();
|
||||
|
||||
updateSubscription = Observable.from(mangas)
|
||||
.doOnNext(manga -> {
|
||||
NotificationUtil.create(this, UPDATE_NOTIFICATION_ID,
|
||||
getString(R.string.notification_progress, count.incrementAndGet(), mangas.size()),
|
||||
manga.title);
|
||||
})
|
||||
.concatMap(manga -> sourceManager.get(manga.source)
|
||||
.pullChaptersFromNetwork(manga.url)
|
||||
.flatMap(chapters -> db.insertOrRemoveChapters(manga, chapters))
|
||||
.filter(result -> result.getNumberOfRowsInserted() > 0)
|
||||
.flatMap(result -> Observable.just(new MangaUpdate(manga, result)))
|
||||
)
|
||||
.subscribe(update -> {
|
||||
updates.add(update);
|
||||
}, error -> {
|
||||
Timber.e("Error syncing");
|
||||
stopSelf(startId);
|
||||
}, () -> {
|
||||
NotificationUtil.createBigText(this, UPDATE_NOTIFICATION_ID,
|
||||
getString(R.string.notification_completed), getUpdatedMangas(updates));
|
||||
stopSelf(startId);
|
||||
});
|
||||
}
|
||||
|
||||
private String getUpdatedMangas(List<MangaUpdate> updates) {
|
||||
final StringBuilder result = new StringBuilder();
|
||||
if (updates.isEmpty()) {
|
||||
result.append(getString(R.string.notification_no_new_chapters)).append("\n");
|
||||
} else {
|
||||
result.append(getString(R.string.notification_new_chapters));
|
||||
|
||||
for (MangaUpdate update : updates) {
|
||||
result.append("\n").append(update.getManga().title);
|
||||
}
|
||||
}
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static class SyncOnConnectionAvailable extends BroadcastReceiver {
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (NetworkUtil.isNetworkConnected(context)) {
|
||||
if (BuildConfig.DEBUG) {
|
||||
Timber.i("Connection is now available, triggering sync...");
|
||||
}
|
||||
AndroidComponentUtil.toggleComponent(context, this.getClass(), false);
|
||||
context.startService(getStartIntent(context));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class MangaUpdate {
|
||||
private Manga manga;
|
||||
private PostResult result;
|
||||
|
||||
public MangaUpdate(Manga manga, PostResult result) {
|
||||
this.manga = manga;
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
public Manga getManga() {
|
||||
return manga;
|
||||
}
|
||||
|
||||
public PostResult getResult() {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -5,6 +5,7 @@ import android.app.Application;
|
|||
import javax.inject.Singleton;
|
||||
|
||||
import dagger.Component;
|
||||
import eu.kanade.mangafeed.data.services.LibraryUpdateService;
|
||||
import eu.kanade.mangafeed.injection.module.AppModule;
|
||||
import eu.kanade.mangafeed.injection.module.DataModule;
|
||||
import eu.kanade.mangafeed.presenter.CataloguePresenter;
|
||||
|
@ -40,6 +41,8 @@ public interface AppComponent {
|
|||
|
||||
void inject(Source source);
|
||||
|
||||
void inject(LibraryUpdateService libraryUpdateService);
|
||||
|
||||
Application application();
|
||||
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ import butterknife.ButterKnife;
|
|||
import butterknife.OnItemClick;
|
||||
import eu.kanade.mangafeed.R;
|
||||
import eu.kanade.mangafeed.data.models.Manga;
|
||||
import eu.kanade.mangafeed.data.services.LibraryUpdateService;
|
||||
import eu.kanade.mangafeed.presenter.LibraryPresenter;
|
||||
import eu.kanade.mangafeed.ui.activity.MainActivity;
|
||||
import eu.kanade.mangafeed.ui.activity.MangaDetailActivity;
|
||||
|
@ -68,6 +69,21 @@ public class LibraryFragment extends BaseRxFragment<LibraryPresenter> {
|
|||
initializeSearch(menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.action_refresh:
|
||||
if (!LibraryUpdateService.isRunning(activity)) {
|
||||
Intent intent = LibraryUpdateService.getStartIntent(activity);
|
||||
activity.startService(intent);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
private void initializeSearch(Menu menu) {
|
||||
final SearchView sv = (SearchView) menu.findItem(R.id.action_search).getActionView();
|
||||
sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
package eu.kanade.mangafeed.util;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityManager.RunningServiceInfo;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
public class AndroidComponentUtil {
|
||||
|
||||
public static void toggleComponent(Context context, Class componentClass, boolean enable) {
|
||||
Timber.i((enable ? "Enabling " : "Disabling ") + componentClass.getSimpleName());
|
||||
ComponentName componentName = new ComponentName(context, componentClass);
|
||||
PackageManager pm = context.getPackageManager();
|
||||
pm.setComponentEnabledSetting(componentName,
|
||||
enable ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED :
|
||||
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
|
||||
PackageManager.DONT_KILL_APP);
|
||||
}
|
||||
|
||||
public static boolean isServiceRunning(Context context, Class serviceClass) {
|
||||
ActivityManager manager =
|
||||
(ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
|
||||
for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
|
||||
if (serviceClass.getName().equals(service.service.getClassName())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package eu.kanade.mangafeed.util;
|
||||
|
||||
import android.app.NotificationManager;
|
||||
import android.content.Context;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
|
||||
import eu.kanade.mangafeed.R;
|
||||
|
||||
public class NotificationUtil {
|
||||
|
||||
public static void create(Context context, int nId, String title, String body, int iconRes) {
|
||||
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(context)
|
||||
.setSmallIcon(iconRes == -1 ? R.drawable.ic_action_refresh : iconRes)
|
||||
.setContentTitle(title)
|
||||
.setContentText(body);
|
||||
|
||||
|
||||
NotificationManager mNotificationManager =
|
||||
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
|
||||
mNotificationManager.notify(nId, mBuilder.build());
|
||||
}
|
||||
|
||||
public static void createBigText(Context context, int nId, String title, String body, int iconRes) {
|
||||
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(context)
|
||||
.setSmallIcon(iconRes == -1 ? R.drawable.ic_action_refresh : iconRes)
|
||||
.setContentTitle(title)
|
||||
.setStyle(new NotificationCompat.BigTextStyle().bigText(body));
|
||||
|
||||
NotificationManager mNotificationManager =
|
||||
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
|
||||
mNotificationManager.notify(nId, mBuilder.build());
|
||||
}
|
||||
|
||||
public static void create(Context context, int nId, String title, String body) {
|
||||
create(context, nId, title, body, -1);
|
||||
}
|
||||
|
||||
public static void createBigText(Context context, int nId, String title, String body) {
|
||||
createBigText(context, nId, title, body, -1);
|
||||
}
|
||||
|
||||
}
|
|
@ -19,17 +19,14 @@ public class PostResult {
|
|||
this.numberOfRowsDeleted = numberOfRowsDeleted;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Integer getNumberOfRowsUpdated() {
|
||||
return numberOfRowsUpdated;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Integer getNumberOfRowsInserted() {
|
||||
return numberOfRowsInserted;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Integer getNumberOfRowsDeleted() {
|
||||
return numberOfRowsDeleted;
|
||||
}
|
||||
|
|
|
@ -82,4 +82,10 @@
|
|||
<string name="action_mark_as_unread">Mark as unread</string>
|
||||
<string name="selected_chapters_title">Selected chapters: %1$d</string>
|
||||
|
||||
<string name="notification_progress">Update progress: %1$d/%2$d</string>
|
||||
<string name="notification_completed">Update completed</string>
|
||||
<string name="notification_no_new_chapters">No new chapters found</string>
|
||||
<string name="notification_new_chapters">Found new chapters for:</string>
|
||||
|
||||
|
||||
</resources>
|
||||
|
|
Loading…
Reference in a new issue