From 8ec0a9672b75f74272ebe6fdf512155e7b536bb2 Mon Sep 17 00:00:00 2001 From: Litun Date: Sun, 7 Aug 2016 21:17:45 +0300 Subject: [PATCH] Collage loader implementation --- app/build.gradle | 7 ++ .../java/ru/yandex/yamblz/api/ApiManager.java | 64 +++++++++++++++ .../java/ru/yandex/yamblz/api/ApiService.java | 21 +++++ .../yamblz/loader/SimpleCollageStrategy.java | 46 +++++++++++ .../yamblz/loader/StubCollageLoader.java | 55 ++++++++++++- .../java/ru/yandex/yamblz/model/Artist.java | 59 +++++++++++++ .../java/ru/yandex/yamblz/model/Cover.java | 11 +++ .../java/ru/yandex/yamblz/model/Genre.java | 30 +++++++ .../yamblz/ui/adapters/GenreListAdapter.java | 82 +++++++++++++++++++ .../yamblz/ui/fragments/ContentFragment.java | 46 +++++++++++ app/src/main/res/layout/fragment_content.xml | 14 ++-- app/src/main/res/layout/genre_cover_item.xml | 27 ++++++ dependencies.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 4 +- 14 files changed, 454 insertions(+), 14 deletions(-) create mode 100644 app/src/main/java/ru/yandex/yamblz/api/ApiManager.java create mode 100644 app/src/main/java/ru/yandex/yamblz/api/ApiService.java create mode 100644 app/src/main/java/ru/yandex/yamblz/loader/SimpleCollageStrategy.java create mode 100644 app/src/main/java/ru/yandex/yamblz/model/Artist.java create mode 100644 app/src/main/java/ru/yandex/yamblz/model/Cover.java create mode 100644 app/src/main/java/ru/yandex/yamblz/model/Genre.java create mode 100644 app/src/main/java/ru/yandex/yamblz/ui/adapters/GenreListAdapter.java create mode 100644 app/src/main/res/layout/genre_cover_item.xml diff --git a/app/build.gradle b/app/build.gradle index 445372a..0efdb26 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -84,6 +84,13 @@ android { // Libraries declared in dependencies.gradle in the root of the project dependencies { + + compile 'com.squareup.retrofit2:retrofit:2.1.0' + compile 'com.squareup.retrofit2:converter-gson:2.1.0' + compile 'com.google.code.gson:gson:2.6.2' + compile 'io.reactivex:rxandroid:1.2.1' + compile 'io.reactivex:rxjava:1.1.8' + compile libraries.dagger apt libraries.daggerCompiler diff --git a/app/src/main/java/ru/yandex/yamblz/api/ApiManager.java b/app/src/main/java/ru/yandex/yamblz/api/ApiManager.java new file mode 100644 index 0000000..a162779 --- /dev/null +++ b/app/src/main/java/ru/yandex/yamblz/api/ApiManager.java @@ -0,0 +1,64 @@ +package ru.yandex.yamblz.api; + +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.support.annotation.WorkerThread; + +import java.io.IOException; +import java.util.List; + +import okhttp3.ResponseBody; +import retrofit2.Call; +import retrofit2.Response; +import retrofit2.Retrofit; +import retrofit2.converter.gson.GsonConverterFactory; +import ru.yandex.yamblz.model.Artist; +import rx.Observable; + +/** + * Encapsulates api work. + * Created by Litun on 21.04.2016. + */ +public class ApiManager { + private static final String URL = "http://cache-default06d.cdn.yandex.net/download.cdn.yandex.net/mobilization-2016/"; + private ApiService service; + + public ApiManager() { + + Retrofit retrofit = new Retrofit.Builder() + .baseUrl(URL) + .addConverterFactory(GsonConverterFactory.create()) + .build(); + + service = retrofit.create(ApiService.class); + } + + public Observable> requestArtists() { + return Observable.just(true) + .map(this::loadArtists); + } + + @WorkerThread + private List loadArtists(boolean ignored) { + try { + Response> response = service.listArtist().execute(); + if (response.isSuccessful()) + return response.body(); + } catch (IOException e) { + } + return null; + } + + @WorkerThread + public Bitmap downloadImageSync(String url) { + Call call = service.downloadImage(url); + try { + Response response = call.execute(); + if (response.isSuccessful()) { + return BitmapFactory.decodeStream(response.body().byteStream()); + } + } catch (IOException ignored) { + } + return null; + } +} diff --git a/app/src/main/java/ru/yandex/yamblz/api/ApiService.java b/app/src/main/java/ru/yandex/yamblz/api/ApiService.java new file mode 100644 index 0000000..0b0750d --- /dev/null +++ b/app/src/main/java/ru/yandex/yamblz/api/ApiService.java @@ -0,0 +1,21 @@ +package ru.yandex.yamblz.api; + +import java.util.List; + +import okhttp3.ResponseBody; +import retrofit2.Call; +import retrofit2.http.GET; +import retrofit2.http.Url; +import ru.yandex.yamblz.model.Artist; + +/** + * Created by Litun on 07.08.2016. + */ + +public interface ApiService { + @GET("artists.json") + Call> listArtist(); + + @GET + Call downloadImage(@Url String fileUrl); +} diff --git a/app/src/main/java/ru/yandex/yamblz/loader/SimpleCollageStrategy.java b/app/src/main/java/ru/yandex/yamblz/loader/SimpleCollageStrategy.java new file mode 100644 index 0000000..dbb9203 --- /dev/null +++ b/app/src/main/java/ru/yandex/yamblz/loader/SimpleCollageStrategy.java @@ -0,0 +1,46 @@ +package ru.yandex.yamblz.loader; + +import android.graphics.Bitmap; +import android.graphics.Canvas; + +import java.util.List; + +/** + * Created by Litun on 07.08.2016. + */ +public class SimpleCollageStrategy implements CollageStrategy { + @Override + public Bitmap create(List bitmaps) { + if (bitmaps == null || bitmaps.size() == 0) + return null; + + int width = 400, height = 400; + Bitmap collageBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); + + Canvas collageCanvas = new Canvas(collageBitmap); + + switch (bitmaps.size()) { + case 1: + return bitmaps.get(0); + case 2: + collageCanvas.drawBitmap(bitmaps.get(0), 0f, 0f, null); + collageCanvas.drawBitmap(bitmaps.get(1), width / 2, 0f, null); + break; + case 3: + collageCanvas.drawBitmap(bitmaps.get(0), 0f, 0f, null); + collageCanvas.drawBitmap(bitmaps.get(1), width / 2, 0f, null); + collageCanvas.drawBitmap(bitmaps.get(2), 0, height / 2, null); + break; + default: + case 4: + collageCanvas.drawBitmap(bitmaps.get(0), 0f, 0f, null); + collageCanvas.drawBitmap(bitmaps.get(1), 0f, height / 2, null); + collageCanvas.drawBitmap(bitmaps.get(2), width / 2, 0f, null); + collageCanvas.drawBitmap(bitmaps.get(3), width / 2, height / 2, null); + break; + } + + return collageBitmap; + + } +} diff --git a/app/src/main/java/ru/yandex/yamblz/loader/StubCollageLoader.java b/app/src/main/java/ru/yandex/yamblz/loader/StubCollageLoader.java index 1dc051b..744a715 100644 --- a/app/src/main/java/ru/yandex/yamblz/loader/StubCollageLoader.java +++ b/app/src/main/java/ru/yandex/yamblz/loader/StubCollageLoader.java @@ -1,31 +1,82 @@ package ru.yandex.yamblz.loader; +import android.graphics.Bitmap; +import android.support.annotation.NonNull; +import android.support.annotation.WorkerThread; +import android.support.v4.util.LruCache; import android.widget.ImageView; +import java.lang.ref.WeakReference; import java.util.List; +import java.util.concurrent.Executors; + +import ru.yandex.yamblz.api.ApiManager; +import rx.Observable; +import rx.android.schedulers.AndroidSchedulers; +import rx.schedulers.Schedulers; public class StubCollageLoader implements CollageLoader { + private LruCache, Bitmap> collageCache = new LruCache<>(20); + ApiManager apiManager = new ApiManager(); + private CollageStrategy defaultCollageStrategy; + + public StubCollageLoader(@NonNull CollageStrategy defaultCollageStrategy) { + this.defaultCollageStrategy = defaultCollageStrategy; + } + + public StubCollageLoader() { + this(new SimpleCollageStrategy()); + } + @Override public void loadCollage(List urls, ImageView imageView) { - + loadCollage(urls, imageView, defaultCollageStrategy); } @Override public void loadCollage(List urls, ImageTarget imageTarget) { - + loadCollage(urls, imageTarget, defaultCollageStrategy); } @Override public void loadCollage(List urls, ImageView imageView, CollageStrategy collageStrategy) { + final WeakReference imageViewWeakReference = new WeakReference<>(imageView); + loadCollage(urls, bitmap -> { + ImageView weekImageView = imageViewWeakReference.get(); + if (weekImageView != null) + weekImageView.setImageBitmap(bitmap); + }, collageStrategy); } @Override public void loadCollage(List urls, ImageTarget imageTarget, CollageStrategy collageStrategy) { + Bitmap cachedBitmap = collageCache.get(urls); + if (cachedBitmap == null) + Observable.from(urls) + .take(4) + .subscribeOn(Schedulers.from(Executors.newFixedThreadPool(4))) + .map(this::downloadImage) + .observeOn(Schedulers.io()) + .toList() + .map(collageStrategy::create) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(imageTarget::onLoadBitmap); + else + imageTarget.onLoadBitmap(cachedBitmap); + } + @WorkerThread + private Bitmap downloadImage(String url) { + return apiManager.downloadImageSync(url); } + private static class CollageData { + ImageTarget imageTarget; + CollageStrategy collageStrategy; + Bitmap collage; + } } diff --git a/app/src/main/java/ru/yandex/yamblz/model/Artist.java b/app/src/main/java/ru/yandex/yamblz/model/Artist.java new file mode 100644 index 0000000..ddb1242 --- /dev/null +++ b/app/src/main/java/ru/yandex/yamblz/model/Artist.java @@ -0,0 +1,59 @@ +package ru.yandex.yamblz.model; + +import java.util.ArrayList; +import java.util.Collection; + +/** + * Created by Litun on 21.04.2016. + */ +public class Artist { + + int id; + String name; + Collection genres = new ArrayList<>(); + int tracks; + int albums; + String link; + String description; + Cover cover; + + public int getId() { + return id; + } + + public String getName() { + return name; + } + + public String getSmallCover() { + return cover == null ? null : cover.small; + } + + public Cover getCover() { + return cover; + } + + public String getBigCover() { + return cover == null ? null : cover.big; + } + + public Collection getGenres() { + return genres; + } + + public void setGenres(Collection genres) { + this.genres = genres; + } + + public int getAlbums() { + return albums; + } + + public int getTracks() { + return tracks; + } + + public String getDescription() { + return description; + } +} diff --git a/app/src/main/java/ru/yandex/yamblz/model/Cover.java b/app/src/main/java/ru/yandex/yamblz/model/Cover.java new file mode 100644 index 0000000..ad50edb --- /dev/null +++ b/app/src/main/java/ru/yandex/yamblz/model/Cover.java @@ -0,0 +1,11 @@ +package ru.yandex.yamblz.model; + + +/** + * Created by Litun on 21.04.2016. + */ +public class Cover { + int id; + String small; + String big; +} diff --git a/app/src/main/java/ru/yandex/yamblz/model/Genre.java b/app/src/main/java/ru/yandex/yamblz/model/Genre.java new file mode 100644 index 0000000..9c488d7 --- /dev/null +++ b/app/src/main/java/ru/yandex/yamblz/model/Genre.java @@ -0,0 +1,30 @@ +package ru.yandex.yamblz.model; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by Litun on 20.07.2016. + */ +public class Genre { + + public Genre(String name, Artist artist){ + this.name = name; + this.artist = artist; + } + + String name; + private final Artist artist; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Artist getArtist() { + return artist; + } +} diff --git a/app/src/main/java/ru/yandex/yamblz/ui/adapters/GenreListAdapter.java b/app/src/main/java/ru/yandex/yamblz/ui/adapters/GenreListAdapter.java new file mode 100644 index 0000000..ebd8187 --- /dev/null +++ b/app/src/main/java/ru/yandex/yamblz/ui/adapters/GenreListAdapter.java @@ -0,0 +1,82 @@ +package ru.yandex.yamblz.ui.adapters; + +import android.content.Context; +import android.graphics.drawable.ColorDrawable; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import butterknife.BindView; +import butterknife.ButterKnife; +import ru.yandex.yamblz.R; +import ru.yandex.yamblz.loader.CollageLoaderManager; + +/** + * Created by Litun on 07.08.2016. + */ + +public class GenreListAdapter extends RecyclerView.Adapter { + + private List data = new ArrayList<>(); + private Context context; + + public GenreListAdapter(Context context) { + this.context = context; + } + + public void setData(List data) { + this.data = data; + notifyDataSetChanged(); + } + + @Override + public GenreViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + View view = LayoutInflater.from(context).inflate(R.layout.genre_cover_item, parent, false); + return new GenreViewHolder(view); + } + + @Override + public void onBindViewHolder(GenreViewHolder holder, int position) { + holder.bind(data.get(position)); + } + + @Override + public int getItemCount() { + return data.size(); + } + + class GenreViewHolder extends RecyclerView.ViewHolder { + @BindView(R.id.image) + ImageView cover; + @BindView(R.id.text) + TextView text; + + public GenreViewHolder(View itemView) { + super(itemView); + ButterKnife.bind(this, itemView); + } + + public void bind(GenreCovers covers) { + text.setText(covers.genre); + cover.setImageDrawable(new ColorDrawable(context.getResources().getColor(R.color.white))); + CollageLoaderManager.getLoader().loadCollage(new ArrayList<>(covers.covers), cover); + } + } + + public static class GenreCovers { + public GenreCovers(String genre, Collection covers) { + this.covers = covers; + this.genre = genre; + } + + private String genre; + private Collection covers; + } +} diff --git a/app/src/main/java/ru/yandex/yamblz/ui/fragments/ContentFragment.java b/app/src/main/java/ru/yandex/yamblz/ui/fragments/ContentFragment.java index d46490f..4a734f8 100644 --- a/app/src/main/java/ru/yandex/yamblz/ui/fragments/ContentFragment.java +++ b/app/src/main/java/ru/yandex/yamblz/ui/fragments/ContentFragment.java @@ -3,16 +3,62 @@ import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.ImageView; +import java.util.ArrayList; +import java.util.Collection; + +import butterknife.BindView; import ru.yandex.yamblz.R; +import ru.yandex.yamblz.api.ApiManager; +import ru.yandex.yamblz.loader.CollageLoaderManager; +import ru.yandex.yamblz.loader.SimpleCollageStrategy; +import ru.yandex.yamblz.model.Artist; +import ru.yandex.yamblz.model.Genre; +import ru.yandex.yamblz.ui.adapters.GenreListAdapter; +import rx.Observable; +import rx.android.schedulers.AndroidSchedulers; +import rx.schedulers.Schedulers; public class ContentFragment extends BaseFragment { + + @BindView(R.id.recycler) + RecyclerView recyclerView; + @NonNull @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_content, container, false); } + + @Override + public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + GenreListAdapter adapter = new GenreListAdapter(getContext()); + recyclerView.setAdapter(adapter); + recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); + + ApiManager apiManager = new ApiManager(); + apiManager.requestArtists() + .subscribeOn(Schedulers.io()) + .flatMap(Observable::from) + .flatMap(this::toGenres) + .toMultimap(Genre::getName, genre -> genre.getArtist().getSmallCover()) + .flatMap(map -> Observable.from(map.entrySet())) + .map(entry -> new GenreListAdapter.GenreCovers(entry.getKey(), entry.getValue())) + .toList() + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(adapter::setData); + } + + private Observable toGenres(Artist artist) { + return Observable.from(artist.getGenres()) + .map(genre -> new Genre(genre, artist)); + } } diff --git a/app/src/main/res/layout/fragment_content.xml b/app/src/main/res/layout/fragment_content.xml index 81016ea..ea96658 100644 --- a/app/src/main/res/layout/fragment_content.xml +++ b/app/src/main/res/layout/fragment_content.xml @@ -1,14 +1,10 @@ + android:layout_width="match_parent" + android:layout_height="match_parent"> - - + android:layout_height="match_parent" /> \ No newline at end of file diff --git a/app/src/main/res/layout/genre_cover_item.xml b/app/src/main/res/layout/genre_cover_item.xml new file mode 100644 index 0000000..4dc7b4d --- /dev/null +++ b/app/src/main/res/layout/genre_cover_item.xml @@ -0,0 +1,27 @@ + + + + + + + + + + \ No newline at end of file diff --git a/dependencies.gradle b/dependencies.gradle index d9cb375..d340e7b 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -7,7 +7,7 @@ ext.versions = [ compileSdk : 23, buildTools : '23.0.3', - androidGradlePlugin : '2.2.0-alpha4', + androidGradlePlugin : '2.2.0-alpha6', aptGradlePlugin : '1.8', retrolambdaGradlePlugin : '3.2.5', lombokGradlePlugin : '0.2.3.a2', diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 86a3e94..fc6933b 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Mon May 30 20:17:48 ICT 2016 +#Sun Aug 07 03:22:23 MSK 2016 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.13-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip