Added Android code
[wl-app.git] / Android / app / src / main / java / com / moiseum / wolnelektury / view / book / BookPresenter.java
1 package com.moiseum.wolnelektury.view.book;
2
3 import android.os.Bundle;
4 import android.util.Log;
5
6 import com.folioreader.util.AppUtil;
7 import com.moiseum.wolnelektury.base.WLApplication;
8 import com.moiseum.wolnelektury.base.mvp.FragmentPresenter;
9 import com.moiseum.wolnelektury.connection.downloads.FileCacheUtils;
10 import com.moiseum.wolnelektury.connection.downloads.FileDownloadIntentService;
11 import com.moiseum.wolnelektury.connection.models.BookDetailsModel;
12 import com.moiseum.wolnelektury.connection.models.BookModel;
13 import com.moiseum.wolnelektury.connection.models.FavouriteStateModel;
14 import com.moiseum.wolnelektury.connection.models.ReadingStateModel;
15 import com.moiseum.wolnelektury.connection.services.BooksService;
16 import com.moiseum.wolnelektury.events.BookFavouriteEvent;
17 import com.moiseum.wolnelektury.storage.BookStorage;
18 import com.moiseum.wolnelektury.utils.SharedPreferencesUtils;
19 import com.moiseum.wolnelektury.view.book.components.ProgressDownloadButton;
20
21 import org.greenrobot.eventbus.EventBus;
22 import org.greenrobot.eventbus.Subscribe;
23 import org.greenrobot.eventbus.ThreadMode;
24
25 import java.util.ArrayList;
26 import java.util.List;
27
28 import io.reactivex.Completable;
29 import io.reactivex.Single;
30 import io.reactivex.android.schedulers.AndroidSchedulers;
31 import io.reactivex.schedulers.Schedulers;
32
33 /**
34  * Created by Piotr Ostrowski on 17.11.2017.
35  */
36
37 class BookPresenter extends FragmentPresenter<BookView> {
38
39         private static final String TAG = BookPresenter.class.getSimpleName();
40
41         private BookDetailsModel book;
42         private BookModel storedBook;
43         private String bookSlug;
44         private BookType bookType;
45
46         private SharedPreferencesUtils preferences = WLApplication.getInstance().getPreferences();
47         private BooksService booksService = WLApplication.getInstance().getRestClient().obtainBookService();
48         private BookStorage storage = WLApplication.getInstance().getBookStorage();
49
50         BookPresenter(String slug, BookType type, BookView view) {
51                 super(view);
52                 this.bookSlug = slug;
53                 this.bookType = type;
54         }
55
56         @Override
57         public void onViewCreated(Bundle savedInstanceState) {
58                 super.onViewCreated(savedInstanceState);
59                 loadBookDetails();
60                 getView().showPremiumLock(!preferences.isUserPremium() && bookType.shouldShowPremiumLock());
61                 EventBus.getDefault().register(this);
62         }
63
64         @Override
65         public void onResume() {
66                 super.onResume();
67                 this.storedBook = storage.find(bookSlug);
68                 if (storedBook != null && storedBook.isEbookDownloaded()) {
69                         getView().changeDownloadButtonState(ProgressDownloadButton.ProgressDownloadButtonState.STATE_READING, false);
70                         getView().updateReadingProgress(storedBook.getCurrentChapter(), storedBook.getTotalChapters(), false);
71                 }
72                 if (storedBook != null && storedBook.isAudioDownloaded()) {
73                         getView().changeDownloadButtonState(ProgressDownloadButton.ProgressDownloadButtonState.STATE_READING, true);
74                         getView().updateReadingProgress(storedBook.getCurrentAudioChapter(), storedBook.getTotalAudioChapters(), true);
75                 }
76         }
77
78         @Override
79         public void onDestroy() {
80                 super.onDestroy();
81                 EventBus.getDefault().unregister(this);
82         }
83
84         @Subscribe(threadMode = ThreadMode.MAIN)
85         public void onMessageEvent(FileCacheUtils.DownloadProgressEvent event) {
86                 if (event.getFileUrl().equals(book.getEpub())) {
87                         int percentage = (int) ((double) event.getDownloaded() / (double) event.getTotal() * 100.0);
88                         getView().changeDownloadButtonState(ProgressDownloadButton.ProgressDownloadButtonState.STATE_DOWNLOADING, false);
89                         getView().showCurrentStateProgress(percentage, false);
90                 } else if (book.getAudiobookFilesUrls().contains(event.getFileUrl())) {
91                         ArrayList<String> filesUrls = book.getAudiobookFilesUrls();
92                         int fileIndex = filesUrls.indexOf(event.getFileUrl());
93                         double part = (double) event.getDownloaded() / (double) event.getTotal() / (double) filesUrls.size();
94                         double completed = (double) fileIndex / (double) filesUrls.size();
95                         int percentage = (int) ((part + completed) * 100.0);
96                         getView().changeDownloadButtonState(ProgressDownloadButton.ProgressDownloadButtonState.STATE_DOWNLOADING, true);
97                         getView().showCurrentStateProgress(percentage, true);
98                 }
99         }
100
101         @Subscribe(threadMode = ThreadMode.MAIN)
102         public void onMessageEvent(FileDownloadIntentService.DownloadFileEvent event) {
103                 if (event.getFileUrl().equals(book.getEpub())) {
104                         if (event.isSuccess()) {
105                 storeDownloadedBook(false);
106                                 getView().showCurrentStateProgress(0, false);
107                                 getView().changeDownloadButtonState(ProgressDownloadButton.ProgressDownloadButtonState.STATE_READING, false);
108                                 launchFolioReader();
109                         } else {
110                                 getView().showCurrentStateProgress(0, false);
111                                 getView().changeDownloadButtonState(ProgressDownloadButton.ProgressDownloadButtonState.STATE_INITIAL, false);
112                                 getView().showDownloadFileError();
113                         }
114                 } else if (book.getAudiobookFilesUrls().contains(event.getFileUrl())) {
115                         if (event.isSuccess()) {
116                                 ArrayList<String> filesUrls = book.getAudiobookFilesUrls();
117                                 int fileIndex = filesUrls.indexOf(event.getFileUrl());
118                                 if (fileIndex == filesUrls.size() - 1) {
119                     storeDownloadedBook(true);
120                                         getView().showCurrentStateProgress(0, true);
121                                         getView().changeDownloadButtonState(ProgressDownloadButton.ProgressDownloadButtonState.STATE_READING, true);
122                                         launchAudioPlayer();
123                                 } else {
124                                         int percentage = (int) ((fileIndex + 1.0) / ((double) filesUrls.size()) * 100.0);
125                                         getView().changeDownloadButtonState(ProgressDownloadButton.ProgressDownloadButtonState.STATE_DOWNLOADING, true);
126                                         getView().showCurrentStateProgress(percentage, true);
127                                 }
128                         } else {
129                                 getView().showCurrentStateProgress(0, true);
130                                 getView().changeDownloadButtonState(ProgressDownloadButton.ProgressDownloadButtonState.STATE_INITIAL, true);
131                                 getView().showDownloadFileError();
132                         }
133
134                 }
135         }
136
137         void launchEbookForState(ProgressDownloadButton.ProgressDownloadButtonState state) {
138                 if (state.isDownloaded()) {
139                         launchFolioReader();
140                 } else {
141                         getView().changeDownloadButtonState(ProgressDownloadButton.ProgressDownloadButtonState.STATE_DOWNLOADING, false);
142                         FileDownloadIntentService.downloadFile(getView().getContext(), book.getEpub());
143                 }
144         }
145
146         void launchAudiobookForState(ProgressDownloadButton.ProgressDownloadButtonState state) {
147                 if (state.isDownloaded()) {
148                         launchAudioPlayer();
149                 } else {
150                         getView().changeDownloadButtonState(ProgressDownloadButton.ProgressDownloadButtonState.STATE_DOWNLOADING, true);
151                         FileDownloadIntentService.downloadFiles(getView().getContext(), book.getAudiobookFilesUrls());
152                 }
153         }
154
155         void reloadBookDetails() {
156                 loadBookDetails();
157         }
158
159         void deleteEbook() {
160                 addDisposable(FileCacheUtils.deleteEbookFile(book.getEpub())
161                                 .andThen(Completable.fromAction(this::updateStoredBookAfterDeletion))
162                                 .subscribeOn(Schedulers.io())
163                                 .observeOn(AndroidSchedulers.mainThread())
164                                 .subscribe(
165                                                 () -> getView().changeDownloadButtonState(ProgressDownloadButton.ProgressDownloadButtonState.STATE_INITIAL, false),
166                                                 error -> { }
167                                 )
168                 );
169         }
170
171         void deleteAudiobook() {
172                 addDisposable(FileCacheUtils.deleteAudiobookFiles(book.getAudiobookFilesUrls())
173                                 .andThen(Completable.fromAction(this::updateStoredAudiobookAfterDeletion))
174                                 .subscribeOn(Schedulers.io())
175                                 .observeOn(AndroidSchedulers.mainThread())
176                                 .subscribe(
177                                                 () -> getView().changeDownloadButtonState(ProgressDownloadButton.ProgressDownloadButtonState.STATE_INITIAL, true),
178                                                 error -> { }
179                                 )
180                 );
181         }
182
183         void onShareEbookClicked() {
184                 getView().startShareActivity(book.getUrl());
185         }
186
187         void onFavouriteEbookClicked() {
188                 if (!book.getFavouriteState()) {
189                         getView().startLikeClicked();
190                         updateFavouriteState(true);
191                 } else {
192                         getView().stopLikeClicked();
193                         updateFavouriteState(false);
194                 }
195         }
196
197         void onBackFromReader(String bookName, int currentChapter, int count) {
198                 storedBook.setEbookName(bookName);
199                 storedBook.setCurrentChapter(currentChapter);
200                 storedBook.setTotalChapters(count);
201                 storage.update(storedBook);
202                 getView().updateReadingProgress(currentChapter, count, false);
203
204                 if (currentChapter == count && book.getState() == ReadingStateModel.ReadingState.STATE_READING) {
205                         updateReadingState(ReadingStateModel.ReadingState.STATE_COMPLETED);
206                 }
207         }
208
209         // ------------------------------------------------------------------------------------------------------------------------------------------
210         // Helper methods
211         // ------------------------------------------------------------------------------------------------------------------------------------------
212
213         private void updateStoredBookAfterDeletion() {
214                 if (!storedBook.isAudioDownloaded()) {
215                         storage.remove(storedBook.getSlug(), true);
216                         storedBook = null;
217                         return;
218                 }
219
220                 AppUtil.removeBookState(WLApplication.getInstance().getApplicationContext(), storedBook.getEbookName());
221                 storedBook.setEbookName(null);
222                 storedBook.setEbookFileUrl(null);
223                 storedBook.setCurrentChapter(0);
224                 storedBook.setTotalChapters(0);
225                 storage.update(storedBook);
226         }
227
228         private void updateStoredAudiobookAfterDeletion() {
229                 if (!storedBook.isEbookDownloaded()) {
230                         storage.remove(storedBook.getSlug(), true);
231                         storedBook = null;
232                         return;
233                 }
234
235                 storedBook.setAudioFileUrls(null);
236                 storedBook.setCurrentAudioChapter(0);
237                 storedBook.setTotalAudioChapters(0);
238                 storage.update(storedBook);
239         }
240
241         private Single<BookDetailsModel> getBookDetails() {
242                 Single<ReadingStateModel> readingStateSingle = preferences.isUserLoggedIn() ? booksService.getReadingState(bookSlug) : Single.just(new ReadingStateModel());
243                 Single<FavouriteStateModel> favouriteStateModelSingle = preferences.isUserLoggedIn() ? booksService.getFavouriteState(bookSlug) : Single.just(new FavouriteStateModel());
244                 Single<BookDetailsModel> bookDetailsSingle = Single.zip(
245                                 readingStateSingle,
246                                 favouriteStateModelSingle,
247                                 booksService.getBookDetails(bookSlug),
248                                 (readingStateModel, favouriteStateModel, bookDetailsModel) -> {
249                                         bookDetailsModel.setState(readingStateModel.getState());
250                                         bookDetailsModel.setFavouriteState(favouriteStateModel.getState());
251                         return bookDetailsModel;
252                 });
253                 return bookDetailsSingle.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
254         }
255
256         private void loadBookDetails() {
257                 addDisposable(getBookDetails().subscribe(bookDetailsModel -> {
258                         book = bookDetailsModel;
259                         getView().initializeBookView(bookDetailsModel);
260                 }, error -> getView().showInitializationError()));
261         }
262
263         private void updateReadingState(ReadingStateModel.ReadingState state) {
264                 if (preferences.isUserLoggedIn()) {
265                         addDisposable(booksService.setReadingState(bookSlug, state.getStateName())
266                                         .subscribeOn(Schedulers.io())
267                                         .observeOn(AndroidSchedulers.mainThread())
268                                         .subscribe(updatedState -> book.setState(updatedState.getState()), error -> Log.e(TAG, "Failed to update reading state.", error)));
269                 }
270         }
271
272         private void updateFavouriteState(boolean state) {
273                 addDisposable(booksService.setFavouriteState(bookSlug, book.getFavouriteString(state))
274                                 .subscribeOn(Schedulers.io())
275                                 .observeOn(AndroidSchedulers.mainThread())
276                                 .subscribe(updatedState -> {
277                                         book.setFavouriteState(state);
278                                         EventBus.getDefault().post(new BookFavouriteEvent(state));
279                                 }, error -> Log.e(TAG, "Failed to update favourite state.", error)));
280         }
281
282         private void launchFolioReader() {
283                 if (book.getState() == ReadingStateModel.ReadingState.STATE_NOT_STARTED) {
284                         updateReadingState(ReadingStateModel.ReadingState.STATE_READING);
285                 }
286                 String downloadedBookUrl = FileCacheUtils.getCachedFileForUrl(book.getEpub());
287                 if (downloadedBookUrl != null) {
288                         getView().openBook(downloadedBookUrl);
289                 }
290         }
291
292         private void launchAudioPlayer() {
293                 if (book.getState() == ReadingStateModel.ReadingState.STATE_NOT_STARTED) {
294                         updateReadingState(ReadingStateModel.ReadingState.STATE_READING);
295                 }
296                 getView().launchPlayer(book);
297         }
298
299         private void storeDownloadedBook(boolean forAudiobook) {
300                 BookModel stored = storedBook == null ? book.getStorageModel(bookSlug) : storedBook;
301                 if (forAudiobook) {
302                     List<String> mediaUrls = book.getAudiobookFilesUrls();
303                         stored.setAudioFileUrls(mediaUrls);
304                         stored.setTotalAudioChapters(mediaUrls.size());
305                 } else {
306                         stored.setEbookFileUrl(book.getEpub());
307                 }
308                 if (storedBook == null) {
309                         storedBook = stored;
310                         storage.add(storedBook);
311                 } else {
312                         storage.update(stored);
313                 }
314         }
315
316         public void showFavouriteButton(BookDetailsModel book) {
317                 if(preferences.isUserLoggedIn()) {
318                         getView().showFavouriteButton(book);
319                 }
320         }
321 }