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/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/handler/DefaultCriticalSectionsHandler.java b/app/src/main/java/ru/yandex/yamblz/handler/DefaultCriticalSectionsHandler.java new file mode 100644 index 0000000..8ba4fa7 --- /dev/null +++ b/app/src/main/java/ru/yandex/yamblz/handler/DefaultCriticalSectionsHandler.java @@ -0,0 +1,91 @@ +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() { + sections.clear(); + 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 new file mode 100644 index 0000000..b0162da --- /dev/null +++ b/app/src/main/java/ru/yandex/yamblz/loader/DefaultCollageLoader.java @@ -0,0 +1,143 @@ +package ru.yandex.yamblz.loader; + +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.support.v4.util.LruCache; +import android.util.Log; +import android.widget.ImageView; + +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; +import rx.schedulers.Schedulers; +import rx.subscriptions.CompositeSubscription; + +public class DefaultCollageLoader implements CollageLoader { + private CollageStrategy defaultStrategy = new SquareCollageStrategy(); + private CompositeSubscription compositeSubscription; + private Map subscriptionsMap = new WeakHashMap<>(); + private LruCache, Bitmap> bitmapCache; + + private Map> loadingTasks = new WeakHashMap<>(); + + + // Начитался 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 + public void loadCollage(List urls, ImageView imageView) { + loadCollage(urls, new SimpleImageTarget(imageView), defaultStrategy); + } + + @Override + public void loadCollage(List urls, ImageTarget imageTarget) { + loadCollage(urls, imageTarget, defaultStrategy); + } + + @Override + public void loadCollage(List urls, ImageView imageView, + CollageStrategy collageStrategy) { + loadCollage(urls, new SimpleImageTarget(imageView), collageStrategy); + } + + @Override + public void loadCollage(List urls, ImageTarget imageTarget, + CollageStrategy collageStrategy) { + if (subscriptionsMap.containsKey(imageTarget)) { + compositeSubscription.remove(subscriptionsMap.get(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( + (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") + ); + subscriptionsMap.put(imageTarget, loading); + compositeSubscription.add(loading); + } + + private Observable collageFromCache(List urls) { + return Observable.create(subscriber -> { + subscriber.onNext(bitmapCache.get(urls)); + subscriber.onCompleted(); + }); + } + + private Observable collageFromNetwork(List urls, CollageStrategy collageStrategy) { + return Observable.from(urls) + .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 { + 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 { + if (connection != null) { + connection.disconnect(); + } + if (inputStream != null) { + inputStream.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + } +} \ No newline at end of file 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..6803bb2 --- /dev/null +++ b/app/src/main/java/ru/yandex/yamblz/loader/SimpleImageTarget.java @@ -0,0 +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); + } + } +} 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/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..d7449fe --- /dev/null +++ b/app/src/main/java/ru/yandex/yamblz/model/Genre.java @@ -0,0 +1,85 @@ +package ru.yandex.yamblz.model; + +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() { + } + + public String getName() { + return name; + } + + public List getArtists() { + return artists; + } + + 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++) { + stringBuilder.append(artists.get(i).getName()); + stringBuilder.append(", "); + } + stringBuilder.append(artists.get(artists.size() - 1).getName()); + stringBuilder.append("."); + } else { + stringBuilder.append("Нет исполнителей данного жанра"); + } + return stringBuilder; + } + + public List getArtistsPhotos() { + return artistsSmallPhotos; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(name); + dest.writeList(artists); + dest.writeList(artistsSmallPhotos); + } + + 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..b6031e4 --- /dev/null +++ b/app/src/main/java/ru/yandex/yamblz/ui/adapters/GenreAdapter.java @@ -0,0 +1,77 @@ +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.loader.CollageLoaderManager; +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) { + 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); + } + + + @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..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,18 +1,134 @@ package ru.yandex.yamblz.ui.fragments; import android.os.Bundle; -import android.support.annotation.NonNull; +import android.os.Handler; 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.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; +import ru.yandex.yamblz.model.ArtistsService; +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 { - @NonNull + @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(new DefaultCriticalSectionsHandler(new Handler(getMainLooper()))); + } + + @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()) + //http://download.cdn.yandex.net/mobilization-2016/artists.json + .baseUrl("http://download.cdn.yandex.net/") + .build(); + ArtistsService artistsService = retrofit.create(ArtistsService.class); + Observable> artistsObservable = artistsService.listArtists(); + + Subscription artistsSubscription = artistsObservable + .subscribeOn(Schedulers.io()) + .flatMapIterable(artists -> artists) + .flatMap(genresWithArtist) + .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 artists->genres ", "not good in download" + e), + () -> { + recyclerView.requestLayout(); + 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 -> { + 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/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..19413bd 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-beta1', 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