diff --git a/app/build.gradle b/app/build.gradle
index cecba703..e31db384 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -86,8 +86,6 @@ android {
disable 'MissingTranslation'
disable 'InvalidPackage'
abortOnError false
- // remove as soon as it has been fixed
- disable 'PrivateResource'
}
}
@@ -96,26 +94,6 @@ dependencies {
compile('com.crashlytics.sdk.android:crashlytics:2.5.3@aar') {
transitive = true;
}
- compile 'com.android.support:support-v4:23.1.1'
- compile 'com.android.support:support-v13:23.1.1'
- compile 'com.android.support:appcompat-v7:23.1.1'
- compile 'com.android.support:recyclerview-v7:23.1.1'
- compile 'com.android.support:gridlayout-v7:23.1.1'
- compile 'com.android.support:cardview-v7:23.1.1'
- compile 'com.android.support:palette-v7:23.1.1'
- compile 'com.android.support:design:23.1.1'
- compile 'com.android.support:support-annotations:23.1.1'
-
- compile 'com.github.ksoichiro:android-observablescrollview:1.6.0'
- compile 'asia.ivity.android:drag-sort-listview:1.0'
- compile 'com.github.semoncat.seekarc:library:0.1'
- compile 'com.sothree.slidinguppanel:library:3.2.0'
-
- compile 'com.squareup.retrofit:retrofit:2.0.0-beta2'
- compile 'com.squareup.retrofit:converter-gson:2.0.0-beta2'
- compile 'com.squareup.okhttp:okhttp:2.5.0'
-
- compile 'com.github.kabouzeid:Android-Universal-Image-Loader:8ffb5d4afa'
compile('com.afollestad.material-dialogs:core:0.8.5.1@aar') {
transitive = true
@@ -127,11 +105,27 @@ dependencies {
transitive = true
}
- compile 'com.jakewharton:butterknife:7.0.1'
-
- //noinspection GradleDynamicVersion
- compile 'com.anjlab.android.iab.v3:library:1.0.+@aar'
-
debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3.1'
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3.1'
+ compile 'com.android.support:support-v4:23.1.1'
+ compile 'com.android.support:support-v13:23.1.1'
+ compile 'com.android.support:appcompat-v7:23.1.1'
+ compile 'com.android.support:recyclerview-v7:23.1.1'
+ compile 'com.android.support:gridlayout-v7:23.1.1'
+ compile 'com.android.support:cardview-v7:23.1.1'
+ compile 'com.android.support:palette-v7:23.1.1'
+ compile 'com.android.support:design:23.1.1'
+ compile 'com.android.support:support-annotations:23.1.1'
+ compile 'com.android.support:percent:23.1.1'
+ compile 'com.github.ksoichiro:android-observablescrollview:1.6.0'
+ compile 'asia.ivity.android:drag-sort-listview:1.0'
+ compile 'com.github.semoncat.seekarc:library:0.1'
+ compile 'com.sothree.slidinguppanel:library:3.2.0'
+ compile 'com.squareup.retrofit:retrofit:2.0.0-beta2'
+ compile 'com.squareup.retrofit:converter-gson:2.0.0-beta2'
+ compile 'com.squareup.okhttp:okhttp:2.5.0'
+ compile 'com.github.kabouzeid:Android-Universal-Image-Loader:c2894ad9f1'
+ compile 'com.jakewharton:butterknife:7.0.1'
+ //noinspection GradleDynamicVersion
+ compile 'com.anjlab.android.iab.v3:library:1.0.+@aar'
}
diff --git a/app/src/main/java/com/kabouzeid/gramophone/helper/StackBlur.java b/app/src/main/java/com/kabouzeid/gramophone/helper/StackBlur.java
new file mode 100644
index 00000000..535d2a7a
--- /dev/null
+++ b/app/src/main/java/com/kabouzeid/gramophone/helper/StackBlur.java
@@ -0,0 +1,333 @@
+package com.kabouzeid.gramophone.helper;
+
+import android.graphics.Bitmap;
+
+import java.util.ArrayList;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+ * Blur using Java code.
+ *
+ * This is a compromise between Gaussian Blur and Box blur
+ * It creates much better looking blurs than Box Blur, but is
+ * 7x faster than my Gaussian Blur implementation.
+ *
+ * I called it Stack Blur because this describes best how this
+ * filter works internally: it creates a kind of moving stack
+ * of colors whilst scanning through the image. Thereby it
+ * just has to add one new block of color to the right side
+ * of the stack and remove the leftmost color. The remaining
+ * colors on the topmost layer of the stack are either added on
+ * or reduced by one, depending on if they are on the right or
+ * on the left side of the stack.
+ *
+ * @author Enrique López Mañas
+ * http://www.neo-tech.es
+ *
+ * Author of the original algorithm: Mario Klingemann
+ *
+ * Based heavily on http://vitiy.info/Code/stackblur.cpp
+ * See http://vitiy.info/stackblur-algorithm-multi-threaded-blur-for-cpp/
+ * @copyright: Enrique López Mañas
+ * @license: Apache License 2.0
+ */
+public class StackBlur {
+
+ static final int EXECUTOR_THREADS = Runtime.getRuntime().availableProcessors();
+ static final ExecutorService EXECUTOR = Executors.newFixedThreadPool(EXECUTOR_THREADS);
+
+ private static final short[] stackblur_mul = {
+ 512, 512, 456, 512, 328, 456, 335, 512, 405, 328, 271, 456, 388, 335, 292, 512,
+ 454, 405, 364, 328, 298, 271, 496, 456, 420, 388, 360, 335, 312, 292, 273, 512,
+ 482, 454, 428, 405, 383, 364, 345, 328, 312, 298, 284, 271, 259, 496, 475, 456,
+ 437, 420, 404, 388, 374, 360, 347, 335, 323, 312, 302, 292, 282, 273, 265, 512,
+ 497, 482, 468, 454, 441, 428, 417, 405, 394, 383, 373, 364, 354, 345, 337, 328,
+ 320, 312, 305, 298, 291, 284, 278, 271, 265, 259, 507, 496, 485, 475, 465, 456,
+ 446, 437, 428, 420, 412, 404, 396, 388, 381, 374, 367, 360, 354, 347, 341, 335,
+ 329, 323, 318, 312, 307, 302, 297, 292, 287, 282, 278, 273, 269, 265, 261, 512,
+ 505, 497, 489, 482, 475, 468, 461, 454, 447, 441, 435, 428, 422, 417, 411, 405,
+ 399, 394, 389, 383, 378, 373, 368, 364, 359, 354, 350, 345, 341, 337, 332, 328,
+ 324, 320, 316, 312, 309, 305, 301, 298, 294, 291, 287, 284, 281, 278, 274, 271,
+ 268, 265, 262, 259, 257, 507, 501, 496, 491, 485, 480, 475, 470, 465, 460, 456,
+ 451, 446, 442, 437, 433, 428, 424, 420, 416, 412, 408, 404, 400, 396, 392, 388,
+ 385, 381, 377, 374, 370, 367, 363, 360, 357, 354, 350, 347, 344, 341, 338, 335,
+ 332, 329, 326, 323, 320, 318, 315, 312, 310, 307, 304, 302, 299, 297, 294, 292,
+ 289, 287, 285, 282, 280, 278, 275, 273, 271, 269, 267, 265, 263, 261, 259
+ };
+
+ private static final byte[] stackblur_shr = {
+ 9, 11, 12, 13, 13, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17,
+ 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20,
+ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22,
+ 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
+ 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23,
+ 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
+ 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
+ 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
+ 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24
+ };
+
+ public static Bitmap blur(Bitmap original, float radius) {
+ int w = original.getWidth();
+ int h = original.getHeight();
+ int[] currentPixels = new int[w * h];
+ original.getPixels(currentPixels, 0, w, 0, 0, w, h);
+ int cores = EXECUTOR_THREADS;
+
+ ArrayList horizontal = new ArrayList(cores);
+ ArrayList vertical = new ArrayList(cores);
+ for (int i = 0; i < cores; i++) {
+ horizontal.add(new BlurTask(currentPixels, w, h, (int) radius, cores, i, 1));
+ vertical.add(new BlurTask(currentPixels, w, h, (int) radius, cores, i, 2));
+ }
+
+ try {
+ EXECUTOR.invokeAll(horizontal);
+ } catch (InterruptedException e) {
+ return null;
+ }
+
+ try {
+ EXECUTOR.invokeAll(vertical);
+ } catch (InterruptedException e) {
+ return null;
+ }
+
+ return Bitmap.createBitmap(currentPixels, w, h, Bitmap.Config.ARGB_8888);
+ }
+
+ private static void blurIteration(int[] src, int w, int h, int radius, int cores, int core, int step) {
+ int x, y, xp, yp, i;
+ int sp;
+ int stack_start;
+ int stack_i;
+
+ int src_i;
+ int dst_i;
+
+ long sum_r, sum_g, sum_b,
+ sum_in_r, sum_in_g, sum_in_b,
+ sum_out_r, sum_out_g, sum_out_b;
+
+ int wm = w - 1;
+ int hm = h - 1;
+ int div = (radius * 2) + 1;
+ int mul_sum = stackblur_mul[radius];
+ byte shr_sum = stackblur_shr[radius];
+ int[] stack = new int[div];
+
+ if (step == 1) {
+ int minY = core * h / cores;
+ int maxY = (core + 1) * h / cores;
+
+ for (y = minY; y < maxY; y++) {
+ sum_r = sum_g = sum_b =
+ sum_in_r = sum_in_g = sum_in_b =
+ sum_out_r = sum_out_g = sum_out_b = 0;
+
+ src_i = w * y; // start of line (0,y)
+
+ for (i = 0; i <= radius; i++) {
+ stack_i = i;
+ stack[stack_i] = src[src_i];
+ sum_r += ((src[src_i] >>> 16) & 0xff) * (i + 1);
+ sum_g += ((src[src_i] >>> 8) & 0xff) * (i + 1);
+ sum_b += (src[src_i] & 0xff) * (i + 1);
+ sum_out_r += ((src[src_i] >>> 16) & 0xff);
+ sum_out_g += ((src[src_i] >>> 8) & 0xff);
+ sum_out_b += (src[src_i] & 0xff);
+ }
+
+
+ for (i = 1; i <= radius; i++) {
+ if (i <= wm) src_i += 1;
+ stack_i = i + radius;
+ stack[stack_i] = src[src_i];
+ sum_r += ((src[src_i] >>> 16) & 0xff) * (radius + 1 - i);
+ sum_g += ((src[src_i] >>> 8) & 0xff) * (radius + 1 - i);
+ sum_b += (src[src_i] & 0xff) * (radius + 1 - i);
+ sum_in_r += ((src[src_i] >>> 16) & 0xff);
+ sum_in_g += ((src[src_i] >>> 8) & 0xff);
+ sum_in_b += (src[src_i] & 0xff);
+ }
+
+
+ sp = radius;
+ xp = radius;
+ if (xp > wm) xp = wm;
+ src_i = xp + y * w; // img.pix_ptr(xp, y);
+ dst_i = y * w; // img.pix_ptr(0, y);
+ for (x = 0; x < w; x++) {
+ src[dst_i] = (int)
+ ((src[dst_i] & 0xff000000) |
+ ((((sum_r * mul_sum) >>> shr_sum) & 0xff) << 16) |
+ ((((sum_g * mul_sum) >>> shr_sum) & 0xff) << 8) |
+ ((((sum_b * mul_sum) >>> shr_sum) & 0xff)));
+ dst_i += 1;
+
+ sum_r -= sum_out_r;
+ sum_g -= sum_out_g;
+ sum_b -= sum_out_b;
+
+ stack_start = sp + div - radius;
+ if (stack_start >= div) stack_start -= div;
+ stack_i = stack_start;
+
+ sum_out_r -= ((stack[stack_i] >>> 16) & 0xff);
+ sum_out_g -= ((stack[stack_i] >>> 8) & 0xff);
+ sum_out_b -= (stack[stack_i] & 0xff);
+
+ if (xp < wm) {
+ src_i += 1;
+ ++xp;
+ }
+
+ stack[stack_i] = src[src_i];
+
+ sum_in_r += ((src[src_i] >>> 16) & 0xff);
+ sum_in_g += ((src[src_i] >>> 8) & 0xff);
+ sum_in_b += (src[src_i] & 0xff);
+ sum_r += sum_in_r;
+ sum_g += sum_in_g;
+ sum_b += sum_in_b;
+
+ ++sp;
+ if (sp >= div) sp = 0;
+ stack_i = sp;
+
+ sum_out_r += ((stack[stack_i] >>> 16) & 0xff);
+ sum_out_g += ((stack[stack_i] >>> 8) & 0xff);
+ sum_out_b += (stack[stack_i] & 0xff);
+ sum_in_r -= ((stack[stack_i] >>> 16) & 0xff);
+ sum_in_g -= ((stack[stack_i] >>> 8) & 0xff);
+ sum_in_b -= (stack[stack_i] & 0xff);
+ }
+
+ }
+ }
+
+ // step 2
+ else if (step == 2) {
+ int minX = core * w / cores;
+ int maxX = (core + 1) * w / cores;
+
+ for (x = minX; x < maxX; x++) {
+ sum_r = sum_g = sum_b =
+ sum_in_r = sum_in_g = sum_in_b =
+ sum_out_r = sum_out_g = sum_out_b = 0;
+
+ src_i = x; // x,0
+ for (i = 0; i <= radius; i++) {
+ stack_i = i;
+ stack[stack_i] = src[src_i];
+ sum_r += ((src[src_i] >>> 16) & 0xff) * (i + 1);
+ sum_g += ((src[src_i] >>> 8) & 0xff) * (i + 1);
+ sum_b += (src[src_i] & 0xff) * (i + 1);
+ sum_out_r += ((src[src_i] >>> 16) & 0xff);
+ sum_out_g += ((src[src_i] >>> 8) & 0xff);
+ sum_out_b += (src[src_i] & 0xff);
+ }
+ for (i = 1; i <= radius; i++) {
+ if (i <= hm) src_i += w; // +stride
+
+ stack_i = i + radius;
+ stack[stack_i] = src[src_i];
+ sum_r += ((src[src_i] >>> 16) & 0xff) * (radius + 1 - i);
+ sum_g += ((src[src_i] >>> 8) & 0xff) * (radius + 1 - i);
+ sum_b += (src[src_i] & 0xff) * (radius + 1 - i);
+ sum_in_r += ((src[src_i] >>> 16) & 0xff);
+ sum_in_g += ((src[src_i] >>> 8) & 0xff);
+ sum_in_b += (src[src_i] & 0xff);
+ }
+
+ sp = radius;
+ yp = radius;
+ if (yp > hm) yp = hm;
+ src_i = x + yp * w; // img.pix_ptr(x, yp);
+ dst_i = x; // img.pix_ptr(x, 0);
+ for (y = 0; y < h; y++) {
+ src[dst_i] = (int)
+ ((src[dst_i] & 0xff000000) |
+ ((((sum_r * mul_sum) >>> shr_sum) & 0xff) << 16) |
+ ((((sum_g * mul_sum) >>> shr_sum) & 0xff) << 8) |
+ ((((sum_b * mul_sum) >>> shr_sum) & 0xff)));
+ dst_i += w;
+
+ sum_r -= sum_out_r;
+ sum_g -= sum_out_g;
+ sum_b -= sum_out_b;
+
+ stack_start = sp + div - radius;
+ if (stack_start >= div) stack_start -= div;
+ stack_i = stack_start;
+
+ sum_out_r -= ((stack[stack_i] >>> 16) & 0xff);
+ sum_out_g -= ((stack[stack_i] >>> 8) & 0xff);
+ sum_out_b -= (stack[stack_i] & 0xff);
+
+ if (yp < hm) {
+ src_i += w; // stride
+ ++yp;
+ }
+
+ stack[stack_i] = src[src_i];
+
+ sum_in_r += ((src[src_i] >>> 16) & 0xff);
+ sum_in_g += ((src[src_i] >>> 8) & 0xff);
+ sum_in_b += (src[src_i] & 0xff);
+ sum_r += sum_in_r;
+ sum_g += sum_in_g;
+ sum_b += sum_in_b;
+
+ ++sp;
+ if (sp >= div) sp = 0;
+ stack_i = sp;
+
+ sum_out_r += ((stack[stack_i] >>> 16) & 0xff);
+ sum_out_g += ((stack[stack_i] >>> 8) & 0xff);
+ sum_out_b += (stack[stack_i] & 0xff);
+ sum_in_r -= ((stack[stack_i] >>> 16) & 0xff);
+ sum_in_g -= ((stack[stack_i] >>> 8) & 0xff);
+ sum_in_b -= (stack[stack_i] & 0xff);
+ }
+ }
+ }
+
+ }
+
+ private static class BlurTask implements Callable {
+ private final int[] _src;
+ private final int _w;
+ private final int _h;
+ private final int _radius;
+ private final int _totalCores;
+ private final int _coreIndex;
+ private final int _round;
+
+ public BlurTask(int[] src, int w, int h, int radius, int totalCores, int coreIndex, int round) {
+ _src = src;
+ _w = w;
+ _h = h;
+ _radius = radius;
+ _totalCores = totalCores;
+ _coreIndex = coreIndex;
+ _round = round;
+ }
+
+ @Override
+ public Void call() throws Exception {
+ blurIteration(_src, _w, _h, _radius, _totalCores, _coreIndex, _round);
+ return null;
+ }
+
+ }
+}
diff --git a/app/src/main/java/com/kabouzeid/gramophone/imageloader/BlurProcessor.java b/app/src/main/java/com/kabouzeid/gramophone/imageloader/BlurProcessor.java
new file mode 100644
index 00000000..f30a3b36
--- /dev/null
+++ b/app/src/main/java/com/kabouzeid/gramophone/imageloader/BlurProcessor.java
@@ -0,0 +1,121 @@
+package com.kabouzeid.gramophone.imageloader;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.support.annotation.FloatRange;
+import android.support.annotation.NonNull;
+import android.support.v8.renderscript.Allocation;
+import android.support.v8.renderscript.Element;
+import android.support.v8.renderscript.RSRuntimeException;
+import android.support.v8.renderscript.RenderScript;
+import android.support.v8.renderscript.ScriptIntrinsicBlur;
+
+import com.kabouzeid.gramophone.BuildConfig;
+import com.kabouzeid.gramophone.helper.StackBlur;
+import com.kabouzeid.gramophone.util.ImageUtil;
+import com.nostra13.universalimageloader.core.process.BitmapProcessor;
+
+/**
+ * @author Karim Abou Zeid (kabouzeid)
+ */
+public class BlurProcessor implements BitmapProcessor {
+ public static final float DEFAULT_BLUR_RADIUS = 5f;
+
+ private Context context;
+ private final float blurRadius;
+ private final int sampling;
+
+ private BlurProcessor(Builder builder) {
+ this.context = builder.context;
+ this.blurRadius = builder.blurRadius;
+ this.sampling = builder.sampling;
+ }
+
+ // Something here seems to cause a memory leak... Go into LeakCanary for more details.
+ @Override
+ public Bitmap process(Bitmap bitmap) {
+ int sampling;
+ if (this.sampling == 0) {
+ sampling = ImageUtil.calculateInSampleSize(bitmap.getWidth(), bitmap.getHeight(), 100);
+ } else {
+ sampling = this.sampling;
+ }
+
+ int width = bitmap.getWidth();
+ int height = bitmap.getHeight();
+ int scaledWidth = width / sampling;
+ int scaledHeight = height / sampling;
+
+ Bitmap out = Bitmap.createBitmap(scaledWidth, scaledHeight, Bitmap.Config.ARGB_8888);
+
+ Canvas canvas = new Canvas(out);
+ canvas.scale(1 / (float) sampling, 1 / (float) sampling);
+ Paint paint = new Paint();
+ paint.setFlags(Paint.FILTER_BITMAP_FLAG);
+ canvas.drawBitmap(bitmap, 0, 0, paint);
+
+ try {
+ final RenderScript rs = RenderScript.create(context.getApplicationContext());
+ final Allocation input = Allocation.createFromBitmap(rs, out, Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
+ final Allocation output = Allocation.createTyped(rs, input.getType());
+ final ScriptIntrinsicBlur script = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
+
+ script.setRadius(blurRadius);
+ script.setInput(input);
+ script.forEach(output);
+
+ output.copyTo(out);
+
+ rs.destroy();
+
+ return out;
+
+ } catch (RSRuntimeException e) {
+ // on some devices RenderScript.create() throws: android.support.v8.renderscript.RSRuntimeException: Error loading libRSSupport library
+ if (BuildConfig.DEBUG) e.printStackTrace();
+
+ return StackBlur.blur(out, blurRadius);
+ }
+ }
+
+ public static class Builder {
+ private Context context;
+ private float blurRadius = DEFAULT_BLUR_RADIUS;
+ private int sampling;
+
+ public Builder(@NonNull Context context) {
+ this.context = context;
+ }
+
+ /**
+ * @param blurRadius The radius to use. Must be between 0 and 25. Default is 5.
+ * @return the same Builder
+ */
+ public Builder blurRadius(@FloatRange(from = 0.0f, to = 25.0f) float blurRadius) {
+ this.blurRadius = blurRadius;
+ return this;
+ }
+
+ /**
+ * @param sampling The inSampleSize to use. Must be a power of 2, or 1 for no down sampling or 0 for auto detect sampling. Default is 0.
+ * @return the same Builder
+ */
+ public Builder sampling(int sampling) {
+ this.sampling = sampling;
+ return this;
+ }
+
+ public Builder fromPrototype(BlurProcessor prototype) {
+ context = prototype.context;
+ blurRadius = prototype.blurRadius;
+ sampling = prototype.sampling;
+ return this;
+ }
+
+ public BlurProcessor build() {
+ return new BlurProcessor(this);
+ }
+ }
+}
diff --git a/app/src/main/java/com/kabouzeid/gramophone/service/MusicService.java b/app/src/main/java/com/kabouzeid/gramophone/service/MusicService.java
index 78a6b377..e0015895 100644
--- a/app/src/main/java/com/kabouzeid/gramophone/service/MusicService.java
+++ b/app/src/main/java/com/kabouzeid/gramophone/service/MusicService.java
@@ -31,12 +31,12 @@ import android.support.annotation.Nullable;
import android.view.View;
import android.widget.Toast;
-import com.kabouzeid.gramophone.BuildConfig;
import com.kabouzeid.gramophone.R;
import com.kabouzeid.gramophone.appwidget.WidgetMedium;
import com.kabouzeid.gramophone.helper.PlayingNotificationHelper;
import com.kabouzeid.gramophone.helper.ShuffleHelper;
import com.kabouzeid.gramophone.helper.StopWatch;
+import com.kabouzeid.gramophone.imageloader.BlurProcessor;
import com.kabouzeid.gramophone.model.Song;
import com.kabouzeid.gramophone.provider.HistoryStore;
import com.kabouzeid.gramophone.provider.MusicPlaybackQueueStore;
@@ -51,7 +51,6 @@ import com.nostra13.universalimageloader.core.assist.ImageSize;
import com.nostra13.universalimageloader.core.assist.ViewScaleType;
import com.nostra13.universalimageloader.core.imageaware.NonViewAware;
import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener;
-import com.nostra13.universalimageloader.core.process.BitmapProcessor;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
@@ -447,22 +446,7 @@ public class MusicService extends Service implements SharedPreferences.OnSharedP
ImageLoader.getInstance().displayImage(
currentAlbumArtUri,
new NonViewAware(new ImageSize(screenSize.x, screenSize.y), ViewScaleType.CROP),
- new DisplayImageOptions.Builder()
- .postProcessor(new BitmapProcessor() {
- @Override
- public Bitmap process(Bitmap bitmap) {
- Bitmap.Config config = bitmap.getConfig();
- if (config == null) {
- config = Bitmap.Config.ARGB_8888;
- }
- try {
- return bitmap.copy(config, false);
- } catch (OutOfMemoryError e) {
- if (BuildConfig.DEBUG) e.printStackTrace();
- return null;
- }
- }
- }).build(),
+ new DisplayImageOptions.Builder().postProcessor(new BlurProcessor.Builder(this).build()).build(),
new SimpleImageLoadingListener() {
@Override
public void onLoadingComplete(String imageUri, View view, @Nullable Bitmap loadedImage) {
diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/ArtistDetailActivity.java b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/ArtistDetailActivity.java
index 0d82793b..ea9c97fd 100644
--- a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/ArtistDetailActivity.java
+++ b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/ArtistDetailActivity.java
@@ -15,6 +15,7 @@ import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
+import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
@@ -43,7 +44,6 @@ import com.kabouzeid.gramophone.util.ColorUtil;
import com.kabouzeid.gramophone.util.MusicUtil;
import com.kabouzeid.gramophone.util.NavigationUtil;
import com.kabouzeid.gramophone.util.Util;
-import com.kabouzeid.gramophone.views.SquareIfPlaceImageView;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.assist.FailReason;
@@ -68,7 +68,7 @@ public class ArtistDetailActivity extends AbsSlidingMusicPanelActivity implement
public static final String EXTRA_ARTIST_ID = "extra_artist_id";
@Bind(R.id.image)
- SquareIfPlaceImageView artistImage;
+ ImageView artistImage;
@Bind(R.id.list_background)
View songListBackground;
@Bind(R.id.list)
diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/PlaybackControlsFragment.java b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/PlaybackControlsFragment.java
index 301a9bb2..640864ec 100644
--- a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/PlaybackControlsFragment.java
+++ b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/PlaybackControlsFragment.java
@@ -1,40 +1,31 @@
package com.kabouzeid.gramophone.ui.fragments;
-import android.animation.Animator;
-import android.animation.AnimatorSet;
import android.content.Context;
-import android.content.SharedPreferences;
import android.graphics.Color;
-import android.os.Build;
+import android.graphics.PorterDuff;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.app.Fragment;
-import android.support.v7.widget.CardView;
-import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
-import android.view.ViewAnimationUtils;
import android.view.ViewGroup;
import android.view.animation.DecelerateInterpolator;
import android.widget.ImageButton;
-import android.widget.LinearLayout;
-import android.widget.RelativeLayout;
+import android.widget.SeekBar;
import android.widget.TextView;
-import com.afollestad.materialdialogs.internal.ThemeSingleton;
import com.kabouzeid.gramophone.R;
import com.kabouzeid.gramophone.helper.MusicPlayerRemote;
+import com.kabouzeid.gramophone.helper.MusicProgressViewUpdateHelper;
import com.kabouzeid.gramophone.helper.PlayPauseButtonOnClickHandler;
import com.kabouzeid.gramophone.interfaces.MusicServiceEventListener;
import com.kabouzeid.gramophone.misc.FloatingActionButtonProperties;
-import com.kabouzeid.gramophone.misc.SimpleAnimatorListener;
-import com.kabouzeid.gramophone.model.Song;
+import com.kabouzeid.gramophone.misc.SimpleOnSeekbarChangeListener;
import com.kabouzeid.gramophone.service.MusicService;
import com.kabouzeid.gramophone.ui.activities.base.AbsMusicServiceActivity;
import com.kabouzeid.gramophone.util.ColorUtil;
-import com.kabouzeid.gramophone.util.PreferenceUtil;
+import com.kabouzeid.gramophone.util.MusicUtil;
import com.kabouzeid.gramophone.util.Util;
-import com.kabouzeid.gramophone.util.ViewUtil;
import com.kabouzeid.gramophone.views.PlayPauseDrawable;
import butterknife.Bind;
@@ -43,20 +34,10 @@ import butterknife.ButterKnife;
/**
* @author Karim Abou Zeid (kabouzeid)
*/
-public class PlaybackControlsFragment extends Fragment implements MusicServiceEventListener, SharedPreferences.OnSharedPreferenceChangeListener {
- private static final int FAB_CIRCULAR_REVEAL_ANIMATION_TIME = 1000;
+public class PlaybackControlsFragment extends Fragment implements MusicServiceEventListener, MusicProgressViewUpdateHelper.Callback {
@Bind(R.id.player_play_pause_fab)
FloatingActionButton playPauseFab;
-
- @Bind(R.id.player_title)
- TextView songTitle;
- @Bind(R.id.player_text)
- TextView songText;
- @Bind(R.id.player_footer)
- LinearLayout footer;
- @Bind(R.id.player_playback_controller_card)
- CardView playbackControllerCard;
@Bind(R.id.player_prev_button)
ImageButton prevButton;
@Bind(R.id.player_next_button)
@@ -65,20 +46,20 @@ public class PlaybackControlsFragment extends Fragment implements MusicServiceEv
ImageButton repeatButton;
@Bind(R.id.player_shuffle_button)
ImageButton shuffleButton;
- @Bind(R.id.player_media_controller_container)
- RelativeLayout mediaControllerContainer;
- @Bind(R.id.player_media_controller_container_background)
- View mediaControllerContainerBackground;
- private int lastFooterColor;
- private int lastPlaybackControlsColor;
- private int lastTitleTextColor;
- private int lastCaptionTextColor;
+ @Bind(R.id.player_progress_slider)
+ SeekBar progressSlider;
+ @Bind(R.id.player_song_total_time)
+ TextView songTotalTime;
+ @Bind(R.id.player_song_current_progress)
+ TextView songCurrentProgress;
private PlayPauseDrawable playerFabPlayPauseDrawable;
- private AnimatorSet colorTransitionAnimator;
private AbsMusicServiceActivity activity;
+ private int lastPlaybackControlsColor;
+
+ private MusicProgressViewUpdateHelper progressViewUpdateHelper;
@Override
public void onAttach(Context context) {
@@ -96,6 +77,12 @@ public class PlaybackControlsFragment extends Fragment implements MusicServiceEv
activity = null;
}
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ progressViewUpdateHelper = new MusicProgressViewUpdateHelper(this);
+ }
+
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
@@ -106,23 +93,32 @@ public class PlaybackControlsFragment extends Fragment implements MusicServiceEv
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
ButterKnife.bind(this, view);
- PreferenceUtil.getInstance(getContext()).registerOnSharedPreferenceChangedListener(this);
activity.addMusicServiceEventListener(this);
-
setUpMusicControllers();
+ updateProgressTextColor();
}
@Override
public void onDestroyView() {
super.onDestroyView();
activity.removeMusicServiceEventListener(this);
- PreferenceUtil.getInstance(activity).unregisterOnSharedPreferenceChangedListener(this);
ButterKnife.unbind(this);
}
+ @Override
+ public void onResume() {
+ super.onResume();
+ progressViewUpdateHelper.start();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ progressViewUpdateHelper.stop();
+ }
+
@Override
public void onPlayingMetaChanged() {
- updateMetaTexts();
}
@Override
@@ -145,31 +141,19 @@ public class PlaybackControlsFragment extends Fragment implements MusicServiceEv
}
- @Override
- public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
- switch (key) {
- case PreferenceUtil.PLAYBACK_CONTROLLER_CARD_NOW_PLAYING:
- updatePlaybackControllerCardVisibility();
- break;
- case PreferenceUtil.COLOR_PLAYBACK_CONTROLS_NOW_PLAYING:
- updateRepeatState();
- updateShuffleState();
- updatePlayPauseFabTint();
- break;
- case PreferenceUtil.LARGER_TITLE_BOX_NOW_PLAYING:
- updateTitleBoxSize();
- break;
- }
- }
-
public void setColor(int color) {
- animateColorChange(color);
+ lastPlaybackControlsColor = ColorUtil.getOpaqueColor(ColorUtil.getSecondaryTextColorForBackground(getContext(), color));
+ updateRepeatState();
+ updateShuffleState();
+ updatePrevNextColor();
+ updateProgressSliderTint();
+ updateProgressTextColor();
}
private void setUpPlayPauseFab() {
updatePlayPauseDrawableState(false);
playPauseFab.setImageDrawable(playerFabPlayPauseDrawable);
- updatePlayPauseFabTint();
+ FloatingActionButtonProperties.COLOR.set(playPauseFab, Color.WHITE);
playPauseFab.setOnClickListener(new PlayPauseButtonOnClickHandler());
playPauseFab.post(new Runnable() {
@Override
@@ -180,11 +164,6 @@ public class PlaybackControlsFragment extends Fragment implements MusicServiceEv
});
}
- private void updatePlayPauseFabTint() {
- int fabColor = PreferenceUtil.getInstance(activity).colorPlaybackControlsNowPlaying() ? lastPlaybackControlsColor : activity.getThemeColorAccent();
- FloatingActionButtonProperties.COLOR.set(playPauseFab, fabColor);
- }
-
protected void updatePlayPauseDrawableState(boolean animate) {
if (playerFabPlayPauseDrawable == null) {
playerFabPlayPauseDrawable = new PlayPauseDrawable(activity);
@@ -196,37 +175,16 @@ public class PlaybackControlsFragment extends Fragment implements MusicServiceEv
}
}
- private void updateTitleBoxSize() {
- boolean largerTitleBox = PreferenceUtil.getInstance(activity).largerTitleBoxNowPlaying();
- int paddingTopBottom = largerTitleBox ? getResources().getDimensionPixelSize(R.dimen.title_box_padding_large) : getResources().getDimensionPixelSize(R.dimen.title_box_padding_small);
- footer.setPadding(footer.getPaddingLeft(), paddingTopBottom, footer.getPaddingRight(), paddingTopBottom);
-
- songTitle.setPadding(songTitle.getPaddingLeft(), songTitle.getPaddingTop(), songTitle.getPaddingRight(), largerTitleBox ? getResources().getDimensionPixelSize(R.dimen.title_box_text_spacing_large) : getResources().getDimensionPixelSize(R.dimen.title_box_text_spacing_small));
- songText.setPadding(songText.getPaddingLeft(), largerTitleBox ? getResources().getDimensionPixelSize(R.dimen.title_box_text_spacing_large) : getResources().getDimensionPixelSize(R.dimen.title_box_text_spacing_small), songText.getPaddingRight(), songText.getPaddingBottom());
-
- songTitle.setTextSize(TypedValue.COMPLEX_UNIT_PX, largerTitleBox ? getResources().getDimensionPixelSize(R.dimen.title_box_title_text_size_large) : getResources().getDimensionPixelSize(R.dimen.title_box_title_text_size_small));
- songText.setTextSize(TypedValue.COMPLEX_UNIT_PX, largerTitleBox ? getResources().getDimensionPixelSize(R.dimen.title_box_caption_text_size_large) : getResources().getDimensionPixelSize(R.dimen.title_box_caption_text_size_small));
- }
-
- private void updatePlaybackControllerCardVisibility() {
- boolean showPlaybackControllerCard = PreferenceUtil.getInstance(activity).playbackControllerCardNowPlaying();
- playbackControllerCard.setVisibility(showPlaybackControllerCard ? View.VISIBLE : View.GONE);
- mediaControllerContainerBackground.setVisibility(showPlaybackControllerCard ? View.GONE : View.VISIBLE);
- }
-
private void setUpMusicControllers() {
setUpPlayPauseFab();
setUpPrevNext();
setUpRepeatButton();
setUpShuffleButton();
+ setUpProgressSlider();
}
private void setUpPrevNext() {
- int themedDrawableColor = ColorUtil.resolveColor(activity, android.R.attr.textColorSecondary);
- nextButton.setImageDrawable(Util.getTintedDrawable(activity,
- R.drawable.ic_skip_next_white_36dp, themedDrawableColor));
- prevButton.setImageDrawable(Util.getTintedDrawable(activity,
- R.drawable.ic_skip_previous_white_36dp, themedDrawableColor));
+ updatePrevNextColor();
nextButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
@@ -241,6 +199,19 @@ public class PlaybackControlsFragment extends Fragment implements MusicServiceEv
});
}
+ private void updateProgressTextColor() {
+ int color = ColorUtil.getPrimaryTextColor(getContext(), false);
+ songTotalTime.setTextColor(color);
+ songCurrentProgress.setTextColor(color);
+ }
+
+ private void updatePrevNextColor() {
+ nextButton.setImageDrawable(Util.getTintedDrawable(activity,
+ R.drawable.ic_skip_next_white_36dp, lastPlaybackControlsColor));
+ prevButton.setImageDrawable(Util.getTintedDrawable(activity,
+ R.drawable.ic_skip_previous_white_36dp, lastPlaybackControlsColor));
+ }
+
private void setUpShuffleButton() {
updateShuffleState();
shuffleButton.setOnClickListener(new View.OnClickListener() {
@@ -255,11 +226,11 @@ public class PlaybackControlsFragment extends Fragment implements MusicServiceEv
switch (MusicPlayerRemote.getShuffleMode()) {
case MusicService.SHUFFLE_MODE_SHUFFLE:
shuffleButton.setImageDrawable(Util.getTintedDrawable(activity, R.drawable.ic_shuffle_white_36dp,
- getActivatedIconColor()));
+ lastPlaybackControlsColor));
break;
default:
shuffleButton.setImageDrawable(Util.getTintedDrawable(activity, R.drawable.ic_shuffle_white_36dp,
- getDeactivatedIconColor()));
+ lastPlaybackControlsColor));
break;
}
}
@@ -278,84 +249,19 @@ public class PlaybackControlsFragment extends Fragment implements MusicServiceEv
switch (MusicPlayerRemote.getRepeatMode()) {
case MusicService.REPEAT_MODE_ALL:
repeatButton.setImageDrawable(Util.getTintedDrawable(activity, R.drawable.ic_repeat_white_36dp,
- getActivatedIconColor()));
+ lastPlaybackControlsColor));
break;
case MusicService.REPEAT_MODE_THIS:
repeatButton.setImageDrawable(Util.getTintedDrawable(activity, R.drawable.ic_repeat_one_white_36dp,
- getActivatedIconColor()));
+ lastPlaybackControlsColor));
break;
default:
repeatButton.setImageDrawable(Util.getTintedDrawable(activity, R.drawable.ic_repeat_white_36dp,
- getDeactivatedIconColor()));
+ lastPlaybackControlsColor));
break;
}
}
- private int getActivatedIconColor() {
- if (PreferenceUtil.getInstance(activity).colorPlaybackControlsNowPlaying()) {
- return ensureActivatedColorVisibleIfNecessary(lastPlaybackControlsColor);
- } else {
- return ThemeSingleton.get().positiveColor.getDefaultColor();
- }
- }
-
- private int getDeactivatedIconColor() {
- return ColorUtil.resolveColor(activity, android.R.attr.textColorSecondary);
- }
-
- /**
- * @return If the activated color wont have enough difference to the deactivated color Color.WHITE / Color.BLACK (depending on the theme),
- * else the unmodified accentColor.
- */
- private int ensureActivatedColorVisibleIfNecessary(int activatedColor) {
- // Not optimal, but much easier then computing the opaque deactivated color on the background color every time.
- int preBlendedDeactivatedIconColor = ThemeSingleton.get().darkTheme ? Color.argb(255, 188, 188, 188) : Color.argb(255, 115, 115, 115);
- if (ColorUtil.getColorDifference(activatedColor, preBlendedDeactivatedIconColor) <= 30d) {
- return ThemeSingleton.get().darkTheme ? Color.WHITE : Color.BLACK;
- }
- return activatedColor;
- }
-
- private void updateMetaTexts() {
- final Song song = MusicPlayerRemote.getCurrentSong();
- songTitle.setText(song.title);
- songText.setText(song.artistName);
- }
-
- private void animateColorChange(final int newColor) {
- if (colorTransitionAnimator != null && colorTransitionAnimator.isStarted()) {
- colorTransitionAnimator.cancel();
- }
- colorTransitionAnimator = new AnimatorSet();
- AnimatorSet.Builder animatorSetBuilder = colorTransitionAnimator.play(ViewUtil.createBackgroundColorTransition(footer, lastFooterColor, newColor));
-
- int titleTextColor = ColorUtil.getPrimaryTextColorForBackground(activity, newColor);
- int captionTextColor = ColorUtil.getSecondaryTextColorForBackground(activity, newColor);
-
- animatorSetBuilder.with(ViewUtil.createTextColorTransition(songTitle, lastTitleTextColor, titleTextColor));
- animatorSetBuilder.with(ViewUtil.createTextColorTransition(songText, lastCaptionTextColor, captionTextColor));
-
- colorTransitionAnimator.addListener(new SimpleAnimatorListener() {
- @Override
- public void onAnimationStart(Animator animation) {
- if (newColor == ColorUtil.resolveColor(activity, R.attr.default_bar_color) && ThemeSingleton.get().darkTheme) {
- lastPlaybackControlsColor = Color.WHITE;
- } else {
- lastPlaybackControlsColor = newColor;
- }
- updateRepeatState();
- updateShuffleState();
- updatePlayPauseFabTint();
- }
- });
-
- colorTransitionAnimator.start();
-
- lastFooterColor = newColor;
- lastTitleTextColor = titleTextColor;
- lastCaptionTextColor = captionTextColor;
- }
-
public void showControls() {
playPauseFab.animate()
.scaleX(1f)
@@ -363,28 +269,38 @@ public class PlaybackControlsFragment extends Fragment implements MusicServiceEv
.rotation(360f)
.setInterpolator(new DecelerateInterpolator())
.start();
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- if (mediaControllerContainer.getVisibility() == View.INVISIBLE) {
- int cx = (playPauseFab.getLeft() + playPauseFab.getRight()) / 2;
- int cy = (playPauseFab.getTop() + playPauseFab.getBottom()) / 2;
- int finalRadius = Math.max(mediaControllerContainer.getWidth(), mediaControllerContainer.getHeight());
-
- final Animator animator = ViewAnimationUtils.createCircularReveal(mediaControllerContainer, cx, cy, 0, finalRadius);
- animator.setInterpolator(new DecelerateInterpolator());
- animator.setDuration(FAB_CIRCULAR_REVEAL_ANIMATION_TIME);
- animator.start();
- mediaControllerContainer.setVisibility(View.VISIBLE);
- }
- }
}
public void resetShowControlsAnimation() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- mediaControllerContainer.setVisibility(View.INVISIBLE);
- }
playPauseFab.setScaleX(0f);
playPauseFab.setScaleY(0f);
playPauseFab.setRotation(0f);
}
+
+ private void updateProgressSliderTint() {
+ int color = ColorUtil.getPrimaryTextColor(getContext(), false);
+ progressSlider.getThumb().mutate().setColorFilter(color, PorterDuff.Mode.SRC_IN);
+ progressSlider.getProgressDrawable().mutate().setColorFilter(Color.TRANSPARENT, PorterDuff.Mode.SRC_IN);
+ }
+
+ private void setUpProgressSlider() {
+ updateProgressSliderTint();
+ progressSlider.setOnSeekBarChangeListener(new SimpleOnSeekbarChangeListener() {
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ if (fromUser) {
+ MusicPlayerRemote.seekTo(progress);
+ onUpdateProgressViews(MusicPlayerRemote.getSongProgressMillis(), MusicPlayerRemote.getSongDurationMillis());
+ }
+ }
+ });
+ }
+
+ @Override
+ public void onUpdateProgressViews(int progress, int total) {
+ progressSlider.setMax(total);
+ progressSlider.setProgress(progress);
+ songTotalTime.setText(MusicUtil.getReadableDurationString(total));
+ songCurrentProgress.setText(MusicUtil.getReadableDurationString(progress));
+ }
}
diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/PlayerAlbumCoverFragment.java b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/PlayerAlbumCoverFragment.java
index c7b226fd..ecd94d7c 100644
--- a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/PlayerAlbumCoverFragment.java
+++ b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/PlayerAlbumCoverFragment.java
@@ -13,7 +13,6 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.OvershootInterpolator;
-import android.widget.FrameLayout;
import android.widget.ImageView;
import com.kabouzeid.gramophone.R;
@@ -25,13 +24,10 @@ import com.kabouzeid.gramophone.util.ColorUtil;
import com.kabouzeid.gramophone.util.MusicUtil;
import com.kabouzeid.gramophone.util.PreferenceUtil;
import com.kabouzeid.gramophone.util.ViewUtil;
-import com.kabouzeid.gramophone.views.SquareIfPlaceImageView;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.assist.FailReason;
-import com.nostra13.universalimageloader.core.assist.LoadedFrom;
import com.nostra13.universalimageloader.core.display.FadeInBitmapDisplayer;
-import com.nostra13.universalimageloader.core.imageaware.ImageAware;
import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener;
import com.nostra13.universalimageloader.core.process.BitmapProcessor;
@@ -44,13 +40,9 @@ import butterknife.ButterKnife;
public class PlayerAlbumCoverFragment extends Fragment implements MusicServiceEventListener, SharedPreferences.OnSharedPreferenceChangeListener {
@Bind(R.id.player_image)
- SquareIfPlaceImageView albumArt;
+ ImageView albumCover;
@Bind(R.id.player_favorite_icon)
ImageView favoriteIcon;
- @Bind(R.id.player_album_art_frame)
- FrameLayout albumArtFrame;
-
- private boolean forceSquareAlbumArt;
private AbsMusicServiceActivity activity;
private OnColorChangedListener onColorChangedListener;
@@ -82,7 +74,7 @@ public class PlayerAlbumCoverFragment extends Fragment implements MusicServiceEv
super.onViewCreated(view, savedInstanceState);
ButterKnife.bind(this, view);
- albumArt.forceSquare(forceSquareAlbumArt);
+ forceSquareAlbumCover(PreferenceUtil.getInstance(getContext()).forceSquareAlbumCover());
PreferenceUtil.getInstance(getContext()).registerOnSharedPreferenceChangedListener(this);
activity.addMusicServiceEventListener(this);
@@ -124,9 +116,8 @@ public class PlayerAlbumCoverFragment extends Fragment implements MusicServiceEv
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
switch (key) {
- case PreferenceUtil.FORCE_SQUARE_ALBUM_ART:
- forceSquareAlbumArt = PreferenceUtil.getInstance(activity).forceAlbumArtSquared();
- albumArt.forceSquare(forceSquareAlbumArt);
+ case PreferenceUtil.FORCE_SQUARE_ALBUM_COVER:
+ forceSquareAlbumCover(PreferenceUtil.getInstance(activity).forceSquareAlbumCover());
break;
}
}
@@ -140,7 +131,7 @@ public class PlayerAlbumCoverFragment extends Fragment implements MusicServiceEv
final ColorHolder colorHolder = new ColorHolder();
ImageLoader.getInstance().displayImage(
MusicUtil.getSongImageLoaderString(MusicPlayerRemote.getCurrentSong()),
- albumArt,
+ albumCover,
new DisplayImageOptions.Builder()
.cacheInMemory(true)
.showImageOnFail(R.drawable.default_album_art)
@@ -151,13 +142,7 @@ public class PlayerAlbumCoverFragment extends Fragment implements MusicServiceEv
return bitmap;
}
})
- .displayer(new FadeInBitmapDisplayer(ViewUtil.DEFAULT_COLOR_ANIMATION_DURATION) {
- @Override
- public void display(Bitmap bitmap, ImageAware imageAware, LoadedFrom loadedFrom) {
- super.display(bitmap, imageAware, loadedFrom);
- setColor(colorHolder.color);
- }
- })
+ .displayer(new FadeInBitmapDisplayer(ViewUtil.DEFAULT_COLOR_ANIMATION_DURATION))
.build(),
new SimpleImageLoadingListener() {
@Override
@@ -170,7 +155,9 @@ public class PlayerAlbumCoverFragment extends Fragment implements MusicServiceEv
public void onLoadingComplete(String imageUri, View view, @Nullable Bitmap loadedImage) {
if (loadedImage == null) {
onLoadingFailed(imageUri, view, null);
+ return;
}
+ setColor(colorHolder.color);
}
}
);
@@ -211,6 +198,10 @@ public class PlayerAlbumCoverFragment extends Fragment implements MusicServiceEv
.start();
}
+ public void forceSquareAlbumCover(boolean forceSquareAlbumCover) {
+ albumCover.setScaleType(forceSquareAlbumCover ? ImageView.ScaleType.FIT_CENTER : ImageView.ScaleType.CENTER_CROP);
+ }
+
private void setColor(int color) {
if (onColorChangedListener != null) onColorChangedListener.onColorChanged(color);
}
diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/PlayerFragment.java b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/PlayerFragment.java
index ab195d76..e98f0185 100644
--- a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/PlayerFragment.java
+++ b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/PlayerFragment.java
@@ -42,8 +42,6 @@ import butterknife.ButterKnife;
public class PlayerFragment extends Fragment implements SharedPreferences.OnSharedPreferenceChangeListener, MusicServiceEventListener, Toolbar.OnMenuItemClickListener, PaletteColorHolder, MusicProgressViewUpdateHelper.Callback, PlayerAlbumCoverFragment.OnColorChangedListener {
public static final String TAG = PlayerFragment.class.getSimpleName();
- @Bind(R.id.player_status_bar)
- View statusbar;
@Bind(R.id.player_toolbar)
Toolbar toolbar;
@@ -59,6 +57,7 @@ public class PlayerFragment extends Fragment implements SharedPreferences.OnShar
private Callbacks callbacks;
private PlaybackControlsFragment playbackControlsFragment;
+ private PlayingInfoFragment playingInfoFragment;
private PlayerAlbumCoverFragment playerAlbumCoverFragment;
@Override
@@ -97,6 +96,7 @@ public class PlayerFragment extends Fragment implements SharedPreferences.OnShar
ButterKnife.bind(this, view);
playbackControlsFragment = (PlaybackControlsFragment) getChildFragmentManager().findFragmentById(R.id.playback_controls_fragment);
+ playingInfoFragment = (PlayingInfoFragment) getChildFragmentManager().findFragmentById(R.id.playing_info_fragment);
playerAlbumCoverFragment = (PlayerAlbumCoverFragment) getChildFragmentManager().findFragmentById(R.id.player_album_cover_fragment);
playerAlbumCoverFragment.setOnColorChangedListener(this);
@@ -340,46 +340,7 @@ public class PlayerFragment extends Fragment implements SharedPreferences.OnShar
}
private void animateColorChange(final int newColor) {
-// if (colorTransitionAnimator != null && colorTransitionAnimator.isStarted()) {
-// colorTransitionAnimator.cancel();
-// }
-// colorTransitionAnimator = new AnimatorSet();
-// AnimatorSet.Builder animatorSetBuilder = colorTransitionAnimator.play(ViewUtil.createBackgroundColorTransition(footer, lastColor, newColor));
-//
-// if (opaqueToolBar) {
-// animatorSetBuilder.with(ViewUtil.createBackgroundColorTransition(toolbar, lastColor, newColor));
-// ViewUtil.setToolbarContentColorForBackground(activity, toolbar, newColor);
-// } else {
-// toolbar.setBackgroundColor(Color.TRANSPARENT);
-// ViewUtil.setToolbarContentDark(activity, toolbar, false);
-// }
-//
-// if (opaqueStatusBar) {
-// int newStatusbarColor = newColor;
-// int oldStatusbarColor = lastColor;
-// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
-// newStatusbarColor = ColorUtil.shiftColorDown(newStatusbarColor);
-// oldStatusbarColor = ColorUtil.shiftColorDown(oldStatusbarColor);
-// }
-// animatorSetBuilder.with(ViewUtil.createBackgroundColorTransition(statusbar, oldStatusbarColor, newStatusbarColor));
-// } else {
-// statusbar.setBackgroundColor(Color.TRANSPARENT);
-// }
-//
-// colorTransitionAnimator.addListener(new SimpleAnimatorListener() {
-// @Override
-// public void onAnimationStart(Animator animation) {
-// if (newColor == ColorUtil.resolveColor(activity, R.attr.default_bar_color) && ThemeSingleton.get().darkTheme) {
-// lastPlaybackControlsColor = Color.WHITE;
-// } else {
-// lastPlaybackControlsColor = newColor;
-// }
-// updateProgressSliderTint();
-// }
-// });
-//
-// colorTransitionAnimator.start();
-
+ getView().setBackgroundColor(newColor);
lastColor = newColor;
}
diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/PlayingInfoFragment.java b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/PlayingInfoFragment.java
new file mode 100644
index 00000000..c41cbc29
--- /dev/null
+++ b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/PlayingInfoFragment.java
@@ -0,0 +1,83 @@
+package com.kabouzeid.gramophone.ui.fragments;
+
+
+import android.content.Context;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.kabouzeid.gramophone.R;
+import com.kabouzeid.gramophone.interfaces.MusicServiceEventListener;
+import com.kabouzeid.gramophone.ui.activities.base.AbsMusicServiceActivity;
+
+import butterknife.ButterKnife;
+
+/**
+ * @author Karim Abou Zeid (kabouzeid)
+ */
+public class PlayingInfoFragment extends Fragment implements MusicServiceEventListener {
+
+
+ private AbsMusicServiceActivity activity;
+
+ @Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ try {
+ activity = (AbsMusicServiceActivity) context;
+ } catch (ClassCastException e) {
+ throw new RuntimeException(context.getClass().getSimpleName() + " must be an instance of " + AbsMusicServiceActivity.class.getSimpleName());
+ }
+ }
+
+ @Override
+ public void onDetach() {
+ super.onDetach();
+ activity = null;
+ }
+
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ return inflater.inflate(R.layout.fragment_playing_info, container, false);
+ }
+
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ ButterKnife.bind(this, view);
+ activity.addMusicServiceEventListener(this);
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ activity.removeMusicServiceEventListener(this);
+ ButterKnife.unbind(this);
+ }
+
+ @Override
+ public void onPlayingMetaChanged() {
+
+ }
+
+ @Override
+ public void onPlayStateChanged() {
+ }
+
+ @Override
+ public void onRepeatModeChanged() {
+ }
+
+ @Override
+ public void onShuffleModeChanged() {
+ }
+
+ @Override
+ public void onMediaStoreChanged() {
+ }
+
+}
diff --git a/app/src/main/java/com/kabouzeid/gramophone/util/MusicUtil.java b/app/src/main/java/com/kabouzeid/gramophone/util/MusicUtil.java
index 1f53acc1..67b9a7dc 100644
--- a/app/src/main/java/com/kabouzeid/gramophone/util/MusicUtil.java
+++ b/app/src/main/java/com/kabouzeid/gramophone/util/MusicUtil.java
@@ -118,7 +118,7 @@ public class MusicUtil {
public static String getReadableDurationString(long songDurationMillis) {
long minutes = (songDurationMillis / 1000) / 60;
long seconds = (songDurationMillis / 1000) % 60;
- return String.format("%02d:%02d", minutes, seconds);
+ return String.format("%01d:%02d", minutes, seconds);
}
//iTunes uses for example 1002 for track 2 CD1 or 3011 for track 11 CD3.
diff --git a/app/src/main/java/com/kabouzeid/gramophone/util/PreferenceUtil.java b/app/src/main/java/com/kabouzeid/gramophone/util/PreferenceUtil.java
index dc4bd688..d5d274ef 100644
--- a/app/src/main/java/com/kabouzeid/gramophone/util/PreferenceUtil.java
+++ b/app/src/main/java/com/kabouzeid/gramophone/util/PreferenceUtil.java
@@ -40,7 +40,7 @@ public final class PreferenceUtil {
public static final String OPAQUE_TOOLBAR_NOW_PLAYING = "opaque_toolbar_now_playing";
public static final String OPAQUE_STATUSBAR_NOW_PLAYING = "opaque_statusbar_now_playing";
- public static final String FORCE_SQUARE_ALBUM_ART = "force_square_album_art";
+ public static final String FORCE_SQUARE_ALBUM_COVER = "force_square_album_art";
public static final String LARGER_TITLE_BOX_NOW_PLAYING = "larger_title_box_now_playing";
public static final String ALTERNATIVE_PROGRESS_SLIDER_NOW_PLAYING = "alternative_progress_slider_now_playing";
public static final String PLAYBACK_CONTROLLER_CARD_NOW_PLAYING = "playback_controller_card_now_playing";
@@ -167,8 +167,8 @@ public final class PreferenceUtil {
return mPreferences.getBoolean(OPAQUE_TOOLBAR_NOW_PLAYING, false);
}
- public final boolean forceAlbumArtSquared() {
- return mPreferences.getBoolean(FORCE_SQUARE_ALBUM_ART, false);
+ public final boolean forceSquareAlbumCover() {
+ return mPreferences.getBoolean(FORCE_SQUARE_ALBUM_COVER, false);
}
public final boolean playbackControllerCardNowPlaying() {
diff --git a/app/src/main/java/com/kabouzeid/gramophone/views/HeightFitSquareImageView.java b/app/src/main/java/com/kabouzeid/gramophone/views/HeightFitSquareImageView.java
deleted file mode 100644
index 5be34b5d..00000000
--- a/app/src/main/java/com/kabouzeid/gramophone/views/HeightFitSquareImageView.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package com.kabouzeid.gramophone.views;
-
-import android.content.Context;
-import android.support.annotation.NonNull;
-import android.util.AttributeSet;
-import android.widget.ImageView;
-
-/**
- * @author Karim Abou Zeid (kabouzeid)
- */
-public class HeightFitSquareImageView extends ImageView {
-
- public HeightFitSquareImageView(Context context) {
- super(context);
- }
-
- public HeightFitSquareImageView(@NonNull Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public HeightFitSquareImageView(@NonNull Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- //noinspection SuspiciousNameCombination
- super.onMeasure(heightMeasureSpec, heightMeasureSpec);
- }
-
-}
diff --git a/app/src/main/java/com/kabouzeid/gramophone/views/HeightWidthFitSquareImageView.java b/app/src/main/java/com/kabouzeid/gramophone/views/HeightWidthFitSquareImageView.java
deleted file mode 100644
index 710a33db..00000000
--- a/app/src/main/java/com/kabouzeid/gramophone/views/HeightWidthFitSquareImageView.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package com.kabouzeid.gramophone.views;
-
-import android.content.Context;
-import android.support.annotation.NonNull;
-import android.util.AttributeSet;
-import android.widget.ImageView;
-
-/**
- * @author Karim Abou Zeid (kabouzeid)
- */
-public class HeightWidthFitSquareImageView extends ImageView {
-
- public HeightWidthFitSquareImageView(Context context) {
- super(context);
- }
-
- public HeightWidthFitSquareImageView(@NonNull Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public HeightWidthFitSquareImageView(@NonNull Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- final int small = Math.min(widthMeasureSpec, heightMeasureSpec);
- super.onMeasure(small, small);
- }
-
-}
diff --git a/app/src/main/java/com/kabouzeid/gramophone/views/SquareIfPlaceImageView.java b/app/src/main/java/com/kabouzeid/gramophone/views/SquareIfPlaceImageView.java
deleted file mode 100644
index bb2483b8..00000000
--- a/app/src/main/java/com/kabouzeid/gramophone/views/SquareIfPlaceImageView.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package com.kabouzeid.gramophone.views;
-
-import android.content.Context;
-import android.support.annotation.NonNull;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.ImageView;
-
-/**
- * @author Karim Abou Zeid (kabouzeid)
- */
-public class SquareIfPlaceImageView extends ImageView {
- private boolean forceSquare = false;
-
- public SquareIfPlaceImageView(Context context) {
- super(context);
- }
-
- public SquareIfPlaceImageView(@NonNull Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public SquareIfPlaceImageView(@NonNull Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- final int small = Math.min(widthMeasureSpec, heightMeasureSpec);
- final int large = Math.max(widthMeasureSpec, heightMeasureSpec);
-
- if (forceSquare) super.onMeasure(small, small);
- else if (View.MeasureSpec.getSize(large) > View.MeasureSpec.getSize(small) * 1.5)
- super.onMeasure(small, small);
- else super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- }
-
- public void forceSquare(boolean force) {
- if (forceSquare != force) {
- forceSquare = force;
- invalidate();
- }
- }
-
-}
diff --git a/app/src/main/res/drawable/slider_thumb.xml b/app/src/main/res/drawable/slider_thumb.xml
index 086a8041..33c58225 100644
--- a/app/src/main/res/drawable/slider_thumb.xml
+++ b/app/src/main/res/drawable/slider_thumb.xml
@@ -2,7 +2,7 @@
+ android:width="4dp"
+ android:height="24dp" />
\ No newline at end of file
diff --git a/app/src/main/res/layout-land/fragment_player.xml b/app/src/main/res/layout-land/fragment_player.xml
index 1761bcd2..d26d1bbe 100644
--- a/app/src/main/res/layout-land/fragment_player.xml
+++ b/app/src/main/res/layout-land/fragment_player.xml
@@ -1,41 +1,59 @@
-
+ android:focusable="false"
+ android:orientation="horizontal"
+ android:baselineAligned="false">
-
-
-
+ android:layout_height="match_parent"
+ android:layout_weight="1">
-
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_album_detail.xml b/app/src/main/res/layout/activity_album_detail.xml
index 2182eb7a..1bcb8ac4 100644
--- a/app/src/main/res/layout/activity_album_detail.xml
+++ b/app/src/main/res/layout/activity_album_detail.xml
@@ -4,7 +4,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
-
-
-
+ android:layout_height="@dimen/progress_container_height">
+ android:textAppearance="@style/TextAppearance.AppCompat.Title"
+ android:textColor="?android:textColorSecondary"
+ android:textSize="12sp"
+ tools:ignore="RtlHardcoded,RtlSymmetry" />
+ android:textAppearance="@style/TextAppearance.AppCompat.Title"
+ android:textColor="?android:textColorSecondary"
+ android:textSize="12sp"
+ tools:ignore="RtlHardcoded,RtlSymmetry" />
-
+
-
+
+
+ android:layout_height="@dimen/media_controller_container_height"
+ tools:ignore="ContentDescription,UnusedAttribute">
-
+
-
+
-
+
-
-
-
-
-
-
-
-
-
-
-
+
+ android:layout_centerInParent="true" />
-
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_player.xml b/app/src/main/res/layout/fragment_player.xml
index 73e0704e..1eeb223e 100644
--- a/app/src/main/res/layout/fragment_player.xml
+++ b/app/src/main/res/layout/fragment_player.xml
@@ -5,36 +5,43 @@
android:clickable="true"
android:focusable="false">
-
+ android:orientation="vertical">
-
+
+
+
+
+
+
+
-
-
diff --git a/app/src/main/res/layout/fragment_player_album_cover.xml b/app/src/main/res/layout/fragment_player_album_cover.xml
index 92092f5c..d3e60082 100644
--- a/app/src/main/res/layout/fragment_player_album_cover.xml
+++ b/app/src/main/res/layout/fragment_player_album_cover.xml
@@ -8,13 +8,11 @@
android:layout_above="@id/player_footer_frame"
android:background="@android:color/black">
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/navigation_drawer_header.xml b/app/src/main/res/layout/navigation_drawer_header.xml
index d9ec21cb..30471b05 100644
--- a/app/src/main/res/layout/navigation_drawer_header.xml
+++ b/app/src/main/res/layout/navigation_drawer_header.xml
@@ -1,23 +1,24 @@
-
+ tools:ignore="ContentDescription,UnusedAttribute" />
@@ -55,4 +56,4 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/src/main/res/values-v21/styles.xml b/app/src/main/res/values-v21/styles.xml
index 5ca1088b..2b49df5e 100644
--- a/app/src/main/res/values-v21/styles.xml
+++ b/app/src/main/res/values-v21/styles.xml
@@ -23,7 +23,6 @@
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 2d5ad1a9..a9844830 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -205,4 +205,7 @@
For helping me with the design.
Website
Loading products…
+
+
+ Hello blank fragment
diff --git a/build.gradle b/build.gradle
index 542fa556..6c76e542 100644
--- a/build.gradle
+++ b/build.gradle
@@ -3,7 +3,7 @@ buildscript {
jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle:1.3.1'
+ classpath 'com.android.tools.build:gradle:1.5.0'
classpath 'com.github.triplet.gradle:play-publisher:1.1.3'
}
}
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 5f13e354..69a4e3eb 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-2.5-all.zip
\ No newline at end of file
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.9-all.zip
\ No newline at end of file