Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ android {
applicationId 'ru.yandex.yamblz'
minSdkVersion versions.minSdk
targetSdkVersion versions.targetSdk
versionCode versions.code // Notice that you may want to use BUILD_NUMBER from CI in real project with own CI.
versionCode versions.code
// Notice that you may want to use BUILD_NUMBER from CI in real project with own CI.
versionName versions.name

// Make generated apks contain version name and code.
Expand Down Expand Up @@ -121,6 +122,23 @@ dependencies {
exclude module: 'recyclerview-v7'
exclude module: 'support-v4'
}

// Web requests
compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
compile 'com.squareup.okhttp3:logging-interceptor:3.3.0'
compile 'com.squareup.retrofit2:converter-gson:2.1.0'

// Json parsing
compile 'com.google.code.gson:gson:2.7'
compile 'me.tatarka:auto-parcel-gson:0.1'
apt 'me.tatarka:auto-parcel-gson-processor:0.1'

//RX <3
compile 'io.reactivex:rxandroid:1.2.1'
compile 'io.reactivex:rxjava:1.1.6'

compile 'com.github.bumptech.glide:glide:3.7.0'
}

configurations.all {
Expand Down
7 changes: 5 additions & 2 deletions app/src/main/java/ru/yandex/yamblz/App.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@

import android.app.Application;
import android.content.Context;
import android.os.Looper;
import android.support.annotation.NonNull;

import ru.yandex.yamblz.developer_settings.DevMetricsProxy;
import ru.yandex.yamblz.developer_settings.DeveloperSettingsModel;
import ru.yandex.yamblz.handler.CriticalSectionsManager;
import ru.yandex.yamblz.handler.DisableScrollLoadingHandler;
import ru.yandex.yamblz.loader.CollageLoaderManager;
import ru.yandex.yamblz.loader.ParallelCollageLoader;
import timber.log.Timber;

public class App extends Application {
Expand All @@ -34,8 +37,8 @@ public void onCreate() {
devMetricsProxy.apply();
}

CollageLoaderManager.init(null); // add implementation
CriticalSectionsManager.init(null); // add implementation
CollageLoaderManager.init(new ParallelCollageLoader());
CriticalSectionsManager.init(new DisableScrollLoadingHandler(Looper.myQueue())); // add implementation
}

@NonNull
Expand Down
36 changes: 36 additions & 0 deletions app/src/main/java/ru/yandex/yamblz/data/Artist.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package ru.yandex.yamblz.data;

import java.util.List;

public class Artist {
public final int id;
public final String name;
public final List<String> genres;
private final int tracks;
private final int albums;
private final String link;
private final String description;
final Cover cover;

public Artist(int id, String name, List<String> genres, int tracks, int albums, String link, String description, Cover cover) {
this.id = id;
this.name = name;
this.genres = genres;
this.tracks = tracks;
this.albums = albums;
this.link = link;
this.description = description;
this.cover = cover;
}

static class Cover {
final String small;
final String big;

public Cover(String small, String big) {
this.small = small;
this.big = big;
}
}

}
11 changes: 11 additions & 0 deletions app/src/main/java/ru/yandex/yamblz/data/ArtistsApi.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package ru.yandex.yamblz.data;

import java.util.List;

import retrofit2.http.GET;
import rx.Single;

public interface ArtistsApi {
@GET("artists.json")
Single<List<Artist>> getArtists();
}
28 changes: 28 additions & 0 deletions app/src/main/java/ru/yandex/yamblz/data/Genre.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package ru.yandex.yamblz.data;

import java.util.ArrayList;
import java.util.List;

public class Genre {
private final String name;
private final List<Artist> artists;

public Genre(String name, List<Artist> artists) {
this.name = name;
this.artists = artists;
}

public String getName() {
return name;
}

public List<String> getCollageUrls() {
List<String> urls = new ArrayList<>();
for (Artist artist : artists) {
urls.add(artist.cover.small);
if (urls.size() >= 4) return urls;
}

return urls;
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package ru.yandex.yamblz.handler;

public interface CriticalSectionsHandler {
public interface CriticalSectionsHandler {

void startSection(int id);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package ru.yandex.yamblz.handler;

import android.os.Handler;
import android.os.Looper;
import android.os.MessageQueue;

import java.lang.ref.WeakReference;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;

public class DisableScrollLoadingHandler implements CriticalSectionsHandler, MessageQueue.IdleHandler {
private final Set<Integer> runningSections = Collections.newSetFromMap(new ConcurrentHashMap<Integer, Boolean>());
private final List<Task> tasks = new CopyOnWriteArrayList<>();
private final WeakReference<MessageQueue> listenableQueue;

public DisableScrollLoadingHandler(MessageQueue queue) {
listenableQueue = new WeakReference<>(queue);
}

@Override
public void startSection(int id) {
runningSections.add(id);
}

@Override
public void stopSection(int id) {
if (runningSections.contains(id)) {
runningSections.remove(id);
}
addIdleHandler();
}

@Override
public void stopSections() {
runningSections.clear();
addIdleHandler();
}

private void runTasks() {
for (Task task : tasks) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Если во время перебора началась критическая секция?

if (runningSections.size() == 0) {
task.run();
removeLowPriorityTask(task);
}
}
}

// Вот тут я хз, честно.
// Вот вроде по дяде Бобу это неправильно написано, а с другой стороны вроде логично выглядит
private boolean addIdleHandler() {
if (listenableQueue.get() != null) {
if (tasks.size() != 0 && !queueIdle()) {
listenableQueue.get().addIdleHandler(this);
}
return true;
}

return false;
}

@Override
public void postLowPriorityTask(Task task) {
if (addIdleHandler()) tasks.add(task);
// throw exception?
}

@Override
public void postLowPriorityTaskDelayed(Task task, int delay) {
new Handler(Looper.getMainLooper()).postDelayed(() -> postLowPriorityTask(task), delay);
}

@Override
public void removeLowPriorityTask(Task task) {
tasks.remove(task);
}

@Override
public void removeLowPriorityTasks() {
tasks.clear();
}

@Override
public boolean queueIdle() {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

С точки зрения ООП не очень хорошо получилось)
Этот метод в интерфейсе не нужен. Лучше внутренним нестатическим классом создать реализацию

@aleien aleien Aug 3, 2016

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Не понимаю, как тогда передавать idleHandler в MessageQueue (: У нас же менеджер возвращает интерфейс, значит, доступны только методы из него.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Но да, с точки зрения ООП плохо)

@kotiknorm kotiknorm Aug 3, 2016

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

При создании обьекта реализующего интерфейс, в конструкторе.

if (runningSections.size() == 0) runTasks();
return runningSections.size() == 0 && tasks.size() != 0;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ public interface CollageLoader {

void loadCollage(List<String> urls, ImageTarget imageTarget, CollageStrategy collageStrategy);

void destroyAll();
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import java.util.List;

public interface CollageStrategy {
interface CollageStrategy {

Bitmap create(List<Bitmap> bitmaps);
}
2 changes: 1 addition & 1 deletion app/src/main/java/ru/yandex/yamblz/loader/ImageTarget.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import android.graphics.Bitmap;

public interface ImageTarget {
interface ImageTarget {

void onLoadBitmap(Bitmap bitmap);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package ru.yandex.yamblz.loader;

import android.graphics.Bitmap;
import android.support.annotation.NonNull;
import android.util.LruCache;
import android.widget.ImageView;

import java.util.List;
import java.util.WeakHashMap;

import ru.yandex.yamblz.handler.CriticalSectionsManager;
import ru.yandex.yamblz.utils.Utils;
import rx.Observable;
import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;
import rx.subscriptions.CompositeSubscription;
import timber.log.Timber;


public class ParallelCollageLoader implements CollageLoader {
@NonNull private final WeakHashMap<ImageTarget, Subscription> subscriptionTargets;
@NonNull private final CompositeSubscription subs;
@NonNull private final CollageStrategy collageStrategy;
@NonNull private final LruCache<List<String>, Bitmap> memoryCache;

public ParallelCollageLoader() {
subscriptionTargets = new WeakHashMap<>();
subs = new CompositeSubscription();
collageStrategy = new SimpleCollageStrategy();
final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
final int cacheSize = maxMemory / 8;
memoryCache = new LruCache<>(cacheSize);
}

@Override
public void loadCollage(List<String> urls, ImageView imageView) {
loadCollage(urls, imageView, this.collageStrategy);
}

@Override
public void loadCollage(List<String> urls, ImageTarget imageTarget) {
loadCollage(urls, imageTarget, this.collageStrategy);
}

@Override
public void loadCollage(List<String> urls, final ImageView imageView, CollageStrategy collageStrategy) {
ImageTarget target = bitmap -> {
imageView.setAlpha(0f);
imageView.setImageBitmap(bitmap);
imageView.animate().alpha(1);
};
loadCollage(urls, target, collageStrategy);
}

private void loadBitmap(ImageTarget imageTarget, Bitmap cachedCollage) {
CriticalSectionsManager.getHandler().postLowPriorityTask(() -> imageTarget.onLoadBitmap(cachedCollage));
}

@Override
public void loadCollage(List<String> urls, ImageTarget imageTarget, CollageStrategy collageStrategy) {
Subscription s = subscriptionTargets.get(imageTarget);
if (s != null) {
subs.remove(s);
}
Bitmap cachedCollage = memoryCache.get(urls);
if (cachedCollage != null) {
Timber.d("Cache hit! Adding task to handler");
loadBitmap(imageTarget, cachedCollage);
} else {
Subscription subscription = Observable.from(urls)
.flatMap(url -> Utils.loadBitmapAsync(url).subscribeOn(Schedulers.io()))
.toList()
.map(collageStrategy::create)
.doOnNext(collage -> memoryCache.put(urls, collage))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(collage -> loadBitmap(imageTarget, collage),
Throwable::printStackTrace
);
subs.add(subscription);
subscriptionTargets.put(imageTarget, subscription);
}
}

@Override
public void destroyAll() {
subs.clear();
subscriptionTargets.clear();
}

}
Loading