Merge branch 'RetroMusicPlayer:dev' into feat/audio_focus_2
This commit is contained in:
commit
51f772247a
305 changed files with 2587 additions and 5938 deletions
|
@ -1,54 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Hemanth Savarala.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
package code.name.monkey.retromusic.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.bumptech.glide.signature.ObjectKey;
|
||||
|
||||
/** @author Karim Abou Zeid (kabouzeid) */
|
||||
public class ArtistSignatureUtil {
|
||||
private static final String ARTIST_SIGNATURE_PREFS = "artist_signatures";
|
||||
|
||||
private static ArtistSignatureUtil sInstance;
|
||||
|
||||
private final SharedPreferences mPreferences;
|
||||
|
||||
private ArtistSignatureUtil(@NonNull final Context context) {
|
||||
mPreferences = context.getSharedPreferences(ARTIST_SIGNATURE_PREFS, Context.MODE_PRIVATE);
|
||||
}
|
||||
|
||||
public static ArtistSignatureUtil getInstance(@NonNull final Context context) {
|
||||
if (sInstance == null) {
|
||||
sInstance = new ArtistSignatureUtil(context.getApplicationContext());
|
||||
}
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
public void updateArtistSignature(String artistName) {
|
||||
mPreferences.edit().putLong(artistName, System.currentTimeMillis()).apply();
|
||||
}
|
||||
|
||||
public long getArtistSignatureRaw(String artistName) {
|
||||
return mPreferences.getLong(artistName, 0);
|
||||
}
|
||||
|
||||
public ObjectKey getArtistSignature(String artistName) {
|
||||
return new ObjectKey(String.valueOf(getArtistSignatureRaw(artistName)));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Hemanth Savarala.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*/
|
||||
package code.name.monkey.retromusic.util
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import androidx.core.content.edit
|
||||
import com.bumptech.glide.signature.ObjectKey
|
||||
|
||||
/** @author Karim Abou Zeid (kabouzeid)
|
||||
*/
|
||||
class ArtistSignatureUtil private constructor(context: Context) {
|
||||
private val mPreferences: SharedPreferences =
|
||||
context.getSharedPreferences(ARTIST_SIGNATURE_PREFS, Context.MODE_PRIVATE)
|
||||
|
||||
fun updateArtistSignature(artistName: String?) {
|
||||
mPreferences.edit { putLong(artistName, System.currentTimeMillis()) }
|
||||
}
|
||||
|
||||
private fun getArtistSignatureRaw(artistName: String?): Long {
|
||||
return mPreferences.getLong(artistName, 0)
|
||||
}
|
||||
|
||||
fun getArtistSignature(artistName: String?): ObjectKey {
|
||||
return ObjectKey(getArtistSignatureRaw(artistName).toString())
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val ARTIST_SIGNATURE_PREFS = "artist_signatures"
|
||||
private var INSTANCE: ArtistSignatureUtil? = null
|
||||
fun getInstance(context: Context): ArtistSignatureUtil {
|
||||
if (INSTANCE == null) {
|
||||
INSTANCE = ArtistSignatureUtil(context.applicationContext)
|
||||
}
|
||||
return INSTANCE!!
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
package code.name.monkey.retromusic.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import code.name.monkey.retromusic.R;
|
||||
import code.name.monkey.retromusic.model.Song;
|
||||
|
||||
public class AutoGeneratedPlaylistBitmap {
|
||||
|
||||
public static Bitmap getBitmap(
|
||||
Context context, List<Song> songPlaylist) {
|
||||
if (songPlaylist == null || songPlaylist.isEmpty()) return getDefaultBitmap(context);
|
||||
if (songPlaylist.size() == 1)
|
||||
return getBitmapWithAlbumId(context, songPlaylist.get(0).getAlbumId());
|
||||
List<Long> albumID = new ArrayList<>();
|
||||
for (Song song : songPlaylist) {
|
||||
if (!albumID.contains(song.getAlbumId())) albumID.add(song.getAlbumId());
|
||||
}
|
||||
List<Bitmap> art = new ArrayList<>();
|
||||
for (Long id : albumID) {
|
||||
Bitmap bitmap = getBitmapWithAlbumId(context, id);
|
||||
if (bitmap != null) art.add(BitmapEditor.getRoundedCornerBitmap(bitmap, 20));
|
||||
if (art.size() == 9) break;
|
||||
}
|
||||
return MergedImageUtils.INSTANCE.joinImages(art);
|
||||
}
|
||||
|
||||
private static Bitmap getBitmapWithAlbumId(@NonNull Context context, Long id) {
|
||||
try {
|
||||
return Glide.with(context)
|
||||
.asBitmap()
|
||||
.load(MusicUtil.getMediaStoreAlbumCoverUri(id))
|
||||
.submit(200, 200)
|
||||
.get();
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static Bitmap getDefaultBitmap(@NonNull Context context) {
|
||||
return BitmapFactory.decodeResource(context.getResources(), R.drawable.default_album_art);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package code.name.monkey.retromusic.util
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.glide.GlideApp
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.util.MergedImageUtils.joinImages
|
||||
import code.name.monkey.retromusic.util.MusicUtil.getMediaStoreAlbumCoverUri
|
||||
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
|
||||
|
||||
object AutoGeneratedPlaylistBitmap {
|
||||
fun getBitmap(
|
||||
context: Context, songPlaylist: List<Song>?
|
||||
): Bitmap? {
|
||||
if (songPlaylist == null || songPlaylist.isEmpty()) return getDefaultBitmap(context)
|
||||
if (songPlaylist.size == 1) return getBitmapWithAlbumId(context, songPlaylist[0].albumId)
|
||||
val albumID: MutableList<Long> = ArrayList()
|
||||
for (song in songPlaylist) {
|
||||
if (!albumID.contains(song.albumId)) albumID.add(song.albumId)
|
||||
}
|
||||
val art: MutableList<Bitmap> = ArrayList()
|
||||
for (id in albumID) {
|
||||
val bitmap = getBitmapWithAlbumId(context, id)
|
||||
if (bitmap != null) art.add(bitmap)
|
||||
if (art.size == 9) break
|
||||
}
|
||||
return joinImages(art)
|
||||
}
|
||||
|
||||
private fun getBitmapWithAlbumId(context: Context, id: Long): Bitmap? {
|
||||
return try {
|
||||
GlideApp.with(context)
|
||||
.asBitmap()
|
||||
.transform(RoundedCorners(20))
|
||||
.load(getMediaStoreAlbumCoverUri(id))
|
||||
.submit(200, 200)
|
||||
.get()
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun getDefaultBitmap(context: Context): Bitmap {
|
||||
return BitmapFactory.decodeResource(context.resources, R.drawable.default_album_art)
|
||||
}
|
||||
}
|
|
@ -2,8 +2,8 @@ package code.name.monkey.retromusic.util
|
|||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.widget.Toast
|
||||
import androidx.core.content.FileProvider
|
||||
import code.name.monkey.retromusic.extensions.showToast
|
||||
import java.io.File
|
||||
|
||||
object BackupUtil {
|
||||
|
@ -19,11 +19,9 @@ object BackupUtil {
|
|||
).addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION).setType("*/*")
|
||||
} catch (e: IllegalArgumentException) {
|
||||
e.printStackTrace()
|
||||
Toast.makeText(
|
||||
context,
|
||||
"Could not share this file.",
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
context.showToast(
|
||||
"Could not share this file."
|
||||
)
|
||||
Intent()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,998 +0,0 @@
|
|||
package code.name.monkey.retromusic.util;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.ColorFilter;
|
||||
import android.graphics.ColorMatrix;
|
||||
import android.graphics.ColorMatrixColorFilter;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffColorFilter;
|
||||
import android.graphics.PorterDuffXfermode;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.renderscript.Allocation;
|
||||
import android.renderscript.Element;
|
||||
import android.renderscript.RenderScript;
|
||||
import android.renderscript.ScriptIntrinsicBlur;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
|
||||
/** Created by trung on 7/11/2017. */
|
||||
public final class BitmapEditor {
|
||||
/**
|
||||
* Stack Blur v1.0 from http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html Java
|
||||
* Author: Mario Klingemann <mario at quasimondo.com> http://incubator.quasimondo.com
|
||||
*
|
||||
* <p>created Feburary 29, 2004 Android port : Yahel Bouaziz <yahel at kayenko.com>
|
||||
* http://www.kayenko.com ported april 5th, 2012
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>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 removeFromParent 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 x side of the stack.
|
||||
*
|
||||
* <p>If you are using this algorithm in your code please add the following line: Stack Blur
|
||||
* Algorithm by Mario Klingemann <mario@quasimondo.com>
|
||||
*/
|
||||
public static Bitmap FastBlurSupportAlpha(Bitmap sentBitmap, float scale, int radius) {
|
||||
|
||||
int width = Math.round(sentBitmap.getWidth() * scale);
|
||||
int height = Math.round(sentBitmap.getHeight() * scale);
|
||||
sentBitmap = Bitmap.createScaledBitmap(sentBitmap, width, height, false);
|
||||
|
||||
Bitmap bitmap = sentBitmap.copy(sentBitmap.getConfig(), true);
|
||||
|
||||
if (radius < 1) {
|
||||
return (null);
|
||||
}
|
||||
|
||||
int w = bitmap.getWidth();
|
||||
int h = bitmap.getHeight();
|
||||
|
||||
int[] pix = new int[w * h];
|
||||
// Log.e("pix", w + " " + h + " " + pix.length);
|
||||
bitmap.getPixels(pix, 0, w, 0, 0, w, h);
|
||||
|
||||
int wm = w - 1;
|
||||
int hm = h - 1;
|
||||
int wh = w * h;
|
||||
int div = radius + radius + 1;
|
||||
|
||||
int[] r = new int[wh];
|
||||
int[] g = new int[wh];
|
||||
int[] b = new int[wh];
|
||||
int[] a = new int[wh];
|
||||
int rsum, gsum, bsum, asum, x, y, i, p, yp, yi, yw;
|
||||
int[] vmin = new int[Math.max(w, h)];
|
||||
|
||||
int divsum = (div + 1) >> 1;
|
||||
divsum *= divsum;
|
||||
int[] dv = new int[256 * divsum];
|
||||
for (i = 0; i < 256 * divsum; i++) {
|
||||
dv[i] = (i / divsum);
|
||||
}
|
||||
|
||||
yw = yi = 0;
|
||||
|
||||
int[][] stack = new int[div][4];
|
||||
int stackpointer;
|
||||
int stackstart;
|
||||
int[] sir;
|
||||
int rbs;
|
||||
int r1 = radius + 1;
|
||||
int routsum, goutsum, boutsum, aoutsum;
|
||||
int rinsum, ginsum, binsum, ainsum;
|
||||
|
||||
for (y = 0; y < h; y++) {
|
||||
rinsum =
|
||||
ginsum =
|
||||
binsum =
|
||||
ainsum = routsum = goutsum = boutsum = aoutsum = rsum = gsum = bsum = asum = 0;
|
||||
for (i = -radius; i <= radius; i++) {
|
||||
p = pix[yi + Math.min(wm, Math.max(i, 0))];
|
||||
sir = stack[i + radius];
|
||||
sir[0] = (p & 0xff0000) >> 16;
|
||||
sir[1] = (p & 0x00ff00) >> 8;
|
||||
sir[2] = (p & 0x0000ff);
|
||||
sir[3] = 0xff & (p >> 24);
|
||||
|
||||
rbs = r1 - Math.abs(i);
|
||||
rsum += sir[0] * rbs;
|
||||
gsum += sir[1] * rbs;
|
||||
bsum += sir[2] * rbs;
|
||||
asum += sir[3] * rbs;
|
||||
if (i > 0) {
|
||||
rinsum += sir[0];
|
||||
ginsum += sir[1];
|
||||
binsum += sir[2];
|
||||
ainsum += sir[3];
|
||||
} else {
|
||||
routsum += sir[0];
|
||||
goutsum += sir[1];
|
||||
boutsum += sir[2];
|
||||
aoutsum += sir[3];
|
||||
}
|
||||
}
|
||||
stackpointer = radius;
|
||||
|
||||
for (x = 0; x < w; x++) {
|
||||
|
||||
r[yi] = dv[rsum];
|
||||
g[yi] = dv[gsum];
|
||||
b[yi] = dv[bsum];
|
||||
a[yi] = dv[asum];
|
||||
|
||||
rsum -= routsum;
|
||||
gsum -= goutsum;
|
||||
bsum -= boutsum;
|
||||
asum -= aoutsum;
|
||||
|
||||
stackstart = stackpointer - radius + div;
|
||||
sir = stack[stackstart % div];
|
||||
|
||||
routsum -= sir[0];
|
||||
goutsum -= sir[1];
|
||||
boutsum -= sir[2];
|
||||
aoutsum -= sir[3];
|
||||
|
||||
if (y == 0) {
|
||||
vmin[x] = Math.min(x + radius + 1, wm);
|
||||
}
|
||||
p = pix[yw + vmin[x]];
|
||||
|
||||
sir[0] = (p & 0xff0000) >> 16;
|
||||
sir[1] = (p & 0x00ff00) >> 8;
|
||||
sir[2] = (p & 0x0000ff);
|
||||
sir[3] = 0xff & (p >> 24);
|
||||
|
||||
rinsum += sir[0];
|
||||
ginsum += sir[1];
|
||||
binsum += sir[2];
|
||||
ainsum += sir[3];
|
||||
|
||||
rsum += rinsum;
|
||||
gsum += ginsum;
|
||||
bsum += binsum;
|
||||
asum += ainsum;
|
||||
|
||||
stackpointer = (stackpointer + 1) % div;
|
||||
sir = stack[(stackpointer) % div];
|
||||
|
||||
routsum += sir[0];
|
||||
goutsum += sir[1];
|
||||
boutsum += sir[2];
|
||||
aoutsum += sir[3];
|
||||
|
||||
rinsum -= sir[0];
|
||||
ginsum -= sir[1];
|
||||
binsum -= sir[2];
|
||||
ainsum -= sir[3];
|
||||
|
||||
yi++;
|
||||
}
|
||||
yw += w;
|
||||
}
|
||||
for (x = 0; x < w; x++) {
|
||||
rinsum =
|
||||
ginsum =
|
||||
binsum =
|
||||
ainsum = routsum = goutsum = boutsum = aoutsum = rsum = gsum = bsum = asum = 0;
|
||||
yp = -radius * w;
|
||||
for (i = -radius; i <= radius; i++) {
|
||||
yi = Math.max(0, yp) + x;
|
||||
|
||||
sir = stack[i + radius];
|
||||
|
||||
sir[0] = r[yi];
|
||||
sir[1] = g[yi];
|
||||
sir[2] = b[yi];
|
||||
sir[3] = a[yi];
|
||||
|
||||
rbs = r1 - Math.abs(i);
|
||||
|
||||
rsum += r[yi] * rbs;
|
||||
gsum += g[yi] * rbs;
|
||||
bsum += b[yi] * rbs;
|
||||
asum += a[yi] * rbs;
|
||||
|
||||
if (i > 0) {
|
||||
rinsum += sir[0];
|
||||
ginsum += sir[1];
|
||||
binsum += sir[2];
|
||||
ainsum += sir[3];
|
||||
} else {
|
||||
routsum += sir[0];
|
||||
goutsum += sir[1];
|
||||
boutsum += sir[2];
|
||||
aoutsum += sir[3];
|
||||
}
|
||||
|
||||
if (i < hm) {
|
||||
yp += w;
|
||||
}
|
||||
}
|
||||
yi = x;
|
||||
stackpointer = radius;
|
||||
for (y = 0; y < h; y++) {
|
||||
pix[yi] = (dv[asum] << 24) | (dv[rsum] << 16) | (dv[gsum] << 8) | dv[bsum];
|
||||
|
||||
rsum -= routsum;
|
||||
gsum -= goutsum;
|
||||
bsum -= boutsum;
|
||||
asum -= aoutsum;
|
||||
|
||||
stackstart = stackpointer - radius + div;
|
||||
sir = stack[stackstart % div];
|
||||
|
||||
routsum -= sir[0];
|
||||
goutsum -= sir[1];
|
||||
boutsum -= sir[2];
|
||||
aoutsum -= sir[3];
|
||||
|
||||
if (x == 0) {
|
||||
vmin[y] = Math.min(y + r1, hm) * w;
|
||||
}
|
||||
p = x + vmin[y];
|
||||
|
||||
sir[0] = r[p];
|
||||
sir[1] = g[p];
|
||||
sir[2] = b[p];
|
||||
sir[3] = a[p];
|
||||
|
||||
rinsum += sir[0];
|
||||
ginsum += sir[1];
|
||||
binsum += sir[2];
|
||||
ainsum += sir[3];
|
||||
|
||||
rsum += rinsum;
|
||||
gsum += ginsum;
|
||||
bsum += binsum;
|
||||
asum += ainsum;
|
||||
|
||||
stackpointer = (stackpointer + 1) % div;
|
||||
sir = stack[stackpointer];
|
||||
|
||||
routsum += sir[0];
|
||||
goutsum += sir[1];
|
||||
boutsum += sir[2];
|
||||
aoutsum += sir[3];
|
||||
|
||||
rinsum -= sir[0];
|
||||
ginsum -= sir[1];
|
||||
binsum -= sir[2];
|
||||
ainsum -= sir[3];
|
||||
|
||||
yi += w;
|
||||
}
|
||||
}
|
||||
|
||||
// Log.e("pix", w + " " + h + " " + pix.length);
|
||||
bitmap.setPixels(pix, 0, w, 0, 0, w, h);
|
||||
|
||||
return (bitmap);
|
||||
}
|
||||
|
||||
public static boolean PerceivedBrightness(int will_White, int[] c) {
|
||||
double TBT = Math.sqrt(c[0] * c[0] * .241 + c[1] * c[1] * .691 + c[2] * c[2] * .068);
|
||||
// Log.d("themee",TBT+"");
|
||||
return !(TBT > will_White);
|
||||
}
|
||||
|
||||
public static int[] getAverageColorRGB(Bitmap bitmap) {
|
||||
final int width = bitmap.getWidth();
|
||||
final int height = bitmap.getHeight();
|
||||
int size = width * height;
|
||||
int pixelColor;
|
||||
int r, g, b;
|
||||
r = g = b = 0;
|
||||
for (int x = 0; x < width; ++x) {
|
||||
for (int y = 0; y < height; ++y) {
|
||||
pixelColor = bitmap.getPixel(x, y);
|
||||
if (pixelColor == 0) {
|
||||
size--;
|
||||
continue;
|
||||
}
|
||||
r += Color.red(pixelColor);
|
||||
g += Color.green(pixelColor);
|
||||
b += Color.blue(pixelColor);
|
||||
}
|
||||
}
|
||||
r /= size;
|
||||
g /= size;
|
||||
b /= size;
|
||||
return new int[] {r, g, b};
|
||||
}
|
||||
|
||||
public static Bitmap updateSat(Bitmap src, float settingSat) {
|
||||
|
||||
int w = src.getWidth();
|
||||
int h = src.getHeight();
|
||||
|
||||
Bitmap bitmapResult = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
|
||||
Canvas canvasResult = new Canvas(bitmapResult);
|
||||
Paint paint = new Paint();
|
||||
ColorMatrix colorMatrix = new ColorMatrix();
|
||||
colorMatrix.setSaturation(settingSat);
|
||||
ColorMatrixColorFilter filter = new ColorMatrixColorFilter(colorMatrix);
|
||||
paint.setColorFilter(filter);
|
||||
canvasResult.drawBitmap(src, 0, 0, paint);
|
||||
canvasResult.setBitmap(null);
|
||||
canvasResult = null;
|
||||
return bitmapResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stack Blur v1.0 from http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html Java
|
||||
* Author: Mario Klingemann <mario at quasimondo.com> http://incubator.quasimondo.com
|
||||
*
|
||||
* <p>created Feburary 29, 2004 Android port : Yahel Bouaziz <yahel at kayenko.com>
|
||||
* http://www.kayenko.com ported april 5th, 2012
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>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 removeFromParent 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 x side of the stack.
|
||||
*
|
||||
* <p>If you are using this algorithm in your code please add the following line: Stack Blur
|
||||
* Algorithm by Mario Klingemann <mario@quasimondo.com>
|
||||
*/
|
||||
public static Bitmap fastblur(Bitmap sentBitmap, float scale, int radius) {
|
||||
|
||||
Bitmap afterscaleSentBitmap;
|
||||
Bitmap bitmap;
|
||||
if (scale != 1) {
|
||||
int width = Math.round(sentBitmap.getWidth() * scale); // lấy chiều rộng làm tròn
|
||||
int height = Math.round(sentBitmap.getHeight() * scale); // lấy chiều cao làm tròn
|
||||
afterscaleSentBitmap =
|
||||
Bitmap.createScaledBitmap(sentBitmap, width, height, false); // tạo bitmap scaled
|
||||
bitmap = afterscaleSentBitmap.copy(afterscaleSentBitmap.getConfig(), true);
|
||||
afterscaleSentBitmap.recycle();
|
||||
} else {
|
||||
bitmap = sentBitmap.copy(sentBitmap.getConfig(), true); // đơn giản chỉ copy
|
||||
}
|
||||
|
||||
if (radius < 1) {
|
||||
return (sentBitmap.copy(sentBitmap.getConfig(), true));
|
||||
}
|
||||
|
||||
int w = bitmap.getWidth(); // w is the width of sample bitmap
|
||||
int h = bitmap.getHeight(); // h is the height of sample bitmap
|
||||
|
||||
int[] pix = new int[w * h]; // pix is the arrary of all bitmap pixel
|
||||
|
||||
bitmap.getPixels(pix, 0, w, 0, 0, w, h);
|
||||
|
||||
int wm = w - 1;
|
||||
int hm = h - 1;
|
||||
int wh = w * h;
|
||||
int div = radius + radius + 1;
|
||||
|
||||
int[] r = new int[wh];
|
||||
int[] g = new int[wh];
|
||||
int[] b = new int[wh];
|
||||
int rsum, gsum, bsum, x, y, i, p, yp, yi, yw;
|
||||
int[] vmin = new int[Math.max(w, h)];
|
||||
|
||||
int divsum = (div + 1) >> 1;
|
||||
divsum *= divsum;
|
||||
int[] dv = new int[256 * divsum];
|
||||
for (i = 0; i < 256 * divsum; i++) {
|
||||
dv[i] = (i / divsum);
|
||||
}
|
||||
|
||||
yw = yi = 0;
|
||||
|
||||
int[][] stack = new int[div][3];
|
||||
int stackpointer;
|
||||
int stackstart;
|
||||
int[] sir;
|
||||
int rbs;
|
||||
int r1 = radius + 1;
|
||||
int routsum, goutsum, boutsum;
|
||||
int rinsum, ginsum, binsum;
|
||||
|
||||
for (y = 0; y < h; y++) {
|
||||
rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
|
||||
for (i = -radius; i <= radius; i++) {
|
||||
p = pix[yi + Math.min(wm, Math.max(i, 0))];
|
||||
sir = stack[i + radius];
|
||||
sir[0] = (p & 0xff0000) >> 16;
|
||||
sir[1] = (p & 0x00ff00) >> 8;
|
||||
sir[2] = (p & 0x0000ff);
|
||||
rbs = r1 - Math.abs(i);
|
||||
rsum += sir[0] * rbs;
|
||||
gsum += sir[1] * rbs;
|
||||
bsum += sir[2] * rbs;
|
||||
if (i > 0) {
|
||||
rinsum += sir[0];
|
||||
ginsum += sir[1];
|
||||
binsum += sir[2];
|
||||
} else {
|
||||
routsum += sir[0];
|
||||
goutsum += sir[1];
|
||||
boutsum += sir[2];
|
||||
}
|
||||
}
|
||||
stackpointer = radius;
|
||||
|
||||
for (x = 0; x < w; x++) {
|
||||
|
||||
r[yi] = dv[rsum];
|
||||
g[yi] = dv[gsum];
|
||||
b[yi] = dv[bsum];
|
||||
|
||||
rsum -= routsum;
|
||||
gsum -= goutsum;
|
||||
bsum -= boutsum;
|
||||
|
||||
stackstart = stackpointer - radius + div;
|
||||
sir = stack[stackstart % div];
|
||||
|
||||
routsum -= sir[0];
|
||||
goutsum -= sir[1];
|
||||
boutsum -= sir[2];
|
||||
|
||||
if (y == 0) {
|
||||
vmin[x] = Math.min(x + radius + 1, wm);
|
||||
}
|
||||
p = pix[yw + vmin[x]];
|
||||
|
||||
sir[0] = (p & 0xff0000) >> 16;
|
||||
sir[1] = (p & 0x00ff00) >> 8;
|
||||
sir[2] = (p & 0x0000ff);
|
||||
|
||||
rinsum += sir[0];
|
||||
ginsum += sir[1];
|
||||
binsum += sir[2];
|
||||
|
||||
rsum += rinsum;
|
||||
gsum += ginsum;
|
||||
bsum += binsum;
|
||||
|
||||
stackpointer = (stackpointer + 1) % div;
|
||||
sir = stack[(stackpointer) % div];
|
||||
|
||||
routsum += sir[0];
|
||||
goutsum += sir[1];
|
||||
boutsum += sir[2];
|
||||
|
||||
rinsum -= sir[0];
|
||||
ginsum -= sir[1];
|
||||
binsum -= sir[2];
|
||||
|
||||
yi++;
|
||||
}
|
||||
yw += w;
|
||||
}
|
||||
for (x = 0; x < w; x++) {
|
||||
rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
|
||||
yp = -radius * w;
|
||||
for (i = -radius; i <= radius; i++) {
|
||||
yi = Math.max(0, yp) + x;
|
||||
|
||||
sir = stack[i + radius];
|
||||
|
||||
sir[0] = r[yi];
|
||||
sir[1] = g[yi];
|
||||
sir[2] = b[yi];
|
||||
|
||||
rbs = r1 - Math.abs(i);
|
||||
|
||||
rsum += r[yi] * rbs;
|
||||
gsum += g[yi] * rbs;
|
||||
bsum += b[yi] * rbs;
|
||||
|
||||
if (i > 0) {
|
||||
rinsum += sir[0];
|
||||
ginsum += sir[1];
|
||||
binsum += sir[2];
|
||||
} else {
|
||||
routsum += sir[0];
|
||||
goutsum += sir[1];
|
||||
boutsum += sir[2];
|
||||
}
|
||||
|
||||
if (i < hm) {
|
||||
yp += w;
|
||||
}
|
||||
}
|
||||
yi = x;
|
||||
stackpointer = radius;
|
||||
for (y = 0; y < h; y++) {
|
||||
// Preserve alpha channel: ( 0xff000000 & pix[yi] )
|
||||
pix[yi] = (0xff000000 & pix[yi]) | (dv[rsum] << 16) | (dv[gsum] << 8) | dv[bsum];
|
||||
|
||||
rsum -= routsum;
|
||||
gsum -= goutsum;
|
||||
bsum -= boutsum;
|
||||
|
||||
stackstart = stackpointer - radius + div;
|
||||
sir = stack[stackstart % div];
|
||||
|
||||
routsum -= sir[0];
|
||||
goutsum -= sir[1];
|
||||
boutsum -= sir[2];
|
||||
|
||||
if (x == 0) {
|
||||
vmin[y] = Math.min(y + r1, hm) * w;
|
||||
}
|
||||
p = x + vmin[y];
|
||||
|
||||
sir[0] = r[p];
|
||||
sir[1] = g[p];
|
||||
sir[2] = b[p];
|
||||
|
||||
rinsum += sir[0];
|
||||
ginsum += sir[1];
|
||||
binsum += sir[2];
|
||||
|
||||
rsum += rinsum;
|
||||
gsum += ginsum;
|
||||
bsum += binsum;
|
||||
|
||||
stackpointer = (stackpointer + 1) % div;
|
||||
sir = stack[stackpointer];
|
||||
|
||||
routsum += sir[0];
|
||||
goutsum += sir[1];
|
||||
boutsum += sir[2];
|
||||
|
||||
rinsum -= sir[0];
|
||||
ginsum -= sir[1];
|
||||
binsum -= sir[2];
|
||||
|
||||
yi += w;
|
||||
}
|
||||
}
|
||||
|
||||
bitmap.setPixels(pix, 0, w, 0, 0, w, h);
|
||||
|
||||
return (bitmap);
|
||||
}
|
||||
|
||||
public static Bitmap getRoundedCornerBitmap(Bitmap bitmap, int pixels) {
|
||||
Bitmap output =
|
||||
Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);
|
||||
Canvas canvas = new Canvas(output);
|
||||
|
||||
final int color = 0xff424242;
|
||||
final Paint paint = new Paint();
|
||||
final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
|
||||
// final ScreenSize rectF = new ScreenSize(rect);
|
||||
final float roundPx = pixels;
|
||||
|
||||
paint.setAntiAlias(true);
|
||||
canvas.drawARGB(0, 0, 0, 0);
|
||||
paint.setColor(color);
|
||||
// canvas.drawRoundRect(rectF, roundPx, roundPx, paint);
|
||||
canvas.drawPath(
|
||||
BitmapEditor.RoundedRect(
|
||||
0, 0, bitmap.getWidth(), bitmap.getHeight(), roundPx, roundPx, false),
|
||||
paint);
|
||||
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
|
||||
canvas.drawBitmap(bitmap, rect, rect, paint);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* getResizedBitmap method is used to Resized the Image according to custom width and height
|
||||
*
|
||||
* @param image image to be resized
|
||||
* @param newHeight (new desired height)
|
||||
* @param newWidth (new desired Width)
|
||||
* @return image (new resized image)
|
||||
*/
|
||||
public static Bitmap getResizedBitmap(Bitmap image, int newHeight, int newWidth) {
|
||||
int width = image.getWidth();
|
||||
int height = image.getHeight();
|
||||
float scaleWidth = ((float) newWidth) / width;
|
||||
float scaleHeight = ((float) newHeight) / height;
|
||||
// create A matrix for the manipulation
|
||||
Matrix matrix = new Matrix();
|
||||
// onTap the bit map
|
||||
matrix.postScale(scaleWidth, scaleHeight);
|
||||
// recreate the new Bitmap
|
||||
return Bitmap.createBitmap(image, 0, 0, width, height, matrix, false);
|
||||
}
|
||||
|
||||
public static boolean TrueIfBitmapBigger(Bitmap bitmap, int size) {
|
||||
int sizeBitmap =
|
||||
Math.max(bitmap.getHeight(), bitmap.getWidth());
|
||||
return sizeBitmap > size;
|
||||
}
|
||||
|
||||
public static Bitmap getRoundedBitmapWithBlurShadow(
|
||||
Bitmap original, int paddingTop, int paddingBottom, int paddingLeft, int paddingRight) {
|
||||
int original_width = original.getWidth();
|
||||
int original_height = original.getHeight();
|
||||
int bitmap_width = original_width + paddingLeft + paddingRight;
|
||||
int bitmap_height = original_height + paddingTop + paddingBottom;
|
||||
Bitmap bitmap = Bitmap.createBitmap(bitmap_width, bitmap_height, Bitmap.Config.ARGB_8888);
|
||||
Canvas canvas = new Canvas(bitmap);
|
||||
Paint paint = new Paint();
|
||||
paint.setStyle(Paint.Style.FILL);
|
||||
// paint.setAlpha(60);
|
||||
// canvas.drawRect(0,0,bitmap_width,bitmap_height,paint);
|
||||
paint.setAntiAlias(true);
|
||||
canvas.drawBitmap(original, paddingLeft, paddingTop, paint);
|
||||
Bitmap blurred_bitmap = getBlurredWithGoodPerformance(bitmap, 1, 6, 4);
|
||||
canvas.setBitmap(null);
|
||||
bitmap.recycle();
|
||||
return blurred_bitmap;
|
||||
}
|
||||
|
||||
// Activity.
|
||||
// |
|
||||
// Original bitmap.
|
||||
// |
|
||||
// | To make the blur background, the original must to padding.
|
||||
// |
|
||||
// | | | |
|
||||
// |
|
||||
// V
|
||||
// V V V V
|
||||
// V
|
||||
public static Bitmap GetRoundedBitmapWithBlurShadow(
|
||||
Context context,
|
||||
Bitmap original,
|
||||
int paddingTop,
|
||||
int paddingBottom,
|
||||
int paddingLeft,
|
||||
int paddingRight,
|
||||
int TopBack // this value makes the overview bitmap is higher or belower the background.
|
||||
,
|
||||
int alphaBlurBackground // this is the alpha of the background Bitmap, you need A number
|
||||
// between 0 -> 255, the value recommend is 180.
|
||||
,
|
||||
int valueBlurBackground // this is the value used to blur the background Bitmap, the
|
||||
// recommended one is 12.
|
||||
,
|
||||
int valueSaturationBlurBackground // this is the value used to background Bitmap more
|
||||
// colorful, if valueBlur is 12, the valudeSaturation should
|
||||
// be 2.
|
||||
) {
|
||||
int original_width = original.getWidth();
|
||||
int orginal_height = original.getHeight();
|
||||
int bitmap_width = original_width + paddingLeft + paddingRight;
|
||||
int bitmap_height = orginal_height + paddingTop + paddingBottom;
|
||||
Bitmap bitmap = Bitmap.createBitmap(bitmap_width, bitmap_height, Bitmap.Config.ARGB_8888);
|
||||
Canvas canvas = new Canvas(bitmap);
|
||||
Paint paint = new Paint();
|
||||
paint.setStyle(Paint.Style.FILL);
|
||||
paint.setAntiAlias(true);
|
||||
canvas.drawBitmap(original, paddingLeft, paddingTop, paint);
|
||||
Bitmap blurred_bitmap =
|
||||
getBlurredWithGoodPerformance(
|
||||
context, bitmap, 1, valueBlurBackground, valueSaturationBlurBackground);
|
||||
// Bitmap blurred_bitmap= getBlurredWithGoodPerformance(context, bitmap,1,15,3);
|
||||
Bitmap end_bitmap = Bitmap.createBitmap(bitmap_width, bitmap_height, Bitmap.Config.ARGB_8888);
|
||||
canvas.setBitmap(end_bitmap);
|
||||
paint.setAlpha(alphaBlurBackground);
|
||||
|
||||
canvas.drawBitmap(
|
||||
blurred_bitmap,
|
||||
new Rect(0, 0, blurred_bitmap.getWidth(), blurred_bitmap.getHeight()),
|
||||
new Rect(0, 0, bitmap_width, bitmap_height),
|
||||
paint);
|
||||
paint.setAlpha(255);
|
||||
|
||||
canvas.drawBitmap(bitmap, 0, TopBack, paint); // drawVisualWave cái lớn
|
||||
canvas.setBitmap(null);
|
||||
blurred_bitmap.recycle();
|
||||
bitmap.recycle();
|
||||
return end_bitmap;
|
||||
}
|
||||
|
||||
public static void setBitmapforImageView(ImageView imv, Bitmap apply) {
|
||||
Bitmap old = ((BitmapDrawable) imv.getDrawable()).getBitmap();
|
||||
imv.setImageBitmap(apply);
|
||||
if (old != null) old.recycle();
|
||||
}
|
||||
|
||||
public static Bitmap getBlurredWithGoodPerformance(
|
||||
Bitmap bitmap, int scale, int radius, int saturation) {
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
Bitmap bitmap1 = getResizedBitmap(bitmap, 50, 50);
|
||||
Bitmap updateSatBitmap = updateSat(bitmap1, saturation);
|
||||
Bitmap blurredBitmap = FastBlurSupportAlpha(updateSatBitmap, scale, radius);
|
||||
|
||||
updateSatBitmap.recycle();
|
||||
bitmap1.recycle();
|
||||
return blurredBitmap;
|
||||
}
|
||||
|
||||
public static Path RoundedRect(
|
||||
float left,
|
||||
float top,
|
||||
float right,
|
||||
float bottom,
|
||||
float rx,
|
||||
float ry,
|
||||
boolean conformToOriginalPost) {
|
||||
Path path = new Path();
|
||||
if (rx < 0) rx = 0;
|
||||
if (ry < 0) ry = 0;
|
||||
float width = right - left;
|
||||
float height = bottom - top;
|
||||
if (rx > width / 2) rx = width / 2;
|
||||
if (ry > height / 2) ry = height / 2;
|
||||
float widthMinusCorners = (width - (2 * rx)); // do dai phan "thang" cua chieu rong
|
||||
float heightMinusCorners = (height - (2 * ry)); // do dai phan "thang" cua chieu dai
|
||||
|
||||
path.moveTo(right, top + ry); // bat dau tu day
|
||||
path.rQuadTo(0, -ry, -rx, -ry); // y-right corner
|
||||
path.rLineTo(-widthMinusCorners, 0);
|
||||
path.rQuadTo(-rx, 0, -rx, ry); // y-x corner
|
||||
path.rLineTo(0, heightMinusCorners);
|
||||
|
||||
if (conformToOriginalPost) {
|
||||
path.rLineTo(0, ry);
|
||||
path.rLineTo(width, 0);
|
||||
path.rLineTo(0, -ry);
|
||||
} else {
|
||||
|
||||
path.rQuadTo(0, ry, rx, ry); // bottom-x corner
|
||||
path.rLineTo(widthMinusCorners, 0);
|
||||
path.rQuadTo(rx, 0, rx, -ry); // bottom-right corner
|
||||
}
|
||||
|
||||
path.rLineTo(0, -heightMinusCorners);
|
||||
|
||||
path.close(); // Given close, last lineto can be removed.
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
public static int mixTwoColors(int color1, int color2, float amount) {
|
||||
final byte ALPHA_CHANNEL = 24;
|
||||
final byte RED_CHANNEL = 16;
|
||||
final byte GREEN_CHANNEL = 8;
|
||||
final byte BLUE_CHANNEL = 0;
|
||||
|
||||
final float inverseAmount = 1.0f - amount;
|
||||
|
||||
int a =
|
||||
((int)
|
||||
(((float) (color1 >> ALPHA_CHANNEL & 0xff) * amount)
|
||||
+ ((float) (color2 >> ALPHA_CHANNEL & 0xff) * inverseAmount)))
|
||||
& 0xff;
|
||||
int r =
|
||||
((int)
|
||||
(((float) (color1 >> RED_CHANNEL & 0xff) * amount)
|
||||
+ ((float) (color2 >> RED_CHANNEL & 0xff) * inverseAmount)))
|
||||
& 0xff;
|
||||
int g =
|
||||
((int)
|
||||
(((float) (color1 >> GREEN_CHANNEL & 0xff) * amount)
|
||||
+ ((float) (color2 >> GREEN_CHANNEL & 0xff) * inverseAmount)))
|
||||
& 0xff;
|
||||
int b =
|
||||
((int) (((float) (color1 & 0xff) * amount) + ((float) (color2 & 0xff) * inverseAmount)))
|
||||
& 0xff;
|
||||
|
||||
return a << ALPHA_CHANNEL | r << RED_CHANNEL | g << GREEN_CHANNEL | b << BLUE_CHANNEL;
|
||||
}
|
||||
|
||||
public static Bitmap getBlurredWithGoodPerformance(
|
||||
Context context, Bitmap bitmap, int scale, int radius, float saturation) {
|
||||
Bitmap bitmap1 = getResizedBitmap(bitmap, 150, 150);
|
||||
Bitmap updateSatBimap = updateSat(bitmap1, saturation);
|
||||
Bitmap blurredBitmap = BlurBitmapWithRenderScript(context, updateSatBimap, radius);
|
||||
updateSatBimap.recycle();
|
||||
bitmap1.recycle();
|
||||
return blurredBitmap;
|
||||
}
|
||||
|
||||
public static Bitmap getBlurredBimapWithRenderScript(
|
||||
Context context, Bitmap bitmapOriginal, float radius) {
|
||||
// define this only once if blurring multiple times
|
||||
RenderScript rs = RenderScript.create(context);
|
||||
|
||||
// this will blur the bitmapOriginal with A radius of 8 and save it in bitmapOriginal
|
||||
final Allocation input =
|
||||
Allocation.createFromBitmap(
|
||||
rs, bitmapOriginal); // use this constructor for best performance, because it uses
|
||||
// USAGE_SHARED mode which reuses memory
|
||||
final Allocation output = Allocation.createTyped(rs, input.getType());
|
||||
final ScriptIntrinsicBlur script = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
|
||||
script.setRadius(radius);
|
||||
script.setInput(input);
|
||||
script.forEach(output);
|
||||
output.copyTo(bitmapOriginal);
|
||||
return bitmapOriginal;
|
||||
}
|
||||
|
||||
public static Bitmap BlurBitmapWithRenderScript(Context context, Bitmap bitmap, float radius) {
|
||||
// Let's create an empty bitmap with the same size of the bitmap we want to blur
|
||||
Bitmap outBitmap =
|
||||
Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);
|
||||
|
||||
// Instantiate A new Renderscript
|
||||
RenderScript rs = RenderScript.create(context);
|
||||
|
||||
// Create an Intrinsic Blur Script using the Renderscript
|
||||
ScriptIntrinsicBlur blurScript = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
|
||||
|
||||
// Create the Allocations (in/out) with the Renderscript and the in/out bitmaps
|
||||
Allocation allIn = Allocation.createFromBitmap(rs, bitmap);
|
||||
Allocation allOut = Allocation.createFromBitmap(rs, outBitmap);
|
||||
// Set the radius of the blur
|
||||
blurScript.setRadius(radius);
|
||||
|
||||
// Perform the Renderscript
|
||||
blurScript.setInput(allIn);
|
||||
blurScript.forEach(allOut);
|
||||
|
||||
// Copy the final bitmap created by the out Allocation to the outBitmap
|
||||
allOut.copyTo(outBitmap);
|
||||
|
||||
// recycle the original bitmap
|
||||
|
||||
// After finishing everything, we destroy the Renderscript.
|
||||
rs.destroy();
|
||||
|
||||
return outBitmap;
|
||||
}
|
||||
|
||||
public static Drawable covertBitmapToDrawable(Context context, Bitmap bitmap) {
|
||||
Drawable d = new BitmapDrawable(context.getResources(), bitmap);
|
||||
return d;
|
||||
}
|
||||
|
||||
public static Bitmap convertDrawableToBitmap(Drawable drawable) {
|
||||
if (drawable instanceof BitmapDrawable) {
|
||||
return ((BitmapDrawable) drawable).getBitmap();
|
||||
}
|
||||
Bitmap bitmap =
|
||||
Bitmap.createBitmap(
|
||||
drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
|
||||
Canvas canvas = new Canvas(bitmap);
|
||||
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
|
||||
drawable.draw(canvas);
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
public static Bitmap changeBitmapColor(Bitmap sourceBitmap, int color) {
|
||||
Bitmap resultBitmap = sourceBitmap.copy(sourceBitmap.getConfig(), true);
|
||||
Paint paint = new Paint();
|
||||
ColorFilter filter = new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_IN);
|
||||
paint.setColorFilter(filter);
|
||||
Canvas canvas = new Canvas(resultBitmap);
|
||||
canvas.drawBitmap(resultBitmap, 0, 0, paint);
|
||||
return resultBitmap;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mode
|
||||
* @return 0 : CLEAR <br>
|
||||
* 1 : SRC <br>
|
||||
* 2 : DST <br>
|
||||
* 3 : SRC_OVER <br>
|
||||
* 4 : DST_OVER <br>
|
||||
* 5 : SRC_IN <br>
|
||||
* 6 : DST_IN <br>
|
||||
* 7 : SRC_OUT <br>
|
||||
* 8 : DST_OUT <br>
|
||||
* 9 : SRC_ATOP <br>
|
||||
* 10 : DST_ATOP <br>
|
||||
* 11 : XOR <br>
|
||||
* 12 : ADD <br>
|
||||
* 13 : MULTIPLY <br>
|
||||
* 14 : SCREEN <br>
|
||||
* 15 : OVERLAY <br>
|
||||
* 16 : DARKEN <br>
|
||||
* 17 : LIGHTEN
|
||||
*/
|
||||
public static PorterDuff.Mode getPorterMode(int mode) {
|
||||
switch (mode) {
|
||||
default:
|
||||
case 0:
|
||||
return PorterDuff.Mode.CLEAR;
|
||||
case 1:
|
||||
return PorterDuff.Mode.SRC;
|
||||
case 2:
|
||||
return PorterDuff.Mode.DST;
|
||||
case 3:
|
||||
return PorterDuff.Mode.SRC_OVER;
|
||||
case 4:
|
||||
return PorterDuff.Mode.DST_OVER;
|
||||
case 5:
|
||||
return PorterDuff.Mode.SRC_IN;
|
||||
case 6:
|
||||
return PorterDuff.Mode.DST_IN;
|
||||
case 7:
|
||||
return PorterDuff.Mode.SRC_OUT;
|
||||
case 8:
|
||||
return PorterDuff.Mode.DST_OUT;
|
||||
case 9:
|
||||
return PorterDuff.Mode.SRC_ATOP;
|
||||
case 10:
|
||||
return PorterDuff.Mode.DST_ATOP;
|
||||
case 11:
|
||||
return PorterDuff.Mode.XOR;
|
||||
case 16:
|
||||
return PorterDuff.Mode.DARKEN;
|
||||
case 17:
|
||||
return PorterDuff.Mode.LIGHTEN;
|
||||
case 13:
|
||||
return PorterDuff.Mode.MULTIPLY;
|
||||
case 14:
|
||||
return PorterDuff.Mode.SCREEN;
|
||||
case 12:
|
||||
return PorterDuff.Mode.ADD;
|
||||
case 15:
|
||||
return PorterDuff.Mode.OVERLAY;
|
||||
}
|
||||
}
|
||||
|
||||
public static void applyNewColor4Bitmap(
|
||||
Context context, int[] idBitmaps, ImageView[] imageViews, int color, float alpha) {
|
||||
android.content.res.Resources resource = context.getResources();
|
||||
int size = idBitmaps.length;
|
||||
Bitmap usingBitmap, resultBitmap;
|
||||
for (int i = 0; i < size; i++) {
|
||||
usingBitmap = BitmapFactory.decodeResource(resource, idBitmaps[i]);
|
||||
resultBitmap = changeBitmapColor(usingBitmap, color);
|
||||
imageViews[i].setImageBitmap(resultBitmap);
|
||||
imageViews[i].setAlpha(alpha);
|
||||
}
|
||||
}
|
||||
|
||||
public static void applyNewColor4Bitmap(
|
||||
Context context, int idBitmap, ImageView applyView, int color, float alpha) {
|
||||
|
||||
android.content.res.Resources resource = context.getResources();
|
||||
Bitmap usingBitmap = BitmapFactory.decodeResource(resource, idBitmap);
|
||||
Bitmap resultBitmap = changeBitmapColor(usingBitmap, color);
|
||||
applyView.setImageBitmap(resultBitmap);
|
||||
applyView.setAlpha(alpha);
|
||||
}
|
||||
|
||||
public static Bitmap getBitmapFromView(View view) {
|
||||
Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);
|
||||
Canvas c = new Canvas(bitmap);
|
||||
view.layout(view.getLeft(), view.getTop(), view.getRight(), view.getBottom());
|
||||
view.draw(c);
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
public static Bitmap getBitmapFromView(View view, int left, int top, int right, int bottom) {
|
||||
Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);
|
||||
Canvas c = new Canvas(bitmap);
|
||||
view.layout(left, top, right, bottom);
|
||||
view.draw(c);
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
public static Bitmap getBackgroundBitmapAViewWithParent(View childView, View parentView) {
|
||||
int[] pos_child = new int[2];
|
||||
childView.getLocationOnScreen(pos_child);
|
||||
return getBitmapFromView(
|
||||
parentView, pos_child[0], pos_child[1], parentView.getRight(), parentView.getBottom());
|
||||
}
|
||||
|
||||
public static Bitmap getBackgroundBlurAViewWithParent(
|
||||
Activity activity, View childView, View parentView) {
|
||||
Bitmap b1 = getBackgroundBitmapAViewWithParent(childView, parentView);
|
||||
Bitmap b2 = getBlurredWithGoodPerformance(activity, b1, 1, 8, 2);
|
||||
b1.recycle();
|
||||
return b2;
|
||||
}
|
||||
}
|
|
@ -1,140 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Hemanth Savarala.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
package code.name.monkey.retromusic.util;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.GregorianCalendar;
|
||||
|
||||
/** @author Eugene Cheung (arkon) */
|
||||
public class CalendarUtil {
|
||||
private static final long MS_PER_MINUTE = 60 * 1000;
|
||||
private static final long MS_PER_DAY = 24 * 60 * MS_PER_MINUTE;
|
||||
|
||||
private final Calendar calendar;
|
||||
|
||||
public CalendarUtil() {
|
||||
this.calendar = Calendar.getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time elapsed so far today in milliseconds.
|
||||
*
|
||||
* @return Time elapsed today in milliseconds.
|
||||
*/
|
||||
public long getElapsedToday() {
|
||||
// Time elapsed so far today
|
||||
return (calendar.get(Calendar.HOUR_OF_DAY) * 60 + calendar.get(Calendar.MINUTE)) * MS_PER_MINUTE
|
||||
+ calendar.get(Calendar.SECOND) * 1000
|
||||
+ calendar.get(Calendar.MILLISECOND);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time elapsed so far this week in milliseconds.
|
||||
*
|
||||
* @return Time elapsed this week in milliseconds.
|
||||
*/
|
||||
public long getElapsedWeek() {
|
||||
// Today + days passed this week
|
||||
long elapsed = getElapsedToday();
|
||||
|
||||
final int passedWeekdays =
|
||||
calendar.get(Calendar.DAY_OF_WEEK) - 1 - calendar.getFirstDayOfWeek();
|
||||
if (passedWeekdays > 0) {
|
||||
elapsed += passedWeekdays * MS_PER_DAY;
|
||||
}
|
||||
|
||||
return elapsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time elapsed so far this month in milliseconds.
|
||||
*
|
||||
* @return Time elapsed this month in milliseconds.
|
||||
*/
|
||||
public long getElapsedMonth() {
|
||||
// Today + rest of this month
|
||||
return getElapsedToday() + ((calendar.get(Calendar.DAY_OF_MONTH) - 1) * MS_PER_DAY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time elapsed so far this month and the last numMonths months in milliseconds.
|
||||
*
|
||||
* @param numMonths Additional number of months prior to the current month to calculate.
|
||||
* @return Time elapsed this month and the last numMonths months in milliseconds.
|
||||
*/
|
||||
public long getElapsedMonths(int numMonths) {
|
||||
// Today + rest of this month
|
||||
long elapsed = getElapsedMonth();
|
||||
|
||||
// Previous numMonths months
|
||||
int month = calendar.get(Calendar.MONTH);
|
||||
int year = calendar.get(Calendar.YEAR);
|
||||
for (int i = 0; i < numMonths; i++) {
|
||||
month--;
|
||||
|
||||
if (month < Calendar.JANUARY) {
|
||||
month = Calendar.DECEMBER;
|
||||
year--;
|
||||
}
|
||||
|
||||
elapsed += getDaysInMonth(month) * MS_PER_DAY;
|
||||
}
|
||||
|
||||
return elapsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time elapsed so far this year in milliseconds.
|
||||
*
|
||||
* @return Time elapsed this year in milliseconds.
|
||||
*/
|
||||
public long getElapsedYear() {
|
||||
// Today + rest of this month + previous months until January
|
||||
long elapsed = getElapsedMonth();
|
||||
|
||||
int month = calendar.get(Calendar.MONTH) - 1;
|
||||
int year = calendar.get(Calendar.YEAR);
|
||||
while (month > Calendar.JANUARY) {
|
||||
elapsed += getDaysInMonth(month) * MS_PER_DAY;
|
||||
|
||||
month--;
|
||||
}
|
||||
|
||||
return elapsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of days for the given month in the given year.
|
||||
*
|
||||
* @param month The month (1 - 12).
|
||||
* @return The days in that month/year.
|
||||
*/
|
||||
private int getDaysInMonth(int month) {
|
||||
final Calendar monthCal = new GregorianCalendar(calendar.get(Calendar.YEAR), month, 1);
|
||||
return monthCal.getActualMaximum(Calendar.DAY_OF_MONTH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time elapsed so far last N days in milliseconds.
|
||||
*
|
||||
* @return Time elapsed since N days in milliseconds.
|
||||
*/
|
||||
public long getElapsedDays(int numDays) {
|
||||
long elapsed = getElapsedToday();
|
||||
elapsed += numDays * MS_PER_DAY;
|
||||
|
||||
return elapsed;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Hemanth Savarala.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*/
|
||||
package code.name.monkey.retromusic.util
|
||||
|
||||
import java.util.*
|
||||
|
||||
/** @author Eugene Cheung (arkon)
|
||||
*/
|
||||
class CalendarUtil {
|
||||
private val calendar = Calendar.getInstance()// Time elapsed so far today
|
||||
|
||||
/**
|
||||
* Returns the time elapsed so far today in milliseconds.
|
||||
*
|
||||
* @return Time elapsed today in milliseconds.
|
||||
*/
|
||||
val elapsedToday: Long
|
||||
get() =// Time elapsed so far today
|
||||
(calendar[Calendar.HOUR_OF_DAY] * 60 + calendar[Calendar.MINUTE]) * MS_PER_MINUTE + calendar[Calendar.SECOND] * 1000 + calendar[Calendar.MILLISECOND]// Today + days passed this week
|
||||
|
||||
/**
|
||||
* Returns the time elapsed so far this week in milliseconds.
|
||||
*
|
||||
* @return Time elapsed this week in milliseconds.
|
||||
*/
|
||||
val elapsedWeek: Long
|
||||
get() {
|
||||
// Today + days passed this week
|
||||
var elapsed = elapsedToday
|
||||
val passedWeekdays = calendar[Calendar.DAY_OF_WEEK] - 1 - calendar.firstDayOfWeek
|
||||
if (passedWeekdays > 0) {
|
||||
elapsed += passedWeekdays * MS_PER_DAY
|
||||
}
|
||||
return elapsed
|
||||
}// Today + rest of this month
|
||||
|
||||
/**
|
||||
* Returns the time elapsed so far this month in milliseconds.
|
||||
*
|
||||
* @return Time elapsed this month in milliseconds.
|
||||
*/
|
||||
val elapsedMonth: Long
|
||||
get() =// Today + rest of this month
|
||||
elapsedToday + (calendar[Calendar.DAY_OF_MONTH] - 1) * MS_PER_DAY
|
||||
|
||||
/**
|
||||
* Returns the time elapsed so far this month and the last numMonths months in milliseconds.
|
||||
*
|
||||
* @param numMonths Additional number of months prior to the current month to calculate.
|
||||
* @return Time elapsed this month and the last numMonths months in milliseconds.
|
||||
*/
|
||||
fun getElapsedMonths(numMonths: Int): Long {
|
||||
// Today + rest of this month
|
||||
var elapsed = elapsedMonth
|
||||
|
||||
// Previous numMonths months
|
||||
var month = calendar[Calendar.MONTH]
|
||||
var year = calendar[Calendar.YEAR]
|
||||
for (i in 0 until numMonths) {
|
||||
month--
|
||||
if (month < Calendar.JANUARY) {
|
||||
month = Calendar.DECEMBER
|
||||
year--
|
||||
}
|
||||
elapsed += getDaysInMonth(month) * MS_PER_DAY
|
||||
}
|
||||
return elapsed
|
||||
}// Today + rest of this month + previous months until January
|
||||
|
||||
/**
|
||||
* Returns the time elapsed so far this year in milliseconds.
|
||||
*
|
||||
* @return Time elapsed this year in milliseconds.
|
||||
*/
|
||||
val elapsedYear: Long
|
||||
get() {
|
||||
// Today + rest of this month + previous months until January
|
||||
var elapsed = elapsedMonth
|
||||
var month = calendar[Calendar.MONTH] - 1
|
||||
while (month > Calendar.JANUARY) {
|
||||
elapsed += getDaysInMonth(month) * MS_PER_DAY
|
||||
month--
|
||||
}
|
||||
return elapsed
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of days for the given month in the given year.
|
||||
*
|
||||
* @param month The month (1 - 12).
|
||||
* @return The days in that month/year.
|
||||
*/
|
||||
private fun getDaysInMonth(month: Int): Int {
|
||||
val monthCal: Calendar = GregorianCalendar(calendar[Calendar.YEAR], month, 1)
|
||||
return monthCal.getActualMaximum(Calendar.DAY_OF_MONTH)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time elapsed so far last N days in milliseconds.
|
||||
*
|
||||
* @return Time elapsed since N days in milliseconds.
|
||||
*/
|
||||
fun getElapsedDays(numDays: Int): Long {
|
||||
var elapsed = elapsedToday
|
||||
elapsed += numDays * MS_PER_DAY
|
||||
return elapsed
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val MS_PER_MINUTE = (60 * 1000).toLong()
|
||||
private const val MS_PER_DAY = 24 * 60 * MS_PER_MINUTE
|
||||
}
|
||||
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
package code.name.monkey.retromusic.util
|
||||
|
||||
import android.animation.ArgbEvaluator
|
||||
import android.animation.ValueAnimator
|
||||
|
||||
class ColorAnimUtil {
|
||||
companion object {
|
||||
fun createColorAnimator(
|
||||
fromColor: Int,
|
||||
toColor: Int,
|
||||
mDuration: Long = 300
|
||||
): ValueAnimator {
|
||||
return ValueAnimator.ofInt(fromColor, toColor).apply {
|
||||
setEvaluator(ArgbEvaluator())
|
||||
duration = mDuration
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Hemanth Savarala.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
package code.name.monkey.retromusic.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Created on : June 18, 2016 Author : zetbaitsu Name : Zetra GitHub : https://github.com/zetbaitsu
|
||||
*/
|
||||
public class Compressor {
|
||||
// max width and height values of the compressed image is taken as 612x816
|
||||
private int maxWidth = 612;
|
||||
private int maxHeight = 816;
|
||||
private Bitmap.CompressFormat compressFormat = Bitmap.CompressFormat.JPEG;
|
||||
private int quality = 80;
|
||||
private String destinationDirectoryPath;
|
||||
|
||||
public Compressor(Context context) {
|
||||
destinationDirectoryPath = context.getCacheDir().getPath() + File.separator + "images";
|
||||
}
|
||||
|
||||
public Compressor setMaxWidth(int maxWidth) {
|
||||
this.maxWidth = maxWidth;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Compressor setMaxHeight(int maxHeight) {
|
||||
this.maxHeight = maxHeight;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Compressor setCompressFormat(Bitmap.CompressFormat compressFormat) {
|
||||
this.compressFormat = compressFormat;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Compressor setQuality(int quality) {
|
||||
this.quality = quality;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Compressor setDestinationDirectoryPath(String destinationDirectoryPath) {
|
||||
this.destinationDirectoryPath = destinationDirectoryPath;
|
||||
return this;
|
||||
}
|
||||
|
||||
public File compressToFile(File imageFile) throws IOException {
|
||||
return compressToFile(imageFile, imageFile.getName());
|
||||
}
|
||||
|
||||
public File compressToFile(File imageFile, String compressedFileName) throws IOException {
|
||||
return ImageUtil.compressImage(
|
||||
imageFile,
|
||||
maxWidth,
|
||||
maxHeight,
|
||||
compressFormat,
|
||||
quality,
|
||||
destinationDirectoryPath + File.separator + compressedFileName);
|
||||
}
|
||||
|
||||
public Bitmap compressToBitmap(File imageFile) throws IOException {
|
||||
return ImageUtil.decodeSampledBitmapFromFile(imageFile, maxWidth, maxHeight);
|
||||
}
|
||||
}
|
|
@ -14,25 +14,21 @@
|
|||
|
||||
package code.name.monkey.retromusic.util
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.net.Uri
|
||||
import android.os.AsyncTask
|
||||
import android.provider.MediaStore
|
||||
import android.widget.Toast
|
||||
import androidx.core.content.edit
|
||||
import code.name.monkey.retromusic.App
|
||||
import code.name.monkey.retromusic.extensions.showToast
|
||||
import code.name.monkey.retromusic.glide.GlideApp
|
||||
import code.name.monkey.retromusic.model.Artist
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||
import com.bumptech.glide.request.target.SimpleTarget
|
||||
import com.bumptech.glide.request.transition.Transition
|
||||
import java.io.BufferedOutputStream
|
||||
import kotlinx.coroutines.Dispatchers.IO
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.io.IOException
|
||||
import java.util.*
|
||||
|
||||
|
@ -44,78 +40,71 @@ class CustomArtistImageUtil private constructor(context: Context) {
|
|||
Context.MODE_PRIVATE
|
||||
)
|
||||
|
||||
fun setCustomArtistImage(artist: Artist, uri: Uri) {
|
||||
Glide.with(App.getContext())
|
||||
.asBitmap()
|
||||
.load(uri)
|
||||
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
||||
.skipMemoryCache(true)
|
||||
.into(object : SimpleTarget<Bitmap>() {
|
||||
override fun onLoadFailed(errorDrawable: Drawable?) {
|
||||
super.onLoadFailed(errorDrawable)
|
||||
Toast.makeText(App.getContext(), "Load Failed", Toast.LENGTH_LONG).show()
|
||||
suspend fun setCustomArtistImage(artist: Artist, uri: Uri) {
|
||||
val context = App.getContext()
|
||||
withContext(IO) {
|
||||
runCatching {
|
||||
GlideApp.with(context)
|
||||
.asBitmap()
|
||||
.load(uri)
|
||||
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
||||
.skipMemoryCache(true)
|
||||
.submit()
|
||||
.get()
|
||||
}
|
||||
.onSuccess {
|
||||
saveImage(context, artist, it)
|
||||
}
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
|
||||
object : AsyncTask<Void, Void, Void>() {
|
||||
override fun doInBackground(vararg params: Void): Void? {
|
||||
val dir = File(App.getContext().filesDir, FOLDER_NAME)
|
||||
if (!dir.exists()) {
|
||||
if (!dir.mkdirs()) { // create the folder
|
||||
return null
|
||||
}
|
||||
}
|
||||
val file = File(dir, getFileName(artist))
|
||||
|
||||
var succesful = false
|
||||
try {
|
||||
val os = BufferedOutputStream(FileOutputStream(file))
|
||||
succesful = ImageUtil.resizeBitmap(resource, 2048)
|
||||
.compress(Bitmap.CompressFormat.JPEG, 100, os)
|
||||
os.close()
|
||||
} catch (e: IOException) {
|
||||
Toast.makeText(App.getContext(), e.toString(), Toast.LENGTH_LONG)
|
||||
.show()
|
||||
}
|
||||
|
||||
if (succesful) {
|
||||
mPreferences.edit { putBoolean(getFileName(artist), true) }
|
||||
ArtistSignatureUtil.getInstance(App.getContext())
|
||||
.updateArtistSignature(artist.name)
|
||||
App.getContext().contentResolver.notifyChange(
|
||||
MediaStore.Audio.Artists.EXTERNAL_CONTENT_URI,
|
||||
null
|
||||
) // trigger media store changed to force artist image reload
|
||||
}
|
||||
return null
|
||||
}
|
||||
}.execute()
|
||||
.onFailure {
|
||||
context.showToast("Load Failed")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
fun resetCustomArtistImage(artist: Artist) {
|
||||
object : AsyncTask<Void, Void, Void>() {
|
||||
@SuppressLint("ApplySharedPref")
|
||||
override fun doInBackground(vararg params: Void): Void? {
|
||||
mPreferences.edit(commit = true) { putBoolean(getFileName(artist), false) }
|
||||
ArtistSignatureUtil.getInstance(App.getContext()).updateArtistSignature(artist.name)
|
||||
App.getContext().contentResolver.notifyChange(
|
||||
MediaStore.Audio.Artists.EXTERNAL_CONTENT_URI,
|
||||
null
|
||||
) // trigger media store changed to force artist image reload
|
||||
|
||||
val file = getFile(artist)
|
||||
if (!file.exists()) {
|
||||
return null
|
||||
} else {
|
||||
file.delete()
|
||||
}
|
||||
return null
|
||||
private fun saveImage(context: Context, artist: Artist, bitmap: Bitmap) {
|
||||
val dir = File(context.filesDir, FOLDER_NAME)
|
||||
if (!dir.exists()) {
|
||||
if (!dir.mkdirs()) { // create the folder
|
||||
return
|
||||
}
|
||||
}.execute()
|
||||
}
|
||||
val file = File(dir, getFileName(artist))
|
||||
|
||||
var successful = false
|
||||
try {
|
||||
file.outputStream().buffered().use { bos ->
|
||||
successful = ImageUtil.resizeBitmap(bitmap, 2048)
|
||||
.compress(Bitmap.CompressFormat.JPEG, 100, bos)
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
context.showToast(e.toString(), Toast.LENGTH_LONG)
|
||||
}
|
||||
|
||||
if (successful) {
|
||||
mPreferences.edit { putBoolean(getFileName(artist), true) }
|
||||
ArtistSignatureUtil.getInstance(context)
|
||||
.updateArtistSignature(artist.name)
|
||||
context.contentResolver.notifyChange(
|
||||
MediaStore.Audio.Artists.EXTERNAL_CONTENT_URI,
|
||||
null
|
||||
) // trigger media store changed to force artist image reload
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun resetCustomArtistImage(artist: Artist) {
|
||||
withContext(IO) {
|
||||
mPreferences.edit { putBoolean(getFileName(artist), false) }
|
||||
ArtistSignatureUtil.getInstance(App.getContext()).updateArtistSignature(artist.name)
|
||||
App.getContext().contentResolver.notifyChange(
|
||||
MediaStore.Audio.Artists.EXTERNAL_CONTENT_URI,
|
||||
null
|
||||
) // trigger media store changed to force artist image reload
|
||||
|
||||
val file = getFile(artist)
|
||||
if (file.exists()) {
|
||||
file.delete()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// shared prefs saves us many IO operations
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
package code.name.monkey.retromusic.util
|
||||
|
||||
import android.os.Environment
|
||||
import java.io.File
|
||||
|
||||
object FilePathUtil {
|
||||
fun blacklistFilePaths(): List<String> {
|
||||
return listOf<File>(
|
||||
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_ALARMS),
|
||||
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_RINGTONES),
|
||||
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_NOTIFICATIONS)
|
||||
return listOf(
|
||||
getExternalStoragePublicDirectory(Environment.DIRECTORY_ALARMS),
|
||||
getExternalStoragePublicDirectory(Environment.DIRECTORY_RINGTONES),
|
||||
getExternalStoragePublicDirectory(Environment.DIRECTORY_NOTIFICATIONS)
|
||||
).map {
|
||||
FileUtil.safeGetCanonicalPath(it)
|
||||
}
|
||||
|
|
|
@ -14,10 +14,11 @@
|
|||
|
||||
package code.name.monkey.retromusic.util;
|
||||
|
||||
import static code.name.monkey.retromusic.util.FileUtilsKt.getExternalStorageDirectory;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.os.Environment;
|
||||
import android.provider.MediaStore;
|
||||
import android.webkit.MimeTypeMap;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
@ -40,6 +41,7 @@ import java.util.LinkedList;
|
|||
import java.util.List;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import code.name.monkey.retromusic.Constants;
|
||||
import code.name.monkey.retromusic.adapter.Storage;
|
||||
import code.name.monkey.retromusic.model.Song;
|
||||
import code.name.monkey.retromusic.repository.RealSongRepository;
|
||||
|
@ -87,7 +89,7 @@ public final class FileUtil {
|
|||
if (files.size() > 0
|
||||
&& files.size() < 999) { // 999 is the max amount Androids SQL implementation can handle.
|
||||
selection =
|
||||
MediaStore.Audio.AudioColumns.DATA + " IN (" + makePlaceholders(files.size()) + ")";
|
||||
Constants.DATA + " IN (" + makePlaceholders(files.size()) + ")";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -96,7 +98,7 @@ public final class FileUtil {
|
|||
|
||||
return songCursor == null
|
||||
? null
|
||||
: new SortedCursor(songCursor, paths, MediaStore.Audio.AudioColumns.DATA);
|
||||
: new SortedCursor(songCursor, paths, Constants.DATA);
|
||||
}
|
||||
|
||||
private static String makePlaceholders(int len) {
|
||||
|
@ -113,12 +115,6 @@ public final class FileUtil {
|
|||
if (files != null) {
|
||||
String[] paths = new String[files.size()];
|
||||
for (int i = 0; i < files.size(); i++) {
|
||||
/*try {
|
||||
paths[i] = files.get(i).getCanonicalPath(); // canonical path is important here because we want to compare the path with the media store entry later
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
paths[i] = files.get(i).getPath();
|
||||
}*/
|
||||
paths[i] = safeGetCanonicalPath(files.get(i));
|
||||
}
|
||||
return paths;
|
||||
|
@ -268,7 +264,7 @@ public final class FileUtil {
|
|||
public static ArrayList<Storage> listRoots() {
|
||||
ArrayList<Storage> storageItems = new ArrayList<>();
|
||||
HashSet<String> paths = new HashSet<>();
|
||||
String defaultPath = Environment.getExternalStorageDirectory().getPath();
|
||||
String defaultPath = getExternalStorageDirectory().getPath();
|
||||
String defaultPathState = Environment.getExternalStorageState();
|
||||
if (defaultPathState.equals(Environment.MEDIA_MOUNTED) || defaultPathState.equals(Environment.MEDIA_MOUNTED_READ_ONLY)) {
|
||||
Storage ext = new Storage();
|
||||
|
@ -277,7 +273,7 @@ public final class FileUtil {
|
|||
} else {
|
||||
ext.title = "Internal Storage";
|
||||
}
|
||||
ext.file = Environment.getExternalStorageDirectory();
|
||||
ext.file = getExternalStorageDirectory();
|
||||
storageItems.add(ext);
|
||||
paths.add(defaultPath);
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package code.name.monkey.retromusic.util
|
|||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.os.Environment
|
||||
import android.util.Log
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
|
@ -23,7 +24,13 @@ object FileUtils {
|
|||
* @return the file
|
||||
* @throws IOException
|
||||
*/
|
||||
fun createFile(context: Context, directoryName: String, fileName: String, body: String, fileType: String): File {
|
||||
fun createFile(
|
||||
context: Context,
|
||||
directoryName: String,
|
||||
fileName: String,
|
||||
body: String,
|
||||
fileType: String
|
||||
): File {
|
||||
val root = createDirectory(context, directoryName)
|
||||
val filePath = "$root/$fileName$fileType"
|
||||
val file = File(filePath)
|
||||
|
@ -57,4 +64,13 @@ object FileUtils {
|
|||
}
|
||||
return file
|
||||
}
|
||||
}
|
||||
@Suppress("Deprecation")
|
||||
fun getExternalStorageDirectory(): File {
|
||||
return Environment.getExternalStorageDirectory()
|
||||
}
|
||||
|
||||
@Suppress("Deprecation")
|
||||
fun getExternalStoragePublicDirectory(type: String): File {
|
||||
return Environment.getExternalStoragePublicDirectory(type)
|
||||
}
|
|
@ -14,128 +14,17 @@
|
|||
|
||||
package code.name.monkey.retromusic.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffColorFilter;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.media.ExifInterface;
|
||||
|
||||
import androidx.annotation.ColorInt;
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.res.ResourcesCompat;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import code.name.monkey.appthemehelper.util.TintHelper;
|
||||
|
||||
/**
|
||||
* Created on : June 18, 2016 Author : zetbaitsu Name : Zetra GitHub : https://github.com/zetbaitsu
|
||||
*/
|
||||
public class ImageUtil {
|
||||
|
||||
private static final int TOLERANCE = 20;
|
||||
// Alpha amount for which values below are considered transparent.
|
||||
private static final int ALPHA_TOLERANCE = 50;
|
||||
private static int[] mTempBuffer;
|
||||
|
||||
private ImageUtil() {}
|
||||
|
||||
public static boolean isGrayscale(Bitmap bitmap) {
|
||||
final int height = bitmap.getHeight();
|
||||
final int width = bitmap.getWidth();
|
||||
int size = height * width;
|
||||
ensureBufferSize(size);
|
||||
bitmap.getPixels(mTempBuffer, 0, width, 0, 0, width, height);
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (!isGrayscale(mTempBuffer[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static Bitmap createBitmap(Drawable drawable) {
|
||||
return createBitmap(drawable, 1f);
|
||||
}
|
||||
|
||||
public static Bitmap createBitmap(Drawable drawable, float sizeMultiplier) {
|
||||
Bitmap bitmap =
|
||||
Bitmap.createBitmap(
|
||||
(int) (drawable.getIntrinsicWidth() * sizeMultiplier),
|
||||
(int) (drawable.getIntrinsicHeight() * sizeMultiplier),
|
||||
Bitmap.Config.ARGB_8888);
|
||||
Canvas c = new Canvas(bitmap);
|
||||
drawable.setBounds(0, 0, c.getWidth(), c.getHeight());
|
||||
drawable.draw(c);
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
public static Drawable getTintedVectorDrawable(
|
||||
@NonNull Resources res,
|
||||
@DrawableRes int resId,
|
||||
@Nullable Resources.Theme theme,
|
||||
@ColorInt int color) {
|
||||
return TintHelper.createTintedDrawable(getVectorDrawable(res, resId, theme), color);
|
||||
}
|
||||
|
||||
public static Drawable getTintedVectorDrawable(
|
||||
@NonNull Context context, @DrawableRes int id, @ColorInt int color) {
|
||||
return TintHelper.createTintedDrawable(
|
||||
getVectorDrawable(context.getResources(), id, context.getTheme()), color);
|
||||
}
|
||||
|
||||
public static Drawable getVectorDrawable(@NonNull Context context, @DrawableRes int id) {
|
||||
return getVectorDrawable(context.getResources(), id, context.getTheme());
|
||||
}
|
||||
|
||||
public static Drawable getVectorDrawable(
|
||||
@NonNull Resources res, @DrawableRes int resId, @Nullable Resources.Theme theme) {
|
||||
return ResourcesCompat.getDrawable(res,resId, theme);
|
||||
}
|
||||
|
||||
/** Makes sure that {@code mTempBuffer} has at least length {@code size}. */
|
||||
private static void ensureBufferSize(int size) {
|
||||
if (mTempBuffer == null || mTempBuffer.length < size) {
|
||||
mTempBuffer = new int[size];
|
||||
}
|
||||
}
|
||||
|
||||
public static Bitmap setBitmapColor(Bitmap bitmap, int color) {
|
||||
Bitmap result =
|
||||
Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth() - 1, bitmap.getHeight() - 1);
|
||||
Paint paint = new Paint();
|
||||
paint.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_ATOP));
|
||||
|
||||
Canvas canvas = new Canvas(result);
|
||||
canvas.drawBitmap(result, 0, 0, paint);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static boolean isGrayscale(int color) {
|
||||
int alpha = 0xFF & (color >> 24);
|
||||
if (alpha < ALPHA_TOLERANCE) {
|
||||
return true;
|
||||
}
|
||||
int r = 0xFF & (color >> 16);
|
||||
int g = 0xFF & (color >> 8);
|
||||
int b = 0xFF & color;
|
||||
return Math.abs(r - g) < TOLERANCE
|
||||
&& Math.abs(r - b) < TOLERANCE
|
||||
&& Math.abs(g - b) < TOLERANCE;
|
||||
} // Amount (max is 255) that two channels can differ before the color is no longer "gray".
|
||||
|
||||
public static Bitmap resizeBitmap(@NonNull Bitmap src, int maxForSmallerSize) {
|
||||
int width = src.getWidth();
|
||||
int height = src.getHeight();
|
||||
|
@ -185,108 +74,4 @@ public class ImageUtil {
|
|||
|
||||
return inSampleSize;
|
||||
}
|
||||
|
||||
static File compressImage(
|
||||
File imageFile,
|
||||
int reqWidth,
|
||||
int reqHeight,
|
||||
Bitmap.CompressFormat compressFormat,
|
||||
int quality,
|
||||
String destinationPath)
|
||||
throws IOException {
|
||||
FileOutputStream fileOutputStream = null;
|
||||
File file = new File(destinationPath).getParentFile();
|
||||
if (!file.exists()) {
|
||||
file.mkdirs();
|
||||
}
|
||||
try {
|
||||
fileOutputStream = new FileOutputStream(destinationPath);
|
||||
// write the compressed bitmap at the destination specified by destinationPath.
|
||||
decodeSampledBitmapFromFile(imageFile, reqWidth, reqHeight)
|
||||
.compress(compressFormat, quality, fileOutputStream);
|
||||
} finally {
|
||||
if (fileOutputStream != null) {
|
||||
fileOutputStream.flush();
|
||||
fileOutputStream.close();
|
||||
}
|
||||
}
|
||||
|
||||
return new File(destinationPath);
|
||||
}
|
||||
|
||||
static Bitmap decodeSampledBitmapFromFile(File imageFile, int reqWidth, int reqHeight)
|
||||
throws IOException {
|
||||
// First decode with inJustDecodeBounds=true to check dimensions
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inJustDecodeBounds = true;
|
||||
BitmapFactory.decodeFile(imageFile.getAbsolutePath(), options);
|
||||
|
||||
// Calculate inSampleSize
|
||||
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
|
||||
|
||||
// Decode bitmap with inSampleSize set
|
||||
options.inJustDecodeBounds = false;
|
||||
|
||||
Bitmap scaledBitmap = BitmapFactory.decodeFile(imageFile.getAbsolutePath(), options);
|
||||
|
||||
// check the rotation of the image and display it properly
|
||||
ExifInterface exif;
|
||||
exif = new ExifInterface(imageFile.getAbsolutePath());
|
||||
int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 0);
|
||||
Matrix matrix = new Matrix();
|
||||
if (orientation == 6) {
|
||||
matrix.postRotate(90);
|
||||
} else if (orientation == 3) {
|
||||
matrix.postRotate(180);
|
||||
} else if (orientation == 8) {
|
||||
matrix.postRotate(270);
|
||||
}
|
||||
scaledBitmap =
|
||||
Bitmap.createBitmap(
|
||||
scaledBitmap, 0, 0, scaledBitmap.getWidth(), scaledBitmap.getHeight(), matrix, true);
|
||||
return scaledBitmap;
|
||||
}
|
||||
|
||||
private static int calculateInSampleSize(
|
||||
BitmapFactory.Options options, int reqWidth, int reqHeight) {
|
||||
// Raw height and width of image
|
||||
final int height = options.outHeight;
|
||||
final int width = options.outWidth;
|
||||
int inSampleSize = 1;
|
||||
|
||||
if (height > reqHeight || width > reqWidth) {
|
||||
|
||||
final int halfHeight = height / 2;
|
||||
final int halfWidth = width / 2;
|
||||
|
||||
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
|
||||
// height and width larger than the requested height and width.
|
||||
while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth) {
|
||||
inSampleSize *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
return inSampleSize;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static Bitmap getResizedBitmap(@NonNull Bitmap image, int maxSize) {
|
||||
int width = image.getWidth();
|
||||
int height = image.getHeight();
|
||||
|
||||
float bitmapRatio = (float) width / (float) height;
|
||||
if (bitmapRatio > 1) {
|
||||
width = maxSize;
|
||||
height = (int) (width / bitmapRatio);
|
||||
} else {
|
||||
height = maxSize;
|
||||
width = (int) (height * bitmapRatio);
|
||||
}
|
||||
return Bitmap.createScaledBitmap(image, width, height, true);
|
||||
}
|
||||
|
||||
public static Bitmap resize(InputStream stream, int scaledWidth, int scaledHeight) {
|
||||
final Bitmap bitmap = BitmapFactory.decodeStream(stream);
|
||||
return Bitmap.createScaledBitmap(bitmap, scaledWidth, scaledHeight, true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
*/
|
||||
package code.name.monkey.retromusic.util
|
||||
|
||||
import android.os.Environment
|
||||
import android.util.Log
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.model.lyrics.AbsSynchronizedLyrics
|
||||
|
@ -26,7 +25,7 @@ import java.io.*
|
|||
*/
|
||||
object LyricUtil {
|
||||
private val lrcRootPath =
|
||||
Environment.getExternalStorageDirectory().toString() + "/RetroMusic/lyrics/"
|
||||
getExternalStorageDirectory().toString() + "/RetroMusic/lyrics/"
|
||||
private const val TAG = "LyricUtil"
|
||||
fun writeLrcToLoc(
|
||||
title: String, artist: String, lrcContext: String
|
||||
|
|
|
@ -5,27 +5,25 @@ import android.content.Context
|
|||
import android.content.Intent
|
||||
import android.database.Cursor
|
||||
import android.net.Uri
|
||||
import android.os.Environment
|
||||
import android.provider.BaseColumns
|
||||
import android.provider.MediaStore
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.core.content.FileProvider
|
||||
import androidx.core.content.contentValuesOf
|
||||
import androidx.core.net.toUri
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import code.name.monkey.appthemehelper.util.VersionUtils
|
||||
import code.name.monkey.retromusic.Constants
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.db.PlaylistEntity
|
||||
import code.name.monkey.retromusic.db.SongEntity
|
||||
import code.name.monkey.retromusic.db.toSongEntity
|
||||
import code.name.monkey.retromusic.extensions.getLong
|
||||
import code.name.monkey.retromusic.extensions.showToast
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote.removeFromQueue
|
||||
import code.name.monkey.retromusic.model.Artist
|
||||
import code.name.monkey.retromusic.model.Playlist
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.model.lyrics.AbsSynchronizedLyrics
|
||||
import code.name.monkey.retromusic.repository.RealPlaylistRepository
|
||||
import code.name.monkey.retromusic.repository.Repository
|
||||
import code.name.monkey.retromusic.repository.SongRepository
|
||||
import code.name.monkey.retromusic.service.MusicService
|
||||
|
@ -56,14 +54,10 @@ object MusicUtil : KoinComponent {
|
|||
)
|
||||
).addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION).setType("audio/*")
|
||||
} catch (e: IllegalArgumentException) {
|
||||
// TODO the path is most likely not like /storage/emulated/0/... but something like /storage/28C7-75B0/...
|
||||
e.printStackTrace()
|
||||
Toast.makeText(
|
||||
context,
|
||||
"Could not share this file, I'm aware of the issue.",
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
Intent()
|
||||
Intent().setAction(Intent.ACTION_SEND).putExtra(
|
||||
Intent.EXTRA_STREAM,
|
||||
getSongFileUri(song.id)
|
||||
).addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION).setType("audio/*")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -83,7 +77,7 @@ object MusicUtil : KoinComponent {
|
|||
|
||||
private fun createAlbumArtDir(context: Context): File {
|
||||
val albumArtDir = File(
|
||||
if (VersionUtils.hasR()) context.cacheDir else Environment.getExternalStorageDirectory(),
|
||||
if (VersionUtils.hasR()) context.cacheDir else getExternalStorageDirectory(),
|
||||
"/albumthumbs/"
|
||||
)
|
||||
if (!albumArtDir.exists()) {
|
||||
|
@ -268,15 +262,14 @@ object MusicUtil : KoinComponent {
|
|||
)
|
||||
}
|
||||
|
||||
fun getSongFilePath(context: Context, uri: Uri): String? {
|
||||
val projection = arrayOf(MediaStore.MediaColumns.DATA)
|
||||
return context.contentResolver.query(uri, projection, null, null, null)?.use {
|
||||
fun getSongFilePath(context: Context, uri: Uri): String {
|
||||
val projection = arrayOf(Constants.DATA)
|
||||
context.contentResolver.query(uri, projection, null, null, null)?.use {
|
||||
if (it.moveToFirst()) {
|
||||
it.getString(0)
|
||||
} else {
|
||||
""
|
||||
return it.getString(0)
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
fun getTotalDuration(songs: List<Song>): Long {
|
||||
|
@ -340,48 +333,21 @@ object MusicUtil : KoinComponent {
|
|||
return false
|
||||
}
|
||||
|
||||
fun isFavorite(context: Context, song: Song): Boolean {
|
||||
return PlaylistsUtil
|
||||
.doPlaylistContains(context, getFavoritesPlaylist(context).id, song.id)
|
||||
}
|
||||
|
||||
fun isFavoritePlaylist(
|
||||
context: Context,
|
||||
playlist: Playlist
|
||||
): Boolean {
|
||||
return playlist.name == context.getString(R.string.favorites)
|
||||
}
|
||||
|
||||
val repository = get<Repository>()
|
||||
fun toggleFavorite(context: Context, song: Song) {
|
||||
GlobalScope.launch {
|
||||
val playlist: PlaylistEntity = repository.favoritePlaylist()
|
||||
if (playlist != null) {
|
||||
val songEntity = song.toSongEntity(playlist.playListId)
|
||||
val isFavorite = repository.isFavoriteSong(songEntity).isNotEmpty()
|
||||
if (isFavorite) {
|
||||
repository.removeSongFromPlaylist(songEntity)
|
||||
} else {
|
||||
repository.insertSongs(listOf(song.toSongEntity(playlist.playListId)))
|
||||
}
|
||||
val songEntity = song.toSongEntity(playlist.playListId)
|
||||
val isFavorite = repository.isFavoriteSong(songEntity).isNotEmpty()
|
||||
if (isFavorite) {
|
||||
repository.removeSongFromPlaylist(songEntity)
|
||||
} else {
|
||||
repository.insertSongs(listOf(song.toSongEntity(playlist.playListId)))
|
||||
}
|
||||
context.sendBroadcast(Intent(MusicService.FAVORITE_STATE_CHANGED))
|
||||
}
|
||||
}
|
||||
|
||||
private fun getFavoritesPlaylist(context: Context): Playlist {
|
||||
return RealPlaylistRepository(context.contentResolver).playlist(context.getString(R.string.favorites))
|
||||
}
|
||||
|
||||
private fun getOrCreateFavoritesPlaylist(context: Context): Playlist {
|
||||
return RealPlaylistRepository(context.contentResolver).playlist(
|
||||
PlaylistsUtil.createPlaylist(
|
||||
context,
|
||||
context.getString(R.string.favorites)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun deleteTracks(
|
||||
activity: FragmentActivity,
|
||||
songs: List<Song>,
|
||||
|
@ -390,7 +356,7 @@ object MusicUtil : KoinComponent {
|
|||
) {
|
||||
val songRepository: SongRepository = get()
|
||||
val projection = arrayOf(
|
||||
BaseColumns._ID, MediaStore.MediaColumns.DATA
|
||||
BaseColumns._ID, Constants.DATA
|
||||
)
|
||||
// Split the query into multiple batches, and merge the resulting cursors
|
||||
var batchStart: Int
|
||||
|
@ -457,20 +423,14 @@ object MusicUtil : KoinComponent {
|
|||
}
|
||||
activity.contentResolver.notifyChange("content://media".toUri(), null)
|
||||
activity.runOnUiThread {
|
||||
Toast.makeText(
|
||||
activity,
|
||||
activity.getString(R.string.deleted_x_songs, songCount),
|
||||
Toast.LENGTH_SHORT
|
||||
)
|
||||
.show()
|
||||
activity.showToast(activity.getString(R.string.deleted_x_songs, songCount))
|
||||
callback?.run()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun deleteTracks(context: Context, songs: List<Song>) {
|
||||
val projection = arrayOf(BaseColumns._ID, MediaStore.MediaColumns.DATA)
|
||||
val projection = arrayOf(BaseColumns._ID, Constants.DATA)
|
||||
val selection = StringBuilder()
|
||||
selection.append(BaseColumns._ID + " IN (")
|
||||
for (i in songs.indices) {
|
||||
|
@ -520,11 +480,7 @@ object MusicUtil : KoinComponent {
|
|||
cursor.close()
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getString(R.string.deleted_x_songs, deletedCount),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
context.showToast(context.getString(R.string.deleted_x_songs, deletedCount))
|
||||
}
|
||||
|
||||
} catch (ignored: SecurityException) {
|
||||
|
|
|
@ -19,9 +19,11 @@ import android.content.Context
|
|||
import android.content.Intent
|
||||
import android.media.audiofx.AudioEffect
|
||||
import android.widget.Toast
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.*
|
||||
import code.name.monkey.retromusic.activities.bugreport.BugReportActivity
|
||||
import code.name.monkey.retromusic.extensions.showToast
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote.audioSessionId
|
||||
|
||||
object NavigationUtil {
|
||||
|
@ -55,10 +57,9 @@ object NavigationUtil {
|
|||
)
|
||||
}
|
||||
|
||||
fun gotoWhatNews(activity: Activity) {
|
||||
activity.startActivity(
|
||||
Intent(activity, WhatsNewActivity::class.java), null
|
||||
)
|
||||
fun gotoWhatNews(activity: FragmentActivity) {
|
||||
val changelogBottomSheet = WhatsNewFragment()
|
||||
changelogBottomSheet.show(activity.supportFragmentManager, WhatsNewFragment.TAG)
|
||||
}
|
||||
|
||||
fun openEqualizer(activity: Activity) {
|
||||
|
@ -68,10 +69,7 @@ object NavigationUtil {
|
|||
private fun stockEqualizer(activity: Activity) {
|
||||
val sessionId = audioSessionId
|
||||
if (sessionId == AudioEffect.ERROR_BAD_VALUE) {
|
||||
Toast.makeText(
|
||||
activity, activity.resources.getString(R.string.no_audio_ID), Toast.LENGTH_LONG
|
||||
)
|
||||
.show()
|
||||
activity.showToast(R.string.no_audio_ID, Toast.LENGTH_LONG)
|
||||
} else {
|
||||
try {
|
||||
val effects = Intent(AudioEffect.ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL)
|
||||
|
@ -79,12 +77,7 @@ object NavigationUtil {
|
|||
effects.putExtra(AudioEffect.EXTRA_CONTENT_TYPE, AudioEffect.CONTENT_TYPE_MUSIC)
|
||||
activity.startActivityForResult(effects, 0)
|
||||
} catch (notFound: ActivityNotFoundException) {
|
||||
Toast.makeText(
|
||||
activity,
|
||||
activity.resources.getString(R.string.no_equalizer),
|
||||
Toast.LENGTH_SHORT
|
||||
)
|
||||
.show()
|
||||
activity.showToast(R.string.no_equalizer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -71,7 +71,7 @@ class PackageValidator(
|
|||
|
||||
/**
|
||||
* Checks whether the caller attempting to connect to a [MediaBrowserServiceCompat] is known.
|
||||
* See [MusicService.onGetRoot] for where this is utilized.
|
||||
* See [MediaBrowserServiceCompat.onGetRoot] for where this is utilized.
|
||||
*
|
||||
* @param callingPackage The package name of the caller.
|
||||
* @param callingUid The user id of the caller.
|
||||
|
|
|
@ -1,333 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Hemanth Savarala.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
package code.name.monkey.retromusic.util;
|
||||
|
||||
import static android.provider.MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Environment;
|
||||
import android.provider.BaseColumns;
|
||||
import android.provider.MediaStore;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import code.name.monkey.retromusic.R;
|
||||
import code.name.monkey.retromusic.db.PlaylistWithSongs;
|
||||
import code.name.monkey.retromusic.helper.M3UWriter;
|
||||
import code.name.monkey.retromusic.model.Playlist;
|
||||
import code.name.monkey.retromusic.model.PlaylistSong;
|
||||
import code.name.monkey.retromusic.model.Song;
|
||||
|
||||
public class PlaylistsUtil {
|
||||
|
||||
public static long createPlaylist(@NonNull final Context context, @Nullable final String name) {
|
||||
int id = -1;
|
||||
if (name != null && name.length() > 0) {
|
||||
try {
|
||||
Cursor cursor =
|
||||
context
|
||||
.getContentResolver()
|
||||
.query(
|
||||
EXTERNAL_CONTENT_URI,
|
||||
new String[] {MediaStore.Audio.Playlists._ID},
|
||||
MediaStore.Audio.PlaylistsColumns.NAME + "=?",
|
||||
new String[] {name},
|
||||
null);
|
||||
if (cursor == null || cursor.getCount() < 1) {
|
||||
final ContentValues values = new ContentValues(1);
|
||||
values.put(MediaStore.Audio.PlaylistsColumns.NAME, name);
|
||||
final Uri uri = context.getContentResolver().insert(EXTERNAL_CONTENT_URI, values);
|
||||
if (uri != null) {
|
||||
// Necessary because somehow the MediaStoreObserver is not notified when adding a
|
||||
// playlist
|
||||
context.getContentResolver().notifyChange(Uri.parse("content://media"), null);
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getResources().getString(R.string.created_playlist_x, name),
|
||||
Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
id = Integer.parseInt(uri.getLastPathSegment());
|
||||
}
|
||||
} else {
|
||||
// Playlist exists
|
||||
if (cursor.moveToFirst()) {
|
||||
id = cursor.getInt(cursor.getColumnIndex(MediaStore.Audio.Playlists._ID));
|
||||
}
|
||||
}
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
} catch (SecurityException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
if (id == -1) {
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getResources().getString(R.string.could_not_create_playlist),
|
||||
Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
public static void deletePlaylists(
|
||||
@NonNull final Context context, @NonNull final List<Playlist> playlists) {
|
||||
final StringBuilder selection = new StringBuilder();
|
||||
selection.append(MediaStore.Audio.Playlists._ID + " IN (");
|
||||
for (int i = 0; i < playlists.size(); i++) {
|
||||
selection.append(playlists.get(i).getId());
|
||||
if (i < playlists.size() - 1) {
|
||||
selection.append(",");
|
||||
}
|
||||
}
|
||||
selection.append(")");
|
||||
try {
|
||||
context.getContentResolver().delete(EXTERNAL_CONTENT_URI, selection.toString(), null);
|
||||
context.getContentResolver().notifyChange(Uri.parse("content://media"), null);
|
||||
} catch (SecurityException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
public static void addToPlaylist(
|
||||
@NonNull final Context context,
|
||||
final Song song,
|
||||
final long playlistId,
|
||||
final boolean showToastOnFinish) {
|
||||
List<Song> helperList = new ArrayList<>();
|
||||
helperList.add(song);
|
||||
addToPlaylist(context, helperList, playlistId, showToastOnFinish);
|
||||
}
|
||||
|
||||
public static void addToPlaylist(
|
||||
@NonNull final Context context,
|
||||
@NonNull final List<Song> songs,
|
||||
final long playlistId,
|
||||
final boolean showToastOnFinish) {
|
||||
final int size = songs.size();
|
||||
final ContentResolver resolver = context.getContentResolver();
|
||||
final String[] projection =
|
||||
new String[] {
|
||||
"max(" + MediaStore.Audio.Playlists.Members.PLAY_ORDER + ")",
|
||||
};
|
||||
final Uri uri = MediaStore.Audio.Playlists.Members.getContentUri("external", playlistId);
|
||||
Cursor cursor = null;
|
||||
int base = 0;
|
||||
|
||||
try {
|
||||
try {
|
||||
cursor = resolver.query(uri, projection, null, null, null);
|
||||
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
base = cursor.getInt(0) + 1;
|
||||
}
|
||||
} finally {
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
int numInserted = 0;
|
||||
for (int offSet = 0; offSet < size; offSet += 1000)
|
||||
numInserted += resolver.bulkInsert(uri, makeInsertItems(songs, offSet, 1000, base));
|
||||
|
||||
if (showToastOnFinish) {
|
||||
Toast.makeText(
|
||||
context,
|
||||
context
|
||||
.getResources()
|
||||
.getString(
|
||||
R.string.inserted_x_songs_into_playlist_x,
|
||||
numInserted,
|
||||
getNameForPlaylist(context, playlistId)),
|
||||
Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
}
|
||||
} catch (SecurityException exception) {
|
||||
exception.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static ContentValues[] makeInsertItems(
|
||||
@NonNull final List<Song> songs, final int offset, int len, final int base) {
|
||||
if (offset + len > songs.size()) {
|
||||
len = songs.size() - offset;
|
||||
}
|
||||
|
||||
ContentValues[] contentValues = new ContentValues[len];
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
contentValues[i] = new ContentValues();
|
||||
contentValues[i].put(MediaStore.Audio.Playlists.Members.PLAY_ORDER, base + offset + i);
|
||||
contentValues[i].put(
|
||||
MediaStore.Audio.Playlists.Members.AUDIO_ID, songs.get(offset + i).getId());
|
||||
}
|
||||
return contentValues;
|
||||
}
|
||||
|
||||
public static String getNameForPlaylist(@NonNull final Context context, final long id) {
|
||||
try {
|
||||
Cursor cursor =
|
||||
context
|
||||
.getContentResolver()
|
||||
.query(
|
||||
EXTERNAL_CONTENT_URI,
|
||||
new String[] {MediaStore.Audio.PlaylistsColumns.NAME},
|
||||
BaseColumns._ID + "=?",
|
||||
new String[] {String.valueOf(id)},
|
||||
null);
|
||||
if (cursor != null) {
|
||||
try {
|
||||
if (cursor.moveToFirst()) {
|
||||
return cursor.getString(0);
|
||||
}
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
} catch (SecurityException ignored) {
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public static void removeFromPlaylist(
|
||||
@NonNull final Context context, @NonNull final Song song, long playlistId) {
|
||||
Uri uri = MediaStore.Audio.Playlists.Members.getContentUri("external", playlistId);
|
||||
String selection = MediaStore.Audio.Playlists.Members.AUDIO_ID + " =?";
|
||||
String[] selectionArgs = new String[] {String.valueOf(song.getId())};
|
||||
|
||||
try {
|
||||
context.getContentResolver().delete(uri, selection, selectionArgs);
|
||||
} catch (SecurityException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
public static void removeFromPlaylist(
|
||||
@NonNull final Context context, @NonNull final List<PlaylistSong> songs) {
|
||||
final long playlistId = songs.get(0).getPlaylistId();
|
||||
Uri uri = MediaStore.Audio.Playlists.Members.getContentUri("external", playlistId);
|
||||
String[] selectionArgs = new String[songs.size()];
|
||||
for (int i = 0; i < selectionArgs.length; i++) {
|
||||
selectionArgs[i] = String.valueOf(songs.get(i).getIdInPlayList());
|
||||
}
|
||||
StringBuilder selection = new StringBuilder(MediaStore.Audio.Playlists.Members._ID + " in (");
|
||||
|
||||
for (String selectionArg : selectionArgs) selection.append("?, ");
|
||||
selection = new StringBuilder(selection.substring(0, selection.length() - 2) + ")");
|
||||
|
||||
try {
|
||||
context.getContentResolver().delete(uri, selection.toString(), selectionArgs);
|
||||
} catch (SecurityException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean doPlaylistContains(
|
||||
@NonNull final Context context, final long playlistId, final long songId) {
|
||||
if (playlistId != -1) {
|
||||
try {
|
||||
Cursor c =
|
||||
context
|
||||
.getContentResolver()
|
||||
.query(
|
||||
MediaStore.Audio.Playlists.Members.getContentUri("external", playlistId),
|
||||
new String[] {MediaStore.Audio.Playlists.Members.AUDIO_ID},
|
||||
MediaStore.Audio.Playlists.Members.AUDIO_ID + "=?",
|
||||
new String[] {String.valueOf(songId)},
|
||||
null);
|
||||
int count = 0;
|
||||
if (c != null) {
|
||||
count = c.getCount();
|
||||
c.close();
|
||||
}
|
||||
return count > 0;
|
||||
} catch (SecurityException ignored) {
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean moveItem(
|
||||
@NonNull final Context context, long playlistId, int from, int to) {
|
||||
return MediaStore.Audio.Playlists.Members.moveItem(
|
||||
context.getContentResolver(), playlistId, from, to);
|
||||
}
|
||||
|
||||
public static void renamePlaylist(
|
||||
@NonNull final Context context, final long id, final String newName) {
|
||||
ContentValues contentValues = new ContentValues();
|
||||
contentValues.put(MediaStore.Audio.PlaylistsColumns.NAME, newName);
|
||||
try {
|
||||
context
|
||||
.getContentResolver()
|
||||
.update(
|
||||
EXTERNAL_CONTENT_URI,
|
||||
contentValues,
|
||||
MediaStore.Audio.Playlists._ID + "=?",
|
||||
new String[] {String.valueOf(id)});
|
||||
context.getContentResolver().notifyChange(Uri.parse("content://media"), null);
|
||||
} catch (SecurityException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
public static File savePlaylist(Context context, Playlist playlist) throws IOException {
|
||||
return M3UWriter.write(
|
||||
new File(Environment.getExternalStorageDirectory(), "Playlists"), playlist);
|
||||
}
|
||||
|
||||
public static File savePlaylistWithSongs(PlaylistWithSongs playlist) throws IOException {
|
||||
return M3UWriter.writeIO(
|
||||
new File(Environment.getExternalStorageDirectory(), "Playlists"), playlist);
|
||||
}
|
||||
|
||||
public static boolean doesPlaylistExist(@NonNull final Context context, final int playlistId) {
|
||||
return playlistId != -1
|
||||
&& doesPlaylistExist(
|
||||
context,
|
||||
MediaStore.Audio.Playlists._ID + "=?",
|
||||
new String[] {String.valueOf(playlistId)});
|
||||
}
|
||||
|
||||
public static boolean doesPlaylistExist(@NonNull final Context context, final String name) {
|
||||
return doesPlaylistExist(
|
||||
context, MediaStore.Audio.PlaylistsColumns.NAME + "=?", new String[] {name});
|
||||
}
|
||||
|
||||
private static boolean doesPlaylistExist(
|
||||
@NonNull Context context, @NonNull final String selection, @NonNull final String[] values) {
|
||||
Cursor cursor =
|
||||
context
|
||||
.getContentResolver()
|
||||
.query(EXTERNAL_CONTENT_URI, new String[]{}, selection, values, null);
|
||||
|
||||
boolean exists = false;
|
||||
if (cursor != null) {
|
||||
exists = cursor.getCount() != 0;
|
||||
cursor.close();
|
||||
}
|
||||
return exists;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Hemanth Savarala.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*/
|
||||
package code.name.monkey.retromusic.util
|
||||
|
||||
import code.name.monkey.retromusic.db.PlaylistWithSongs
|
||||
import code.name.monkey.retromusic.helper.M3UWriter.writeIO
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
|
||||
object PlaylistsUtil {
|
||||
@Throws(IOException::class)
|
||||
fun savePlaylistWithSongs(playlist: PlaylistWithSongs?): File {
|
||||
return writeIO(
|
||||
File(getExternalStorageDirectory(), "Playlists"), playlist!!
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,10 +1,12 @@
|
|||
package code.name.monkey.retromusic.util
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences.OnSharedPreferenceChangeListener
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.NetworkInfo
|
||||
import androidx.core.content.edit
|
||||
import androidx.core.content.getSystemService
|
||||
import androidx.core.content.res.use
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.viewpager.widget.ViewPager
|
||||
import code.name.monkey.appthemehelper.util.VersionUtils
|
||||
|
@ -97,10 +99,10 @@ object PreferenceUtil {
|
|||
|
||||
val languageCode: String get() = sharedPreferences.getString(LANGUAGE_NAME, "auto") ?: "auto"
|
||||
|
||||
var userName
|
||||
var Fragment.userName
|
||||
get() = sharedPreferences.getString(
|
||||
USER_NAME,
|
||||
App.getContext().getString(R.string.user_name)
|
||||
getString(R.string.user_name)
|
||||
)
|
||||
set(value) = sharedPreferences.edit {
|
||||
putString(USER_NAME, value)
|
||||
|
@ -333,15 +335,12 @@ object PreferenceUtil {
|
|||
|
||||
val isLockScreen get() = sharedPreferences.getBoolean(LOCK_SCREEN, false)
|
||||
|
||||
fun isAllowedToDownloadMetadata(): Boolean {
|
||||
fun isAllowedToDownloadMetadata(context: Context): Boolean {
|
||||
return when (autoDownloadImagesPolicy) {
|
||||
"always" -> true
|
||||
"only_wifi" -> {
|
||||
val connectivityManager = App.getContext().getSystemService<ConnectivityManager>()
|
||||
var netInfo: NetworkInfo? = null
|
||||
if (connectivityManager != null) {
|
||||
netInfo = connectivityManager.activeNetworkInfo
|
||||
}
|
||||
val connectivityManager = context.getSystemService<ConnectivityManager>()
|
||||
val netInfo = connectivityManager?.activeNetworkInfo
|
||||
netInfo != null && netInfo.type == ConnectivityManager.TYPE_WIFI && netInfo.isConnectedOrConnecting
|
||||
}
|
||||
"never" -> false
|
||||
|
@ -395,9 +394,15 @@ object PreferenceUtil {
|
|||
val filterLength get() = sharedPreferences.getInt(FILTER_SONG, 20)
|
||||
|
||||
var lastVersion
|
||||
get() = sharedPreferences.getInt(LAST_CHANGELOG_VERSION, 0)
|
||||
// This was stored as an integer before now it's a long, so avoid a ClassCastException
|
||||
get() = try {
|
||||
sharedPreferences.getLong(LAST_CHANGELOG_VERSION, 0)
|
||||
} catch (e: ClassCastException) {
|
||||
sharedPreferences.edit { remove(LAST_CHANGELOG_VERSION) }
|
||||
0
|
||||
}
|
||||
set(value) = sharedPreferences.edit {
|
||||
putInt(LAST_CHANGELOG_VERSION, value)
|
||||
putLong(LAST_CHANGELOG_VERSION, value)
|
||||
}
|
||||
|
||||
var lastSleepTimerValue
|
||||
|
@ -432,10 +437,11 @@ object PreferenceUtil {
|
|||
val position = sharedPreferences.getStringOrDefault(
|
||||
HOME_ARTIST_GRID_STYLE, "0"
|
||||
).toInt()
|
||||
val typedArray = App.getContext()
|
||||
.resources.obtainTypedArray(R.array.pref_home_grid_style_layout)
|
||||
val layoutRes = typedArray.getResourceId(position, 0)
|
||||
typedArray.recycle()
|
||||
val layoutRes =
|
||||
App.getContext().resources.obtainTypedArray(R.array.pref_home_grid_style_layout)
|
||||
.use {
|
||||
it.getResourceId(position, 0)
|
||||
}
|
||||
return if (layoutRes == 0) {
|
||||
R.layout.item_artist
|
||||
} else layoutRes
|
||||
|
@ -446,10 +452,10 @@ object PreferenceUtil {
|
|||
val position = sharedPreferences.getStringOrDefault(
|
||||
HOME_ALBUM_GRID_STYLE, "4"
|
||||
).toInt()
|
||||
val typedArray = App.getContext()
|
||||
.resources.obtainTypedArray(R.array.pref_home_grid_style_layout)
|
||||
val layoutRes = typedArray.getResourceId(position, 0)
|
||||
typedArray.recycle()
|
||||
val layoutRes = App.getContext()
|
||||
.resources.obtainTypedArray(R.array.pref_home_grid_style_layout).use {
|
||||
it.getResourceId(position, 0)
|
||||
}
|
||||
return if (layoutRes == 0) {
|
||||
R.layout.item_image
|
||||
} else layoutRes
|
||||
|
@ -590,7 +596,7 @@ object PreferenceUtil {
|
|||
4 -> VerticalFlipTransformation()
|
||||
5 -> HingeTransformation()
|
||||
6 -> VerticalStackTransformer()
|
||||
else -> NormalPageTransformer()
|
||||
else -> ViewPager.PageTransformer { _, _ -> }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,262 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Hemanth Savarala.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
package code.name.monkey.retromusic.util;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.Display;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
|
||||
import androidx.annotation.ColorInt;
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.res.ResourcesCompat;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import code.name.monkey.appthemehelper.util.TintHelper;
|
||||
import code.name.monkey.retromusic.App;
|
||||
|
||||
public class RetroUtil {
|
||||
|
||||
private static final int[] TEMP_ARRAY = new int[1];
|
||||
|
||||
private static final String SHOW_NAV_BAR_RES_NAME = "config_showNavigationBar";
|
||||
|
||||
public static int calculateNoOfColumns(@NonNull Context context) {
|
||||
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
|
||||
float dpWidth = displayMetrics.widthPixels / displayMetrics.density;
|
||||
return (int) (dpWidth / 180);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static Bitmap createBitmap(@NonNull Drawable drawable, float sizeMultiplier) {
|
||||
Bitmap bitmap =
|
||||
Bitmap.createBitmap(
|
||||
(int) (drawable.getIntrinsicWidth() * sizeMultiplier),
|
||||
(int) (drawable.getIntrinsicHeight() * sizeMultiplier),
|
||||
Bitmap.Config.ARGB_8888);
|
||||
Canvas c = new Canvas(bitmap);
|
||||
drawable.setBounds(0, 0, c.getWidth(), c.getHeight());
|
||||
drawable.draw(c);
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
public static String formatValue(float value) {
|
||||
String[] arr = {"", "K", "M", "B", "T", "P", "E"};
|
||||
int index = 0;
|
||||
while ((value / 1000) >= 1) {
|
||||
value = value / 1000;
|
||||
index++;
|
||||
}
|
||||
DecimalFormat decimalFormat = new DecimalFormat("#.##");
|
||||
return String.format("%s %s", decimalFormat.format(value), arr[index]);
|
||||
}
|
||||
|
||||
public static float frequencyCount(int frequency) {
|
||||
return (float) (frequency / 1000.0);
|
||||
}
|
||||
|
||||
public static Point getScreenSize(@NonNull Context c) {
|
||||
Display display = null;
|
||||
if (c.getSystemService(Context.WINDOW_SERVICE) != null) {
|
||||
display = ((WindowManager) c.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
|
||||
}
|
||||
Point size = new Point();
|
||||
if (display != null) {
|
||||
display.getSize(size);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
public static int getStatusBarHeight() {
|
||||
int result = 0;
|
||||
int resourceId =
|
||||
App.Companion.getContext()
|
||||
.getResources()
|
||||
.getIdentifier("status_bar_height", "dimen", "android");
|
||||
if (resourceId > 0) {
|
||||
result = App.Companion.getContext().getResources().getDimensionPixelSize(resourceId);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static int getNavigationBarHeight() {
|
||||
int result = 0;
|
||||
int resourceId =
|
||||
App.Companion.getContext()
|
||||
.getResources()
|
||||
.getIdentifier("navigation_bar_height", "dimen", "android");
|
||||
if (resourceId > 0) {
|
||||
result = App.Companion.getContext().getResources().getDimensionPixelSize(resourceId);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static Drawable getTintedVectorDrawable(
|
||||
@NonNull Context context, @DrawableRes int id, @ColorInt int color) {
|
||||
return TintHelper.createTintedDrawable(
|
||||
getVectorDrawable(context.getResources(), id, context.getTheme()), color);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static Drawable getTintedVectorDrawable(
|
||||
@NonNull Resources res,
|
||||
@DrawableRes int resId,
|
||||
@Nullable Resources.Theme theme,
|
||||
@ColorInt int color) {
|
||||
return TintHelper.createTintedDrawable(getVectorDrawable(res, resId, theme), color);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Drawable getVectorDrawable(
|
||||
@NonNull Resources res, @DrawableRes int resId, @Nullable Resources.Theme theme) {
|
||||
return ResourcesCompat.getDrawable(res, resId, theme);
|
||||
}
|
||||
|
||||
public static void hideSoftKeyboard(@Nullable Activity activity) {
|
||||
if (activity != null) {
|
||||
View currentFocus = activity.getCurrentFocus();
|
||||
if (currentFocus != null) {
|
||||
InputMethodManager inputMethodManager =
|
||||
(InputMethodManager) activity.getSystemService(Activity.INPUT_METHOD_SERVICE);
|
||||
if (inputMethodManager != null) {
|
||||
inputMethodManager.hideSoftInputFromWindow(currentFocus.getWindowToken(), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isAllowedToDownloadMetadata(final @NonNull Context context) {
|
||||
switch (PreferenceUtil.INSTANCE.getAutoDownloadImagesPolicy()) {
|
||||
case "always":
|
||||
return true;
|
||||
case "only_wifi":
|
||||
final ConnectivityManager connectivityManager =
|
||||
(ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
NetworkInfo netInfo = connectivityManager.getActiveNetworkInfo();
|
||||
return netInfo != null
|
||||
&& netInfo.getType() == ConnectivityManager.TYPE_WIFI
|
||||
&& netInfo.isConnectedOrConnecting();
|
||||
case "never":
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isLandscape() {
|
||||
return App.Companion.getContext().getResources().getConfiguration().orientation
|
||||
== Configuration.ORIENTATION_LANDSCAPE;
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
|
||||
public static boolean isRTL(@NonNull Context context) {
|
||||
Configuration config = context.getResources().getConfiguration();
|
||||
return config.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
|
||||
}
|
||||
|
||||
public static boolean isTablet() {
|
||||
return App.Companion.getContext().getResources().getConfiguration().smallestScreenWidthDp
|
||||
>= 600;
|
||||
}
|
||||
|
||||
public static void openUrl(@NonNull Activity context, @NonNull String str) {
|
||||
Intent intent = new Intent("android.intent.action.VIEW");
|
||||
intent.setData(Uri.parse(str));
|
||||
intent.setFlags(268435456);
|
||||
context.startActivity(intent);
|
||||
}
|
||||
|
||||
public static void setAllowDrawUnderNavigationBar(Window window) {
|
||||
window.setNavigationBarColor(Color.TRANSPARENT);
|
||||
window
|
||||
.getDecorView()
|
||||
.setSystemUiVisibility(
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
|
||||
? View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||
| View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||
: View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
|
||||
}
|
||||
|
||||
public static void setAllowDrawUnderStatusBar(@NonNull Window window) {
|
||||
window
|
||||
.getDecorView()
|
||||
.setSystemUiVisibility(
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
|
||||
window.setStatusBarColor(Color.TRANSPARENT);
|
||||
}
|
||||
|
||||
public static String getIpAddress(boolean useIPv4) {
|
||||
try {
|
||||
List<NetworkInterface> interfaces =
|
||||
Collections.list(NetworkInterface.getNetworkInterfaces());
|
||||
for (NetworkInterface intf : interfaces) {
|
||||
List<InetAddress> addrs = Collections.list(intf.getInetAddresses());
|
||||
for (InetAddress addr : addrs) {
|
||||
if (!addr.isLoopbackAddress()) {
|
||||
String sAddr = addr.getHostAddress();
|
||||
//boolean isIPv4 = InetAddressUtils.isIPv4Address(sAddr);
|
||||
boolean isIPv4 = sAddr.indexOf(':') < 0;
|
||||
if (useIPv4) {
|
||||
if (isIPv4) return sAddr;
|
||||
} else {
|
||||
if (!isIPv4) {
|
||||
int delim = sAddr.indexOf('%'); // drop ip6 zone suffix
|
||||
if (delim < 0) {
|
||||
return sAddr.toUpperCase();
|
||||
} else {
|
||||
return sAddr.substring(
|
||||
0,
|
||||
delim
|
||||
).toUpperCase();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
117
app/src/main/java/code/name/monkey/retromusic/util/RetroUtil.kt
Normal file
117
app/src/main/java/code/name/monkey/retromusic/util/RetroUtil.kt
Normal file
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Hemanth Savarala.
|
||||
*
|
||||
* Licensed under the GNU General Public License v3
|
||||
*
|
||||
* This is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*/
|
||||
package code.name.monkey.retromusic.util
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.Configuration
|
||||
import android.graphics.Point
|
||||
import code.name.monkey.retromusic.App.Companion.getContext
|
||||
import java.net.InetAddress
|
||||
import java.net.NetworkInterface
|
||||
import java.text.DecimalFormat
|
||||
import java.util.*
|
||||
|
||||
object RetroUtil {
|
||||
fun formatValue(numValue: Float): String {
|
||||
var value = numValue
|
||||
val arr = arrayOf("", "K", "M", "B", "T", "P", "E")
|
||||
var index = 0
|
||||
while (value / 1000 >= 1) {
|
||||
value /= 1000
|
||||
index++
|
||||
}
|
||||
val decimalFormat = DecimalFormat("#.##")
|
||||
return String.format("%s %s", decimalFormat.format(value.toDouble()), arr[index])
|
||||
}
|
||||
|
||||
fun frequencyCount(frequency: Int): Float {
|
||||
return (frequency / 1000.0).toFloat()
|
||||
}
|
||||
|
||||
fun getScreenSize(context: Context): Point {
|
||||
val x: Int = context.resources.displayMetrics.widthPixels
|
||||
val y: Int = context.resources.displayMetrics.heightPixels
|
||||
return Point(x, y)
|
||||
}
|
||||
|
||||
val statusBarHeight: Int
|
||||
get() {
|
||||
var result = 0
|
||||
val resourceId = getContext()
|
||||
.resources
|
||||
.getIdentifier("status_bar_height", "dimen", "android")
|
||||
if (resourceId > 0) {
|
||||
result = getContext().resources.getDimensionPixelSize(resourceId)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
val navigationBarHeight: Int
|
||||
get() {
|
||||
var result = 0
|
||||
val resourceId = getContext()
|
||||
.resources
|
||||
.getIdentifier("navigation_bar_height", "dimen", "android")
|
||||
if (resourceId > 0) {
|
||||
result = getContext().resources.getDimensionPixelSize(resourceId)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
val isLandscape: Boolean
|
||||
get() = (getContext().resources.configuration.orientation
|
||||
== Configuration.ORIENTATION_LANDSCAPE)
|
||||
val isTablet: Boolean
|
||||
get() = (getContext().resources.configuration.smallestScreenWidthDp
|
||||
>= 600)
|
||||
|
||||
fun getIpAddress(useIPv4: Boolean): String? {
|
||||
try {
|
||||
val interfaces: List<NetworkInterface> =
|
||||
Collections.list(NetworkInterface.getNetworkInterfaces())
|
||||
for (intf in interfaces) {
|
||||
val addrs: List<InetAddress> = Collections.list(intf.inetAddresses)
|
||||
for (addr in addrs) {
|
||||
if (!addr.isLoopbackAddress) {
|
||||
val sAddr = addr.hostAddress
|
||||
|
||||
if (sAddr != null) {
|
||||
val isIPv4 = sAddr.indexOf(':') < 0
|
||||
if (useIPv4) {
|
||||
if (isIPv4) return sAddr
|
||||
} else {
|
||||
if (!isIPv4) {
|
||||
val delim = sAddr.indexOf('%') // drop ip6 zone suffix
|
||||
return if (delim < 0) {
|
||||
sAddr.uppercase()
|
||||
} else {
|
||||
sAddr.substring(
|
||||
0,
|
||||
delim
|
||||
).uppercase()
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (ignored: Exception) {
|
||||
}
|
||||
return ""
|
||||
}
|
||||
}
|
|
@ -19,10 +19,10 @@ import android.content.Intent
|
|||
import android.provider.BaseColumns
|
||||
import android.provider.MediaStore
|
||||
import android.provider.Settings
|
||||
import android.widget.Toast
|
||||
import androidx.core.net.toUri
|
||||
import code.name.monkey.appthemehelper.util.VersionUtils
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.extensions.showToast
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.util.MusicUtil.getSongFileUri
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
|
@ -45,7 +45,7 @@ class RingtoneManager(val context: Context) {
|
|||
Settings.System.putString(resolver, Settings.System.RINGTONE, uri.toString())
|
||||
val message = context
|
||||
.getString(R.string.x_has_been_set_as_ringtone, cursorSong.getString(0))
|
||||
Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
|
||||
context.showToast(message)
|
||||
}
|
||||
}
|
||||
} catch (ignored: SecurityException) {
|
||||
|
|
|
@ -6,6 +6,7 @@ import android.net.Uri
|
|||
import android.os.Build
|
||||
import android.provider.MediaStore
|
||||
import androidx.annotation.RequiresApi
|
||||
import code.name.monkey.retromusic.Constants
|
||||
|
||||
object UriUtil {
|
||||
@RequiresApi(Build.VERSION_CODES.Q)
|
||||
|
@ -13,7 +14,7 @@ object UriUtil {
|
|||
val uri = MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL)
|
||||
val proj = arrayOf(MediaStore.Files.FileColumns._ID)
|
||||
context.contentResolver.query(
|
||||
uri, proj, MediaStore.Files.FileColumns.DATA + "=?", arrayOf(path), null
|
||||
uri, proj, Constants.DATA + "=?", arrayOf(path), null
|
||||
)?.use { cursor ->
|
||||
if (cursor.count != 0) {
|
||||
cursor.moveToFirst()
|
||||
|
|
|
@ -1,31 +1,29 @@
|
|||
package code.name.monkey.retromusic.util.theme
|
||||
|
||||
import android.content.Context
|
||||
import androidx.annotation.StyleRes
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import code.name.monkey.retromusic.App
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.extensions.generalThemeValue
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import code.name.monkey.retromusic.util.theme.ThemeMode.*
|
||||
|
||||
object ThemeManager {
|
||||
|
||||
@StyleRes
|
||||
fun getThemeResValue(): Int =
|
||||
if (PreferenceUtil.materialYou) {
|
||||
R.style.Theme_RetroMusic_MD3
|
||||
} else {
|
||||
when (App.getContext().generalThemeValue) {
|
||||
LIGHT -> R.style.Theme_RetroMusic_Light
|
||||
DARK -> R.style.Theme_RetroMusic_Base
|
||||
BLACK -> R.style.Theme_RetroMusic_Black
|
||||
AUTO -> R.style.Theme_RetroMusic_FollowSystem
|
||||
}
|
||||
@StyleRes
|
||||
fun Context.getThemeResValue(): Int =
|
||||
if (PreferenceUtil.materialYou) {
|
||||
if (generalThemeValue == BLACK) R.style.Theme_RetroMusic_MD3_Black
|
||||
else R.style.Theme_RetroMusic_MD3
|
||||
} else {
|
||||
when (generalThemeValue) {
|
||||
LIGHT -> R.style.Theme_RetroMusic_Light
|
||||
DARK -> R.style.Theme_RetroMusic_Base
|
||||
BLACK -> R.style.Theme_RetroMusic_Black
|
||||
AUTO -> R.style.Theme_RetroMusic_FollowSystem
|
||||
}
|
||||
|
||||
fun getNightMode(): Int = when (App.getContext().generalThemeValue) {
|
||||
LIGHT -> AppCompatDelegate.MODE_NIGHT_NO
|
||||
DARK -> AppCompatDelegate.MODE_NIGHT_YES
|
||||
else -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
|
||||
}
|
||||
|
||||
fun Context.getNightMode(): Int = when (generalThemeValue) {
|
||||
LIGHT -> AppCompatDelegate.MODE_NIGHT_NO
|
||||
DARK -> AppCompatDelegate.MODE_NIGHT_YES
|
||||
else -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue