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