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