diff --git a/app/build.gradle b/app/build.gradle
index 445372a..6220c53 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -96,6 +96,11 @@ dependencies {
compile libraries.butterKnife
apt libraries.butterKnifeCompiler
+ compile libraries.retrofit2
+ compile libraries.rxAndroid
+ compile libraries.converterGson
+ compile libraries.adapterRxjava
+
compile libraries.timber
// Developer tools (Developer Settings)
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 7baef8f..9ee0029 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -3,6 +3,7 @@
package="ru.yandex.yamblz">
+
presenter;
+ private CollageAdapter collageAdapter;
+
+ @BindView(R.id.rv_collages)
+ RecyclerView recyclerView;
+ @BindView(R.id.swipe_view)
+ SwipeRefreshLayout swipeLayout;
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState)
+ {
+ super.onCreate(savedInstanceState);
+ presenter = new GenresPresenterImpl(App.get(getContext()).getArtistRepository());
+ }
+
+ @NonNull
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle bundle)
+ {
+ return inflater.inflate(R.layout.fragment_content, container, false);
+ }
+
+ @Override
+ public void onViewCreated(View view, @Nullable Bundle savedInstanceState)
+ {
+ super.onViewCreated(view, savedInstanceState);
+ collageAdapter = new CollageAdapter();
+ recyclerView.setAdapter(collageAdapter);
+ recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
+ swipeLayout.setOnRefreshListener(this);
+ recyclerView.addOnScrollListener(new RecyclerScrollListener());
+ }
+
+ @Override
+ public void onResume()
+ {
+ super.onResume();
+ presenter.bind(this);
+ presenter.getGenres(false);
+ }
+
+ @Override
+ public void onPause()
+ {
+ super.onPause();
+ presenter.unsubscribe();
+ presenter.unbind();
+ }
+
+ @Override
+ public void showProgress(boolean show)
+ {
+ swipeLayout.setRefreshing(show);
+ }
+
+ @Override
+ public void showGenres(List genresList) {
+ collageAdapter.swap(genresList);
+ }
+
+ @Override
+ public void showError(String error)
+ {
+ Toast.makeText(getContext(), error, Toast.LENGTH_SHORT).show();
+ }
+
+ @Override
+ public void onRefresh()
+ {
+ presenter.getGenres(true);
+ }
+}
diff --git a/app/src/main/java/ru/yandex/yamblz/genre/GenresPresenterImpl.java b/app/src/main/java/ru/yandex/yamblz/genre/GenresPresenterImpl.java
new file mode 100644
index 0000000..efc517e
--- /dev/null
+++ b/app/src/main/java/ru/yandex/yamblz/genre/GenresPresenterImpl.java
@@ -0,0 +1,82 @@
+package ru.yandex.yamblz.genre;
+
+import java.util.List;
+
+import ru.yandex.yamblz.genre.data.entity.Genre;
+import ru.yandex.yamblz.genre.data.source.DataSource;
+import ru.yandex.yamblz.genre.interfaces.GenresPresenter;
+import ru.yandex.yamblz.genre.interfaces.GenresView;
+import ru.yandex.yamblz.genre.util.INetworkManager;
+import ru.yandex.yamblz.genre.util.NetworkManager;
+import ru.yandex.yamblz.genre.util.Utils;
+import rx.android.schedulers.AndroidSchedulers;
+import rx.functions.Action1;
+import rx.schedulers.Schedulers;
+import rx.subscriptions.CompositeSubscription;
+
+/**
+ * Created by platon on 27.07.2016.
+ */
+public class GenresPresenterImpl implements GenresPresenter
+{
+ private GenresView genresView;
+ private CompositeSubscription subscriptions;
+ private DataSource dataSource;
+ private INetworkManager networkManager;
+
+ public GenresPresenterImpl(DataSource dataSource)
+ {
+ this.dataSource = dataSource;
+ networkManager = NetworkManager.getManager();
+ subscriptions = new CompositeSubscription();
+ }
+
+ @Override
+ public void getGenres(boolean forceLoad)
+ {
+ if (networkManager.networkIsAvailable())
+ {
+ genresView.showProgress(true);
+ if (forceLoad) dataSource.delete();
+
+ subscriptions.add(dataSource.getGenres()
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(onNext, onError));
+ }
+ else
+ {
+ genresView.showError("no connection");
+ }
+ }
+
+ @Override
+ public void bind(GenresView view)
+ {
+ genresView = view;
+ }
+
+ @Override
+ public void unbind()
+ {
+ genresView = null;
+ }
+
+ @Override
+ public void unsubscribe()
+ {
+ subscriptions.clear();
+ }
+
+ private Action1> onNext = genres ->
+ {
+ genresView.showProgress(false);
+ genresView.showGenres(genres);
+ };
+
+ private Action1 onError = e ->
+ {
+ genresView.showProgress(false);
+ genresView.showError(e.getMessage());
+ };
+}
diff --git a/app/src/main/java/ru/yandex/yamblz/genre/RecyclerScrollListener.java b/app/src/main/java/ru/yandex/yamblz/genre/RecyclerScrollListener.java
new file mode 100644
index 0000000..c4352ba
--- /dev/null
+++ b/app/src/main/java/ru/yandex/yamblz/genre/RecyclerScrollListener.java
@@ -0,0 +1,37 @@
+package ru.yandex.yamblz.genre;
+
+import android.support.v7.widget.RecyclerView;
+
+import ru.yandex.yamblz.handler.CriticalSectionsHandler;
+import ru.yandex.yamblz.handler.CriticalSectionsManager;
+
+/**
+ * Created by platon on 30.07.2016.
+ */
+public class RecyclerScrollListener extends RecyclerView.OnScrollListener
+{
+ public static final int CS_ID = 1991;
+ private final CriticalSectionsHandler criticalSectionsHandler;
+
+ public RecyclerScrollListener()
+ {
+ super();
+ criticalSectionsHandler = CriticalSectionsManager.getHandler();
+ }
+
+ @Override
+ public void onScrollStateChanged(RecyclerView recyclerView, int newState)
+ {
+ switch (newState)
+ {
+ case RecyclerView.SCROLL_STATE_IDLE:
+ criticalSectionsHandler.stopSection(CS_ID);
+ break;
+
+ case RecyclerView.SCROLL_STATE_DRAGGING:
+ criticalSectionsHandler.startSection(CS_ID);
+ break;
+ }
+
+ }
+}
diff --git a/app/src/main/java/ru/yandex/yamblz/genre/adapter/CollageAdapter.java b/app/src/main/java/ru/yandex/yamblz/genre/adapter/CollageAdapter.java
new file mode 100644
index 0000000..76c0d41
--- /dev/null
+++ b/app/src/main/java/ru/yandex/yamblz/genre/adapter/CollageAdapter.java
@@ -0,0 +1,77 @@
+package ru.yandex.yamblz.genre.adapter;
+
+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 butterknife.BindView;
+import butterknife.ButterKnife;
+import ru.yandex.yamblz.R;
+import ru.yandex.yamblz.loader.CollageLoaderManager;
+import ru.yandex.yamblz.loader.interfaces.CollageLoader;
+import ru.yandex.yamblz.loader.interfaces.ImageTarget;
+import ru.yandex.yamblz.loader.ImageTargetImpl;
+import ru.yandex.yamblz.genre.data.entity.Genre;
+
+/**
+ * Created by platon on 26.07.2016.
+ */
+public class CollageAdapter extends RecyclerView.Adapter
+{
+ private CollageLoader collageLoader;
+ private List genres;
+
+ public CollageAdapter()
+ {
+ collageLoader = CollageLoaderManager.getLoader();
+ }
+
+ @Override
+ public CollageItemHolder onCreateViewHolder(ViewGroup parent, int viewType)
+ {
+ View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_genre, parent, false);
+ return new CollageItemHolder(v);
+ }
+
+ @Override
+ public void onBindViewHolder(CollageItemHolder holder, int position)
+ {
+ Genre genre = genres.get(position);
+ holder.bind(genre);
+ }
+
+ @Override
+ public int getItemCount()
+ {
+ return genres == null ? 0 : genres.size();
+ }
+
+ public void swap(List newList)
+ {
+ genres = newList;
+ notifyDataSetChanged();
+ }
+
+ public class CollageItemHolder extends RecyclerView.ViewHolder
+ {
+ @BindView(R.id.iv_collage_container) ImageView collageContainer;
+ @BindView(R.id.tw_genre) TextView genreTextView;
+
+ public CollageItemHolder(View view)
+ {
+ super(view);
+ ButterKnife.bind(this, view);
+ }
+
+ public void bind(Genre genre)
+ {
+ genreTextView.setText(genre.getName());
+ collageLoader.loadCollage(genre.getUrls(), collageContainer);
+ }
+ }
+}
diff --git a/app/src/main/java/ru/yandex/yamblz/genre/api/ArtistsApi.java b/app/src/main/java/ru/yandex/yamblz/genre/api/ArtistsApi.java
new file mode 100644
index 0000000..b38cc39
--- /dev/null
+++ b/app/src/main/java/ru/yandex/yamblz/genre/api/ArtistsApi.java
@@ -0,0 +1,16 @@
+package ru.yandex.yamblz.genre.api;
+
+import java.util.List;
+
+import retrofit2.http.GET;
+import ru.yandex.yamblz.genre.data.entity.Artist;
+import rx.Observable;
+
+/**
+ * Created by platon on 27.07.2016.
+ */
+public interface ArtistsApi
+{
+ @GET("mobilization-2016/artists.json")
+ Observable> listArtists();
+}
diff --git a/app/src/main/java/ru/yandex/yamblz/genre/data/entity/Artist.java b/app/src/main/java/ru/yandex/yamblz/genre/data/entity/Artist.java
new file mode 100644
index 0000000..a55c265
--- /dev/null
+++ b/app/src/main/java/ru/yandex/yamblz/genre/data/entity/Artist.java
@@ -0,0 +1,105 @@
+package ru.yandex.yamblz.genre.data.entity;
+
+import com.google.gson.annotations.SerializedName;
+
+import java.io.Serializable;
+import java.util.Arrays;
+
+/**
+ * Created by platon on 26.07.2016.
+ */
+public class Artist implements Serializable
+{
+ @SerializedName("id")
+ private String id;
+
+ @SerializedName("name")
+ private String name;
+
+ @SerializedName("genres")
+ private String[] genres;
+
+ @SerializedName("cover")
+ private Cover cover;
+
+ public String getId()
+ {
+ return id;
+ }
+
+ public void setId(String id)
+ {
+ this.id = id;
+ }
+
+ public String getName()
+ {
+ return name;
+ }
+
+ public void setName(String name)
+ {
+ this.name = name;
+ }
+
+ public String[] getGenres()
+ {
+ return genres;
+ }
+
+ public void setGenres(String[] genres)
+ {
+ this.genres = genres;
+ }
+
+ public Cover getCover()
+ {
+ return cover;
+ }
+
+ public void setCover(Cover cover)
+ {
+ this.cover = cover;
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ Artist artist = (Artist) o;
+
+ if (!id.equals(artist.id)) return false;
+ if (!name.equals(artist.name)) return false;
+ return cover.equals(artist.cover);
+
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int result = id.hashCode();
+ result = 31 * result + name.hashCode();
+ result = 31 * result + cover.hashCode();
+ return result;
+ }
+
+ @Override
+ public String toString()
+ {
+ StringBuilder builder = new StringBuilder();
+ builder.append("Artist{");
+ builder.append("id=");
+ builder.append(id);
+ builder.append(", name=");
+ builder.append(name);
+ builder.append(", genres=");
+ builder.append(Arrays.toString(genres));
+ builder.append(", cover=");
+ builder.append(cover);
+ builder.append("}");
+
+ return builder.toString();
+ }
+}
diff --git a/app/src/main/java/ru/yandex/yamblz/genre/data/entity/Cover.java b/app/src/main/java/ru/yandex/yamblz/genre/data/entity/Cover.java
new file mode 100644
index 0000000..1645ead
--- /dev/null
+++ b/app/src/main/java/ru/yandex/yamblz/genre/data/entity/Cover.java
@@ -0,0 +1,54 @@
+package ru.yandex.yamblz.genre.data.entity;
+
+import com.google.gson.annotations.SerializedName;
+
+import java.io.Serializable;
+
+/**
+ * Created by platon on 26.07.2016.
+ */
+public class Cover implements Serializable
+{
+ @SerializedName("small")
+ private String coverUrl;
+
+ public String getCoverUrl()
+ {
+ return coverUrl;
+ }
+
+ public void setCoverUrl(String coverUrl)
+ {
+ this.coverUrl = coverUrl;
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ Cover cover = (Cover) o;
+
+ return coverUrl.equals(cover.coverUrl);
+
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return coverUrl.hashCode();
+ }
+
+ @Override
+ public String toString()
+ {
+ StringBuilder builder = new StringBuilder();
+ builder.append("Cover{");
+ builder.append("coverUrl=");
+ builder.append(coverUrl);
+ builder.append("}");
+
+ return builder.toString();
+ }
+}
diff --git a/app/src/main/java/ru/yandex/yamblz/genre/data/entity/Genre.java b/app/src/main/java/ru/yandex/yamblz/genre/data/entity/Genre.java
new file mode 100644
index 0000000..43b36a5
--- /dev/null
+++ b/app/src/main/java/ru/yandex/yamblz/genre/data/entity/Genre.java
@@ -0,0 +1,67 @@
+package ru.yandex.yamblz.genre.data.entity;
+
+import java.util.List;
+
+/**
+ * Created by platon on 26.07.2016.
+ */
+public class Genre
+{
+ private List urls;
+ private String name;
+
+ public List getUrls()
+ {
+ return urls;
+ }
+
+ public void setUrls(List urls)
+ {
+ this.urls = urls;
+ }
+
+ public String getName()
+ {
+ return name;
+ }
+
+ public void setName(String name)
+ {
+ this.name = name;
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ Genre genre = (Genre) o;
+
+ if (!urls.equals(genre.urls)) return false;
+ return name.equals(genre.name);
+
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int result = urls.hashCode();
+ result = 31 * result + name.hashCode();
+ return result;
+ }
+
+ @Override
+ public String toString()
+ {
+ StringBuilder builder = new StringBuilder();
+ builder.append("Genre{");
+ builder.append("urls=");
+ builder.append(urls);
+ builder.append(", name=");
+ builder.append(name);
+ builder.append("}");
+
+ return builder.toString();
+ }
+}
diff --git a/app/src/main/java/ru/yandex/yamblz/genre/data/source/DataSource.java b/app/src/main/java/ru/yandex/yamblz/genre/data/source/DataSource.java
new file mode 100644
index 0000000..138b323
--- /dev/null
+++ b/app/src/main/java/ru/yandex/yamblz/genre/data/source/DataSource.java
@@ -0,0 +1,17 @@
+package ru.yandex.yamblz.genre.data.source;
+
+import java.util.List;
+
+import ru.yandex.yamblz.genre.data.entity.Artist;
+import ru.yandex.yamblz.genre.data.entity.Genre;
+import rx.Observable;
+
+/**
+ * Created by platon on 27.07.2016.
+ */
+public interface DataSource
+{
+ Observable> getArtists();
+ Observable> getGenres();
+ void delete();
+}
diff --git a/app/src/main/java/ru/yandex/yamblz/genre/data/source/RemoteDataSource.java b/app/src/main/java/ru/yandex/yamblz/genre/data/source/RemoteDataSource.java
new file mode 100644
index 0000000..881f0d7
--- /dev/null
+++ b/app/src/main/java/ru/yandex/yamblz/genre/data/source/RemoteDataSource.java
@@ -0,0 +1,43 @@
+package ru.yandex.yamblz.genre.data.source;
+
+import java.util.List;
+
+import retrofit2.Retrofit;
+import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory;
+import retrofit2.converter.gson.GsonConverterFactory;
+import ru.yandex.yamblz.genre.api.ArtistsApi;
+import ru.yandex.yamblz.genre.data.entity.Artist;
+import ru.yandex.yamblz.genre.data.entity.Genre;
+import rx.Observable;
+
+public class RemoteDataSource implements DataSource
+{
+ private static final String ENDPOINT = "http://download.cdn.yandex.net/";
+ private ArtistsApi api;
+
+ public RemoteDataSource()
+ {
+ Retrofit retrofit = new Retrofit.Builder()
+ .baseUrl(ENDPOINT)
+ .addConverterFactory(GsonConverterFactory.create())
+ .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
+ .build();
+
+ api = retrofit.create(ArtistsApi.class);
+ }
+
+ @Override
+ public Observable> getArtists()
+ {
+ return api.listArtists();
+ }
+
+ @Override
+ public Observable> getGenres()
+ {
+ return null;
+ }
+
+ @Override
+ public void delete() {}
+}
diff --git a/app/src/main/java/ru/yandex/yamblz/genre/data/source/Repository.java b/app/src/main/java/ru/yandex/yamblz/genre/data/source/Repository.java
new file mode 100644
index 0000000..99ba931
--- /dev/null
+++ b/app/src/main/java/ru/yandex/yamblz/genre/data/source/Repository.java
@@ -0,0 +1,53 @@
+package ru.yandex.yamblz.genre.data.source;
+
+import java.util.List;
+
+import ru.yandex.yamblz.genre.data.entity.Artist;
+import ru.yandex.yamblz.genre.data.entity.Genre;
+import ru.yandex.yamblz.genre.data.source.local.ICache;
+import ru.yandex.yamblz.genre.util.Utils;
+import rx.Observable;
+
+/**
+ * Created by platon on 28.07.2016.
+ */
+public class Repository implements DataSource
+{
+ private ICache cache;
+ private DataSource remote;
+
+ public Repository(ICache cache, DataSource remote)
+ {
+ this.cache = cache;
+ this.remote = remote;
+ }
+
+ @Override
+ public Observable> getArtists()
+ {
+ if (cache.isEmpty()) return fromRemote();
+ return fromLocal();
+ }
+
+ @Override
+ public Observable> getGenres()
+ {
+ return getArtists().map(Utils::transformArtistToGenres);
+ }
+
+ @Override
+ public void delete()
+ {
+ cache.clear();
+ }
+
+ private Observable> fromRemote()
+ {
+ return remote.getArtists().doOnNext(artists -> cache.put(artists));
+ }
+
+ private Observable> fromLocal()
+ {
+ return Observable.just(cache.get());
+ }
+}
diff --git a/app/src/main/java/ru/yandex/yamblz/genre/data/source/local/CacheImpl.java b/app/src/main/java/ru/yandex/yamblz/genre/data/source/local/CacheImpl.java
new file mode 100644
index 0000000..e862b2e
--- /dev/null
+++ b/app/src/main/java/ru/yandex/yamblz/genre/data/source/local/CacheImpl.java
@@ -0,0 +1,55 @@
+package ru.yandex.yamblz.genre.data.source.local;
+
+
+import java.io.File;
+import java.util.List;
+
+import ru.yandex.yamblz.genre.data.entity.Artist;
+
+
+/**
+ * Created by platon on 19.07.2016.
+ */
+public class CacheImpl implements ICache
+{
+ private final File cachedFile;
+ private final JsonSerializer serializer;
+ private final FileManager fileManager;
+
+ public CacheImpl(File cacheDir, String name, JsonSerializer artistCacheSerializer)
+ {
+ cachedFile = new File(cacheDir, name);
+ fileManager = new FileManager();
+ serializer = artistCacheSerializer;
+ }
+
+ public CacheImpl(File cacheDir, JsonSerializer artistCacheSerializer)
+ {
+ this(cacheDir, "artists.list", artistCacheSerializer);
+ }
+
+ @Override
+ public List get()
+ {
+ String json = fileManager.readFileContent(cachedFile);
+ return serializer.deserialize(json);
+ }
+
+ @Override
+ public void put(List list)
+ {
+ fileManager.writeToFile(cachedFile, serializer.serialize(list));
+ }
+
+ @Override
+ public boolean clear()
+ {
+ return cachedFile.delete();
+ }
+
+ @Override
+ public boolean isEmpty()
+ {
+ return !cachedFile.exists();
+ }
+}
diff --git a/app/src/main/java/ru/yandex/yamblz/genre/data/source/local/FileManager.java b/app/src/main/java/ru/yandex/yamblz/genre/data/source/local/FileManager.java
new file mode 100644
index 0000000..486156b
--- /dev/null
+++ b/app/src/main/java/ru/yandex/yamblz/genre/data/source/local/FileManager.java
@@ -0,0 +1,58 @@
+package ru.yandex.yamblz.genre.data.source.local;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+
+/**
+ * Created by platon on 10.08.2016.
+ */
+public class FileManager
+{
+ public void writeToFile(File file, String fileContent)
+ {
+ if (!file.exists())
+ {
+ try
+ {
+ FileWriter writer = new FileWriter(file);
+ writer.write(fileContent);
+ writer.close();
+ }
+ catch (IOException e)
+ {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ public String readFileContent(File file)
+ {
+ StringBuilder fileContentBuilder = new StringBuilder();
+ if (file.exists())
+ {
+ String stringLine;
+ try
+ {
+ FileReader fileReader = new FileReader(file);
+ BufferedReader bufferedReader = new BufferedReader(fileReader);
+
+ while ((stringLine = bufferedReader.readLine()) != null)
+ {
+ fileContentBuilder.append(stringLine).append("\n");
+ }
+
+ bufferedReader.close();
+ fileReader.close();
+ }
+ catch (IOException e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ return fileContentBuilder.toString();
+ }
+}
diff --git a/app/src/main/java/ru/yandex/yamblz/genre/data/source/local/ICache.java b/app/src/main/java/ru/yandex/yamblz/genre/data/source/local/ICache.java
new file mode 100644
index 0000000..212b63a
--- /dev/null
+++ b/app/src/main/java/ru/yandex/yamblz/genre/data/source/local/ICache.java
@@ -0,0 +1,14 @@
+package ru.yandex.yamblz.genre.data.source.local;
+
+import java.util.List;
+
+/**
+ * Created by platon on 19.07.2016.
+ */
+public interface ICache
+{
+ List get();
+ void put(List list);
+ boolean clear();
+ boolean isEmpty();
+}
diff --git a/app/src/main/java/ru/yandex/yamblz/genre/data/source/local/JsonSerializer.java b/app/src/main/java/ru/yandex/yamblz/genre/data/source/local/JsonSerializer.java
new file mode 100644
index 0000000..4fa732d
--- /dev/null
+++ b/app/src/main/java/ru/yandex/yamblz/genre/data/source/local/JsonSerializer.java
@@ -0,0 +1,26 @@
+package ru.yandex.yamblz.genre.data.source.local;
+
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+
+import java.util.List;
+
+import ru.yandex.yamblz.genre.data.entity.Artist;
+
+/**
+ * Created by platon on 10.08.2016.
+ */
+public class JsonSerializer
+{
+ private final Gson gson = new Gson();
+
+ public String serialize(List artist)
+ {
+ return gson.toJson(artist);
+ }
+
+ public List deserialize(String jsonString)
+ {
+ return gson.fromJson(jsonString, new TypeToken>() {}.getType());
+ }
+}
diff --git a/app/src/main/java/ru/yandex/yamblz/genre/interfaces/GenresPresenter.java b/app/src/main/java/ru/yandex/yamblz/genre/interfaces/GenresPresenter.java
new file mode 100644
index 0000000..82bd826
--- /dev/null
+++ b/app/src/main/java/ru/yandex/yamblz/genre/interfaces/GenresPresenter.java
@@ -0,0 +1,11 @@
+package ru.yandex.yamblz.genre.interfaces;
+
+/**
+ * Created by platon on 26.07.2016.
+ */
+public interface GenresPresenter {
+ void getGenres(boolean forceLoad);
+ void bind(T view);
+ void unbind();
+ void unsubscribe();
+}
diff --git a/app/src/main/java/ru/yandex/yamblz/genre/interfaces/GenresView.java b/app/src/main/java/ru/yandex/yamblz/genre/interfaces/GenresView.java
new file mode 100644
index 0000000..d5fad20
--- /dev/null
+++ b/app/src/main/java/ru/yandex/yamblz/genre/interfaces/GenresView.java
@@ -0,0 +1,14 @@
+package ru.yandex.yamblz.genre.interfaces;
+
+import java.util.List;
+
+import ru.yandex.yamblz.genre.data.entity.Genre;
+
+/**
+ * Created by platon on 26.07.2016.
+ */
+public interface GenresView {
+ void showProgress(boolean show);
+ void showGenres(List genres);
+ void showError(String error);
+}
diff --git a/app/src/main/java/ru/yandex/yamblz/genre/util/BitmapUtils.java b/app/src/main/java/ru/yandex/yamblz/genre/util/BitmapUtils.java
new file mode 100644
index 0000000..5a16f6c
--- /dev/null
+++ b/app/src/main/java/ru/yandex/yamblz/genre/util/BitmapUtils.java
@@ -0,0 +1,42 @@
+package ru.yandex.yamblz.genre.util;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+
+/**
+ * Created by platon on 28.07.2016.
+ */
+public class BitmapUtils
+{
+ public static Bitmap decodeBitmapFromByte(byte[] bitmapBytes, int offset, int length, int reqWidth,
+ int reqHeight) {
+ final BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inJustDecodeBounds = true;
+ BitmapFactory.decodeByteArray(bitmapBytes, 0, bitmapBytes.length, options);
+
+ options.inSampleSize = calculateIiSampleSize(options, reqWidth, reqHeight);
+ options.inJustDecodeBounds = false;
+
+ return BitmapFactory.decodeByteArray(bitmapBytes, 0, bitmapBytes.length, options);
+ }
+
+ public static int calculateIiSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight)
+ {
+ final int height = options.outHeight;
+ final int width = options.outWidth;
+ int inSampleSize = 1;
+
+ if (height > reqHeight || width > reqWidth)
+ {
+ final int halfHeight = height / 2;
+ final int halfWidth = width / 2;
+
+ while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth)
+ {
+ inSampleSize *= 2;
+ }
+ }
+
+ return inSampleSize;
+ }
+}
diff --git a/app/src/main/java/ru/yandex/yamblz/genre/util/INetworkManager.java b/app/src/main/java/ru/yandex/yamblz/genre/util/INetworkManager.java
new file mode 100644
index 0000000..910a344
--- /dev/null
+++ b/app/src/main/java/ru/yandex/yamblz/genre/util/INetworkManager.java
@@ -0,0 +1,9 @@
+package ru.yandex.yamblz.genre.util;
+
+/**
+ * Created by platon on 29.07.2016.
+ */
+public interface INetworkManager
+{
+ boolean networkIsAvailable();
+}
diff --git a/app/src/main/java/ru/yandex/yamblz/genre/util/NetworkManager.java b/app/src/main/java/ru/yandex/yamblz/genre/util/NetworkManager.java
new file mode 100644
index 0000000..f8bee82
--- /dev/null
+++ b/app/src/main/java/ru/yandex/yamblz/genre/util/NetworkManager.java
@@ -0,0 +1,37 @@
+package ru.yandex.yamblz.genre.util;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+
+/**
+ * Created by platon on 29.07.2016.
+ */
+public class NetworkManager implements INetworkManager
+{
+ private static NetworkManager sInstance;
+ private Context context;
+
+ public static void init(Context context)
+ {
+ sInstance = new NetworkManager(context);
+ }
+
+ public static NetworkManager getManager()
+ {
+ return sInstance;
+ }
+
+ private NetworkManager(Context context)
+ {
+ this.context = context;
+ }
+
+ @Override
+ public boolean networkIsAvailable()
+ {
+ ConnectivityManager connMgr = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+ NetworkInfo activeInfo = connMgr.getActiveNetworkInfo();
+ return (activeInfo != null && activeInfo.isConnected());
+ }
+}
diff --git a/app/src/main/java/ru/yandex/yamblz/genre/util/Utils.java b/app/src/main/java/ru/yandex/yamblz/genre/util/Utils.java
new file mode 100644
index 0000000..18f1669
--- /dev/null
+++ b/app/src/main/java/ru/yandex/yamblz/genre/util/Utils.java
@@ -0,0 +1,43 @@
+package ru.yandex.yamblz.genre.util;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import ru.yandex.yamblz.genre.data.entity.Artist;
+import ru.yandex.yamblz.genre.data.entity.Genre;
+
+/**
+ * Created by platon on 27.07.2016.
+ */
+public class Utils
+{
+ public static List transformArtistToGenres(List artists)
+ {
+ HashMap genresMap = new HashMap<>();
+ List genresList = new ArrayList<>();
+
+ for (Artist a : artists)
+ {
+ for (String s : a.getGenres())
+ {
+ if (genresMap.containsKey(s))
+ {
+ genresMap.get(s).getUrls().add(a.getCover().getCoverUrl());
+ }
+ else
+ {
+ List urls = new ArrayList<>();
+ urls.add(a.getCover().getCoverUrl());
+ Genre genre = new Genre();
+ genre.setName(s);
+ genre.setUrls(urls);
+ genresMap.put(s, genre);
+ }
+ }
+ }
+ genresList.addAll(genresMap.values());
+
+ return genresList;
+ }
+}
diff --git a/app/src/main/java/ru/yandex/yamblz/handler/CriticalSectionsHandler.java b/app/src/main/java/ru/yandex/yamblz/handler/CriticalSectionsHandler.java
index 8b8cc56..c92c2da 100644
--- a/app/src/main/java/ru/yandex/yamblz/handler/CriticalSectionsHandler.java
+++ b/app/src/main/java/ru/yandex/yamblz/handler/CriticalSectionsHandler.java
@@ -1,7 +1,7 @@
package ru.yandex.yamblz.handler;
-public interface CriticalSectionsHandler {
-
+public interface CriticalSectionsHandler
+{
void startSection(int id);
void stopSection(int id);
@@ -15,5 +15,4 @@ public interface CriticalSectionsHandler {
void removeLowPriorityTask(Task task);
void removeLowPriorityTasks();
-
}
diff --git a/app/src/main/java/ru/yandex/yamblz/handler/CriticalSectionsManager.java b/app/src/main/java/ru/yandex/yamblz/handler/CriticalSectionsManager.java
index 8a3d496..2876ff7 100644
--- a/app/src/main/java/ru/yandex/yamblz/handler/CriticalSectionsManager.java
+++ b/app/src/main/java/ru/yandex/yamblz/handler/CriticalSectionsManager.java
@@ -1,17 +1,21 @@
package ru.yandex.yamblz.handler;
-public class CriticalSectionsManager {
-
+public class CriticalSectionsManager
+{
private static CriticalSectionsHandler sCriticalSectionsHandler;
- public static void init(CriticalSectionsHandler criticalSectionsHandler) {
+ public static void init(CriticalSectionsHandler criticalSectionsHandler)
+ {
sCriticalSectionsHandler = criticalSectionsHandler;
}
- public static CriticalSectionsHandler getHandler() {
- if (sCriticalSectionsHandler == null) {
+ public static CriticalSectionsHandler getHandler()
+ {
+ if (sCriticalSectionsHandler == null)
+ {
sCriticalSectionsHandler = new StubCriticalSectionsHandler();
}
+
return sCriticalSectionsHandler;
}
}
diff --git a/app/src/main/java/ru/yandex/yamblz/handler/SimpleCriticalSectionHandler.java b/app/src/main/java/ru/yandex/yamblz/handler/SimpleCriticalSectionHandler.java
new file mode 100644
index 0000000..2b77f39
--- /dev/null
+++ b/app/src/main/java/ru/yandex/yamblz/handler/SimpleCriticalSectionHandler.java
@@ -0,0 +1,94 @@
+package ru.yandex.yamblz.handler;
+
+import android.os.Handler;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Created by platon on 29.07.2016.
+ */
+public class SimpleCriticalSectionHandler implements CriticalSectionsHandler
+{
+ private final List tasks;
+ private final Set sections;
+ private final Handler mainHandler;
+
+ public SimpleCriticalSectionHandler(Handler handler)
+ {
+ mainHandler = handler;
+ tasks = Collections.synchronizedList(new ArrayList<>());
+ sections = new TreeSet<>();
+ }
+
+ @Override
+ public void startSection(int id)
+ {
+ sections.add(id);
+ }
+
+ @Override
+ public void stopSection(int id)
+ {
+ sections.remove(id);
+
+ for (Task task : tasks)
+ {
+ if (sections.isEmpty()) mainHandler.post(task::run);
+ else break;
+ }
+
+ removeLowPriorityTasks();
+ }
+
+ @Override
+ public void stopSections()
+ {
+ sections.clear();
+
+ for (Task task : tasks)
+ {
+ if (sections.isEmpty()) mainHandler.post(task::run);
+ else break;
+ }
+
+ removeLowPriorityTasks();
+ }
+
+ @Override
+ public void postLowPriorityTask(Task task)
+ {
+ if (sections.isEmpty()) mainHandler.post(task::run);
+ else tasks.add(task);
+ }
+
+ @Override
+ public void postLowPriorityTaskDelayed(Task task, int delay)
+ {
+ mainHandler.postDelayed(() -> postLowPriorityTask(task), delay);
+ }
+
+ @Override
+ public void removeLowPriorityTask(Task task)
+ {
+ Iterator iterator = tasks.iterator();
+ while (iterator.hasNext())
+ {
+ if (iterator.next().equals(task)) iterator.remove();
+ break;
+ }
+ }
+
+ @Override
+ public void removeLowPriorityTasks()
+ {
+ tasks.clear();
+ }
+}
diff --git a/app/src/main/java/ru/yandex/yamblz/handler/StubCriticalSectionsHandler.java b/app/src/main/java/ru/yandex/yamblz/handler/StubCriticalSectionsHandler.java
index 0af9646..060f504 100644
--- a/app/src/main/java/ru/yandex/yamblz/handler/StubCriticalSectionsHandler.java
+++ b/app/src/main/java/ru/yandex/yamblz/handler/StubCriticalSectionsHandler.java
@@ -1,39 +1,25 @@
package ru.yandex.yamblz.handler;
-public class StubCriticalSectionsHandler implements CriticalSectionsHandler {
-
+public class StubCriticalSectionsHandler implements CriticalSectionsHandler
+{
@Override
- public void startSection(int id) {
-
- }
+ public void startSection(int id) {}
@Override
- public void stopSection(int id) {
-
- }
+ public void stopSection(int id) {}
@Override
- public void stopSections() {
-
- }
+ public void stopSections() {}
@Override
- public void postLowPriorityTask(Task task) {
-
- }
+ public void postLowPriorityTask(Task task) {}
@Override
- public void postLowPriorityTaskDelayed(Task task, int delay) {
-
- }
+ public void postLowPriorityTaskDelayed(Task task, int delay) {}
@Override
- public void removeLowPriorityTask(Task task) {
-
- }
+ public void removeLowPriorityTask(Task task) {}
@Override
- public void removeLowPriorityTasks() {
-
- }
+ public void removeLowPriorityTasks() {}
}
diff --git a/app/src/main/java/ru/yandex/yamblz/handler/Task.java b/app/src/main/java/ru/yandex/yamblz/handler/Task.java
index 164704c..c75f77c 100644
--- a/app/src/main/java/ru/yandex/yamblz/handler/Task.java
+++ b/app/src/main/java/ru/yandex/yamblz/handler/Task.java
@@ -1,7 +1,6 @@
package ru.yandex.yamblz.handler;
-public interface Task {
-
+public interface Task
+{
void run();
-
}
diff --git a/app/src/main/java/ru/yandex/yamblz/handler/TaskSetImageView.java b/app/src/main/java/ru/yandex/yamblz/handler/TaskSetImageView.java
new file mode 100644
index 0000000..3332c91
--- /dev/null
+++ b/app/src/main/java/ru/yandex/yamblz/handler/TaskSetImageView.java
@@ -0,0 +1,27 @@
+package ru.yandex.yamblz.handler;
+
+import android.graphics.Bitmap;
+import android.util.Log;
+
+import ru.yandex.yamblz.loader.interfaces.ImageTarget;
+
+/**
+ * Created by platon on 29.07.2016.
+ */
+public class TaskSetImageView implements Task
+{
+ private ImageTarget imageTarget;
+ private Bitmap bitmap;
+
+ public TaskSetImageView(ImageTarget imageTarget, Bitmap bitmap)
+ {
+ this.imageTarget = imageTarget;
+ this.bitmap = bitmap;
+ }
+
+ @Override
+ public void run()
+ {
+ imageTarget.onLoadBitmap(bitmap);
+ }
+}
diff --git a/app/src/main/java/ru/yandex/yamblz/loader/BitmapCache.java b/app/src/main/java/ru/yandex/yamblz/loader/BitmapCache.java
new file mode 100644
index 0000000..302e644
--- /dev/null
+++ b/app/src/main/java/ru/yandex/yamblz/loader/BitmapCache.java
@@ -0,0 +1,54 @@
+package ru.yandex.yamblz.loader;
+
+import android.graphics.Bitmap;
+import android.support.v4.util.LruCache;
+import ru.yandex.yamblz.loader.interfaces.ImageCache;
+
+/**
+ * Created by platon on 28.07.2016.
+ */
+public class BitmapCache implements ImageCache
+{
+ private static BitmapCache sInstance;
+ private LruCache mMemoryCache;
+
+ public static BitmapCache getCache()
+ {
+ if (sInstance == null)
+ {
+ sInstance = new BitmapCache();
+ }
+
+ return sInstance;
+ }
+
+ private BitmapCache()
+ {
+ int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
+ int cacheSize = maxMemory / 8;
+
+ mMemoryCache = new LruCache(cacheSize)
+ {
+ @Override
+ protected int sizeOf(String key, Bitmap bitmap)
+ {
+ return bitmap.getByteCount() / 1024;
+ }
+ };
+ }
+
+ @Override
+ public void put(String key, Bitmap bitmap)
+ {
+ if (get(key) == null)
+ {
+ mMemoryCache.put(key, bitmap);
+ }
+ }
+
+ @Override
+ public Bitmap get(String key)
+ {
+ return mMemoryCache.get(key);
+ }
+}
diff --git a/app/src/main/java/ru/yandex/yamblz/loader/CollageConsumer.java b/app/src/main/java/ru/yandex/yamblz/loader/CollageConsumer.java
new file mode 100644
index 0000000..9d6f72f
--- /dev/null
+++ b/app/src/main/java/ru/yandex/yamblz/loader/CollageConsumer.java
@@ -0,0 +1,48 @@
+package ru.yandex.yamblz.loader;
+
+import android.graphics.Bitmap;
+
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+
+import ru.yandex.yamblz.loader.interfaces.CollageStrategy;
+
+/**
+ * Created by platon on 29.07.2016.
+ */
+public class CollageConsumer extends Thread
+{
+ private Callback callback;
+ private CountDownLatch countDownLatch;
+ private CollageStrategy collageStrategy;
+ private List bitmaps;
+
+ public interface Callback
+ {
+ void postCollage(Bitmap bitmap);
+ }
+
+ public CollageConsumer(Callback callback, List bitmaps, CollageStrategy strategy, CountDownLatch cdl)
+ {
+ this.countDownLatch = cdl;
+ this.collageStrategy = strategy;
+ this.bitmaps = bitmaps;
+ this.callback = callback;
+ }
+
+ @Override
+ public void run()
+ {
+ super.run();
+ try
+ {
+ countDownLatch.await();
+ Bitmap bitmap = collageStrategy.create(bitmaps);
+ callback.postCollage(bitmap);
+ }
+ catch (InterruptedException e)
+ {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/app/src/main/java/ru/yandex/yamblz/loader/CollageLoaderManager.java b/app/src/main/java/ru/yandex/yamblz/loader/CollageLoaderManager.java
index ef00d00..268c3ee 100644
--- a/app/src/main/java/ru/yandex/yamblz/loader/CollageLoaderManager.java
+++ b/app/src/main/java/ru/yandex/yamblz/loader/CollageLoaderManager.java
@@ -1,17 +1,28 @@
package ru.yandex.yamblz.loader;
-public class CollageLoaderManager {
+import ru.yandex.yamblz.loader.interfaces.CollageLoader;
+public class CollageLoaderManager
+{
private static CollageLoader sCollageLoader;
- public static void init(CollageLoader collageLoader) {
+ public static void init(CollageLoader collageLoader)
+ {
sCollageLoader = collageLoader;
}
- public static CollageLoader getLoader() {
- if (sCollageLoader == null) {
+ public static CollageLoader getLoader()
+ {
+ if (sCollageLoader == null)
+ {
sCollageLoader = new StubCollageLoader();
}
+
return sCollageLoader;
}
+
+ public static void destroy()
+ {
+ sCollageLoader = null;
+ }
}
diff --git a/app/src/main/java/ru/yandex/yamblz/loader/CollageStrategyImpl.java b/app/src/main/java/ru/yandex/yamblz/loader/CollageStrategyImpl.java
new file mode 100644
index 0000000..428d5e3
--- /dev/null
+++ b/app/src/main/java/ru/yandex/yamblz/loader/CollageStrategyImpl.java
@@ -0,0 +1,63 @@
+package ru.yandex.yamblz.loader;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+
+import java.util.List;
+
+import ru.yandex.yamblz.loader.interfaces.CollageStrategy;
+
+/**
+ * Created by platon on 26.07.2016.
+ */
+public class CollageStrategyImpl implements CollageStrategy
+{
+ @Override
+ public Bitmap create(List bitmaps)
+ {
+ return combine(bitmaps);
+ }
+
+ private Bitmap combine(List bitmaps)
+ {
+
+ if (bitmaps.size() == 4 && !containsNull(bitmaps))
+ {
+ Paint paint = new Paint();
+ paint.setColor(Color.WHITE);
+
+ Bitmap first = bitmaps.get(0);
+ Bitmap second = bitmaps.get(1);
+ Bitmap third = bitmaps.get(2);
+ Bitmap fourth = bitmaps.get(3);
+
+ int centerX = first.getWidth();
+ int centerY = first.getHeight();
+
+ int width = centerX * 2;
+ int height = centerY * 2;
+
+ Bitmap collage = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
+ Canvas canvas = new Canvas(collage);
+
+ canvas.drawBitmap(first, 0f, 0f, paint);
+ canvas.drawBitmap(second, centerX, 0f, paint);
+ canvas.drawBitmap(third, 0f, centerY, paint);
+ canvas.drawBitmap(fourth, centerX, centerY, paint);
+
+ return collage;
+ }
+ else
+ {
+ return bitmaps.get(0);
+ }
+ }
+
+ private boolean containsNull(List bitmaps)
+ {
+ return (bitmaps.contains(null));
+ }
+}
diff --git a/app/src/main/java/ru/yandex/yamblz/loader/CriticalSectionsCollageLoader.java b/app/src/main/java/ru/yandex/yamblz/loader/CriticalSectionsCollageLoader.java
new file mode 100644
index 0000000..cbcf970
--- /dev/null
+++ b/app/src/main/java/ru/yandex/yamblz/loader/CriticalSectionsCollageLoader.java
@@ -0,0 +1,112 @@
+package ru.yandex.yamblz.loader;
+
+import android.graphics.Bitmap;
+import android.widget.ImageView;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import ru.yandex.yamblz.handler.CriticalSectionsHandler;
+import ru.yandex.yamblz.handler.CriticalSectionsManager;
+import ru.yandex.yamblz.handler.TaskSetImageView;
+import ru.yandex.yamblz.loader.interfaces.CollageLoader;
+import ru.yandex.yamblz.loader.interfaces.CollageStrategy;
+import ru.yandex.yamblz.loader.interfaces.ImageTarget;
+
+public class CriticalSectionsCollageLoader implements CollageLoader
+{
+ private static final int DEF_THREAD_COUNT = 4;
+
+ private final CollageStrategy collageStrategy;
+ private final List imageTargets;
+ private final LinkedList executors;
+ private final CriticalSectionsHandler criticalSections;
+
+ public CriticalSectionsCollageLoader()
+ {
+ collageStrategy = new CollageStrategyImpl();
+ imageTargets = Collections.synchronizedList(new LinkedList<>());
+ executors = new LinkedList<>();
+ criticalSections = CriticalSectionsManager.getHandler();
+ }
+
+ @Override
+ public void loadCollage(List urls, ImageView imageView)
+ {
+ ImageTarget imageTarget = new ImageTargetImpl(imageView);
+ loadCollage(urls, imageTarget);
+ }
+
+ @Override
+ public void loadCollage(List urls, ImageTarget imageTarget)
+ {
+ loadCollage(urls, imageTarget, collageStrategy);
+ }
+
+ @Override
+ public void loadCollage(List urls, ImageView imageView, CollageStrategy strategy)
+ {
+ ImageTarget imageTarget = new ImageTargetImpl(imageView);
+ loadCollage(urls, imageTarget, strategy);
+ }
+
+ @Override
+ public void loadCollage(List urls, ImageTarget imageTarget, CollageStrategy strategy)
+ {
+ if (urls.size() != 0)
+ {
+ int threadsCount = urls.size() < DEF_THREAD_COUNT ? 1 : DEF_THREAD_COUNT;
+
+ CountDownLatch cdl = new CountDownLatch(threadsCount);
+ List bitmaps = Collections.synchronizedList(new ArrayList<>());
+ ExecutorService downloadExecutor = Executors.newFixedThreadPool(threadsCount);
+
+ clearDuplicate(imageTarget.getImageView());
+ imageTargets.add(imageTarget);
+ executors.push(downloadExecutor);
+
+ for (int i = 0; i < threadsCount; i++)
+ {
+ ImageDownloader imgDownloader = new ImageDownloader(urls.get(i), bitmaps, cdl);
+ downloadExecutor.execute(imgDownloader);
+ }
+
+ CollageConsumer collageConsumer = new CollageConsumer(
+ bitmap -> postResult(imageTarget, bitmap),
+ bitmaps, strategy, cdl);
+
+ downloadExecutor.execute(collageConsumer);
+ downloadExecutor.shutdown();
+ }
+ }
+
+ private void postResult(ImageTarget imageTarget, Bitmap bitmap)
+ {
+ criticalSections.postLowPriorityTask(new TaskSetImageView(imageTarget, bitmap));
+ }
+
+ private void clearDuplicate(ImageView newImgView)
+ {
+ if (newImgView != null)
+ {
+ Iterator iterator = imageTargets.iterator();
+ while (iterator.hasNext())
+ {
+ ImageTarget cachedImageTarget = iterator.next();
+ ImageView cachedImgView = cachedImageTarget.getImageView();
+ if (cachedImgView!= null && cachedImgView.equals(newImgView))
+ {
+ cachedImageTarget.clear();
+ executors.pollLast().shutdownNow();
+ iterator.remove();
+ }
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/ru/yandex/yamblz/loader/ImageDownloader.java b/app/src/main/java/ru/yandex/yamblz/loader/ImageDownloader.java
new file mode 100644
index 0000000..2f11a04
--- /dev/null
+++ b/app/src/main/java/ru/yandex/yamblz/loader/ImageDownloader.java
@@ -0,0 +1,104 @@
+package ru.yandex.yamblz.loader;
+
+import android.graphics.Bitmap;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+
+import ru.yandex.yamblz.genre.util.BitmapUtils;
+import ru.yandex.yamblz.loader.interfaces.ImageCache;
+
+/**
+ * Created by platon on 27.07.2016.
+ */
+public class ImageDownloader implements Runnable
+{
+ private static final int READ_TIMEOUT = 10000;
+ private static final int CONNECTION_TIMEOUT = 15000;
+ private static final String REQUEST_METHOD = "GET";
+ private static final int REQ_WIDTH = 100;
+ private static final int REQ_HEIGHT = 100;
+
+ private final List bitmaps;
+ private final String url;
+ private final CountDownLatch countDownLatch;
+ private ImageCache bitmapCache;
+
+ public ImageDownloader(String url, List bitmaps, CountDownLatch countDownLatch)
+ {
+ this.countDownLatch = countDownLatch;
+ bitmapCache = BitmapCache.getCache();
+ this.bitmaps = bitmaps;
+ this.url = url;
+ }
+
+ @Override
+ public void run()
+ {
+ if (bitmapCache.get(url) != null) bitmaps.add(cachedBitmap());
+ else bitmaps.add(remoteBitmap());
+
+ countDownLatch.countDown();
+ }
+
+ private Bitmap remoteBitmap()
+ {
+ HttpURLConnection conn = null;
+ Bitmap bitmap = null;
+
+ try
+ {
+ conn = getConnection(url);
+ byte[] bitmapBytes = getBytes(conn.getInputStream());
+ bitmap = BitmapUtils.decodeBitmapFromByte(bitmapBytes, 0, bitmapBytes.length, REQ_WIDTH, REQ_HEIGHT);
+ bitmapCache.put(url, bitmap);
+ }
+ catch (IOException e)
+ {
+ e.printStackTrace();
+ }
+ finally
+ {
+ if (conn != null) { conn.disconnect(); }
+ }
+
+ return bitmap;
+ }
+
+ private Bitmap cachedBitmap()
+ {
+ return bitmapCache.get(url);
+ }
+
+ private byte[] getBytes(InputStream stream) throws IOException
+ {
+ ByteArrayOutputStream result = new ByteArrayOutputStream();
+ byte[] buffer = new byte[1024];
+ int length;
+
+ while ((length = stream.read(buffer)) != -1)
+ {
+ result.write(buffer, 0, length);
+ }
+
+ return result.toByteArray();
+ }
+
+ private HttpURLConnection getConnection(String urlString) throws IOException
+ {
+ URL url = new URL(urlString);
+ HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+ connection.setReadTimeout(READ_TIMEOUT);
+ connection.setConnectTimeout(CONNECTION_TIMEOUT);
+ connection.setRequestMethod(REQUEST_METHOD);
+ connection.setDoInput(true);
+
+ connection.connect();
+ return connection;
+ }
+}
diff --git a/app/src/main/java/ru/yandex/yamblz/loader/ImageTarget.java b/app/src/main/java/ru/yandex/yamblz/loader/ImageTarget.java
deleted file mode 100644
index 9a8be2b..0000000
--- a/app/src/main/java/ru/yandex/yamblz/loader/ImageTarget.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package ru.yandex.yamblz.loader;
-
-import android.graphics.Bitmap;
-
-public interface ImageTarget {
-
- void onLoadBitmap(Bitmap bitmap);
-}
diff --git a/app/src/main/java/ru/yandex/yamblz/loader/ImageTargetImpl.java b/app/src/main/java/ru/yandex/yamblz/loader/ImageTargetImpl.java
new file mode 100644
index 0000000..28027a5
--- /dev/null
+++ b/app/src/main/java/ru/yandex/yamblz/loader/ImageTargetImpl.java
@@ -0,0 +1,63 @@
+package ru.yandex.yamblz.loader;
+
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.graphics.Bitmap;
+import android.util.Log;
+import android.view.animation.AccelerateInterpolator;
+import android.widget.ImageView;
+
+import java.lang.ref.WeakReference;
+
+import ru.yandex.yamblz.loader.interfaces.ImageTarget;
+
+/**
+ * Created by platon on 26.07.2016.
+ */
+public class ImageTargetImpl implements ImageTarget
+{
+ private static final long DURATION = 300;
+ private static final int EMPTY = 0;
+
+ private final WeakReference weakReference;
+
+ public ImageTargetImpl(ImageView imageView)
+ {
+ imageView.setImageResource(EMPTY);
+ weakReference = new WeakReference<>(imageView);
+ }
+
+ @Override
+ public void onLoadBitmap(Bitmap bitmap)
+ {
+ ImageView imageView = weakReference.get();
+
+ if (imageView != null && bitmap != null)
+ {
+ animateChange(imageView);
+ imageView.setImageBitmap(bitmap);
+ }
+ }
+
+ @Override
+ public ImageView getImageView()
+ {
+ return weakReference.get();
+ }
+
+ @Override
+ public void clear()
+ {
+ weakReference.clear();
+ }
+
+ private void animateChange(final ImageView imageView)
+ {
+ ObjectAnimator fadeIn = ObjectAnimator.ofFloat(imageView, "alpha", 0.0f, 1.0f);
+ fadeIn.setInterpolator(new AccelerateInterpolator());
+
+ AnimatorSet animatorSet = new AnimatorSet();
+ animatorSet.setDuration(DURATION).play(fadeIn);
+ animatorSet.start();
+ }
+}
diff --git a/app/src/main/java/ru/yandex/yamblz/loader/SimpleCollageLoader.java b/app/src/main/java/ru/yandex/yamblz/loader/SimpleCollageLoader.java
new file mode 100644
index 0000000..e62105f
--- /dev/null
+++ b/app/src/main/java/ru/yandex/yamblz/loader/SimpleCollageLoader.java
@@ -0,0 +1,94 @@
+package ru.yandex.yamblz.loader;
+
+import android.graphics.Bitmap;
+import android.os.Handler;
+import android.widget.ImageView;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import ru.yandex.yamblz.loader.interfaces.CollageLoader;
+import ru.yandex.yamblz.loader.interfaces.CollageStrategy;
+import ru.yandex.yamblz.loader.interfaces.ImageTarget;
+
+public class SimpleCollageLoader implements CollageLoader
+{
+ private static final int DEF_THREAD_COUNT = 4;
+
+ private final CollageStrategy collageStrategy;
+ private final Handler mainThreadHandler;
+ private final LinkedList imageTargets;
+
+ public SimpleCollageLoader(Handler handler)
+ {
+ mainThreadHandler = handler;
+ collageStrategy = new CollageStrategyImpl();
+ imageTargets = new LinkedList<>();
+ }
+
+ @Override
+ public void loadCollage(List urls, ImageView imageView)
+ {
+ ImageTarget imageTarget = new ImageTargetImpl(imageView);
+ loadCollage(urls, imageTarget);
+ }
+
+ @Override
+ public void loadCollage(List urls, ImageTarget imageTarget)
+ {
+ loadCollage(urls, imageTarget, collageStrategy);
+ }
+
+ @Override
+ public void loadCollage(List urls, ImageView imageView, CollageStrategy strategy)
+ {
+ ImageTarget imageTarget = new ImageTargetImpl(imageView);
+ loadCollage(urls, imageTarget, strategy);
+ }
+
+ @Override
+ public void loadCollage(List urls, ImageTarget imageTarget, CollageStrategy strategy)
+ {
+ int threadsCount = urls.size() < DEF_THREAD_COUNT ? 1 : DEF_THREAD_COUNT;
+ CountDownLatch cdl = new CountDownLatch(threadsCount);
+ List bitmaps = Collections.synchronizedList(new ArrayList<>());
+ ExecutorService downloadExecutor = Executors.newFixedThreadPool(threadsCount);
+
+ clearDuplicate(imageTarget.getImageView());
+ imageTargets.push(imageTarget);
+
+ for(int i = 0; i < threadsCount; i++)
+ {
+ ImageDownloader imgDownloader = new ImageDownloader(urls.get(i), bitmaps, cdl);
+ downloadExecutor.execute(imgDownloader);
+ }
+
+ downloadExecutor.shutdown();
+
+ new CollageConsumer(
+ bitmap -> postResult(imageTarget, bitmap),
+ bitmaps, strategy, cdl).start();
+ }
+
+ private void postResult(ImageTarget imageTarget, Bitmap bitmap)
+ {
+ mainThreadHandler.post(() -> imageTarget.onLoadBitmap(bitmap));
+ }
+
+ private void clearDuplicate(ImageView newImgView)
+ {
+ if (newImgView != null)
+ {
+ for (ImageTarget it: imageTargets)
+ {
+ ImageView cachedImgView = it.getImageView();
+ if (cachedImgView!= null && cachedImgView.equals(newImgView)) it.clear();
+ }
+ }
+ }
+}
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..67064b6 100644
--- a/app/src/main/java/ru/yandex/yamblz/loader/StubCollageLoader.java
+++ b/app/src/main/java/ru/yandex/yamblz/loader/StubCollageLoader.java
@@ -4,28 +4,21 @@
import java.util.List;
-public class StubCollageLoader implements CollageLoader {
+import ru.yandex.yamblz.loader.interfaces.CollageLoader;
+import ru.yandex.yamblz.loader.interfaces.CollageStrategy;
+import ru.yandex.yamblz.loader.interfaces.ImageTarget;
+public class StubCollageLoader implements CollageLoader
+{
@Override
- public void loadCollage(List urls, ImageView imageView) {
-
- }
+ public void loadCollage(List urls, ImageView imageView) {}
@Override
- public void loadCollage(List urls, ImageTarget imageTarget) {
-
- }
+ public void loadCollage(List urls, ImageTarget imageTarget) {}
@Override
- public void loadCollage(List urls, ImageView imageView,
- CollageStrategy collageStrategy) {
-
- }
+ public void loadCollage(List urls, ImageView imageView, CollageStrategy strategy) {}
@Override
- public void loadCollage(List urls, ImageTarget imageTarget,
- CollageStrategy collageStrategy) {
-
- }
-
+ public void loadCollage(List urls, ImageTarget imageTarget, CollageStrategy strategy) {}
}
diff --git a/app/src/main/java/ru/yandex/yamblz/loader/CollageLoader.java b/app/src/main/java/ru/yandex/yamblz/loader/interfaces/CollageLoader.java
similarity index 90%
rename from app/src/main/java/ru/yandex/yamblz/loader/CollageLoader.java
rename to app/src/main/java/ru/yandex/yamblz/loader/interfaces/CollageLoader.java
index dad5b76..f1c76d8 100644
--- a/app/src/main/java/ru/yandex/yamblz/loader/CollageLoader.java
+++ b/app/src/main/java/ru/yandex/yamblz/loader/interfaces/CollageLoader.java
@@ -1,4 +1,4 @@
-package ru.yandex.yamblz.loader;
+package ru.yandex.yamblz.loader.interfaces;
import android.widget.ImageView;
@@ -13,5 +13,4 @@ public interface CollageLoader {
void loadCollage(List urls, ImageView imageView, CollageStrategy collageStrategy);
void loadCollage(List urls, ImageTarget imageTarget, CollageStrategy collageStrategy);
-
}
diff --git a/app/src/main/java/ru/yandex/yamblz/loader/CollageStrategy.java b/app/src/main/java/ru/yandex/yamblz/loader/interfaces/CollageStrategy.java
similarity index 75%
rename from app/src/main/java/ru/yandex/yamblz/loader/CollageStrategy.java
rename to app/src/main/java/ru/yandex/yamblz/loader/interfaces/CollageStrategy.java
index d81d52d..f4f30e6 100644
--- a/app/src/main/java/ru/yandex/yamblz/loader/CollageStrategy.java
+++ b/app/src/main/java/ru/yandex/yamblz/loader/interfaces/CollageStrategy.java
@@ -1,4 +1,4 @@
-package ru.yandex.yamblz.loader;
+package ru.yandex.yamblz.loader.interfaces;
import android.graphics.Bitmap;
diff --git a/app/src/main/java/ru/yandex/yamblz/loader/interfaces/ImageCache.java b/app/src/main/java/ru/yandex/yamblz/loader/interfaces/ImageCache.java
new file mode 100644
index 0000000..fb754bb
--- /dev/null
+++ b/app/src/main/java/ru/yandex/yamblz/loader/interfaces/ImageCache.java
@@ -0,0 +1,12 @@
+package ru.yandex.yamblz.loader.interfaces;
+
+import android.graphics.Bitmap;
+
+/**
+ * Created by platon on 28.07.2016.
+ */
+public interface ImageCache
+{
+ void put(String key, I image);
+ I get(String key);
+}
diff --git a/app/src/main/java/ru/yandex/yamblz/loader/interfaces/ImageTarget.java b/app/src/main/java/ru/yandex/yamblz/loader/interfaces/ImageTarget.java
new file mode 100644
index 0000000..3b8113c
--- /dev/null
+++ b/app/src/main/java/ru/yandex/yamblz/loader/interfaces/ImageTarget.java
@@ -0,0 +1,11 @@
+package ru.yandex.yamblz.loader.interfaces;
+
+import android.graphics.Bitmap;
+import android.widget.ImageView;
+
+public interface ImageTarget {
+
+ void onLoadBitmap(Bitmap bitmap);
+ ImageView getImageView();
+ void clear();
+}
diff --git a/app/src/main/java/ru/yandex/yamblz/ui/activities/MainActivity.java b/app/src/main/java/ru/yandex/yamblz/ui/activities/MainActivity.java
index 3b9efea..eb6b11c 100644
--- a/app/src/main/java/ru/yandex/yamblz/ui/activities/MainActivity.java
+++ b/app/src/main/java/ru/yandex/yamblz/ui/activities/MainActivity.java
@@ -9,8 +9,8 @@
import ru.yandex.yamblz.App;
import ru.yandex.yamblz.R;
+import ru.yandex.yamblz.genre.GenresFragment;
import ru.yandex.yamblz.developer_settings.DeveloperSettingsModule;
-import ru.yandex.yamblz.ui.fragments.ContentFragment;
import ru.yandex.yamblz.ui.other.ViewModifier;
public class MainActivity extends BaseActivity {
@@ -29,7 +29,7 @@ protected void onCreate(@Nullable Bundle savedInstanceState) {
if (savedInstanceState == null) {
getSupportFragmentManager()
.beginTransaction()
- .replace(R.id.main_frame_layout, new ContentFragment())
+ .replace(R.id.main_frame_layout, new GenresFragment())
.commit();
}
}
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
deleted file mode 100644
index d46490f..0000000
--- a/app/src/main/java/ru/yandex/yamblz/ui/fragments/ContentFragment.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package ru.yandex.yamblz.ui.fragments;
-
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-import ru.yandex.yamblz.R;
-
-public class ContentFragment extends BaseFragment {
- @NonNull
- @Override
- public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
- return inflater.inflate(R.layout.fragment_content, container, false);
- }
-}
diff --git a/app/src/main/res/drawable/ic_place_holder.xml b/app/src/main/res/drawable/ic_place_holder.xml
new file mode 100644
index 0000000..5cd87d1
--- /dev/null
+++ b/app/src/main/res/drawable/ic_place_holder.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/layout/fragment_content.xml b/app/src/main/res/layout/fragment_content.xml
index 81016ea..2f418ec 100644
--- a/app/src/main/res/layout/fragment_content.xml
+++ b/app/src/main/res/layout/fragment_content.xml
@@ -1,14 +1,22 @@
-
+
-
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_genre.xml b/app/src/main/res/layout/item_genre.xml
new file mode 100644
index 0000000..da08812
--- /dev/null
+++ b/app/src/main/res/layout/item_genre.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 7474f04..de8bd5d 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,4 +1,4 @@
- Yamblz
+ Genres
Hello
diff --git a/dependencies.gradle b/dependencies.gradle
index d9cb375..638b881 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',
@@ -23,6 +23,8 @@ ext.versions = [
supportLibs : '23.1.1',
butterKnife : '8.0.1',
timber : '4.1.2',
+ rxAndroid : '1.2.0',
+ retrofit2 : '2.0.1',
espresso : '2.2.1',
stetho : '1.3.1',
@@ -66,6 +68,10 @@ ext.libraries = [
butterKnife : "com.jakewharton:butterknife:$versions.butterKnife",
butterKnifeCompiler : "com.jakewharton:butterknife-compiler:$versions.butterKnife",
timber : "com.jakewharton.timber:timber:$versions.timber",
+ rxAndroid : "io.reactivex:rxandroid:$versions.rxAndroid",
+ retrofit2 : "com.squareup.retrofit2:retrofit:$versions.retrofit2",
+ converterGson : "com.squareup.retrofit2:converter-gson:$versions.retrofit2",
+ adapterRxjava : "com.squareup.retrofit2:adapter-rxjava:$versions.retrofit2",
// Developer Tools
stetho : "com.facebook.stetho:stetho:$versions.stetho",
diff --git a/gradle.properties b/gradle.properties
index 6b6a634..c1a64b5 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,2 +1,2 @@
# Enlarge the gradle heap size.
-org.gradle.jvmargs=-Xmx3584m -XX:MaxPermSize=1024m -Dfile.encoding=UTF-8
+org.gradle.jvmargs=-Xmx1024m -XX:MaxPermSize=1024m -Dfile.encoding=UTF-8