mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-10-31 22:37:56 +01:00 
			
		
		
		
	Bugfixes in download manager and other minor changes
This commit is contained in:
		| @@ -7,12 +7,10 @@ import com.google.gson.reflect.TypeToken; | ||||
| import com.google.gson.stream.JsonReader; | ||||
|  | ||||
| import java.io.File; | ||||
| import java.io.FileNotFoundException; | ||||
| import java.io.FileOutputStream; | ||||
| import java.io.FileReader; | ||||
| import java.io.IOException; | ||||
| import java.lang.reflect.Type; | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
|  | ||||
| import eu.kanade.mangafeed.data.models.Chapter; | ||||
| @@ -33,16 +31,18 @@ import timber.log.Timber; | ||||
|  | ||||
| public class DownloadManager { | ||||
|  | ||||
|     private PublishSubject<DownloadChaptersEvent> downloadsSubject; | ||||
|     private Subscription downloadSubscription; | ||||
|     private Subscription threadNumberSubscription; | ||||
|  | ||||
|     private Context context; | ||||
|     private SourceManager sourceManager; | ||||
|     private PreferencesHelper preferences; | ||||
|     private Gson gson; | ||||
|  | ||||
|     private PublishSubject<Download> downloadsQueueSubject; | ||||
|     private BehaviorSubject<Integer> threadsNumber; | ||||
|     private Subscription downloadsSubscription; | ||||
|     private Subscription threadNumberSubscription; | ||||
|  | ||||
|     private DownloadQueue queue; | ||||
|     private transient boolean isQueuePaused; | ||||
|  | ||||
|     public static final String PAGE_LIST_FILE = "index.json"; | ||||
|  | ||||
| @@ -54,78 +54,63 @@ public class DownloadManager { | ||||
|  | ||||
|         queue = new DownloadQueue(); | ||||
|  | ||||
|         initializeDownloadSubscription(); | ||||
|         initializeDownloadsSubscription(); | ||||
|     } | ||||
|  | ||||
|     public PublishSubject<DownloadChaptersEvent> getDownloadsSubject() { | ||||
|         return downloadsSubject; | ||||
|     } | ||||
|  | ||||
|     private void initializeDownloadSubscription() { | ||||
|         if (downloadSubscription != null && !downloadSubscription.isUnsubscribed()) { | ||||
|             downloadSubscription.unsubscribe(); | ||||
|         } | ||||
|     private void initializeDownloadsSubscription() { | ||||
|         if (downloadsSubscription != null && !downloadsSubscription.isUnsubscribed()) | ||||
|             downloadsSubscription.unsubscribe(); | ||||
|  | ||||
|         if (threadNumberSubscription != null && !threadNumberSubscription.isUnsubscribed()) | ||||
|             threadNumberSubscription.unsubscribe(); | ||||
|  | ||||
|         downloadsSubject = PublishSubject.create(); | ||||
|         BehaviorSubject<Integer> threads = BehaviorSubject.create(); | ||||
|         downloadsQueueSubject = PublishSubject.create(); | ||||
|         threadsNumber = BehaviorSubject.create(); | ||||
|  | ||||
|         threadNumberSubscription = preferences.getDownloadTheadsObs() | ||||
|                 .subscribe(threads::onNext); | ||||
|         threadNumberSubscription = preferences.getDownloadTheadsObservable() | ||||
|                 .filter(n -> !isQueuePaused) | ||||
|                 .doOnNext(n -> isQueuePaused = (n == 0)) | ||||
|                 .subscribe(threadsNumber::onNext); | ||||
|  | ||||
|         // Listen for download events, add them to queue and download | ||||
|         downloadSubscription = downloadsSubject | ||||
|                 .subscribeOn(Schedulers.io()) | ||||
|                 .flatMap(this::prepareDownloads) | ||||
|                 .lift(new DynamicConcurrentMergeOperator<>(this::downloadChapter, threads)) | ||||
|         downloadsSubscription = downloadsQueueSubject | ||||
|                 .observeOn(Schedulers.newThread()) | ||||
|                 .lift(new DynamicConcurrentMergeOperator<>(this::downloadChapter, threadsNumber)) | ||||
|                 .onBackpressureBuffer() | ||||
|                 .subscribe(page -> {}, | ||||
|                         e -> Timber.e(e.fillInStackTrace(), e.getMessage())); | ||||
|     } | ||||
|  | ||||
|     // Create a download object for every chapter and add it to the downloads queue | ||||
|     private Observable<Download> prepareDownloads(DownloadChaptersEvent event) { | ||||
|     // Create a download object for every chapter in the event and add them to the downloads queue | ||||
|     public void onDownloadChaptersEvent(DownloadChaptersEvent event) { | ||||
|         final Manga manga = event.getManga(); | ||||
|         final Source source = sourceManager.get(manga.source); | ||||
|         List<Download> downloads = new ArrayList<>(); | ||||
|  | ||||
|         for (Chapter chapter : event.getChapters()) { | ||||
|             Download download = new Download(source, manga, chapter); | ||||
|  | ||||
|             if (!isChapterDownloaded(download)) { | ||||
|                 queue.add(download); | ||||
|                 downloads.add(download); | ||||
|                 downloadsQueueSubject.onNext(download); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return Observable.from(downloads); | ||||
|     } | ||||
|  | ||||
|     // Check if a chapter is already downloaded | ||||
|     private boolean isChapterDownloaded(Download download) { | ||||
|         // If the chapter is already queued, don't add it again | ||||
|         for (Download queuedDownload : queue.get()) { | ||||
|             if (download.chapter.id == queuedDownload.chapter.id) | ||||
|             if (download.chapter.id.equals(queuedDownload.chapter.id)) | ||||
|                 return true; | ||||
|         } | ||||
|  | ||||
|         // Add the directory to the download object for future access | ||||
|         download.directory = getAbsoluteChapterDirectory(download); | ||||
|  | ||||
|         // If the directory doesn't exist, the chapter isn't downloaded. Create it in this case | ||||
|         // If the directory doesn't exist, the chapter isn't downloaded. | ||||
|         if (!download.directory.exists()) { | ||||
|             // FIXME Sometimes it's failing to create the directory... My fault? | ||||
|             try { | ||||
|                 DiskUtils.createDirectory(download.directory); | ||||
|             } catch (IOException e) { | ||||
|                 Timber.e("Unable to create directory for chapter"); | ||||
|             } | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|  | ||||
|         // If the page list doesn't exist, the chapter isn't download (or maybe it's, | ||||
|         // but we consider it's not) | ||||
|         List<Page> savedPages = getSavedPageList(download); | ||||
| @@ -142,6 +127,12 @@ public class DownloadManager { | ||||
|  | ||||
|     // Download the entire chapter | ||||
|     private Observable<Page> downloadChapter(Download download) { | ||||
|         try { | ||||
|             DiskUtils.createDirectory(download.directory); | ||||
|         } catch (IOException e) { | ||||
|             Timber.e(e.getMessage()); | ||||
|         } | ||||
|  | ||||
|         Observable<List<Page>> pageListObservable = download.pages == null ? | ||||
|                 // Pull page list from network and add them to download object | ||||
|                 download.source | ||||
| @@ -152,7 +143,6 @@ public class DownloadManager { | ||||
|                 Observable.just(download.pages); | ||||
|  | ||||
|         return pageListObservable | ||||
|                 .subscribeOn(Schedulers.io()) | ||||
|                 .doOnNext(pages -> download.setStatus(Download.DOWNLOADING)) | ||||
|                 // Get all the URLs to the source images, fetch pages if necessary | ||||
|                 .flatMap(pageList -> Observable.merge( | ||||
| @@ -161,31 +151,29 @@ public class DownloadManager { | ||||
|                 // Start downloading images, consider we can have downloaded images already | ||||
|                 .concatMap(page -> getDownloadedImage(page, download.source, download.directory)) | ||||
|                 // Do after download completes | ||||
|                 .doOnCompleted(() -> onChapterDownloaded(download)); | ||||
|                 .doOnCompleted(() -> onDownloadCompleted(download)); | ||||
|     } | ||||
|  | ||||
|     // Get downloaded image if exists, otherwise download it with the method below | ||||
|     public Observable<Page> getDownloadedImage(final Page page, Source source, File chapterDir) { | ||||
|         Observable<Page> obs = Observable.just(page); | ||||
|         Observable<Page> pageObservable = Observable.just(page); | ||||
|         if (page.getImageUrl() == null) | ||||
|             return obs; | ||||
|             return pageObservable; | ||||
|  | ||||
|         String imageFilename = getImageFilename(page); | ||||
|         File imagePath = new File(chapterDir, imageFilename); | ||||
|  | ||||
|         if (!isImageDownloaded(imagePath)) { | ||||
|             page.setStatus(Page.DOWNLOAD_IMAGE); | ||||
|             obs = downloadImage(page, source, chapterDir, imageFilename); | ||||
|             pageObservable = downloadImage(page, source, chapterDir, imageFilename); | ||||
|         } | ||||
|  | ||||
|         return obs.flatMap(p -> { | ||||
|             page.setImagePath(imagePath.getAbsolutePath()); | ||||
|             page.setStatus(Page.READY); | ||||
|             return Observable.just(page); | ||||
|         }).onErrorResumeNext(e -> { | ||||
|             page.setStatus(Page.ERROR); | ||||
|             return Observable.just(page); | ||||
|         }); | ||||
|         return pageObservable | ||||
|                 .doOnNext(p -> p.setImagePath(imagePath.getAbsolutePath())) | ||||
|                 .doOnNext(p -> p.setStatus(Page.READY)) | ||||
|                 .doOnError(e -> page.setStatus(Page.ERROR)) | ||||
|                 // Allow to download the remaining images | ||||
|                 .onErrorResumeNext(e -> Observable.just(page)); | ||||
|     } | ||||
|  | ||||
|     // Download the image | ||||
| @@ -210,31 +198,46 @@ public class DownloadManager { | ||||
|     } | ||||
|  | ||||
|     private boolean isImageDownloaded(File imagePath) { | ||||
|         return imagePath.exists() && !imagePath.isDirectory(); | ||||
|         return imagePath.exists(); | ||||
|     } | ||||
|  | ||||
|     private void onChapterDownloaded(final Download download) { | ||||
|         download.setStatus(Download.DOWNLOADED); | ||||
|         download.totalProgress = download.pages.size() * 100; | ||||
|         savePageList(download.source, download.manga, download.chapter, download.pages); | ||||
|     // Called when a download finishes. This doesn't mean the download was successful, so we check it | ||||
|     private void onDownloadCompleted(final Download download) { | ||||
|         checkDownloadIsSuccessful(download); | ||||
|         savePageList(download); | ||||
|     } | ||||
|  | ||||
|     private void checkDownloadIsSuccessful(final Download download) { | ||||
|         int expectedProgress = download.pages.size() * 100; | ||||
|         int actualProgress = 0; | ||||
|         int status = Download.DOWNLOADED; | ||||
|         // If any page has an error, the download result will be error | ||||
|         for (Page page : download.pages) { | ||||
|             actualProgress += page.getProgress(); | ||||
|             if (page.getStatus() == Page.ERROR) status = Download.ERROR; | ||||
|         } | ||||
|         // If the download is successful, it's safer to use the expected progress | ||||
|         download.totalProgress = (status == Download.DOWNLOADED) ? expectedProgress : actualProgress; | ||||
|         download.setStatus(status); | ||||
|     } | ||||
|  | ||||
|     // Return the page list from the chapter's directory if it exists, null otherwise | ||||
|     public List<Page> getSavedPageList(Source source, Manga manga, Chapter chapter) { | ||||
|         List<Page> pages = null; | ||||
|         File chapterDir = getAbsoluteChapterDirectory(source, manga, chapter); | ||||
|         File pagesFile = new File(chapterDir, PAGE_LIST_FILE); | ||||
|  | ||||
|         try { | ||||
|             if (pagesFile.exists()) { | ||||
|                 JsonReader reader = new JsonReader(new FileReader(pagesFile.getAbsolutePath())); | ||||
|  | ||||
|                 Type collectionType = new TypeToken<List<Page>>() {}.getType(); | ||||
|                 return gson.fromJson(reader, collectionType); | ||||
|                 pages = gson.fromJson(reader, collectionType); | ||||
|                 reader.close(); | ||||
|             } | ||||
|         } catch (FileNotFoundException e) { | ||||
|         } catch (Exception e) { | ||||
|             Timber.e(e.fillInStackTrace(), e.getMessage()); | ||||
|         } | ||||
|         return null; | ||||
|         return pages; | ||||
|     } | ||||
|  | ||||
|     // Shortcut for the method above | ||||
| @@ -287,4 +290,13 @@ public class DownloadManager { | ||||
|     public DownloadQueue getQueue() { | ||||
|         return queue; | ||||
|     } | ||||
|  | ||||
|     public void pauseDownloads() { | ||||
|         threadsNumber.onNext(0); | ||||
|     } | ||||
|  | ||||
|     public void resumeDownloads() { | ||||
|         isQueuePaused = false; | ||||
|         threadsNumber.onNext(preferences.getDownloadThreads()); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -68,7 +68,7 @@ public class PreferencesHelper { | ||||
|         return Integer.parseInt(prefs.getString(getKey(R.string.pref_download_threads_key), "1")); | ||||
|     } | ||||
|  | ||||
|     public Observable<Integer> getDownloadTheadsObs() { | ||||
|     public Observable<Integer> getDownloadTheadsObservable() { | ||||
|         return rxPrefs.getString(getKey(R.string.pref_download_threads_key), "1") | ||||
|                 .asObservable().map(Integer::parseInt); | ||||
|     } | ||||
|   | ||||
| @@ -3,6 +3,8 @@ package eu.kanade.mangafeed.data.services; | ||||
| import android.app.Service; | ||||
| import android.content.Context; | ||||
| import android.content.Intent; | ||||
| import android.content.IntentFilter; | ||||
| import android.net.ConnectivityManager; | ||||
| import android.os.IBinder; | ||||
|  | ||||
| import javax.inject.Inject; | ||||
| @@ -12,14 +14,18 @@ import eu.kanade.mangafeed.App; | ||||
| import eu.kanade.mangafeed.data.helpers.DownloadManager; | ||||
| import eu.kanade.mangafeed.events.DownloadChaptersEvent; | ||||
| import eu.kanade.mangafeed.util.AndroidComponentUtil; | ||||
| import eu.kanade.mangafeed.util.ContentObservable; | ||||
| import eu.kanade.mangafeed.util.EventBusHook; | ||||
| import rx.Subscription; | ||||
|  | ||||
| public class DownloadService extends Service { | ||||
|  | ||||
|     @Inject DownloadManager downloadManager; | ||||
|  | ||||
|     public static Intent getStartIntent(Context context) { | ||||
|         return new Intent(context, DownloadService.class); | ||||
|     private Subscription networkChangeSubscription; | ||||
|  | ||||
|     public static void start(Context context) { | ||||
|         context.startService(new Intent(context, DownloadService.class)); | ||||
|     } | ||||
|  | ||||
|     public static boolean isRunning(Context context) { | ||||
| @@ -30,6 +36,7 @@ public class DownloadService extends Service { | ||||
|     public void onCreate() { | ||||
|         super.onCreate(); | ||||
|         App.get(this).getComponent().inject(this); | ||||
|         listenNetworkChanges(); | ||||
|  | ||||
|         EventBus.getDefault().registerSticky(this); | ||||
|     } | ||||
| @@ -39,6 +46,13 @@ public class DownloadService extends Service { | ||||
|         return START_STICKY; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onDestroy() { | ||||
|         EventBus.getDefault().unregister(this); | ||||
|         networkChangeSubscription.unsubscribe(); | ||||
|         super.onDestroy(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public IBinder onBind(Intent intent) { | ||||
|         return null; | ||||
| @@ -46,14 +60,16 @@ public class DownloadService extends Service { | ||||
|  | ||||
|     @EventBusHook | ||||
|     public void onEvent(DownloadChaptersEvent event) { | ||||
|         downloadManager.getDownloadsSubject().onNext(event); | ||||
|         EventBus.getDefault().removeStickyEvent(event); | ||||
|         downloadManager.onDownloadChaptersEvent(event); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onDestroy() { | ||||
|         EventBus.getDefault().unregister(this); | ||||
|         super.onDestroy(); | ||||
|     private void listenNetworkChanges() { | ||||
|         IntentFilter intentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION); | ||||
|         networkChangeSubscription = ContentObservable.fromBroadcast(this, intentFilter) | ||||
|                 .subscribe(state -> { | ||||
|                     // TODO | ||||
|                 }); | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -21,7 +21,6 @@ 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; | ||||
| import eu.kanade.mangafeed.ui.adapter.LibraryAdapter; | ||||
| import eu.kanade.mangafeed.ui.fragment.base.BaseRxFragment; | ||||
| @@ -31,7 +30,6 @@ import nucleus.factory.RequiresPresenter; | ||||
| public class LibraryFragment extends BaseRxFragment<LibraryPresenter> { | ||||
|  | ||||
|     @Bind(R.id.gridView) GridView grid; | ||||
|     private MainActivity activity; | ||||
|     private LibraryAdapter adapter; | ||||
|  | ||||
|     public static LibraryFragment newInstance() { | ||||
| @@ -45,8 +43,6 @@ public class LibraryFragment extends BaseRxFragment<LibraryPresenter> { | ||||
|     public void onCreate(Bundle savedInstanceState) { | ||||
|         super.onCreate(savedInstanceState); | ||||
|         setHasOptionsMenu(true); | ||||
|  | ||||
|         activity = (MainActivity)getActivity(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
| @@ -54,7 +50,7 @@ public class LibraryFragment extends BaseRxFragment<LibraryPresenter> { | ||||
|                              Bundle savedInstanceState) { | ||||
|         // Inflate the layout for this fragment | ||||
|         View view = inflater.inflate(R.layout.fragment_library, container, false); | ||||
|         activity.setToolbarTitle(getString(R.string.library_title)); | ||||
|         setToolbarTitle(getString(R.string.library_title)); | ||||
|         ButterKnife.bind(this, view); | ||||
|  | ||||
|         createAdapter(); | ||||
| @@ -73,9 +69,9 @@ public class LibraryFragment extends BaseRxFragment<LibraryPresenter> { | ||||
|     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); | ||||
|                 if (!LibraryUpdateService.isRunning(getActivity())) { | ||||
|                     Intent intent = LibraryUpdateService.getStartIntent(getActivity()); | ||||
|                     getActivity().startService(intent); | ||||
|                 } | ||||
|  | ||||
|                 return true; | ||||
|   | ||||
| @@ -131,8 +131,7 @@ public class MangaChaptersFragment extends BaseRxFragment<MangaChaptersPresenter | ||||
|                 getPresenter().markChaptersRead(getSelectedChapters(), false); | ||||
|                 return true; | ||||
|             case R.id.action_download: | ||||
|                 Intent intent = DownloadService.getStartIntent(getActivity()); | ||||
|                 getActivity().startService(intent); | ||||
|                 DownloadService.start(getActivity()); | ||||
|                 getPresenter().downloadChapters(getSelectedChapters()); | ||||
|                 closeActionMode(); | ||||
|                 return true; | ||||
|   | ||||
| @@ -26,19 +26,12 @@ public class SourceFragment extends BaseRxFragment<SourcePresenter> { | ||||
|  | ||||
|     @Bind(R.id.catalogue_list) ListView source_list; | ||||
|  | ||||
|     private MainActivity activity; | ||||
|     private EasyAdapter<Source> adapter; | ||||
|  | ||||
|     public static SourceFragment newInstance() { | ||||
|         return new SourceFragment(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onCreate(Bundle savedInstanceState) { | ||||
|         super.onCreate(savedInstanceState); | ||||
|         activity = (MainActivity)getActivity(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public View onCreateView(LayoutInflater inflater, ViewGroup container, | ||||
|                              Bundle savedInstanceState) { | ||||
| @@ -56,6 +49,7 @@ public class SourceFragment extends BaseRxFragment<SourcePresenter> { | ||||
|     @OnItemClick(R.id.catalogue_list) | ||||
|     public void onSourceClick(int position) { | ||||
|         Source source = adapter.getItem(position); | ||||
|         MainActivity activity = (MainActivity) getActivity(); | ||||
|  | ||||
|         if (getPresenter().isValidSource(source)) { | ||||
|             CatalogueFragment fragment = CatalogueFragment.newInstance(source.getSourceId()); | ||||
| @@ -66,7 +60,7 @@ public class SourceFragment extends BaseRxFragment<SourcePresenter> { | ||||
|     } | ||||
|  | ||||
|     private void createAdapter() { | ||||
|         adapter = new EasyAdapter<>(activity, SourceHolder.class); | ||||
|         adapter = new EasyAdapter<>(getActivity(), SourceHolder.class); | ||||
|         source_list.setAdapter(adapter); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -7,11 +7,15 @@ import eu.kanade.mangafeed.ui.activity.base.BaseActivity; | ||||
| public class BaseFragment extends Fragment { | ||||
|  | ||||
|     public void setToolbarTitle(String title) { | ||||
|         ((BaseActivity)getActivity()).setToolbarTitle(title); | ||||
|         getBaseActivity().setToolbarTitle(title); | ||||
|     } | ||||
|  | ||||
|     public void setToolbarTitle(int resourceId) { | ||||
|         ((BaseActivity)getActivity()).setToolbarTitle(getString(resourceId)); | ||||
|         getBaseActivity().setToolbarTitle(getString(resourceId)); | ||||
|     } | ||||
|  | ||||
|     public BaseActivity getBaseActivity() { | ||||
|         return (BaseActivity) getActivity(); | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,93 @@ | ||||
| /** | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
| package eu.kanade.mangafeed.util; | ||||
|  | ||||
| import android.content.BroadcastReceiver; | ||||
| import android.content.Context; | ||||
| import android.content.Intent; | ||||
| import android.content.IntentFilter; | ||||
| import android.os.Handler; | ||||
|  | ||||
| import rx.Observable; | ||||
| import rx.Subscriber; | ||||
| import rx.Subscription; | ||||
| import rx.functions.Action0; | ||||
| import rx.subscriptions.Subscriptions; | ||||
|  | ||||
| public final class ContentObservable { | ||||
|     private ContentObservable() { | ||||
|         throw new AssertionError("No instances"); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Create Observable that wraps BroadcastReceiver and emits received intents. | ||||
|      * | ||||
|      * @param filter Selects the Intent broadcasts to be received. | ||||
|      */ | ||||
|     public static Observable<Intent> fromBroadcast(Context context, IntentFilter filter){ | ||||
|         return Observable.create(new OnSubscribeBroadcastRegister(context, filter, null, null)); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Create Observable that wraps BroadcastReceiver and emits received intents. | ||||
|      * | ||||
|      * @param filter Selects the Intent broadcasts to be received. | ||||
|      * @param broadcastPermission String naming a permissions that a | ||||
|      *      broadcaster must hold in order to send an Intent to you.  If null, | ||||
|      *      no permission is required. | ||||
|      * @param schedulerHandler Handler identifying the thread that will receive | ||||
|      *      the Intent.  If null, the main thread of the process will be used. | ||||
|      */ | ||||
|     public static Observable<Intent> fromBroadcast(Context context, IntentFilter filter, String broadcastPermission, Handler schedulerHandler){ | ||||
|         return Observable.create(new OnSubscribeBroadcastRegister(context, filter, broadcastPermission, schedulerHandler)); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     static class OnSubscribeBroadcastRegister implements Observable.OnSubscribe<Intent> { | ||||
|  | ||||
|         private final Context context; | ||||
|         private final IntentFilter intentFilter; | ||||
|         private final String broadcastPermission; | ||||
|         private final Handler schedulerHandler; | ||||
|  | ||||
|         public OnSubscribeBroadcastRegister(Context context, IntentFilter intentFilter, String broadcastPermission, Handler schedulerHandler) { | ||||
|             this.context = context; | ||||
|             this.intentFilter = intentFilter; | ||||
|             this.broadcastPermission = broadcastPermission; | ||||
|             this.schedulerHandler = schedulerHandler; | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public void call(final Subscriber<? super Intent> subscriber) { | ||||
|             final BroadcastReceiver broadcastReceiver = new BroadcastReceiver() { | ||||
|                 @Override | ||||
|                 public void onReceive(Context context, Intent intent) { | ||||
|                     subscriber.onNext(intent); | ||||
|                 } | ||||
|             }; | ||||
|  | ||||
|             final Subscription subscription = Subscriptions.create(new Action0() { | ||||
|                 @Override | ||||
|                 public void call() { | ||||
|                     context.unregisterReceiver(broadcastReceiver); | ||||
|                 } | ||||
|             }); | ||||
|  | ||||
|             subscriber.add(subscription); | ||||
|             context.registerReceiver(broadcastReceiver, intentFilter, broadcastPermission, schedulerHandler); | ||||
|  | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -115,9 +115,7 @@ public final class DiskUtils { | ||||
|     } | ||||
|  | ||||
|     public static File saveBufferedSourceToDirectory(BufferedSource bufferedSource, File directory, String name) throws IOException { | ||||
|         if (!directory.exists() && !directory.mkdirs()) { | ||||
|             throw new IOException("Failed Creating Directory"); | ||||
|         } | ||||
|         createDirectory(directory); | ||||
|  | ||||
|         File writeFile = new File(directory, name); | ||||
|         if (writeFile.exists()) { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user