From 3a6841940de1cad12313656d76e92b4a267c224e Mon Sep 17 00:00:00 2001 From: katkalof Date: Mon, 8 Aug 2016 19:05:32 +0300 Subject: [PATCH 1/9] let's begin --- app/build.gradle | 8 +- .../java/ru/yandex/yamblz/model/Artist.java | 116 ++++++++++++++++++ .../yandex/yamblz/model/ArtistsService.java | 11 ++ .../java/ru/yandex/yamblz/model/Cover.java | 53 ++++++++ .../java/ru/yandex/yamblz/model/Genre.java | 74 +++++++++++ .../yamblz/ui/adapters/GenreAdapter.java | 80 ++++++++++++ .../yamblz/ui/fragments/ContentFragment.java | 86 ++++++++++++- app/src/main/res/layout/fragment_content.xml | 9 +- app/src/main/res/layout/genres_list_item.xml | 43 +++++++ app/src/main/res/values/colors.xml | 1 + app/src/main/res/values/dimens.xml | 9 ++ app/src/main/res/values/strings.xml | 3 +- dependencies.gradle | 13 +- gradle/wrapper/gradle-wrapper.properties | 4 +- 14 files changed, 495 insertions(+), 15 deletions(-) create mode 100644 app/src/main/java/ru/yandex/yamblz/model/Artist.java create mode 100644 app/src/main/java/ru/yandex/yamblz/model/ArtistsService.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/GenreAdapter.java create mode 100644 app/src/main/res/layout/genres_list_item.xml diff --git a/app/build.gradle b/app/build.gradle index 445372a..16c87c3 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -12,7 +12,7 @@ android { buildToolsVersion versions.buildTools defaultConfig { - applicationId 'ru.yandex.yamblz' + applicationId 'ru.yandex.yamblz_background' minSdkVersion versions.minSdk targetSdkVersion versions.targetSdk versionCode versions.code // Notice that you may want to use BUILD_NUMBER from CI in real project with own CI. @@ -98,6 +98,12 @@ dependencies { compile libraries.timber + compile libraries.retrofit2 + compile libraries.converterGson + compile libraries.rxandroid + compile libraries.rxjava + compile libraries.retrofit2AdapterRx + // Developer tools (Developer Settings) compile libraries.stetho compile libraries.leakCanary 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..b40da17 --- /dev/null +++ b/app/src/main/java/ru/yandex/yamblz/model/Artist.java @@ -0,0 +1,116 @@ +package ru.yandex.yamblz.model; + +import android.os.Parcel; +import android.os.Parcelable; + +import com.google.gson.annotations.SerializedName; + +import java.util.List; + +public class Artist implements Parcelable { + private int id; + private String name; + private List genres; + + @SerializedName("tracks") + private int countTracks; + + @SerializedName("albums") + private int countAlbums; + private String link; + private String description; + private Cover cover; + + public Artist(int id, String name, List genres, int countTracks, int countAlbums, String link, String description, Cover cover) { + this.id = id; + this.name = name; + this.genres = genres; + this.countTracks = countTracks; + this.countAlbums = countAlbums; + this.link = link; + this.description = description; + this.cover = cover; + } + + private Artist() {} + + public List getGenres() { + return genres; + } + + public String getSmallCover() { + return cover.getSmall(); + } + + public String getBigCover() { + return cover.getBig(); + } + + public int getId() { + return id; + } + + public String getName() { + return name; + } + + public int getCountTracks() { + return countTracks; + } + + public int getCountAlbums() { + return countAlbums; + } + + public String getLink() { + return link; + } + + public String getDescription() { + return description; + } + + public Cover getCover() { + return cover; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeValue(id); + dest.writeString(name); + dest.writeList(genres); + dest.writeValue(countTracks); + dest.writeValue(countAlbums); + dest.writeString(link); + dest.writeString(description); + dest.writeParcelable(cover,0); + } + + protected Artist(Parcel in) { + id = (Integer) in.readValue(Integer.class.getClassLoader()); + name = in.readString(); + in.readList(genres,List.class.getClassLoader()); + countTracks = (Integer) in.readValue(Integer.class.getClassLoader()); + countAlbums = (Integer) in.readValue(Integer.class.getClassLoader()); + link = in.readString(); + description = in.readString(); + cover = in.readParcelable(Cover.class.getClassLoader()); + } + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + @Override + public Artist createFromParcel(Parcel source) { + return new Artist(source); + } + + @Override + public Artist[] newArray(int size) { + return new Artist[size]; + } + }; +} diff --git a/app/src/main/java/ru/yandex/yamblz/model/ArtistsService.java b/app/src/main/java/ru/yandex/yamblz/model/ArtistsService.java new file mode 100644 index 0000000..3b93bc7 --- /dev/null +++ b/app/src/main/java/ru/yandex/yamblz/model/ArtistsService.java @@ -0,0 +1,11 @@ +package ru.yandex.yamblz.model; + +import java.util.List; + +import retrofit2.http.GET; +import rx.Observable; + +public interface ArtistsService { + @GET("mobilization-2016/artists.json") + Observable> listArtists(); +} \ No newline at end of file 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..4941d3f --- /dev/null +++ b/app/src/main/java/ru/yandex/yamblz/model/Cover.java @@ -0,0 +1,53 @@ +package ru.yandex.yamblz.model; + +import android.os.Parcel; +import android.os.Parcelable; + +public class Cover implements Parcelable { + private String small; + private String big; + + public Cover(String small, String big) { + this.small = small; + this.big = big; + } + + public Cover() { + } + + public String getSmall() { + return small; + } + + public String getBig() { + return big; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(this.big); + dest.writeString(this.small); + } + + protected Cover(Parcel in) { + big = in.readString(); + small = in.readString(); + } + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + @Override + public Cover createFromParcel(Parcel source) { + return new Cover(source); + } + + @Override + public Cover[] newArray(int size) { + return new Cover[size]; + } + }; +} 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..d857fa2 --- /dev/null +++ b/app/src/main/java/ru/yandex/yamblz/model/Genre.java @@ -0,0 +1,74 @@ +package ru.yandex.yamblz.model; + +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.List; + +public class Genre implements Parcelable { + private String name; + private List artists; + + public Genre(String name, List artists) { + this.name = name; + this.artists = artists; + } + + private Genre() { + } + + public String getName() { + return name; + } + + public List getArtists() { + return artists; + } + + public void addArtist(Artist artist) { + artists.add(artist); + } + + public StringBuilder getArtistString() { + StringBuilder stringBuilder = new StringBuilder(); + if(artists.size()!=0){ + for (int i = 0; i < artists.size()-1; i++) { + stringBuilder.append(artists.get(i).getName()); + stringBuilder.append(", "); + } + stringBuilder.append(artists.get(artists.size()-1).getName()); + stringBuilder.append("."); + } else { + stringBuilder.append("Нет исполнителей данного жанра"); + } + return stringBuilder; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(name); + dest.writeList(artists); + } + + protected Genre(Parcel in) { + name = in.readString(); + in.readList(artists, Artist.class.getClassLoader()); + } + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + @Override + public Genre createFromParcel(Parcel source) { + return new Genre(source); + } + + @Override + public Genre[] newArray(int size) { + return new Genre[size]; + } + }; +} diff --git a/app/src/main/java/ru/yandex/yamblz/ui/adapters/GenreAdapter.java b/app/src/main/java/ru/yandex/yamblz/ui/adapters/GenreAdapter.java new file mode 100644 index 0000000..637e9d8 --- /dev/null +++ b/app/src/main/java/ru/yandex/yamblz/ui/adapters/GenreAdapter.java @@ -0,0 +1,80 @@ +package ru.yandex.yamblz.ui.adapters; + + +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.List; + +import ru.yandex.yamblz.R; +import ru.yandex.yamblz.model.Genre; + + +public class GenreAdapter extends RecyclerView.Adapter { + + + protected boolean mIsLoading = false; + + // Store a member variable for the contacts + private List genres; + + public interface OnItemClickListener { + void onItemClick(View itemView, int position); + } + + // Define listener member variable + private static OnItemClickListener errorListener; + private static OnItemClickListener itemListener; + // Define the listener interface + + + // Pass in the contact array into the constructor + public GenreAdapter(List genres) { + this.genres = genres; + } + + + @Override + public int getItemCount() { + return !(mIsLoading) ? genres.size() : (genres.size() + 1); + } + + + @Override + public void onBindViewHolder(GenreHolder holder, int position) { + //Фото +// String photo = genres.get(position).getSmallCover(); +// this.mImageLoader.load(photo) +// .fit().centerCrop() +// .into(holder).photoView; +// holder.photoView.setp + //Имя + holder.titleView.setText(genres.get(position).getName()); + holder.descView.setText(genres.get(position).getArtistString()); + } + + + @Override + public GenreHolder onCreateViewHolder(ViewGroup parent, int viewType) { + return new GenreHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.genres_list_item, parent, false)); + } + + + public static class GenreHolder extends RecyclerView.ViewHolder { + ImageView photoView; + TextView titleView; + TextView descView; + + public GenreHolder(View itemView) { + super(itemView); + photoView = (ImageView) itemView.findViewById(R.id.item_image); + titleView = (TextView) itemView.findViewById(R.id.item_title); + descView = (TextView) itemView.findViewById(R.id.item_desc); + } + } + +} 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..8ab7205 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 @@ -1,18 +1,96 @@ package ru.yandex.yamblz.ui.fragments; 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 java.util.ArrayList; +import java.util.List; + +import butterknife.BindView; +import retrofit2.Retrofit; +import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory; +import retrofit2.converter.gson.GsonConverterFactory; import ru.yandex.yamblz.R; +import ru.yandex.yamblz.model.Artist; +import ru.yandex.yamblz.model.ArtistsService; +import ru.yandex.yamblz.model.Genre; +import ru.yandex.yamblz.ui.adapters.GenreAdapter; +import rx.Observable; +import rx.android.schedulers.AndroidSchedulers; +import rx.functions.Func1; +import rx.schedulers.Schedulers; public class ContentFragment extends BaseFragment { - @NonNull + @BindView(R.id.genres_list) + RecyclerView recyclerView; + + @Nullable @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + public View onCreateView(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); + + List genres = new ArrayList<>(); + + Retrofit retrofit = new Retrofit.Builder() + .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) + .addConverterFactory(GsonConverterFactory.create()) + .baseUrl("http://download.cdn.yandex.net/") + .build(); + ArtistsService artistsService = retrofit.create(ArtistsService.class); + Observable> artistsObservable = artistsService.listArtists(); + + artistsObservable + .subscribeOn(Schedulers.io()) + .flatMapIterable(artists -> artists) + .flatMap(new Func1>() { + @Override + public Observable call(Artist artist) { + List artistGenres = new ArrayList<>(); + List forGenre = new ArrayList<>(); + forGenre.add(artist); + + for (String genre : artist.getGenres()) { + artistGenres.add(new Genre(genre, forGenre)); + } + return Observable.from(artistGenres); + } + }) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + genre -> { + for (int i = 0; i < genres.size(); i++) { + if (genre.getName().equals(genres.get(i).getName())) { + genres.get(i).addArtist(genre.getArtists().get(0)); + return; + } + } + genres.add(genre); + }, + e -> Log.e("RX ERROR", "not good in download"), + () -> { + recyclerView.requestLayout(); + Log.e("RX ERROR", "goood"); + } + ); + + recyclerView.setAdapter(new GenreAdapter(genres)); + recyclerView.setHasFixedSize(true); + recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); + } + + @Override + public void onDestroy() { + super.onDestroy(); + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_content.xml b/app/src/main/res/layout/fragment_content.xml index 81016ea..5262a62 100644 --- a/app/src/main/res/layout/fragment_content.xml +++ b/app/src/main/res/layout/fragment_content.xml @@ -3,12 +3,11 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - + android:id="@+id/genres_list" + android:scrollbars="vertical"/> \ No newline at end of file diff --git a/app/src/main/res/layout/genres_list_item.xml b/app/src/main/res/layout/genres_list_item.xml new file mode 100644 index 0000000..2fb8def --- /dev/null +++ b/app/src/main/res/layout/genres_list_item.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index d106673..fde3e32 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -3,4 +3,5 @@ #009688 #00796B #03A9F4 + #F2F2F2 diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index 02d4637..07f900e 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -3,4 +3,13 @@ 16dp 16dp + 108dp + 16sp + 8sp + 3dp + 140dp + 8dp + 24dp + 139dp + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7474f04..dde9618 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,4 +1,3 @@ - Yamblz - Hello + Background diff --git a/dependencies.gradle b/dependencies.gradle index d9cb375..e97f66b 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-alpha7', aptGradlePlugin : '1.8', retrolambdaGradlePlugin : '3.2.5', lombokGradlePlugin : '0.2.3.a2', @@ -24,6 +24,10 @@ ext.versions = [ butterKnife : '8.0.1', timber : '4.1.2', + retrofit2 : '2.1.0', + rxandroid : '1.2.1', + rxjava : '1.1.6', + espresso : '2.2.1', stetho : '1.3.1', leakCanary : '1.4-beta2', @@ -67,6 +71,13 @@ ext.libraries = [ butterKnifeCompiler : "com.jakewharton:butterknife-compiler:$versions.butterKnife", timber : "com.jakewharton.timber:timber:$versions.timber", + retrofit2 : "com.squareup.retrofit2:retrofit:$versions.retrofit2", + converterGson : "com.squareup.retrofit2:converter-gson:$versions.retrofit2", + rxandroid : "io.reactivex:rxandroid:$versions.rxandroid", + rxjava : "io.reactivex:rxjava:$versions.rxjava", + retrofit2AdapterRx : "com.squareup.retrofit2:adapter-rxjava:$versions.retrofit2", + + // Developer Tools stetho : "com.facebook.stetho:stetho:$versions.stetho", leakCanary : "com.squareup.leakcanary:leakcanary-android:$versions.leakCanary", diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 86a3e94..06db62e 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 +#Thu Aug 04 02:37:45 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 From d08e925fc6dfba1cf9858271479ddb2c0488636f Mon Sep 17 00:00:00 2001 From: katkalof Date: Thu, 11 Aug 2016 04:24:42 +0300 Subject: [PATCH 2/9] add simple implementation of first task with rx --- app/src/main/java/ru/yandex/yamblz/App.java | 5 - .../yamblz/loader/DefaultCollageLoader.java | 93 +++++++++++++++++++ .../yamblz/loader/SquareCollageStrategy.java | 47 ++++++++++ .../java/ru/yandex/yamblz/model/Genre.java | 17 +++- .../yamblz/ui/adapters/GenreAdapter.java | 11 +-- .../yamblz/ui/fragments/ContentFragment.java | 45 +++++---- dependencies.gradle | 2 +- 7 files changed, 188 insertions(+), 32 deletions(-) create mode 100644 app/src/main/java/ru/yandex/yamblz/loader/DefaultCollageLoader.java create mode 100644 app/src/main/java/ru/yandex/yamblz/loader/SquareCollageStrategy.java diff --git a/app/src/main/java/ru/yandex/yamblz/App.java b/app/src/main/java/ru/yandex/yamblz/App.java index e5f9972..d8d1c41 100644 --- a/app/src/main/java/ru/yandex/yamblz/App.java +++ b/app/src/main/java/ru/yandex/yamblz/App.java @@ -6,8 +6,6 @@ import ru.yandex.yamblz.developer_settings.DevMetricsProxy; import ru.yandex.yamblz.developer_settings.DeveloperSettingsModel; -import ru.yandex.yamblz.handler.CriticalSectionsManager; -import ru.yandex.yamblz.loader.CollageLoaderManager; import timber.log.Timber; public class App extends Application { @@ -33,9 +31,6 @@ public void onCreate() { DevMetricsProxy devMetricsProxy = applicationComponent.devMetricsProxy(); devMetricsProxy.apply(); } - - CollageLoaderManager.init(null); // add implementation - CriticalSectionsManager.init(null); // add implementation } @NonNull diff --git a/app/src/main/java/ru/yandex/yamblz/loader/DefaultCollageLoader.java b/app/src/main/java/ru/yandex/yamblz/loader/DefaultCollageLoader.java new file mode 100644 index 0000000..d6244ce --- /dev/null +++ b/app/src/main/java/ru/yandex/yamblz/loader/DefaultCollageLoader.java @@ -0,0 +1,93 @@ +package ru.yandex.yamblz.loader; + +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.util.Log; +import android.widget.ImageView; + +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.HashMap; +import java.util.List; + +import rx.Observable; +import rx.Subscription; +import rx.android.schedulers.AndroidSchedulers; +import rx.schedulers.Schedulers; +import rx.subscriptions.CompositeSubscription; + +public class DefaultCollageLoader implements CollageLoader { + private CollageStrategy defaultStrategy = new SquareCollageStrategy(); + private CompositeSubscription compositeSubscription; + private HashMap subscriptionsMap = new HashMap<>(); + + + // Начитался https://habrahabr.ru/post/265997/ + public DefaultCollageLoader(CompositeSubscription compositeSubscription) { + this.compositeSubscription = compositeSubscription; + } + + @Override + public void loadCollage(List urls, ImageView imageView) { + loadCollage(urls, imageView, defaultStrategy); + } + + @Override + public void loadCollage(List urls, ImageTarget imageTarget) { + loadCollage(urls, imageTarget, defaultStrategy); + } + + @Override + public void loadCollage(List urls, ImageView imageView, + CollageStrategy collageStrategy) { + if (subscriptionsMap.containsKey(imageView)) { + compositeSubscription.remove(subscriptionsMap.get(imageView)); + } + Subscription loading = Observable.from(urls) + .observeOn(Schedulers.io()) + .map(this::getBitmapFromURL) + .toList() + .doOnNext(t -> + Log.e("THREAD", " " + Thread.currentThread().toString())) + .map(collageStrategy::create) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + imageView::setImageBitmap, + e -> Log.e("E:DefaultCollageLoader", "Collage downloading failed: " + e), + () -> Log.d("D:DefaultCollageLoader", "Collage downloading completed") + ); + subscriptionsMap.put(imageView, loading); + compositeSubscription.add(loading); + } + + @Override + public void loadCollage(List urls, ImageTarget imageTarget, + CollageStrategy collageStrategy) { + + } + + private Bitmap getBitmapFromURL(String src) { + InputStream inputStream = null; + HttpURLConnection connection = null; + try { + connection = (HttpURLConnection) new URL(src).openConnection(); + connection.setDoInput(true); + connection.connect(); + inputStream = connection.getInputStream(); + return BitmapFactory.decodeStream(inputStream); + } catch (IOException e) { + Log.e("E:DefaultCollageLoader", "Image downloading failed: " + src); + return null; + } finally { + //Что-то мне не совсем это нравится, прокомментируешь? + try { + connection.disconnect(); + inputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } +} diff --git a/app/src/main/java/ru/yandex/yamblz/loader/SquareCollageStrategy.java b/app/src/main/java/ru/yandex/yamblz/loader/SquareCollageStrategy.java new file mode 100644 index 0000000..8f5bfc0 --- /dev/null +++ b/app/src/main/java/ru/yandex/yamblz/loader/SquareCollageStrategy.java @@ -0,0 +1,47 @@ +package ru.yandex.yamblz.loader; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Rect; + +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import static android.graphics.Bitmap.Config.ARGB_8888; + +class SquareCollageStrategy implements CollageStrategy { + @Override + public Bitmap create(List bitmaps) { + + if (bitmaps.isEmpty()) { + return null; + } + + Collections.shuffle(bitmaps); + + if (bitmaps.size() < 4) { + return bitmaps.get(0); + } + + int imgPerSide = (int) Math.sqrt(bitmaps.size()); + int totalSize = bitmaps.get(0).getWidth(); + int imgSize = totalSize / imgPerSide; + + Bitmap collage = Bitmap.createBitmap(totalSize, totalSize, ARGB_8888); + Canvas canvas = new Canvas(collage); + + Iterator iterator = bitmaps.iterator(); + for (int row = 0; row < imgPerSide; row++) { + for (int col = 0; col < imgPerSide; col++) { + int left = col * imgSize; + int top = row * imgSize; + int right = totalSize - (imgPerSide - 1 - col) * imgSize; + int bottom = totalSize - (imgPerSide - 1 - row) * imgSize; + canvas.drawBitmap(iterator.next(), null, new Rect(left, top, right, bottom), null); + } + } + + return collage; + } +} diff --git a/app/src/main/java/ru/yandex/yamblz/model/Genre.java b/app/src/main/java/ru/yandex/yamblz/model/Genre.java index d857fa2..d7449fe 100644 --- a/app/src/main/java/ru/yandex/yamblz/model/Genre.java +++ b/app/src/main/java/ru/yandex/yamblz/model/Genre.java @@ -3,15 +3,20 @@ import android.os.Parcel; import android.os.Parcelable; +import java.util.ArrayList; import java.util.List; public class Genre implements Parcelable { private String name; private List artists; + private List artistsSmallPhotos = new ArrayList<>(); public Genre(String name, List artists) { this.name = name; this.artists = artists; + for (Artist a : artists) { + artistsSmallPhotos.add(a.getSmallCover()); + } } private Genre() { @@ -27,16 +32,17 @@ public List getArtists() { public void addArtist(Artist artist) { artists.add(artist); + artistsSmallPhotos.add(artist.getSmallCover()); } public StringBuilder getArtistString() { StringBuilder stringBuilder = new StringBuilder(); - if(artists.size()!=0){ - for (int i = 0; i < artists.size()-1; i++) { + if (artists.size() != 0) { + for (int i = 0; i < artists.size() - 1; i++) { stringBuilder.append(artists.get(i).getName()); stringBuilder.append(", "); } - stringBuilder.append(artists.get(artists.size()-1).getName()); + stringBuilder.append(artists.get(artists.size() - 1).getName()); stringBuilder.append("."); } else { stringBuilder.append("Нет исполнителей данного жанра"); @@ -44,6 +50,10 @@ public StringBuilder getArtistString() { return stringBuilder; } + public List getArtistsPhotos() { + return artistsSmallPhotos; + } + @Override public int describeContents() { return 0; @@ -53,6 +63,7 @@ public int describeContents() { public void writeToParcel(Parcel dest, int flags) { dest.writeString(name); dest.writeList(artists); + dest.writeList(artistsSmallPhotos); } protected Genre(Parcel in) { diff --git a/app/src/main/java/ru/yandex/yamblz/ui/adapters/GenreAdapter.java b/app/src/main/java/ru/yandex/yamblz/ui/adapters/GenreAdapter.java index 637e9d8..b6031e4 100644 --- a/app/src/main/java/ru/yandex/yamblz/ui/adapters/GenreAdapter.java +++ b/app/src/main/java/ru/yandex/yamblz/ui/adapters/GenreAdapter.java @@ -11,6 +11,7 @@ import java.util.List; import ru.yandex.yamblz.R; +import ru.yandex.yamblz.loader.CollageLoaderManager; import ru.yandex.yamblz.model.Genre; @@ -46,15 +47,11 @@ public int getItemCount() { @Override public void onBindViewHolder(GenreHolder holder, int position) { - //Фото -// String photo = genres.get(position).getSmallCover(); -// this.mImageLoader.load(photo) -// .fit().centerCrop() -// .into(holder).photoView; -// holder.photoView.setp - //Имя + holder.photoView.setImageDrawable(null); holder.titleView.setText(genres.get(position).getName()); holder.descView.setText(genres.get(position).getArtistString()); + CollageLoaderManager.getLoader() + .loadCollage(genres.get(position).getArtistsPhotos(), holder.photoView); } 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 8ab7205..50b1aa0 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 @@ -17,6 +17,9 @@ import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory; import retrofit2.converter.gson.GsonConverterFactory; import ru.yandex.yamblz.R; +import ru.yandex.yamblz.handler.CriticalSectionsManager; +import ru.yandex.yamblz.loader.CollageLoaderManager; +import ru.yandex.yamblz.loader.DefaultCollageLoader; import ru.yandex.yamblz.model.Artist; import ru.yandex.yamblz.model.ArtistsService; import ru.yandex.yamblz.model.Genre; @@ -25,11 +28,21 @@ import rx.android.schedulers.AndroidSchedulers; import rx.functions.Func1; import rx.schedulers.Schedulers; +import rx.subscriptions.CompositeSubscription; public class ContentFragment extends BaseFragment { @BindView(R.id.genres_list) RecyclerView recyclerView; + private CompositeSubscription compositeSubscription = new CompositeSubscription(); + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + CollageLoaderManager.init(new DefaultCollageLoader(compositeSubscription)); + CriticalSectionsManager.init(null); // add implementation + } + @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { @@ -45,6 +58,7 @@ public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { Retrofit retrofit = new Retrofit.Builder() .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .addConverterFactory(GsonConverterFactory.create()) + //http://download.cdn.yandex.net/mobilization-2016/artists.json .baseUrl("http://download.cdn.yandex.net/") .build(); ArtistsService artistsService = retrofit.create(ArtistsService.class); @@ -53,19 +67,7 @@ public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { artistsObservable .subscribeOn(Schedulers.io()) .flatMapIterable(artists -> artists) - .flatMap(new Func1>() { - @Override - public Observable call(Artist artist) { - List artistGenres = new ArrayList<>(); - List forGenre = new ArrayList<>(); - forGenre.add(artist); - - for (String genre : artist.getGenres()) { - artistGenres.add(new Genre(genre, forGenre)); - } - return Observable.from(artistGenres); - } - }) + .flatMap(genresWithArtist) .observeOn(AndroidSchedulers.mainThread()) .subscribe( genre -> { @@ -77,13 +79,12 @@ public Observable call(Artist artist) { } genres.add(genre); }, - e -> Log.e("RX ERROR", "not good in download"), + e -> Log.e("RX artists->genres ", "not good in download" + e), () -> { recyclerView.requestLayout(); - Log.e("RX ERROR", "goood"); + Log.e("RX artists->genres ", "goood"); } ); - recyclerView.setAdapter(new GenreAdapter(genres)); recyclerView.setHasFixedSize(true); recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); @@ -92,5 +93,17 @@ public Observable call(Artist artist) { @Override public void onDestroy() { super.onDestroy(); + compositeSubscription.unsubscribe(); } + + private Func1> genresWithArtist = artist -> { + List artistGenres = new ArrayList<>(); + List forGenre = new ArrayList<>(); + forGenre.add(artist); + + for (String genre : artist.getGenres()) { + artistGenres.add(new Genre(genre, forGenre)); + } + return Observable.from(artistGenres); + }; } \ No newline at end of file diff --git a/dependencies.gradle b/dependencies.gradle index e97f66b..19413bd 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -7,7 +7,7 @@ ext.versions = [ compileSdk : 23, buildTools : '23.0.3', - androidGradlePlugin : '2.2.0-alpha7', + androidGradlePlugin : '2.2.0-beta1', aptGradlePlugin : '1.8', retrolambdaGradlePlugin : '3.2.5', lombokGradlePlugin : '0.2.3.a2', From a37328ea7b1b4997a0c4edde795bf0b22c133040 Mon Sep 17 00:00:00 2001 From: katkalof Date: Thu, 11 Aug 2016 05:07:41 +0300 Subject: [PATCH 3/9] add SimpleImageTarget implementation=) add full realise DefaultCollageLoader for ImageTarget --- .../yamblz/loader/DefaultCollageLoader.java | 31 ++++++++++++------- .../yamblz/loader/SimpleImageTarget.java | 10 ++++++ 2 files changed, 29 insertions(+), 12 deletions(-) create mode 100644 app/src/main/java/ru/yandex/yamblz/loader/SimpleImageTarget.java diff --git a/app/src/main/java/ru/yandex/yamblz/loader/DefaultCollageLoader.java b/app/src/main/java/ru/yandex/yamblz/loader/DefaultCollageLoader.java index d6244ce..41fba4a 100644 --- a/app/src/main/java/ru/yandex/yamblz/loader/DefaultCollageLoader.java +++ b/app/src/main/java/ru/yandex/yamblz/loader/DefaultCollageLoader.java @@ -15,13 +15,14 @@ import rx.Observable; import rx.Subscription; import rx.android.schedulers.AndroidSchedulers; +import rx.functions.Action1; import rx.schedulers.Schedulers; import rx.subscriptions.CompositeSubscription; public class DefaultCollageLoader implements CollageLoader { private CollageStrategy defaultStrategy = new SquareCollageStrategy(); private CompositeSubscription compositeSubscription; - private HashMap subscriptionsMap = new HashMap<>(); + private HashMap subscriptionsMap = new HashMap<>(); // Начитался https://habrahabr.ru/post/265997/ @@ -42,8 +43,21 @@ public void loadCollage(List urls, ImageTarget imageTarget) { @Override public void loadCollage(List urls, ImageView imageView, CollageStrategy collageStrategy) { - if (subscriptionsMap.containsKey(imageView)) { - compositeSubscription.remove(subscriptionsMap.get(imageView)); + loadCollage(urls, imageView, bitmap -> imageView.setImageBitmap((Bitmap) bitmap), collageStrategy); + } + + @Override + public void loadCollage(List urls, ImageTarget imageTarget, + CollageStrategy collageStrategy) { + loadCollage(urls, imageTarget, bitmap -> imageTarget.onLoadBitmap((Bitmap) bitmap), collageStrategy); + } + + //Работает, но такое ощущение, что можно лучше) хочу критики + private void loadCollage(List urls, Object o, Action1 setBitmap, + CollageStrategy collageStrategy) { + if (subscriptionsMap.containsKey(o)) { + compositeSubscription.remove(subscriptionsMap.get(o)); + subscriptionsMap.remove(o); } Subscription loading = Observable.from(urls) .observeOn(Schedulers.io()) @@ -53,21 +67,14 @@ public void loadCollage(List urls, ImageView imageView, Log.e("THREAD", " " + Thread.currentThread().toString())) .map(collageStrategy::create) .observeOn(AndroidSchedulers.mainThread()) - .subscribe( - imageView::setImageBitmap, + .subscribe(setBitmap, e -> Log.e("E:DefaultCollageLoader", "Collage downloading failed: " + e), () -> Log.d("D:DefaultCollageLoader", "Collage downloading completed") ); - subscriptionsMap.put(imageView, loading); + subscriptionsMap.put(o, loading); compositeSubscription.add(loading); } - @Override - public void loadCollage(List urls, ImageTarget imageTarget, - CollageStrategy collageStrategy) { - - } - private Bitmap getBitmapFromURL(String src) { InputStream inputStream = null; HttpURLConnection connection = null; diff --git a/app/src/main/java/ru/yandex/yamblz/loader/SimpleImageTarget.java b/app/src/main/java/ru/yandex/yamblz/loader/SimpleImageTarget.java new file mode 100644 index 0000000..9f7d888 --- /dev/null +++ b/app/src/main/java/ru/yandex/yamblz/loader/SimpleImageTarget.java @@ -0,0 +1,10 @@ +package ru.yandex.yamblz.loader; + +import android.graphics.Bitmap; + +public class SimpleImageTarget implements ImageTarget { + + @Override + public void onLoadBitmap(Bitmap bitmap) { + } +} From 7b37b569e037dc2d749dd6db0a226e158d792c0d Mon Sep 17 00:00:00 2001 From: katkalof Date: Thu, 11 Aug 2016 05:19:53 +0300 Subject: [PATCH 4/9] forgot about weak references --- .../ru/yandex/yamblz/loader/DefaultCollageLoader.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/ru/yandex/yamblz/loader/DefaultCollageLoader.java b/app/src/main/java/ru/yandex/yamblz/loader/DefaultCollageLoader.java index 41fba4a..ea4cafd 100644 --- a/app/src/main/java/ru/yandex/yamblz/loader/DefaultCollageLoader.java +++ b/app/src/main/java/ru/yandex/yamblz/loader/DefaultCollageLoader.java @@ -9,8 +9,8 @@ import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; -import java.util.HashMap; import java.util.List; +import java.util.WeakHashMap; import rx.Observable; import rx.Subscription; @@ -22,7 +22,7 @@ public class DefaultCollageLoader implements CollageLoader { private CollageStrategy defaultStrategy = new SquareCollageStrategy(); private CompositeSubscription compositeSubscription; - private HashMap subscriptionsMap = new HashMap<>(); + private WeakHashMap subscriptionsMap = new WeakHashMap<>(); // Начитался https://habrahabr.ru/post/265997/ @@ -60,7 +60,7 @@ private void loadCollage(List urls, Object o, Action1 setBitmap, subscriptionsMap.remove(o); } Subscription loading = Observable.from(urls) - .observeOn(Schedulers.io()) + .subscribeOn(Schedulers.io()) .map(this::getBitmapFromURL) .toList() .doOnNext(t -> @@ -97,4 +97,4 @@ private Bitmap getBitmapFromURL(String src) { } } } -} +} \ No newline at end of file From 6bbb3cbe1aaffe8d8ca9e2a42b63515b3614dc0b Mon Sep 17 00:00:00 2001 From: katkalof Date: Thu, 11 Aug 2016 11:20:28 +0300 Subject: [PATCH 5/9] add Collage Loader Implementation, add critical section for downloading collage of image --- .../DefaultCriticalSectionsHandler.java | 93 +++++++++++++++++++ .../yamblz/loader/DefaultCollageLoader.java | 3 +- .../yamblz/ui/adapters/GenreAdapter.java | 14 ++- .../yamblz/ui/fragments/ContentFragment.java | 29 +++++- 4 files changed, 135 insertions(+), 4 deletions(-) create mode 100644 app/src/main/java/ru/yandex/yamblz/handler/DefaultCriticalSectionsHandler.java diff --git a/app/src/main/java/ru/yandex/yamblz/handler/DefaultCriticalSectionsHandler.java b/app/src/main/java/ru/yandex/yamblz/handler/DefaultCriticalSectionsHandler.java new file mode 100644 index 0000000..a6c259a --- /dev/null +++ b/app/src/main/java/ru/yandex/yamblz/handler/DefaultCriticalSectionsHandler.java @@ -0,0 +1,93 @@ +package ru.yandex.yamblz.handler; + +import android.os.Handler; + +import java.util.Collections; +import java.util.Queue; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; + +public class DefaultCriticalSectionsHandler implements CriticalSectionsHandler { + private final Handler uiThreadHandler; + private final Queue tasks = new ConcurrentLinkedQueue<>(); + private final Queue featureTasks = new ConcurrentLinkedQueue<>(); + private final Set sections = Collections.newSetFromMap(new ConcurrentHashMap<>()); + + public DefaultCriticalSectionsHandler(Handler uiThreadHandler) { + this.uiThreadHandler = uiThreadHandler; + } + + @Override + public void startSection(int id) { + sections.add(id); + } + + @Override + public void stopSection(int id) { + sections.remove(id); + if (sections.isEmpty()) { + runTasks(); + } + } + + @Override + public void stopSections() { + for (Integer section : sections) { + stopSection(section); + } + runTasks(); + } + + @Override + public void postLowPriorityTask(Task task) { + if (featureTasks.contains(task)) { + featureTasks.remove(task); + } + if (sections.isEmpty()) { + runTask(task); + } else { + tasks.add(task); + } + } + + @Override + public void postLowPriorityTaskDelayed(Task task, int delay) { + if (delay <= 0) { + postLowPriorityTask(task); + } else { + featureTasks.add(task); + uiThreadHandler.postDelayed(() -> postLowPriorityTask(task), delay); + } + } + + @Override + public void removeLowPriorityTask(Task task) { + tasks.remove(task); + if (featureTasks.contains(task)) { + uiThreadHandler.removeCallbacks(() -> postLowPriorityTask(task)); + featureTasks.remove(task); + } + } + + @Override + public void removeLowPriorityTasks() { + for (Task task : tasks) { + removeLowPriorityTask(task); + } + for (Task task : featureTasks) { + removeLowPriorityTask(task); + } + } + + private void runTasks() { + for (Task task : tasks) { + runTask(task); + } + } + + private void runTask(Task task) { + tasks.remove(task); + uiThreadHandler.post(task::run); + } +} diff --git a/app/src/main/java/ru/yandex/yamblz/loader/DefaultCollageLoader.java b/app/src/main/java/ru/yandex/yamblz/loader/DefaultCollageLoader.java index ea4cafd..2300514 100644 --- a/app/src/main/java/ru/yandex/yamblz/loader/DefaultCollageLoader.java +++ b/app/src/main/java/ru/yandex/yamblz/loader/DefaultCollageLoader.java @@ -10,6 +10,7 @@ import java.net.HttpURLConnection; import java.net.URL; import java.util.List; +import java.util.Map; import java.util.WeakHashMap; import rx.Observable; @@ -22,7 +23,7 @@ public class DefaultCollageLoader implements CollageLoader { private CollageStrategy defaultStrategy = new SquareCollageStrategy(); private CompositeSubscription compositeSubscription; - private WeakHashMap subscriptionsMap = new WeakHashMap<>(); + private Map subscriptionsMap = new WeakHashMap<>(); // Начитался https://habrahabr.ru/post/265997/ diff --git a/app/src/main/java/ru/yandex/yamblz/ui/adapters/GenreAdapter.java b/app/src/main/java/ru/yandex/yamblz/ui/adapters/GenreAdapter.java index b6031e4..ecbd2b9 100644 --- a/app/src/main/java/ru/yandex/yamblz/ui/adapters/GenreAdapter.java +++ b/app/src/main/java/ru/yandex/yamblz/ui/adapters/GenreAdapter.java @@ -8,14 +8,20 @@ import android.widget.ImageView; import android.widget.TextView; +import java.lang.ref.WeakReference; import java.util.List; +import java.util.Map; +import java.util.WeakHashMap; import ru.yandex.yamblz.R; +import ru.yandex.yamblz.handler.CriticalSectionsManager; +import ru.yandex.yamblz.handler.Task; import ru.yandex.yamblz.loader.CollageLoaderManager; import ru.yandex.yamblz.model.Genre; public class GenreAdapter extends RecyclerView.Adapter { + private Map> loadingTasks = new WeakHashMap<>(); protected boolean mIsLoading = false; @@ -50,8 +56,14 @@ public void onBindViewHolder(GenreHolder holder, int position) { holder.photoView.setImageDrawable(null); holder.titleView.setText(genres.get(position).getName()); holder.descView.setText(genres.get(position).getArtistString()); - CollageLoaderManager.getLoader() + if (loadingTasks.containsKey(holder.photoView)) { + CriticalSectionsManager.getHandler() + .removeLowPriorityTask(loadingTasks.get(holder.photoView).get()); + } + Task task = () -> CollageLoaderManager.getLoader() .loadCollage(genres.get(position).getArtistsPhotos(), holder.photoView); + CriticalSectionsManager.getHandler().postLowPriorityTask(task); + loadingTasks.put(holder.photoView, new WeakReference<>(task)); } 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 50b1aa0..b83129a 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 @@ -1,6 +1,7 @@ package ru.yandex.yamblz.ui.fragments; import android.os.Bundle; +import android.os.Handler; import android.support.annotation.Nullable; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; @@ -18,6 +19,7 @@ import retrofit2.converter.gson.GsonConverterFactory; import ru.yandex.yamblz.R; import ru.yandex.yamblz.handler.CriticalSectionsManager; +import ru.yandex.yamblz.handler.DefaultCriticalSectionsHandler; import ru.yandex.yamblz.loader.CollageLoaderManager; import ru.yandex.yamblz.loader.DefaultCollageLoader; import ru.yandex.yamblz.model.Artist; @@ -25,11 +27,14 @@ import ru.yandex.yamblz.model.Genre; import ru.yandex.yamblz.ui.adapters.GenreAdapter; import rx.Observable; +import rx.Subscription; import rx.android.schedulers.AndroidSchedulers; import rx.functions.Func1; import rx.schedulers.Schedulers; import rx.subscriptions.CompositeSubscription; +import static android.os.Looper.getMainLooper; + public class ContentFragment extends BaseFragment { @BindView(R.id.genres_list) RecyclerView recyclerView; @@ -40,7 +45,7 @@ public class ContentFragment extends BaseFragment { public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); CollageLoaderManager.init(new DefaultCollageLoader(compositeSubscription)); - CriticalSectionsManager.init(null); // add implementation + CriticalSectionsManager.init(new DefaultCriticalSectionsHandler(new Handler(getMainLooper()))); } @Nullable @@ -64,7 +69,7 @@ public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { ArtistsService artistsService = retrofit.create(ArtistsService.class); Observable> artistsObservable = artistsService.listArtists(); - artistsObservable + Subscription artistsSubscription = artistsObservable .subscribeOn(Schedulers.io()) .flatMapIterable(artists -> artists) .flatMap(genresWithArtist) @@ -85,15 +90,35 @@ public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { Log.e("RX artists->genres ", "goood"); } ); + compositeSubscription.add(artistsSubscription); recyclerView.setAdapter(new GenreAdapter(genres)); recyclerView.setHasFixedSize(true); recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); + recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { + int sectionNumber = 0; + + @Override + public void onScrollStateChanged(RecyclerView recyclerView, int newState) { + super.onScrollStateChanged(recyclerView, newState); + if (newState == RecyclerView.SCROLL_STATE_DRAGGING) { + //входим в критическую секцию, все остальные долой + CriticalSectionsManager.getHandler().startSection(sectionNumber++); + } + //Там еще при fling выпадает SCROLL_STATE_SETTLING, а за сразу SCROLL_STATE_IDLE + //Так что обрабатываем вот так строго + if (newState == RecyclerView.SCROLL_STATE_IDLE) { + //остановили скролл, пора и картинки погрузить + CriticalSectionsManager.getHandler().stopSections(); + } + } + }); } @Override public void onDestroy() { super.onDestroy(); compositeSubscription.unsubscribe(); + CriticalSectionsManager.getHandler().removeLowPriorityTasks(); } private Func1> genresWithArtist = artist -> { From cf7da3f45d03ec383651b0aa9bfffa2a86e79b5b Mon Sep 17 00:00:00 2001 From: katkalof Date: Thu, 11 Aug 2016 12:51:57 +0300 Subject: [PATCH 6/9] add collage caching by lru --- .../yamblz/loader/DefaultCollageLoader.java | 41 +++++++++++++++---- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/ru/yandex/yamblz/loader/DefaultCollageLoader.java b/app/src/main/java/ru/yandex/yamblz/loader/DefaultCollageLoader.java index 2300514..1179316 100644 --- a/app/src/main/java/ru/yandex/yamblz/loader/DefaultCollageLoader.java +++ b/app/src/main/java/ru/yandex/yamblz/loader/DefaultCollageLoader.java @@ -2,6 +2,7 @@ import android.graphics.Bitmap; import android.graphics.BitmapFactory; +import android.support.v4.util.LruCache; import android.util.Log; import android.widget.ImageView; @@ -24,11 +25,21 @@ public class DefaultCollageLoader implements CollageLoader { private CollageStrategy defaultStrategy = new SquareCollageStrategy(); private CompositeSubscription compositeSubscription; private Map subscriptionsMap = new WeakHashMap<>(); + private LruCache, Bitmap> bitmapCache; // Начитался https://habrahabr.ru/post/265997/ public DefaultCollageLoader(CompositeSubscription compositeSubscription) { this.compositeSubscription = compositeSubscription; + + int cacheSize = (int) (Runtime.getRuntime().maxMemory()); + + bitmapCache = new LruCache, Bitmap>(cacheSize) { + @Override + protected int sizeOf(List key, Bitmap value) { + return value.getByteCount(); + } + }; } @Override @@ -60,13 +71,12 @@ private void loadCollage(List urls, Object o, Action1 setBitmap, compositeSubscription.remove(subscriptionsMap.get(o)); subscriptionsMap.remove(o); } - Subscription loading = Observable.from(urls) - .subscribeOn(Schedulers.io()) - .map(this::getBitmapFromURL) - .toList() - .doOnNext(t -> - Log.e("THREAD", " " + Thread.currentThread().toString())) - .map(collageStrategy::create) + + + Subscription loading = Observable + .concat(fromCache(urls), fromNetwork(urls, collageStrategy)) + .first(bitmap -> bitmap != null) + .doOnNext(bitmap -> bitmapCache.put(urls, bitmap)) .observeOn(AndroidSchedulers.mainThread()) .subscribe(setBitmap, e -> Log.e("E:DefaultCollageLoader", "Collage downloading failed: " + e), @@ -76,6 +86,23 @@ private void loadCollage(List urls, Object o, Action1 setBitmap, compositeSubscription.add(loading); } + + private Observable fromCache(List urls) { + return Observable.create(subscriber -> { + subscriber.onNext(bitmapCache.get(urls)); + subscriber.onCompleted(); + }); + } + + private Observable fromNetwork(List urls, CollageStrategy collageStrategy) { + return Observable.from(urls) + .subscribeOn(Schedulers.io()) + .map(this::getBitmapFromURL) + .toList() + .map(collageStrategy::create); + } + + private Bitmap getBitmapFromURL(String src) { InputStream inputStream = null; HttpURLConnection connection = null; From c1a2248edbf557681eb5655057c2aad0f1dd1d4b Mon Sep 17 00:00:00 2001 From: katkalof Date: Thu, 11 Aug 2016 23:16:16 +0300 Subject: [PATCH 7/9] add not empty ImageTarget realisation and use it in CollageLoader, add small fixes after code review --- .../DefaultCriticalSectionsHandler.java | 4 +- .../yamblz/loader/DefaultCollageLoader.java | 42 +++++++++---------- .../yamblz/loader/SimpleImageTarget.java | 12 ++++++ 3 files changed, 32 insertions(+), 26 deletions(-) diff --git a/app/src/main/java/ru/yandex/yamblz/handler/DefaultCriticalSectionsHandler.java b/app/src/main/java/ru/yandex/yamblz/handler/DefaultCriticalSectionsHandler.java index a6c259a..8ba4fa7 100644 --- a/app/src/main/java/ru/yandex/yamblz/handler/DefaultCriticalSectionsHandler.java +++ b/app/src/main/java/ru/yandex/yamblz/handler/DefaultCriticalSectionsHandler.java @@ -33,9 +33,7 @@ public void stopSection(int id) { @Override public void stopSections() { - for (Integer section : sections) { - stopSection(section); - } + sections.clear(); runTasks(); } diff --git a/app/src/main/java/ru/yandex/yamblz/loader/DefaultCollageLoader.java b/app/src/main/java/ru/yandex/yamblz/loader/DefaultCollageLoader.java index 1179316..441ad14 100644 --- a/app/src/main/java/ru/yandex/yamblz/loader/DefaultCollageLoader.java +++ b/app/src/main/java/ru/yandex/yamblz/loader/DefaultCollageLoader.java @@ -17,7 +17,6 @@ import rx.Observable; import rx.Subscription; import rx.android.schedulers.AndroidSchedulers; -import rx.functions.Action1; import rx.schedulers.Schedulers; import rx.subscriptions.CompositeSubscription; @@ -44,7 +43,7 @@ protected int sizeOf(List key, Bitmap value) { @Override public void loadCollage(List urls, ImageView imageView) { - loadCollage(urls, imageView, defaultStrategy); + loadCollage(urls, new SimpleImageTarget(imageView), defaultStrategy); } @Override @@ -55,46 +54,41 @@ public void loadCollage(List urls, ImageTarget imageTarget) { @Override public void loadCollage(List urls, ImageView imageView, CollageStrategy collageStrategy) { - loadCollage(urls, imageView, bitmap -> imageView.setImageBitmap((Bitmap) bitmap), collageStrategy); + loadCollage(urls, new SimpleImageTarget(imageView), collageStrategy); } @Override public void loadCollage(List urls, ImageTarget imageTarget, CollageStrategy collageStrategy) { - loadCollage(urls, imageTarget, bitmap -> imageTarget.onLoadBitmap((Bitmap) bitmap), collageStrategy); - } - - //Работает, но такое ощущение, что можно лучше) хочу критики - private void loadCollage(List urls, Object o, Action1 setBitmap, - CollageStrategy collageStrategy) { - if (subscriptionsMap.containsKey(o)) { - compositeSubscription.remove(subscriptionsMap.get(o)); - subscriptionsMap.remove(o); + if (subscriptionsMap.containsKey(imageTarget)) { + compositeSubscription.remove(subscriptionsMap.get(imageTarget)); + subscriptionsMap.remove(imageTarget); } - Subscription loading = Observable - .concat(fromCache(urls), fromNetwork(urls, collageStrategy)) + .concat(collageFromCache(urls), collageFromNetwork(urls, collageStrategy)) .first(bitmap -> bitmap != null) .doOnNext(bitmap -> bitmapCache.put(urls, bitmap)) + .doOnNext(t -> + Log.e("THREAD", " " + Thread.currentThread().toString())) .observeOn(AndroidSchedulers.mainThread()) - .subscribe(setBitmap, + .subscribe( + imageTarget::onLoadBitmap, e -> Log.e("E:DefaultCollageLoader", "Collage downloading failed: " + e), () -> Log.d("D:DefaultCollageLoader", "Collage downloading completed") ); - subscriptionsMap.put(o, loading); + subscriptionsMap.put(imageTarget, loading); compositeSubscription.add(loading); } - - private Observable fromCache(List urls) { + private Observable collageFromCache(List urls) { return Observable.create(subscriber -> { subscriber.onNext(bitmapCache.get(urls)); subscriber.onCompleted(); }); } - private Observable fromNetwork(List urls, CollageStrategy collageStrategy) { + private Observable collageFromNetwork(List urls, CollageStrategy collageStrategy) { return Observable.from(urls) .subscribeOn(Schedulers.io()) .map(this::getBitmapFromURL) @@ -102,7 +96,6 @@ private Observable fromNetwork(List urls, CollageStrategy collag .map(collageStrategy::create); } - private Bitmap getBitmapFromURL(String src) { InputStream inputStream = null; HttpURLConnection connection = null; @@ -116,10 +109,13 @@ private Bitmap getBitmapFromURL(String src) { Log.e("E:DefaultCollageLoader", "Image downloading failed: " + src); return null; } finally { - //Что-то мне не совсем это нравится, прокомментируешь? try { - connection.disconnect(); - inputStream.close(); + if (connection != null) { + connection.disconnect(); + } + if (inputStream != null) { + inputStream.close(); + } } catch (IOException e) { e.printStackTrace(); } diff --git a/app/src/main/java/ru/yandex/yamblz/loader/SimpleImageTarget.java b/app/src/main/java/ru/yandex/yamblz/loader/SimpleImageTarget.java index 9f7d888..6803bb2 100644 --- a/app/src/main/java/ru/yandex/yamblz/loader/SimpleImageTarget.java +++ b/app/src/main/java/ru/yandex/yamblz/loader/SimpleImageTarget.java @@ -1,10 +1,22 @@ package ru.yandex.yamblz.loader; import android.graphics.Bitmap; +import android.widget.ImageView; + +import java.lang.ref.WeakReference; public class SimpleImageTarget implements ImageTarget { + private final WeakReference refImageView; + + public SimpleImageTarget(ImageView imageView) { + refImageView = new WeakReference<>(imageView); + } @Override public void onLoadBitmap(Bitmap bitmap) { + ImageView imageView = refImageView.get(); + if (imageView != null) { + imageView.setImageBitmap(bitmap); + } } } From 5ccf59c57e38e374e68a21151a29ef02d0c83e63 Mon Sep 17 00:00:00 2001 From: katkalof Date: Fri, 12 Aug 2016 01:12:52 +0300 Subject: [PATCH 8/9] add true parallel downloading --- .../yandex/yamblz/loader/DefaultCollageLoader.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/ru/yandex/yamblz/loader/DefaultCollageLoader.java b/app/src/main/java/ru/yandex/yamblz/loader/DefaultCollageLoader.java index 441ad14..0f347c7 100644 --- a/app/src/main/java/ru/yandex/yamblz/loader/DefaultCollageLoader.java +++ b/app/src/main/java/ru/yandex/yamblz/loader/DefaultCollageLoader.java @@ -69,8 +69,6 @@ public void loadCollage(List urls, ImageTarget imageTarget, .concat(collageFromCache(urls), collageFromNetwork(urls, collageStrategy)) .first(bitmap -> bitmap != null) .doOnNext(bitmap -> bitmapCache.put(urls, bitmap)) - .doOnNext(t -> - Log.e("THREAD", " " + Thread.currentThread().toString())) .observeOn(AndroidSchedulers.mainThread()) .subscribe( imageTarget::onLoadBitmap, @@ -90,13 +88,19 @@ private Observable collageFromCache(List urls) { private Observable collageFromNetwork(List urls, CollageStrategy collageStrategy) { return Observable.from(urls) - .subscribeOn(Schedulers.io()) - .map(this::getBitmapFromURL) + .flatMap(url -> getBitmapObservableFromURL(url).subscribeOn(Schedulers.io())) .toList() .map(collageStrategy::create); } + private Observable getBitmapObservableFromURL(String url) { + return Observable.fromCallable( + () -> getBitmapFromURL(url) + ); + } + private Bitmap getBitmapFromURL(String src) { + Log.e("THREAD", " " + Thread.currentThread().toString()); InputStream inputStream = null; HttpURLConnection connection = null; try { From 32de39805a520536c2d0530eaaf9a7172d17ec88 Mon Sep 17 00:00:00 2001 From: katkalof Date: Fri, 12 Aug 2016 02:20:33 +0300 Subject: [PATCH 9/9] placed in critical zone only setView in main Thread, but not a downloading --- .../yamblz/loader/DefaultCollageLoader.java | 17 ++++++++++++++++- .../yandex/yamblz/ui/adapters/GenreAdapter.java | 14 +------------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/ru/yandex/yamblz/loader/DefaultCollageLoader.java b/app/src/main/java/ru/yandex/yamblz/loader/DefaultCollageLoader.java index 0f347c7..b0162da 100644 --- a/app/src/main/java/ru/yandex/yamblz/loader/DefaultCollageLoader.java +++ b/app/src/main/java/ru/yandex/yamblz/loader/DefaultCollageLoader.java @@ -8,12 +8,15 @@ import java.io.IOException; import java.io.InputStream; +import java.lang.ref.WeakReference; import java.net.HttpURLConnection; import java.net.URL; import java.util.List; import java.util.Map; import java.util.WeakHashMap; +import ru.yandex.yamblz.handler.CriticalSectionsManager; +import ru.yandex.yamblz.handler.Task; import rx.Observable; import rx.Subscription; import rx.android.schedulers.AndroidSchedulers; @@ -26,6 +29,8 @@ public class DefaultCollageLoader implements CollageLoader { private Map subscriptionsMap = new WeakHashMap<>(); private LruCache, Bitmap> bitmapCache; + private Map> loadingTasks = new WeakHashMap<>(); + // Начитался https://habrahabr.ru/post/265997/ public DefaultCollageLoader(CompositeSubscription compositeSubscription) { @@ -65,13 +70,23 @@ public void loadCollage(List urls, ImageTarget imageTarget, subscriptionsMap.remove(imageTarget); } + Subscription loading = Observable .concat(collageFromCache(urls), collageFromNetwork(urls, collageStrategy)) .first(bitmap -> bitmap != null) .doOnNext(bitmap -> bitmapCache.put(urls, bitmap)) + .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe( - imageTarget::onLoadBitmap, + (bitmap) -> { + if (loadingTasks.containsKey(imageTarget)) { + CriticalSectionsManager.getHandler() + .removeLowPriorityTask(loadingTasks.get(imageTarget).get()); + } + Task task = () -> imageTarget.onLoadBitmap(bitmap); + CriticalSectionsManager.getHandler().postLowPriorityTask(task); + loadingTasks.put(imageTarget, new WeakReference<>(task)); + }, e -> Log.e("E:DefaultCollageLoader", "Collage downloading failed: " + e), () -> Log.d("D:DefaultCollageLoader", "Collage downloading completed") ); diff --git a/app/src/main/java/ru/yandex/yamblz/ui/adapters/GenreAdapter.java b/app/src/main/java/ru/yandex/yamblz/ui/adapters/GenreAdapter.java index ecbd2b9..b6031e4 100644 --- a/app/src/main/java/ru/yandex/yamblz/ui/adapters/GenreAdapter.java +++ b/app/src/main/java/ru/yandex/yamblz/ui/adapters/GenreAdapter.java @@ -8,20 +8,14 @@ import android.widget.ImageView; import android.widget.TextView; -import java.lang.ref.WeakReference; import java.util.List; -import java.util.Map; -import java.util.WeakHashMap; import ru.yandex.yamblz.R; -import ru.yandex.yamblz.handler.CriticalSectionsManager; -import ru.yandex.yamblz.handler.Task; import ru.yandex.yamblz.loader.CollageLoaderManager; import ru.yandex.yamblz.model.Genre; public class GenreAdapter extends RecyclerView.Adapter { - private Map> loadingTasks = new WeakHashMap<>(); protected boolean mIsLoading = false; @@ -56,14 +50,8 @@ public void onBindViewHolder(GenreHolder holder, int position) { holder.photoView.setImageDrawable(null); holder.titleView.setText(genres.get(position).getName()); holder.descView.setText(genres.get(position).getArtistString()); - if (loadingTasks.containsKey(holder.photoView)) { - CriticalSectionsManager.getHandler() - .removeLowPriorityTask(loadingTasks.get(holder.photoView).get()); - } - Task task = () -> CollageLoaderManager.getLoader() + CollageLoaderManager.getLoader() .loadCollage(genres.get(position).getArtistsPhotos(), holder.photoView); - CriticalSectionsManager.getHandler().postLowPriorityTask(task); - loadingTasks.put(holder.photoView, new WeakReference<>(task)); }