mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-10-30 22:07:57 +01:00 
			
		
		
		
	Improve the use and caching of the covers' cache. Use restartables in LibraryPresenter
This commit is contained in:
		| @@ -36,16 +36,24 @@ public class CoverCache { | ||||
|         return !cacheDir.exists() && cacheDir.mkdirs(); | ||||
|     } | ||||
|  | ||||
|     public void save(String thumbnailUrl, LazyHeaders headers) { | ||||
|         save(thumbnailUrl, headers, null); | ||||
|     } | ||||
|  | ||||
|     // Download the cover with Glide (it can avoid repeating requests) and save the file on this cache | ||||
|     public void save(String cover, LazyHeaders headers) { | ||||
|         GlideUrl url = new GlideUrl(cover, headers); | ||||
|     // Optionally, load the image in the given image view when the resource is ready, if not null | ||||
|     public void save(String thumbnailUrl, LazyHeaders headers, ImageView imageView) { | ||||
|         GlideUrl url = new GlideUrl(thumbnailUrl, headers); | ||||
|         Glide.with(context) | ||||
|                 .load(url) | ||||
|                 .downloadOnly(new SimpleTarget<File>() { | ||||
|                     @Override | ||||
|                     public void onResourceReady(File resource, GlideAnimation<? super File> anim) { | ||||
|                         try { | ||||
|                             add(cover, resource); | ||||
|                             add(thumbnailUrl, resource); | ||||
|                             if (imageView != null) { | ||||
|                                 loadFromCache(imageView, resource); | ||||
|                             } | ||||
|                         } catch (IOException e) { | ||||
|                             e.printStackTrace(); | ||||
|                         } | ||||
| @@ -54,8 +62,9 @@ public class CoverCache { | ||||
|     } | ||||
|  | ||||
|     // Copy the cover from Glide's cache to this cache | ||||
|     public void add(String key, File source) throws IOException { | ||||
|         File dest = new File(cacheDir, DiskUtils.hashKeyForDisk(key)); | ||||
|     public void add(String thumbnailUrl, File source) throws IOException { | ||||
|         createCacheDir(); | ||||
|         File dest = new File(cacheDir, DiskUtils.hashKeyForDisk(thumbnailUrl)); | ||||
|         if (dest.exists()) | ||||
|             dest.delete(); | ||||
|  | ||||
| @@ -78,37 +87,29 @@ public class CoverCache { | ||||
|     } | ||||
|  | ||||
|     // Get the cover from cache | ||||
|     public File get(String key) { | ||||
|         return new File(cacheDir, DiskUtils.hashKeyForDisk(key)); | ||||
|     public File get(String thumbnailUrl) { | ||||
|         return new File(cacheDir, DiskUtils.hashKeyForDisk(thumbnailUrl)); | ||||
|     } | ||||
|  | ||||
|     // Delete the cover from cache | ||||
|     public boolean delete(String key) { | ||||
|         File file = new File(cacheDir, DiskUtils.hashKeyForDisk(key)); | ||||
|     public boolean delete(String thumbnailUrl) { | ||||
|         File file = new File(cacheDir, DiskUtils.hashKeyForDisk(thumbnailUrl)); | ||||
|         return file.exists() && file.delete(); | ||||
|     } | ||||
|  | ||||
|     // Load the cover from cache or network if it doesn't exist | ||||
|     public void loadOrFetchInto(ImageView imageView, String cover, LazyHeaders headers) { | ||||
|         File localCover = get(cover); | ||||
|     // Save and load the image from cache | ||||
|     public void saveAndLoadFromCache(ImageView imageView, String thumbnailUrl, LazyHeaders headers) { | ||||
|         File localCover = get(thumbnailUrl); | ||||
|         if (localCover.exists()) { | ||||
|             loadLocalInto(context, imageView, localCover); | ||||
|             loadFromCache(imageView, localCover); | ||||
|         } else { | ||||
|             loadRemoteInto(context, imageView, cover, headers); | ||||
|             save(thumbnailUrl, headers, imageView); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Load the cover from cache | ||||
|     public static void loadLocalInto(Context context, ImageView imageView, String cover) { | ||||
|         File cacheDir = new File(context.getCacheDir(), PARAMETER_CACHE_DIRECTORY); | ||||
|         File localCover = new File(cacheDir, DiskUtils.hashKeyForDisk(cover)); | ||||
|         if (localCover.exists()) { | ||||
|             loadLocalInto(context, imageView, localCover); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Load the cover from the cache directory into the specified image view | ||||
|     private static void loadLocalInto(Context context, ImageView imageView, File file) { | ||||
|     // Helper method to load the cover from the cache directory into the specified image view | ||||
|     // The file must exist | ||||
|     private void loadFromCache(ImageView imageView, File file) { | ||||
|         Glide.with(context) | ||||
|                 .load(file) | ||||
|                 .diskCacheStrategy(DiskCacheStrategy.NONE) | ||||
| @@ -116,9 +117,10 @@ public class CoverCache { | ||||
|                 .into(imageView); | ||||
|     } | ||||
|  | ||||
|     // Load the cover from network into the specified image view. It does NOT save the image in cache | ||||
|     private static void loadRemoteInto(Context context, ImageView imageView, String cover, LazyHeaders headers) { | ||||
|         GlideUrl url = new GlideUrl(cover, headers); | ||||
|     // Helper method to load the cover from network into the specified image view. | ||||
|     // It does NOT save the image in cache | ||||
|     public void loadFromNetwork(ImageView imageView, String thumbnailUrl, LazyHeaders headers) { | ||||
|         GlideUrl url = new GlideUrl(thumbnailUrl, headers); | ||||
|         Glide.with(context) | ||||
|                 .load(url) | ||||
|                 .diskCacheStrategy(DiskCacheStrategy.SOURCE) | ||||
|   | ||||
| @@ -25,7 +25,6 @@ public class DownloadQueue { | ||||
|  | ||||
|     public void remove(Download download) { | ||||
|         queue.remove(download); | ||||
|         download.setStatus(Download.NOT_DOWNLOADED); | ||||
|         download.setStatusSubject(null); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| package eu.kanade.mangafeed.ui.library; | ||||
|  | ||||
| import android.content.Context; | ||||
| import android.view.View; | ||||
| import android.view.ViewGroup; | ||||
| import android.widget.Filter; | ||||
| import android.widget.Filterable; | ||||
|  | ||||
| @@ -14,10 +15,12 @@ public class LibraryAdapter extends EasyAdapter<Manga> implements Filterable { | ||||
|  | ||||
|     List<Manga> mangas; | ||||
|     Filter filter; | ||||
|     private LibraryPresenter presenter; | ||||
|  | ||||
|     public LibraryAdapter(Context context) { | ||||
|         super(context, LibraryHolder.class); | ||||
|     public LibraryAdapter(LibraryFragment fragment) { | ||||
|         super(fragment.getActivity(), LibraryHolder.class); | ||||
|         filter = new LibraryFilter(); | ||||
|         presenter = fragment.getPresenter(); | ||||
|     } | ||||
|  | ||||
|     public void setNewItems(List<Manga> list) { | ||||
| @@ -57,9 +60,16 @@ public class LibraryAdapter extends EasyAdapter<Manga> implements Filterable { | ||||
|  | ||||
|         @Override | ||||
|         public void publishResults(CharSequence constraint, FilterResults results) { | ||||
|             setItems((List<Manga >) results.values); | ||||
|             setItems((List<Manga>) results.values); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     @Override | ||||
|     public View getView(int position, View convertView, ViewGroup parent) { | ||||
|         View view = super.getView(position, convertView, parent); | ||||
|         LibraryHolder holder = (LibraryHolder) view.getTag(); | ||||
|         Manga manga = getItem(position); | ||||
|         holder.loadCover(manga, presenter.sourceManager.get(manga.source), presenter.coverCache); | ||||
|         return view; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -3,6 +3,7 @@ package eu.kanade.mangafeed.ui.library; | ||||
| import android.content.Intent; | ||||
| import android.os.Bundle; | ||||
| import android.support.v7.widget.SearchView; | ||||
| import android.util.SparseBooleanArray; | ||||
| import android.view.ActionMode; | ||||
| import android.view.LayoutInflater; | ||||
| import android.view.Menu; | ||||
| @@ -20,9 +21,10 @@ import butterknife.OnItemClick; | ||||
| import eu.kanade.mangafeed.R; | ||||
| import eu.kanade.mangafeed.data.database.models.Manga; | ||||
| import eu.kanade.mangafeed.data.sync.LibraryUpdateService; | ||||
| import eu.kanade.mangafeed.ui.manga.MangaActivity; | ||||
| import eu.kanade.mangafeed.ui.base.fragment.BaseRxFragment; | ||||
| import eu.kanade.mangafeed.ui.manga.MangaActivity; | ||||
| import nucleus.factory.RequiresPresenter; | ||||
| import rx.Observable; | ||||
|  | ||||
| @RequiresPresenter(LibraryPresenter.class) | ||||
| public class LibraryFragment extends BaseRxFragment<LibraryPresenter> { | ||||
| @@ -31,10 +33,7 @@ public class LibraryFragment extends BaseRxFragment<LibraryPresenter> { | ||||
|     private LibraryAdapter adapter; | ||||
|  | ||||
|     public static LibraryFragment newInstance() { | ||||
|         LibraryFragment fragment = new LibraryFragment(); | ||||
|         Bundle args = new Bundle(); | ||||
|         fragment.setArguments(args); | ||||
|         return fragment; | ||||
|         return new LibraryFragment(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
| @@ -95,7 +94,7 @@ public class LibraryFragment extends BaseRxFragment<LibraryPresenter> { | ||||
|     } | ||||
|  | ||||
|     private void createAdapter() { | ||||
|         adapter = new LibraryAdapter(getActivity()); | ||||
|         adapter = new LibraryAdapter(this); | ||||
|         grid.setAdapter(adapter); | ||||
|     } | ||||
|  | ||||
| @@ -136,7 +135,7 @@ public class LibraryFragment extends BaseRxFragment<LibraryPresenter> { | ||||
|             public boolean onActionItemClicked(ActionMode mode, MenuItem item) { | ||||
|                 switch (item.getItemId()) { | ||||
|                     case R.id.action_delete: | ||||
|                         getPresenter().onDelete(grid.getCheckedItemPositions(), adapter); | ||||
|                         getPresenter().deleteMangas(getSelectedMangas()); | ||||
|                         mode.finish(); | ||||
|                         return true; | ||||
|                 } | ||||
| @@ -150,4 +149,11 @@ public class LibraryFragment extends BaseRxFragment<LibraryPresenter> { | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     private Observable<Manga> getSelectedMangas() { | ||||
|         SparseBooleanArray checkedItems = grid.getCheckedItemPositions(); | ||||
|         return Observable.range(0, checkedItems.size()) | ||||
|                 .map(checkedItems::keyAt) | ||||
|                 .map(adapter::getItem); | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -7,6 +7,7 @@ import android.widget.TextView; | ||||
| import eu.kanade.mangafeed.R; | ||||
| import eu.kanade.mangafeed.data.cache.CoverCache; | ||||
| import eu.kanade.mangafeed.data.database.models.Manga; | ||||
| import eu.kanade.mangafeed.data.source.base.Source; | ||||
| import uk.co.ribot.easyadapter.ItemViewHolder; | ||||
| import uk.co.ribot.easyadapter.PositionInfo; | ||||
| import uk.co.ribot.easyadapter.annotations.LayoutId; | ||||
| @@ -17,11 +18,8 @@ import uk.co.ribot.easyadapter.annotations.ViewId; | ||||
| public class LibraryHolder extends ItemViewHolder<Manga> { | ||||
|  | ||||
|     @ViewId(R.id.thumbnail) ImageView thumbnail; | ||||
|  | ||||
|     @ViewId(R.id.title) TextView title; | ||||
|  | ||||
|     @ViewId(R.id.author) TextView author; | ||||
|  | ||||
|     @ViewId(R.id.unreadText) TextView unreadText; | ||||
|  | ||||
|     public LibraryHolder(View view) { | ||||
| @@ -38,12 +36,11 @@ public class LibraryHolder extends ItemViewHolder<Manga> { | ||||
|         } else { | ||||
|             unreadText.setVisibility(View.GONE); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|         if (manga.thumbnail_url != null) { | ||||
|             CoverCache.loadLocalInto(getContext(), thumbnail, manga.thumbnail_url); | ||||
|         } else { | ||||
|             thumbnail.setImageResource(android.R.color.transparent); | ||||
|         } | ||||
|     public void loadCover(Manga manga, Source source, CoverCache coverCache) { | ||||
|         if (manga.thumbnail_url != null) | ||||
|             coverCache.saveAndLoadFromCache(thumbnail, manga.thumbnail_url, source.getGlideHeaders()); | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -1,15 +1,16 @@ | ||||
| package eu.kanade.mangafeed.ui.library; | ||||
|  | ||||
| import android.os.Bundle; | ||||
| import android.util.SparseBooleanArray; | ||||
|  | ||||
| import javax.inject.Inject; | ||||
|  | ||||
| import eu.kanade.mangafeed.data.cache.CoverCache; | ||||
| import eu.kanade.mangafeed.data.database.DatabaseHelper; | ||||
| import eu.kanade.mangafeed.data.database.models.Manga; | ||||
| import eu.kanade.mangafeed.data.preference.PreferencesHelper; | ||||
| import eu.kanade.mangafeed.data.source.SourceManager; | ||||
| import eu.kanade.mangafeed.ui.base.presenter.BasePresenter; | ||||
| import rx.Observable; | ||||
| import rx.Subscription; | ||||
| import rx.android.schedulers.AndroidSchedulers; | ||||
| import rx.schedulers.Schedulers; | ||||
|  | ||||
| @@ -17,44 +18,28 @@ public class LibraryPresenter extends BasePresenter<LibraryFragment> { | ||||
|  | ||||
|     @Inject DatabaseHelper db; | ||||
|     @Inject PreferencesHelper prefs; | ||||
|     @Inject CoverCache coverCache; | ||||
|     @Inject SourceManager sourceManager; | ||||
|  | ||||
|     private Subscription mFavoriteMangasSubscription; | ||||
|     private Subscription mDeleteMangaSubscription; | ||||
|     private static final int GET_MANGAS = 1; | ||||
|  | ||||
|     @Override | ||||
|     protected void onCreate(Bundle savedState) { | ||||
|         super.onCreate(savedState); | ||||
|  | ||||
|         restartableLatestCache(GET_MANGAS, | ||||
|                 () -> db.getMangasWithUnread().createObservable() | ||||
|                         .subscribeOn(Schedulers.io()) | ||||
|                         .observeOn(AndroidSchedulers.mainThread()), | ||||
|                 LibraryFragment::onNextMangas); | ||||
|  | ||||
|         start(GET_MANGAS); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected void onTakeView(LibraryFragment view) { | ||||
|         super.onTakeView(view); | ||||
|         getFavoriteMangas(); | ||||
|     } | ||||
|  | ||||
|     public void getFavoriteMangas() { | ||||
|         if (mFavoriteMangasSubscription != null) | ||||
|             return; | ||||
|  | ||||
|         add(mFavoriteMangasSubscription = db.getMangasWithUnread().createObservable() | ||||
|     public void deleteMangas(Observable<Manga> selectedMangas) { | ||||
|         add(selectedMangas | ||||
|                 .subscribeOn(Schedulers.io()) | ||||
|                 .observeOn(AndroidSchedulers.mainThread()) | ||||
|                 .compose(deliverLatestCache()) | ||||
|                 .subscribe(this.split(LibraryFragment::onNextMangas))); | ||||
|     } | ||||
|  | ||||
|     public void onDelete(SparseBooleanArray checkedItems, LibraryAdapter adapter) { | ||||
|         if (mDeleteMangaSubscription != null) | ||||
|             remove(mDeleteMangaSubscription); | ||||
|  | ||||
|         add(mDeleteMangaSubscription = Observable.range(0, checkedItems.size()) | ||||
|                 .observeOn(Schedulers.io()) | ||||
|                 .map(checkedItems::keyAt) | ||||
|                 .map(adapter::getItem) | ||||
|                 .map(manga -> { | ||||
|                     manga.favorite = false; | ||||
|                     return manga; | ||||
|                 }) | ||||
|                 .doOnNext(manga -> manga.favorite = false) | ||||
|                 .toList() | ||||
|                 .flatMap(mangas -> db.insertMangas(mangas).createObservable()) | ||||
|                 .subscribe()); | ||||
|   | ||||
| @@ -8,9 +8,12 @@ import android.widget.Button; | ||||
| import android.widget.ImageView; | ||||
| import android.widget.TextView; | ||||
|  | ||||
| import com.bumptech.glide.load.model.LazyHeaders; | ||||
|  | ||||
| import butterknife.Bind; | ||||
| import butterknife.ButterKnife; | ||||
| import eu.kanade.mangafeed.R; | ||||
| import eu.kanade.mangafeed.data.cache.CoverCache; | ||||
| import eu.kanade.mangafeed.data.database.models.Manga; | ||||
| import eu.kanade.mangafeed.ui.base.fragment.BaseRxFragment; | ||||
| import nucleus.factory.RequiresPresenter; | ||||
| @@ -62,8 +65,15 @@ public class MangaInfoFragment extends BaseRxFragment<MangaInfoPresenter> { | ||||
|  | ||||
|         setFavoriteText(manga.favorite); | ||||
|  | ||||
|         getPresenter().coverCache.loadOrFetchInto(mCover, | ||||
|                 manga.thumbnail_url, getPresenter().source.getGlideHeaders()); | ||||
|         if (mCover.getDrawable() == null) { | ||||
|             CoverCache coverCache = getPresenter().coverCache; | ||||
|             LazyHeaders headers = getPresenter().source.getGlideHeaders(); | ||||
|             if (manga.favorite) { | ||||
|                 coverCache.saveAndLoadFromCache(mCover, manga.thumbnail_url, headers); | ||||
|             } else { | ||||
|                 coverCache.loadFromNetwork(mCover, manga.thumbnail_url, headers); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user