diff --git a/animation.gif b/animation.gif new file mode 100644 index 0000000..18ea9e9 Binary files /dev/null and b/animation.gif differ diff --git a/app/build.gradle b/app/build.gradle index 445372a..4be638f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -87,6 +87,13 @@ dependencies { compile libraries.dagger apt libraries.daggerCompiler + compile libraries.rxjava + compile libraries.rxandroid + + compile libraries.gson + compile libraries.okhttp + compile libraries.picasso + compile libraries.supportAnnotations compile libraries.supportAppCompat compile libraries.supportDesign diff --git a/app/src/main/java/ru/yandex/yamblz/App.java b/app/src/main/java/ru/yandex/yamblz/App.java index e5f9972..40b371a 100644 --- a/app/src/main/java/ru/yandex/yamblz/App.java +++ b/app/src/main/java/ru/yandex/yamblz/App.java @@ -35,7 +35,7 @@ public void onCreate() { } CollageLoaderManager.init(null); // add implementation - CriticalSectionsManager.init(null); // add implementation + //CriticalSectionsManager.init(null); // add implementation } @NonNull diff --git a/app/src/main/java/ru/yandex/yamblz/handler/CriticalSectionsHandlerImpl.java b/app/src/main/java/ru/yandex/yamblz/handler/CriticalSectionsHandlerImpl.java new file mode 100644 index 0000000..dd3f1a2 --- /dev/null +++ b/app/src/main/java/ru/yandex/yamblz/handler/CriticalSectionsHandlerImpl.java @@ -0,0 +1,82 @@ +package ru.yandex.yamblz.handler; + + +import android.os.Handler; +import android.os.Looper; +import android.util.SparseIntArray; + +import java.util.Queue; +import java.util.concurrent.LinkedBlockingQueue; + +public class CriticalSectionsHandlerImpl implements CriticalSectionsHandler { + private Handler mHandler; + private Queue mTaskQueue; + private SparseIntArray mSectionArray; + + public CriticalSectionsHandlerImpl() { + mHandler = new Handler(Looper.getMainLooper()); + mTaskQueue = new LinkedBlockingQueue<>(); + mSectionArray = new SparseIntArray(); + } + + @Override + public void startSection(int id) { + mSectionArray.append(id, 1); + } + + @Override + public void stopSection(int id) { + mSectionArray.delete(id); + doNext(); + } + + + @Override + public void stopSections() { + mSectionArray.clear(); + doNext(); + } + + @Override + public void postLowPriorityTask(Task task) { + if (mSectionArray.size() != 0) { + mTaskQueue.add(task); + } else { + mHandler.post(task::run); + } + } + + @Override + public void postLowPriorityTaskDelayed(Task task, int delay) { + if (mSectionArray.size() != 0) { + mTaskQueue.add(task); + } else { + mHandler.postDelayed(task::run, delay); + } + } + + @Override + public void removeLowPriorityTask(Task task) { + mHandler.removeCallbacks(task::run); + } + + @Override + public void removeLowPriorityTasks() { + while (!mTaskQueue.isEmpty()) { + mHandler.removeCallbacks(mTaskQueue.poll()::run); + } + } + + private void doNext() { + if (mSectionArray.size() == 0) { + Task task = mTaskQueue.poll(); + if (task != null) { + Task anotherTask = () -> { + task.run(); + doNext(); + }; + mHandler.post(anotherTask::run); + } + } + } +} 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..3c10fae 100644 --- a/app/src/main/java/ru/yandex/yamblz/handler/CriticalSectionsManager.java +++ b/app/src/main/java/ru/yandex/yamblz/handler/CriticalSectionsManager.java @@ -10,8 +10,8 @@ public static void init(CriticalSectionsHandler criticalSectionsHandler) { public static CriticalSectionsHandler getHandler() { if (sCriticalSectionsHandler == null) { - sCriticalSectionsHandler = new StubCriticalSectionsHandler(); + sCriticalSectionsHandler = new CriticalSectionsHandlerImpl(); } return sCriticalSectionsHandler; } -} +} \ No newline at end of file diff --git a/app/src/main/java/ru/yandex/yamblz/handler/StubCriticalSectionsHandler.java b/app/src/main/java/ru/yandex/yamblz/handler/StubCriticalSectionsHandler.java deleted file mode 100644 index 0af9646..0000000 --- a/app/src/main/java/ru/yandex/yamblz/handler/StubCriticalSectionsHandler.java +++ /dev/null @@ -1,39 +0,0 @@ -package ru.yandex.yamblz.handler; - -public class StubCriticalSectionsHandler implements CriticalSectionsHandler { - - @Override - public void startSection(int id) { - - } - - @Override - public void stopSection(int id) { - - } - - @Override - public void stopSections() { - - } - - @Override - public void postLowPriorityTask(Task task) { - - } - - @Override - public void postLowPriorityTaskDelayed(Task task, int delay) { - - } - - @Override - public void removeLowPriorityTask(Task task) { - - } - - @Override - public void removeLowPriorityTasks() { - - } -} diff --git a/app/src/main/java/ru/yandex/yamblz/handler/TaskImpl.java b/app/src/main/java/ru/yandex/yamblz/handler/TaskImpl.java new file mode 100644 index 0000000..f7d642e --- /dev/null +++ b/app/src/main/java/ru/yandex/yamblz/handler/TaskImpl.java @@ -0,0 +1,9 @@ +package ru.yandex.yamblz.handler; + + +public class TaskImpl implements Task { + @Override + public void run() { + + } +} 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..db577a3 100644 --- a/app/src/main/java/ru/yandex/yamblz/loader/CollageLoaderManager.java +++ b/app/src/main/java/ru/yandex/yamblz/loader/CollageLoaderManager.java @@ -10,7 +10,7 @@ public static void init(CollageLoader collageLoader) { public static CollageLoader getLoader() { if (sCollageLoader == null) { - sCollageLoader = new StubCollageLoader(); + throw new RuntimeException("Надо было инициализировать"); } return sCollageLoader; } diff --git a/app/src/main/java/ru/yandex/yamblz/loader/StubCollageLoader.java b/app/src/main/java/ru/yandex/yamblz/loader/StubCollageLoader.java deleted file mode 100644 index 1dc051b..0000000 --- a/app/src/main/java/ru/yandex/yamblz/loader/StubCollageLoader.java +++ /dev/null @@ -1,31 +0,0 @@ -package ru.yandex.yamblz.loader; - -import android.widget.ImageView; - -import java.util.List; - -public class StubCollageLoader implements CollageLoader { - - @Override - public void loadCollage(List urls, ImageView imageView) { - - } - - @Override - public void loadCollage(List urls, ImageTarget imageTarget) { - - } - - @Override - public void loadCollage(List urls, ImageView imageView, - CollageStrategy collageStrategy) { - - } - - @Override - public void loadCollage(List urls, ImageTarget imageTarget, - CollageStrategy collageStrategy) { - - } - -} diff --git a/app/src/main/java/ru/yandex/yamblz/loader/square/CollageLoaderSquare.java b/app/src/main/java/ru/yandex/yamblz/loader/square/CollageLoaderSquare.java new file mode 100644 index 0000000..038f826 --- /dev/null +++ b/app/src/main/java/ru/yandex/yamblz/loader/square/CollageLoaderSquare.java @@ -0,0 +1,133 @@ +package ru.yandex.yamblz.loader.square; + +import android.graphics.Bitmap; +import android.util.SparseArray; +import android.widget.ImageView; + +import com.squareup.picasso.Picasso; + +import java.io.IOException; +import java.lang.ref.WeakReference; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import ru.yandex.yamblz.loader.CollageLoader; +import ru.yandex.yamblz.loader.CollageStrategy; +import ru.yandex.yamblz.loader.ImageTarget; +import rx.Observable; +import rx.Subscription; +import rx.android.schedulers.AndroidSchedulers; +import rx.functions.Action0; +import rx.functions.Action1; +import rx.functions.Func1; +import rx.schedulers.Schedulers; + +public class CollageLoaderSquare implements CollageLoader { + private static final int NUMBER_OF_THREADS = 2; + private final CollageStrategySquare DEFAULT_COLLAGE_STRATEGY = new CollageStrategySquare(); + private final Picasso mPicasso; + private final SparseArray mSubscriptionMap = new SparseArray<>(); // Хранит подписки + + public CollageLoaderSquare(Picasso picasso) { + mPicasso = picasso; + } + + @Override + public void loadCollage(List urls, ImageView imageView) { + loadCollage(urls, imageView, DEFAULT_COLLAGE_STRATEGY); + } + + @Override + public void loadCollage(List urls, ImageTarget imageTarget) { + loadCollage(urls, imageTarget, DEFAULT_COLLAGE_STRATEGY); + } + + @Override + public void loadCollage(List urls, ImageView imageView, CollageStrategy collageStrategy) { + WeakReference imageViewWeakReference = new WeakReference<>(imageView); + ImageTarget imageTarget = new ImageTarget() { + @Override + public void onLoadBitmap(Bitmap bitmap) { + ImageView targetImageView = imageViewWeakReference.get(); + if (targetImageView != null) { + targetImageView.setImageBitmap(bitmap); + } + } + + @Override + public int hashCode() { // Раньше тут был костыль, но теперь его нет + ImageView targetImageView = imageViewWeakReference.get(); + if (targetImageView != null) { + return targetImageView.hashCode(); + } else { + return 0; + } + } + }; + + loadCollage(urls, imageTarget, collageStrategy); + } + + @Override + public void loadCollage(List urls, ImageTarget imageTarget, CollageStrategy collageStrategy) { + int imageTargetId = imageTarget.hashCode(); + + Subscription subscription = mSubscriptionMap.get(imageTargetId); + if (subscription != null) { + subscription.unsubscribe(); + } + + Action1 onCreateCollage = imageTarget::onLoadBitmap; + + Subscription newSubscription = makeCollage(urls, onCreateCollage, collageStrategy); + mSubscriptionMap.put(imageTargetId, newSubscription); + } + + private Subscription makeCollage(List urls, Action1 onCreateCollage, CollageStrategy collageStrategy) { + final AtomicInteger counter = new AtomicInteger(); + final List bitmapList = new CopyOnWriteArrayList<>(); // Скорость?.. + // Чистим за собой + // onComplete после unsubscribe не вызывается, но GC всё равно всё собирает + final Action0 clearBitmaps = () -> { + for (Bitmap bitmap : bitmapList) { + bitmap.recycle(); + } + }; + + return Observable.from(urls) + .groupBy(s -> counter.getAndIncrement() % NUMBER_OF_THREADS) // Делим на NUMBER_OF_THREADS потоков + .flatMap(g -> g.subscribeOn(Schedulers.newThread())) // Грузим картинки (вроде) параллельно + .map(this::downloadImage) // Превращаем String в Bitmap + .filter(b -> b != null) // Выкидываем незагрузившиеся + .flatMap(new Func1>>() { // Превращаем Bitmap в List для CollageStrategy + @Override + public Observable> call(Bitmap bitmap) { + bitmapList.add(bitmap); + return Observable.just(bitmapList); + } + }) + .filter(list -> Math.round(Math.sqrt(list.size())) == Math.sqrt(list.size())) // Будем делать коллаж каждые 1, 4, 9, 25, 36... картинок, дабы было квадратненько + .sample(1, TimeUnit.SECONDS) + .map(list -> list.subList(0, (int) Math.pow(Math.floor(Math.sqrt(list.size())), 2))) // См. ниже + .flatMap(list -> Observable.just(collageStrategy.create(list))) // Собсно делаем коллаж + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(onCreateCollage, Throwable::printStackTrace, clearBitmaps); // Обработка ошибок напргяает + // Проблема с sample (да и вообще со всем кодом выше) в том, что он не даёт Observable отправлять список битмапов чаще, чем раз в секунду + // Однако до него есть оператор flatMap, который всё время что-то добавляет в этот список + // В итоге коллаж делается не из 1, 4, 9... картинок, а из сколько выйдет + // Поэтому приходится брать корень из list.size(), округлять его вниз и возводить в квадрат + // В качестве выхода можно было бы отправлять в CollageStrategy Observable и пусть он там сам обпроверяется, но интерфейсы трогать негоже + } + + // Picasso кеширует картинки на диск, скажем ей спасибо + private Bitmap downloadImage(String url) { + try { + return mPicasso.load(url).get(); + } catch (IOException e) { + e.printStackTrace(); + return null; + } + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/yandex/yamblz/loader/square/CollageStrategySquare.java b/app/src/main/java/ru/yandex/yamblz/loader/square/CollageStrategySquare.java new file mode 100644 index 0000000..ca59153 --- /dev/null +++ b/app/src/main/java/ru/yandex/yamblz/loader/square/CollageStrategySquare.java @@ -0,0 +1,48 @@ +package ru.yandex.yamblz.loader.square; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Rect; + +import java.util.List; + +import ru.yandex.yamblz.loader.CollageStrategy; + +public class CollageStrategySquare implements CollageStrategy { + private static final int COLLAGE_SIZE = 1500; + + @Override + public Bitmap create(List bitmaps) { + if (bitmaps == null || bitmaps.size() == 0) { + return null; // Нечего на вход ерунду подавать + } + + int pictureSize = (int) Math.floor(COLLAGE_SIZE / Math.sqrt(bitmaps.size())); // В меньшую сторону + int realCollageSize = (int) (pictureSize * Math.ceil(Math.sqrt(bitmaps.size()))); // В большую сторону + + Bitmap collage = Bitmap.createBitmap(realCollageSize, realCollageSize, Bitmap.Config.ARGB_4444); + + Canvas canvas = new Canvas(collage); + int top = 0, left = 0; + for (Bitmap currentBitmap : bitmaps) { + if (left >= realCollageSize) { + left = 0; + top += pictureSize; + } + + int currentBitmapSize; + if (currentBitmap.getHeight() < currentBitmap.getWidth()) { + currentBitmapSize = currentBitmap.getHeight(); + } else { + currentBitmapSize = currentBitmap.getWidth(); + } + + canvas.drawBitmap(currentBitmap, new Rect(0, 0, currentBitmapSize, currentBitmapSize), + new Rect(left, top, left + pictureSize, top + pictureSize), null); + left += pictureSize; + } + + canvas.save(); + return collage; + } +} diff --git a/app/src/main/java/ru/yandex/yamblz/model/Artist.java b/app/src/main/java/ru/yandex/yamblz/model/Artist.java new file mode 100644 index 0000000..d715b9f --- /dev/null +++ b/app/src/main/java/ru/yandex/yamblz/model/Artist.java @@ -0,0 +1,138 @@ +package ru.yandex.yamblz.model; + +import android.os.Parcel; +import android.os.Parcelable; +import android.support.annotation.NonNull; +import android.text.TextUtils; + +import com.google.gson.annotations.SerializedName; + +import java.io.Serializable; +import java.util.Collections; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +public class Artist implements Comparable, Parcelable, Serializable { + + public static final Creator CREATOR = new Creator() { + @Override + public Artist createFromParcel(Parcel in) { + return new Artist(in); + } + + @Override + public Artist[] newArray(int size) { + return new Artist[size]; + } + }; + + @SerializedName("id") + private long mId; + @SerializedName("name") + private String mName; + @SerializedName("genres") + private String[] mGenres; + @SerializedName("tracks") + private int mCountOfTracks; + @SerializedName("albums") + private int mCountOfAlbums; + @SerializedName("link") + private String mSiteUrl; + @SerializedName("description") + private String mDescription; + @SerializedName("cover") + private Map mCover; + + protected Artist(Parcel in) { + mId = in.readLong(); + mName = in.readString(); + mGenres = in.createStringArray(); + mCountOfTracks = in.readInt(); + mCountOfAlbums = in.readInt(); + mSiteUrl = in.readString(); + mDescription = in.readString(); + } + + public long getId() { + return mId; + } + + public String getName() { + return mName; + } + + public String getGenres() { + return TextUtils.join(", ", mGenres); + } + + // TODO: Можно ли сразу Set? + public Set getGenresSet() { + Set set = new HashSet<>(); + + Collections.addAll(set, mGenres); + + return set; + } + + public int getCountOfTracks() { + return mCountOfTracks; + } + + public int getCountOfAlbums() { + return mCountOfAlbums; + } + + public String getSiteUrl() { + return mSiteUrl; + } + + public String getDescription() { + return mDescription; + } + + public String getUrlOfBigCover() { + return mCover.get("big"); + } + + public String getUrlOfSmallCover() { + return mCover.get("small"); + } + + @Override + public int compareTo(@NonNull Artist another) { + return getName().compareTo(another.getName()); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Artist)) return false; + + Artist artist = (Artist) o; + + return mId == artist.mId; + + } + + @Override + public int hashCode() { + return (int) (mId ^ (mId >>> 32)); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeLong(mId); + dest.writeString(mName); + dest.writeStringArray(mGenres); + dest.writeInt(mCountOfTracks); + dest.writeInt(mCountOfAlbums); + dest.writeString(mSiteUrl); + dest.writeString(mDescription); + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/yandex/yamblz/model/ArtistFetcher.java b/app/src/main/java/ru/yandex/yamblz/model/ArtistFetcher.java new file mode 100644 index 0000000..2dde09b --- /dev/null +++ b/app/src/main/java/ru/yandex/yamblz/model/ArtistFetcher.java @@ -0,0 +1,43 @@ +package ru.yandex.yamblz.model; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; + +import java.io.IOException; +import java.lang.reflect.Type; +import java.util.List; + +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; + +public class ArtistFetcher { + private static final String JSON_URL = "http://download.cdn.yandex.net/mobilization-2016/artists.json"; + + public String getJson() throws IOException { + return getJsonFromUrl(JSON_URL); + } + + public List getArtistsFromJson() throws IOException { + return getArtistsFromJson(getJson()); + } + + public List getArtistsFromJson(String json) { + Gson gson = new Gson(); + Type type = new TypeToken>() { + }.getType(); + + return gson.fromJson(json, type); + } + + private String getJsonFromUrl(String urlSpec) throws IOException { + OkHttpClient client = new OkHttpClient(); + + Request request = new Request.Builder() + .url(urlSpec) + .build(); + + Response response = client.newCall(request).execute(); + return response.body().string(); + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/yandex/yamblz/model/ArtistLab.java b/app/src/main/java/ru/yandex/yamblz/model/ArtistLab.java new file mode 100644 index 0000000..d1a31f9 --- /dev/null +++ b/app/src/main/java/ru/yandex/yamblz/model/ArtistLab.java @@ -0,0 +1,144 @@ +package ru.yandex.yamblz.model; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.util.Log; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class ArtistLab { + private static final String TAG = "ArtistLab"; + private static final String ARTIST_LIST_NAME = "artistList"; + + + @SuppressLint("StaticFieldLeak") + private static ArtistLab sArtistLab; + + private Context mContext; + private List mArtists; + + private ArtistLab(Context context) { + mContext = context.getApplicationContext(); + mArtists = new ArrayList<>(); + } + + public static ArtistLab get(Context context) { + if (sArtistLab == null) { + sArtistLab = new ArtistLab(context); + } + return sArtistLab; + } + + public List getArtists() { + shouldLoadArtist(); + return mArtists; + } + + public void setArtists(List artists) { + mArtists = artists; + saveArtists(mArtists); + } + + public Artist getArtist(int position) { + shouldLoadArtist(); + return mArtists.get(position); + } + + public Map> getGenresMap() { + shouldLoadArtist(); + return artistsToGenres(mArtists); + } + + public List getGenresList() { + shouldLoadArtist(); + return genresMapToList(artistsToGenres(mArtists)); + } + + private void shouldLoadArtist() { + List tempArtists = loadArtists(); + + if (tempArtists != null) { + mArtists = tempArtists; + } else { + mArtists = new ArrayList<>(); + } + } + + private Map> artistsToGenres(List inputArtists) { + Map> outputGenres = new HashMap<>(); + + for (Artist currentArtist : inputArtists) { + for (String currentGenre : currentArtist.getGenresSet()) { + if (outputGenres.containsKey(currentGenre)) { + outputGenres.get(currentGenre).add(currentArtist); + } else { + Set newArtistSet = new HashSet<>(); + newArtistSet.add(currentArtist); + outputGenres.put(currentGenre, newArtistSet); + } + } + } + + return outputGenres; + } + + private List genresMapToList(Map> genresMap) { + List genreList = new ArrayList<>(); + + for (Map.Entry> mapEntry : genresMap.entrySet()) { + Genre genre = new Genre(mapEntry.getKey()); + + for (Artist artist : mapEntry.getValue()) { + genre.addArtist(artist); + } + + genreList.add(genre); + } + + Collections.sort(genreList); + + return genreList; + } + + // В БД нет смысла, слишком простой случай + public void saveArtists(List artists) { + try { + FileOutputStream fos = mContext.openFileOutput(ARTIST_LIST_NAME, Context.MODE_PRIVATE); + ObjectOutputStream oos = new ObjectOutputStream(fos); + oos.writeObject(artists); + oos.close(); + } catch (IOException ioe) { + Log.w(TAG, "Can't save artists: " + ioe.getMessage()); + } + } + + public List loadArtists() { + try { + FileInputStream fis; + fis = mContext.openFileInput(ARTIST_LIST_NAME); + ObjectInputStream ois = new ObjectInputStream(fis); + List artists = (ArrayList) ois.readObject(); + ois.close(); + return artists; + } catch (Exception e) { + Log.w(TAG, "Can't load artists: " + e.getMessage()); + File file = new File(mContext.getFilesDir().getAbsolutePath() + "/" + ARTIST_LIST_NAME); + if (!file.delete()) { + Log.e(TAG, "Can't delete file!"); + } + return null; + } + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/yandex/yamblz/model/Genre.java b/app/src/main/java/ru/yandex/yamblz/model/Genre.java new file mode 100644 index 0000000..5f86d79 --- /dev/null +++ b/app/src/main/java/ru/yandex/yamblz/model/Genre.java @@ -0,0 +1,64 @@ +package ru.yandex.yamblz.model; + +import android.support.annotation.NonNull; + +import java.util.ArrayList; +import java.util.List; + +public class Genre implements Comparable { + private String mName; + private List mArtistList = new ArrayList<>(); + private List mSmallCoverUrlList = new ArrayList<>(); + + public Genre(@NonNull String name) { + mName = name; + } + + public String getName() { + return mName; + } + + public List getArtistList() { + return mArtistList; + } + + public List getSmallCoverUrlList() { + return mSmallCoverUrlList; + } + + public void addArtist(Artist artist) { + mArtistList.add(artist); + mSmallCoverUrlList.add(artist.getUrlOfSmallCover()); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Genre)) return false; + + Genre genre = (Genre) o; + + return mName.equals(genre.mName) && (mArtistList != null ? mArtistList.equals(genre.mArtistList) : genre.mArtistList == null); + + } + + @Override + public int hashCode() { + int result = mName.hashCode(); + result = 31 * result + (mArtistList != null ? mArtistList.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "Genre{" + + "name='" + mName + '\'' + ';' + + mArtistList.size() + " in artistList" + + '}'; + } + + @Override + public int compareTo(Genre another) { + return another.mArtistList.size() - mArtistList.size(); + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/yandex/yamblz/ui/adapters/ContentGenresRecyclerAdapter.java b/app/src/main/java/ru/yandex/yamblz/ui/adapters/ContentGenresRecyclerAdapter.java new file mode 100644 index 0000000..8e8b464 --- /dev/null +++ b/app/src/main/java/ru/yandex/yamblz/ui/adapters/ContentGenresRecyclerAdapter.java @@ -0,0 +1,77 @@ +package ru.yandex.yamblz.ui.adapters; + +import android.graphics.drawable.Drawable; +import android.support.v7.widget.RecyclerView; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import java.util.List; + +import butterknife.BindView; +import butterknife.ButterKnife; +import ru.yandex.yamblz.R; +import ru.yandex.yamblz.handler.CriticalSectionsHandler; +import ru.yandex.yamblz.handler.CriticalSectionsManager; +import ru.yandex.yamblz.loader.CollageLoader; +import ru.yandex.yamblz.model.Genre; + +public class ContentGenresRecyclerAdapter extends RecyclerView.Adapter { + private List mGenreList; + private LayoutInflater mLayoutInflater; + private Drawable mNoCollage; + private CollageLoader mCollageLoader; + private CriticalSectionsHandler mHandler; + + public ContentGenresRecyclerAdapter(List genreList, LayoutInflater layoutInflater, Drawable drawable, CollageLoader collageLoader) { + mGenreList = genreList; + mLayoutInflater = layoutInflater; + mNoCollage = drawable; + mCollageLoader = collageLoader; + mHandler = CriticalSectionsManager.getHandler(); + } + + @Override + public GenreHolder onCreateViewHolder(ViewGroup parent, int viewType) { + View view = mLayoutInflater.inflate(R.layout.list_genre_item, parent, false); + return new GenreHolder(view, mGenreList, mNoCollage); + } + + @Override + public void onBindViewHolder(GenreHolder holder, int position) { + holder.bind(position); + } + + @Override + public int getItemCount() { + return mGenreList.size(); + } + + public class GenreHolder extends RecyclerView.ViewHolder { + List mGenreList; + Drawable mNoCollage; + + @BindView(R.id.list_genre_item_image) + ImageView mCollageImageView; + + @BindView(R.id.list_genre_item_text) + TextView mText; + + public GenreHolder(View itemView, List genreList, Drawable drawable) { + super(itemView); + mGenreList = genreList; + mNoCollage = drawable; + ButterKnife.bind(this, itemView); + } + + public void bind(int position) { + mCollageImageView.setImageDrawable(mNoCollage); + mHandler.postLowPriorityTask(() -> mCollageLoader.loadCollage(mGenreList.get(position).getSmallCoverUrlList(), mCollageImageView)); + mText.setText(mGenreList.get(position).getName()); + Log.d("BIND", "Hash=" + hashCode() + "; Pos=" + position + "; Genre=" + mGenreList.get(position).getName()); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/yandex/yamblz/ui/fragments/ContentFragment.java b/app/src/main/java/ru/yandex/yamblz/ui/fragments/ContentFragment.java index d46490f..0a417d0 100644 --- a/app/src/main/java/ru/yandex/yamblz/ui/fragments/ContentFragment.java +++ b/app/src/main/java/ru/yandex/yamblz/ui/fragments/ContentFragment.java @@ -1,18 +1,92 @@ package ru.yandex.yamblz.ui.fragments; +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.os.AsyncTask; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.support.v4.content.ContextCompat; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import com.squareup.picasso.Picasso; + +import java.io.IOException; +import java.util.List; + +import butterknife.BindView; +import butterknife.ButterKnife; import ru.yandex.yamblz.R; +import ru.yandex.yamblz.handler.CriticalSectionsHandler; +import ru.yandex.yamblz.handler.CriticalSectionsManager; +import ru.yandex.yamblz.loader.CollageLoaderManager; +import ru.yandex.yamblz.loader.square.CollageLoaderSquare; +import ru.yandex.yamblz.model.ArtistFetcher; +import ru.yandex.yamblz.model.ArtistLab; +import ru.yandex.yamblz.model.Genre; +import ru.yandex.yamblz.ui.adapters.ContentGenresRecyclerAdapter; public class ContentFragment extends BaseFragment { + private CriticalSectionsHandler mHandler; + + @BindView(R.id.fragment_content_recycler_view) + RecyclerView mRecyclerView; + @NonNull @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - return inflater.inflate(R.layout.fragment_content, container, false); + mHandler = CriticalSectionsManager.getHandler(); + View view = inflater.inflate(R.layout.fragment_content, container, false); + ButterKnife.bind(this, view); + + // Вот сделаю ДЗ с БД и наступит мне счастье. А пока только так + new AsyncTask() { + List genreList; + + @Override + protected Void doInBackground(Void... params) { + + ArtistLab artistLab = ArtistLab.get(getContext()); + genreList = artistLab.getGenresList(); + if (genreList == null || genreList.size() == 0) { + try { + artistLab.setArtists(new ArtistFetcher().getArtistsFromJson()); + genreList = artistLab.getGenresList(); + } catch (IOException e) { + e.printStackTrace(); + } + } + return null; + } + + @Override + protected void onPostExecute(Void aVoid) { + Context context = getContext(); + Drawable drawable = ContextCompat.getDrawable(context, R.drawable.ic_album_black_240dp); + + CollageLoaderManager.init(new CollageLoaderSquare(Picasso.with(context.getApplicationContext()))); + + mRecyclerView.setLayoutManager(new LinearLayoutManager(context)); + mRecyclerView.setAdapter(new ContentGenresRecyclerAdapter(genreList, inflater, drawable, CollageLoaderManager.getLoader())); + mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { + @Override + public void onScrollStateChanged(RecyclerView recyclerView, int newState) { + super.onScrollStateChanged(recyclerView, newState); + if (newState == RecyclerView.SCROLL_STATE_IDLE) { + mHandler.stopSection(1); + } + if (newState == RecyclerView.SCROLL_STATE_DRAGGING) { + mHandler.startSection(1); + } + } + }); + } + }.execute(); + + return view; } } diff --git a/app/src/main/res/drawable/ic_album_black_240dp.xml b/app/src/main/res/drawable/ic_album_black_240dp.xml new file mode 100644 index 0000000..9bfdd16 --- /dev/null +++ b/app/src/main/res/drawable/ic_album_black_240dp.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout/fragment_content.xml b/app/src/main/res/layout/fragment_content.xml index 81016ea..ec7a14f 100644 --- a/app/src/main/res/layout/fragment_content.xml +++ b/app/src/main/res/layout/fragment_content.xml @@ -3,12 +3,9 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - + android:layout_height="match_parent"/> \ No newline at end of file diff --git a/app/src/main/res/layout/list_genre_item.xml b/app/src/main/res/layout/list_genre_item.xml new file mode 100644 index 0000000..03bf9ca --- /dev/null +++ b/app/src/main/res/layout/list_genre_item.xml @@ -0,0 +1,21 @@ + + + + + + + + \ No newline at end of file diff --git a/dependencies.gradle b/dependencies.gradle index d9cb375..5e21e95 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', @@ -18,6 +18,13 @@ ext.versions = [ dexcountPlugin : '0.5.4', versionsGradlePlugin : '0.13.0', + rxjava : '1.1.6', + rxandroid : '1.2.1', + + gson : '2.6.2', + okhttp : '3.4.1', + picasso : '2.5.2', + dagger : '2.4', supportLibs : '23.1.1', @@ -52,6 +59,13 @@ ext.gradlePlugins = [ ] ext.libraries = [ + rxjava : "io.reactivex:rxjava:$versions.rxjava", + rxandroid : "io.reactivex:rxandroid:$versions.rxandroid", + + gson : "com.google.code.gson:gson:$versions.gson", + okhttp : "com.squareup.okhttp3:okhttp:$versions.okhttp", + picasso : "com.squareup.picasso:picasso:$versions.picasso", + dagger : "com.google.dagger:dagger:$versions.dagger", daggerCompiler : "com.google.dagger:dagger-compiler:$versions.dagger", diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 86a3e94..aa19d55 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Mon May 30 20:17:48 ICT 2016 +#Tue Aug 02 13:27:44 MSK 2016 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.13-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip