Fix classic notification size
This commit is contained in:
parent
b490311cbf
commit
8ef8a3955f
19 changed files with 403 additions and 1484 deletions
|
@ -12,10 +12,10 @@ import androidx.appcompat.widget.Toolbar
|
|||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import code.name.monkey.retromusic.util.ViewUtil
|
||||
import code.name.monkey.retromusic.views.DrawableGradient
|
||||
|
@ -100,16 +100,6 @@ class PlayerFragment : AbsPlayerFragment() {
|
|||
super.onViewCreated(view, savedInstanceState)
|
||||
setUpSubFragments()
|
||||
setUpPlayerToolbar()
|
||||
|
||||
|
||||
//val display = activity?.windowManager?.defaultDisplay
|
||||
//val outMetrics = DisplayMetrics()
|
||||
//display?.getMetrics(outMetrics)
|
||||
|
||||
//val density = resources.displayMetrics.density
|
||||
//val dpWidth = outMetrics.widthPixels / density
|
||||
|
||||
//playerAlbumCoverContainer?.layoutParams?.height = RetroUtil.convertDpToPixel((dpWidth - getCutOff()), context!!).toInt()
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -77,7 +77,6 @@ import code.name.monkey.retromusic.service.notification.PlayingNotificationImpl2
|
|||
import code.name.monkey.retromusic.service.notification.PlayingNotificationOreo;
|
||||
import code.name.monkey.retromusic.service.playback.Playback;
|
||||
import code.name.monkey.retromusic.util.MusicUtil;
|
||||
import code.name.monkey.retromusic.util.PackageValidator;
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil;
|
||||
import code.name.monkey.retromusic.util.RetroUtil;
|
||||
|
||||
|
@ -275,7 +274,6 @@ public class MusicService extends Service implements
|
|||
}
|
||||
}
|
||||
};
|
||||
private PackageValidator mPackageValidator;
|
||||
|
||||
private static String getTrackUri(@NonNull Song song) {
|
||||
return MusicUtil.getSongFileUri(song.getId()).toString();
|
||||
|
@ -341,9 +339,6 @@ public class MusicService extends Service implements
|
|||
|
||||
restoreState();
|
||||
|
||||
mPackageValidator = new PackageValidator(this, R.xml.allowed_media_browser_callers);
|
||||
|
||||
|
||||
sendBroadcast(new Intent("code.name.monkey.retromusic.RETRO_MUSIC_SERVICE_CREATED"));
|
||||
|
||||
registerHeadsetEvents();
|
||||
|
|
|
@ -1,30 +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 androidx.annotation.AttrRes
|
||||
|
||||
object AttrsUtil {
|
||||
@JvmOverloads
|
||||
fun resolveColor(context: Context, @AttrRes attr: Int, fallback: Int = 0): Int {
|
||||
val a = context.theme.obtainStyledAttributes(intArrayOf(attr))
|
||||
try {
|
||||
return a.getColor(0, fallback)
|
||||
} finally {
|
||||
a.recycle()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,9 +16,7 @@ package code.name.monkey.retromusic.util;
|
|||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.TypedValue;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
|
@ -33,38 +31,9 @@ public class DensityUtil {
|
|||
return displayMetrics.heightPixels;
|
||||
}
|
||||
|
||||
public static int getScreenWidth(@NonNull Context context) {
|
||||
DisplayMetrics displayMetrics = new DisplayMetrics();
|
||||
((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
|
||||
return displayMetrics.widthPixels;
|
||||
}
|
||||
|
||||
public static int dip2px(@NonNull Context context, float dpVale) {
|
||||
final float scale = context.getResources().getDisplayMetrics().density;
|
||||
return (int) (dpVale * scale + 0.5f);
|
||||
}
|
||||
|
||||
public static int getStatusBarHeight(@NonNull Context context) {
|
||||
Resources resources = context.getResources();
|
||||
int resourcesId = resources.getIdentifier("status_bar_height", "dimen", "android");
|
||||
int height = resources.getDimensionPixelSize(resourcesId);
|
||||
return height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts sp to px
|
||||
*
|
||||
* @param context Context
|
||||
* @param sp the value in sp
|
||||
* @return int
|
||||
*/
|
||||
public static int dip2sp(@NonNull Context context, float sp) {
|
||||
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, context.getResources().getDisplayMetrics());
|
||||
}
|
||||
|
||||
public static int px2dip(@NonNull Context context, float pxValue) {
|
||||
final float scale = context.getResources().getDisplayMetrics().density;
|
||||
return (int) (pxValue / scale + 0.5f);
|
||||
}
|
||||
|
||||
}
|
|
@ -47,7 +47,6 @@ import code.name.monkey.retromusic.helper.MusicPlayerRemote;
|
|||
import code.name.monkey.retromusic.loaders.PlaylistLoader;
|
||||
import code.name.monkey.retromusic.loaders.SongLoader;
|
||||
import code.name.monkey.retromusic.model.Artist;
|
||||
import code.name.monkey.retromusic.model.Genre;
|
||||
import code.name.monkey.retromusic.model.Playlist;
|
||||
import code.name.monkey.retromusic.model.Song;
|
||||
import code.name.monkey.retromusic.model.lyrics.AbsSynchronizedLyrics;
|
||||
|
@ -72,17 +71,6 @@ public class MusicUtil {
|
|||
|
||||
@NonNull
|
||||
public static Intent createShareSongFileIntent(@NonNull final Song song, @NonNull Context context) {
|
||||
/*Uri file = FileProvider.getUriForFile(context, context.getPackageName() + ".provider", new File(song.getData()));
|
||||
try {
|
||||
return new Intent().setAction(Intent.ACTION_SEND).putExtra(Intent.EXTRA_STREAM, file)
|
||||
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
.setType("audio/*");
|
||||
} catch (IllegalArgumentException e) {
|
||||
e.printStackTrace();
|
||||
Toast.makeText(context, "Could not share this file, I'm aware of the issue.", Toast.LENGTH_SHORT).show();
|
||||
return new Intent();
|
||||
}*/
|
||||
|
||||
try {
|
||||
return new Intent()
|
||||
.setAction(Intent.ACTION_SEND)
|
||||
|
@ -123,36 +111,6 @@ public class MusicUtil {
|
|||
return albumCount + " " + albumString + " • " + songCount + " " + songString;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static String getArtistInfoStringSmall(@NonNull final Context context,
|
||||
@NonNull final Artist artist) {
|
||||
int songCount = artist.getSongCount();
|
||||
String songString = songCount == 1 ? context.getResources().getString(R.string.song)
|
||||
: context.getResources().getString(R.string.songs);
|
||||
return songCount + " " + songString;
|
||||
}
|
||||
|
||||
/*@NonNull
|
||||
public static String getPlaylistInfoString(@NonNull final Context context,
|
||||
@NonNull List<Song> songs) {
|
||||
final int songCount = songs.size();
|
||||
final String songString = songCount == 1 ? context.getResources().getString(R.string.song)
|
||||
: context.getResources().getString(R.string.songs);
|
||||
|
||||
long duration = 0;
|
||||
for (int i = 0; i < songs.size(); i++) {
|
||||
duration += songs.get(i).getDuration();
|
||||
}
|
||||
|
||||
return songCount + " " + songString + " • " + MusicUtil.getReadableDurationString(duration);
|
||||
}*/
|
||||
|
||||
@NonNull
|
||||
public static String getGenreInfoString(@NonNull final Context context, @NonNull final Genre genre) {
|
||||
int songCount = genre.getSongCount();
|
||||
return MusicUtil.getSongCountString(context, songCount);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static String getPlaylistInfoString(@NonNull final Context context, @NonNull List<Song> songs) {
|
||||
final long duration = getTotalDuration(context, songs);
|
||||
|
@ -185,31 +143,6 @@ public class MusicUtil {
|
|||
return string1 + " • " + string2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a concatenated string from the provided arguments
|
||||
* The intended purpose is to show extra annotations
|
||||
* to a music library item.
|
||||
* Ex: for a given album --> buildInfoString(album.artist, album.songCount)
|
||||
*/
|
||||
@NonNull
|
||||
public static String buildInfoString(@Nullable final String string1, @Nullable final String string2, @NonNull final String string3) {
|
||||
// Skip empty strings
|
||||
if (TextUtils.isEmpty(string1)) {
|
||||
//noinspection ConstantConditions
|
||||
return TextUtils.isEmpty(string2) ? "" : string2;
|
||||
}
|
||||
if (TextUtils.isEmpty(string2)) {
|
||||
//noinspection ConstantConditions
|
||||
return TextUtils.isEmpty(string1) ? "" : string1;
|
||||
}
|
||||
if (TextUtils.isEmpty(string3)) {
|
||||
//noinspection ConstantConditions
|
||||
return TextUtils.isEmpty(string1) ? "" : string3;
|
||||
}
|
||||
|
||||
return string1 + " • " + string2 + " • " + string3;
|
||||
}
|
||||
|
||||
public static String getReadableDurationString(long songDurationMillis) {
|
||||
long minutes = (songDurationMillis / 1000) / 60;
|
||||
long seconds = (songDurationMillis / 1000) % 60;
|
||||
|
|
|
@ -1,347 +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.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE
|
||||
import android.Manifest.permission.MEDIA_CONTENT_CONTROL
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageInfo
|
||||
import android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.res.XmlResourceParser
|
||||
import android.os.Process
|
||||
import android.support.v4.media.session.MediaSessionCompat
|
||||
import android.util.Base64
|
||||
import android.util.Log
|
||||
import androidx.annotation.XmlRes
|
||||
import androidx.media.MediaBrowserServiceCompat
|
||||
import code.name.monkey.retromusic.BuildConfig
|
||||
import org.xmlpull.v1.XmlPullParserException
|
||||
import java.io.IOException
|
||||
import java.security.MessageDigest
|
||||
import java.security.NoSuchAlgorithmException
|
||||
|
||||
/**
|
||||
* Validates that the calling package is authorized to browse a [MediaBrowserServiceCompat].
|
||||
*
|
||||
* The list of allowed signing certificates and their corresponding package names is defined in
|
||||
* res/xml/allowed_media_browser_callers.xml.
|
||||
*
|
||||
* If you want to add a new caller to allowed_media_browser_callers.xml and you don't know
|
||||
* its signature, this class will print to logcat (INFO level) a message with the proper
|
||||
* xml tags to add to allow the caller.
|
||||
*
|
||||
* For more information, see res/xml/allowed_media_browser_callers.xml.
|
||||
*/
|
||||
class PackageValidator(
|
||||
context: Context,
|
||||
@XmlRes xmlResId: Int
|
||||
) {
|
||||
private val context: Context
|
||||
private val packageManager: PackageManager
|
||||
|
||||
private val certificateWhitelist: Map<String, KnownCallerInfo>
|
||||
private val platformSignature: String
|
||||
|
||||
private val callerChecked = mutableMapOf<String, Pair<Int, Boolean>>()
|
||||
|
||||
init {
|
||||
val parser = context.resources.getXml(xmlResId)
|
||||
this.context = context.applicationContext
|
||||
this.packageManager = this.context.packageManager
|
||||
|
||||
certificateWhitelist = buildCertificateWhitelist(parser)
|
||||
platformSignature = getSystemSignature()
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the caller attempting to connect to a [MediaBrowserServiceCompat] is known.
|
||||
* See [MusicService.onGetRoot] for where this is utilized.
|
||||
*
|
||||
* @param callingPackage The package name of the caller.
|
||||
* @param callingUid The user id of the caller.
|
||||
* @return `true` if the caller is known, `false` otherwise.
|
||||
*/
|
||||
fun isKnownCaller(callingPackage: String, callingUid: Int): Boolean {
|
||||
// If the caller has already been checked, return the previous result here.
|
||||
val (checkedUid, checkResult) = callerChecked[callingPackage] ?: Pair(0, false)
|
||||
if (checkedUid == callingUid) {
|
||||
return checkResult
|
||||
}
|
||||
|
||||
/**
|
||||
* Because some of these checks can be slow, we save the results in [callerChecked] after
|
||||
* this code is run.
|
||||
*
|
||||
* In particular, there's little reason to recompute the calling package's certificate
|
||||
* signature (SHA-256) each call.
|
||||
*
|
||||
* This is safe to do as we know the UID matches the package's UID (from the check above),
|
||||
* and app UIDs are set at install time. Additionally, a package name + UID is guaranteed to
|
||||
* be constant until a reboot. (After a reboot then a previously assigned UID could be
|
||||
* reassigned.)
|
||||
*/
|
||||
|
||||
// Build the caller info for the rest of the checks here.
|
||||
val callerPackageInfo = buildCallerInfo(callingPackage)
|
||||
?: throw IllegalStateException("Caller wasn't found in the system?")
|
||||
|
||||
// Verify that things aren't ... broken. (This test should always pass.)
|
||||
if (callerPackageInfo.uid != callingUid) {
|
||||
throw IllegalStateException("Caller's package UID doesn't match caller's actual UID?")
|
||||
}
|
||||
|
||||
val callerSignature = callerPackageInfo.signature
|
||||
val isPackageInWhitelist = certificateWhitelist[callingPackage]?.signatures?.first {
|
||||
it.signature == callerSignature
|
||||
} != null
|
||||
|
||||
val isCallerKnown = when {
|
||||
// If it's our own app making the call, allow it.
|
||||
callingUid == Process.myUid() -> true
|
||||
// If it's one of the apps on the whitelist, allow it.
|
||||
isPackageInWhitelist -> true
|
||||
// If the system is making the call, allow it.
|
||||
callingUid == Process.SYSTEM_UID -> true
|
||||
// If the app was signed by the same certificate as the platform itself, also allow it.
|
||||
callerSignature == platformSignature -> true
|
||||
/**
|
||||
* [MEDIA_CONTENT_CONTROL] permission is only available to system applications, and
|
||||
* while it isn't required to allow these apps to connect to a
|
||||
* [MediaBrowserServiceCompat], allowing this ensures optimal compatability with apps
|
||||
* such as Android TV and the Google Assistant.
|
||||
*/
|
||||
callerPackageInfo.permissions.contains(MEDIA_CONTENT_CONTROL) -> true
|
||||
/**
|
||||
* This last permission can be specifically granted to apps, and, in addition to
|
||||
* allowing them to retrieve notifications, it also allows them to connect to an
|
||||
* active [MediaSessionCompat].
|
||||
* As with the above, it's not required to allow apps holding this permission to
|
||||
* connect to your [MediaBrowserServiceCompat], but it does allow easy comparability
|
||||
* with apps such as Wear OS.
|
||||
*/
|
||||
callerPackageInfo.permissions.contains(BIND_NOTIFICATION_LISTENER_SERVICE) -> true
|
||||
// If none of the pervious checks succeeded, then the caller is unrecognized.
|
||||
else -> false
|
||||
}
|
||||
|
||||
if (!isCallerKnown) {
|
||||
logUnknownCaller(callerPackageInfo)
|
||||
}
|
||||
|
||||
// Save our work for next time.
|
||||
callerChecked[callingPackage] = Pair(callingUid, isCallerKnown)
|
||||
return isCallerKnown
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs an info level message with details of how to add a caller to the allowed callers list
|
||||
* when the app is debuggable.
|
||||
*/
|
||||
private fun logUnknownCaller(callerPackageInfo: CallerPackageInfo) {
|
||||
if (BuildConfig.DEBUG && callerPackageInfo.signature != null) {
|
||||
Log.i(TAG, "PackageValidator call" + callerPackageInfo.name + callerPackageInfo.packageName + callerPackageInfo.signature)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a [CallerPackageInfo] for a given package that can be used for all the
|
||||
* various checks that are performed before allowing an app to connect to a
|
||||
* [MediaBrowserServiceCompat].
|
||||
*/
|
||||
private fun buildCallerInfo(callingPackage: String): CallerPackageInfo? {
|
||||
val packageInfo = getPackageInfo(callingPackage) ?: return null
|
||||
|
||||
val appName = packageInfo.applicationInfo.loadLabel(packageManager).toString()
|
||||
val uid = packageInfo.applicationInfo.uid
|
||||
val signature = getSignature(packageInfo)
|
||||
|
||||
val requestedPermissions = packageInfo.requestedPermissions
|
||||
val permissionFlags = packageInfo.requestedPermissionsFlags
|
||||
val activePermissions = mutableSetOf<String>()
|
||||
requestedPermissions?.forEachIndexed { index, permission ->
|
||||
if (permissionFlags[index] and REQUESTED_PERMISSION_GRANTED != 0) {
|
||||
activePermissions += permission
|
||||
}
|
||||
}
|
||||
|
||||
return CallerPackageInfo(appName, callingPackage, uid, signature, activePermissions.toSet())
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks up the [PackageInfo] for a package name.
|
||||
* This requests both the signatures (for checking if an app is on the whitelist) and
|
||||
* the app's permissions, which allow for more flexibility in the whitelist.
|
||||
*
|
||||
* @return [PackageInfo] for the package name or null if it's not found.
|
||||
*/
|
||||
@SuppressLint("PackageManagerGetSignatures")
|
||||
private fun getPackageInfo(callingPackage: String): PackageInfo? =
|
||||
packageManager.getPackageInfo(callingPackage,
|
||||
PackageManager.GET_SIGNATURES or PackageManager.GET_PERMISSIONS)
|
||||
|
||||
/**
|
||||
* Gets the signature of a given package's [PackageInfo].
|
||||
*
|
||||
* The "signature" is a SHA-256 hash of the public key of the signing certificate used by
|
||||
* the app.
|
||||
*
|
||||
* If the app is not found, or if the app does not have exactly one signature, this method
|
||||
* returns `null` as the signature.
|
||||
*/
|
||||
private fun getSignature(packageInfo: PackageInfo): String? {
|
||||
// Security best practices dictate that an app should be signed with exactly one (1)
|
||||
// signature. Because of this, if there are multiple signatures, reject it.
|
||||
if (packageInfo.signatures == null || packageInfo.signatures.size != 1) {
|
||||
return null
|
||||
} else {
|
||||
val certificate = packageInfo.signatures[0].toByteArray()
|
||||
return getSignatureSha256(certificate)
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildCertificateWhitelist(parser: XmlResourceParser): Map<String, KnownCallerInfo> {
|
||||
|
||||
val certificateWhitelist = LinkedHashMap<String, KnownCallerInfo>()
|
||||
try {
|
||||
var eventType = parser.next()
|
||||
while (eventType != XmlResourceParser.END_DOCUMENT) {
|
||||
if (eventType == XmlResourceParser.START_TAG) {
|
||||
val callerInfo = when (parser.name) {
|
||||
"signing_certificate" -> parseV1Tag(parser)
|
||||
"signature" -> parseV2Tag(parser)
|
||||
else -> null
|
||||
}
|
||||
|
||||
callerInfo?.let { info ->
|
||||
val packageName = info.packageName
|
||||
val existingCallerInfo = certificateWhitelist[packageName]
|
||||
if (existingCallerInfo != null) {
|
||||
existingCallerInfo.signatures += callerInfo.signatures
|
||||
} else {
|
||||
certificateWhitelist[packageName] = callerInfo
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
eventType = parser.next()
|
||||
}
|
||||
} catch (xmlException: XmlPullParserException) {
|
||||
Log.e(TAG, "Could not read allowed callers from XML.", xmlException)
|
||||
} catch (ioException: IOException) {
|
||||
Log.e(TAG, "Could not read allowed callers from XML.", ioException)
|
||||
}
|
||||
|
||||
return certificateWhitelist
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a v1 format tag. See allowed_media_browser_callers.xml for more details.
|
||||
*/
|
||||
private fun parseV1Tag(parser: XmlResourceParser): KnownCallerInfo {
|
||||
val name = parser.getAttributeValue(null, "name")
|
||||
val packageName = parser.getAttributeValue(null, "package")
|
||||
val isRelease = parser.getAttributeBooleanValue(null, "release", false)
|
||||
val certificate = parser.nextText().replace(WHITESPACE_REGEX, "")
|
||||
val signature = getSignatureSha256(certificate)
|
||||
|
||||
val callerSignature = KnownSignature(signature, isRelease)
|
||||
return KnownCallerInfo(name, packageName, mutableSetOf(callerSignature))
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a v2 format tag. See allowed_media_browser_callers.xml for more details.
|
||||
*/
|
||||
private fun parseV2Tag(parser: XmlResourceParser): KnownCallerInfo {
|
||||
val name = parser.getAttributeValue(null, "name")
|
||||
val packageName = parser.getAttributeValue(null, "package")
|
||||
|
||||
val callerSignatures = mutableSetOf<KnownSignature>()
|
||||
var eventType = parser.next()
|
||||
while (eventType != XmlResourceParser.END_TAG) {
|
||||
val isRelease = parser.getAttributeBooleanValue(null, "release", false)
|
||||
val signature = parser.nextText().replace(WHITESPACE_REGEX, "").toLowerCase()
|
||||
callerSignatures += KnownSignature(signature, isRelease)
|
||||
|
||||
eventType = parser.next()
|
||||
}
|
||||
|
||||
return KnownCallerInfo(name, packageName, callerSignatures)
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the Android platform signing key signature. This key is never null.
|
||||
*/
|
||||
private fun getSystemSignature(): String =
|
||||
getPackageInfo(ANDROID_PLATFORM)?.let { platformInfo ->
|
||||
getSignature(platformInfo)
|
||||
} ?: throw IllegalStateException("Platform signature not found")
|
||||
|
||||
/**
|
||||
* Creates a SHA-256 signature given a Base64 encoded certificate.
|
||||
*/
|
||||
private fun getSignatureSha256(certificate: String): String {
|
||||
return getSignatureSha256(Base64.decode(certificate, Base64.DEFAULT))
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a SHA-256 signature given a certificate byte array.
|
||||
*/
|
||||
private fun getSignatureSha256(certificate: ByteArray): String {
|
||||
val md: MessageDigest
|
||||
try {
|
||||
md = MessageDigest.getInstance("SHA256")
|
||||
} catch (noSuchAlgorithmException: NoSuchAlgorithmException) {
|
||||
Log.e(TAG, "No such algorithm: $noSuchAlgorithmException")
|
||||
throw RuntimeException("Could not find SHA256 hash algorithm", noSuchAlgorithmException)
|
||||
}
|
||||
md.update(certificate)
|
||||
|
||||
// This code takes the byte array generated by `md.digest()` and joins each of the bytes
|
||||
// to a string, applying the string format `%02x` on each digit before it's appended, with
|
||||
// a colon (':') between each of the items.
|
||||
// For example: input=[0,2,4,6,8,10,12], output="00:02:04:06:08:0a:0c"
|
||||
return md.digest().joinToString(":") { String.format("%02x", it) }
|
||||
}
|
||||
|
||||
private data class KnownCallerInfo(
|
||||
internal val name: String,
|
||||
internal val packageName: String,
|
||||
internal val signatures: MutableSet<KnownSignature>
|
||||
)
|
||||
|
||||
private data class KnownSignature(
|
||||
internal val signature: String,
|
||||
internal val release: Boolean
|
||||
)
|
||||
|
||||
/**
|
||||
* Convenience class to hold all of the information about an app that's being checked
|
||||
* to see if it's a known caller.
|
||||
*/
|
||||
private data class CallerPackageInfo(
|
||||
internal val name: String,
|
||||
internal val packageName: String,
|
||||
internal val uid: Int,
|
||||
internal val signature: String?,
|
||||
internal val permissions: Set<String>
|
||||
)
|
||||
}
|
||||
|
||||
private const val TAG = "PackageValidator"
|
||||
private const val ANDROID_PLATFORM = "android"
|
||||
private val WHITESPACE_REGEX = "\\s|\\n".toRegex()
|
|
@ -21,7 +21,6 @@ import android.content.Context;
|
|||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
|
@ -31,9 +30,6 @@ import android.net.ConnectivityManager;
|
|||
import android.net.NetworkInfo;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.ResultReceiver;
|
||||
import android.provider.BaseColumns;
|
||||
import android.provider.MediaStore;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.Display;
|
||||
import android.view.View;
|
||||
|
@ -45,16 +41,8 @@ import androidx.annotation.ColorInt;
|
|||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.InetAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import code.name.monkey.appthemehelper.ThemeStore;
|
||||
import code.name.monkey.appthemehelper.util.TintHelper;
|
||||
import code.name.monkey.retromusic.App;
|
||||
|
||||
|
@ -88,40 +76,6 @@ public class RetroUtil {
|
|||
return config.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
|
||||
}
|
||||
|
||||
@TargetApi(19)
|
||||
public static void setStatusBarTranslucent(@NonNull Window window) {
|
||||
window.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS,
|
||||
WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
|
||||
}
|
||||
|
||||
public static boolean isMarshMellow() {
|
||||
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
|
||||
}
|
||||
|
||||
public static boolean isNougat() {
|
||||
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
|
||||
}
|
||||
|
||||
public static boolean isOreo() {
|
||||
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
|
||||
}
|
||||
|
||||
public static float getDistance(float x1, float y1, float x2, float y2) {
|
||||
return (float) Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
|
||||
}
|
||||
|
||||
public static float convertDpToPixel(float dp, @NonNull Context context) {
|
||||
Resources resources = context.getResources();
|
||||
DisplayMetrics metrics = resources.getDisplayMetrics();
|
||||
return dp * ((float) metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT);
|
||||
}
|
||||
|
||||
public static float convertPixelsToDp(float px, @NonNull Context context) {
|
||||
Resources resources = context.getResources();
|
||||
DisplayMetrics metrics = resources.getDisplayMetrics();
|
||||
return px / ((float) metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT);
|
||||
}
|
||||
|
||||
public static void openUrl(@NonNull Activity context, @NonNull String str) {
|
||||
Intent intent = new Intent("android.intent.action.VIEW");
|
||||
intent.setData(Uri.parse(str));
|
||||
|
@ -154,20 +108,6 @@ public class RetroUtil {
|
|||
}
|
||||
}
|
||||
|
||||
public static void showIme(@NonNull View view) {
|
||||
InputMethodManager imm = (InputMethodManager) view.getContext().getSystemService
|
||||
(Context.INPUT_METHOD_SERVICE);
|
||||
// the public methods don't seem to work for me, so… reflection.
|
||||
try {
|
||||
Method showSoftInputUnchecked = InputMethodManager.class.getMethod(
|
||||
"showSoftInputUnchecked", int.class, ResultReceiver.class);
|
||||
showSoftInputUnchecked.setAccessible(true);
|
||||
showSoftInputUnchecked.invoke(imm, 0, null);
|
||||
} catch (Exception e) {
|
||||
// ho hum
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Drawable getVectorDrawable(@NonNull Resources res, @DrawableRes int resId,
|
||||
@Nullable Resources.Theme theme) {
|
||||
|
@ -184,17 +124,6 @@ public class RetroUtil {
|
|||
getVectorDrawable(context.getResources(), id, context.getTheme()), color);
|
||||
}
|
||||
|
||||
public static Drawable getTintedDrawable(@NonNull Context context, @DrawableRes int id,
|
||||
@ColorInt int color) {
|
||||
return TintHelper.createTintedDrawable(ContextCompat.getDrawable(context, id), color);
|
||||
}
|
||||
|
||||
public static Drawable getTintedDrawable(@DrawableRes int id) {
|
||||
return TintHelper
|
||||
.createTintedDrawable(ContextCompat.getDrawable(App.Companion.getContext(), id),
|
||||
ThemeStore.Companion.accentColor(App.Companion.getContext()));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static Bitmap createBitmap(@NonNull Drawable drawable, float sizeMultiplier) {
|
||||
Bitmap bitmap = Bitmap.createBitmap((int) (drawable.getIntrinsicWidth() * sizeMultiplier),
|
||||
|
@ -227,64 +156,6 @@ public class RetroUtil {
|
|||
}
|
||||
}
|
||||
|
||||
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
|
||||
return delim < 0 ? sAddr.toUpperCase() : sAddr.substring(0, delim).toUpperCase();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public static Uri getSongUri(Context context, long id) {
|
||||
final String[] projection = new String[]{
|
||||
BaseColumns._ID, MediaStore.MediaColumns.DATA, MediaStore.Audio.AudioColumns.ALBUM_ID
|
||||
};
|
||||
final StringBuilder selection = new StringBuilder();
|
||||
selection.append(BaseColumns._ID + " IN (");
|
||||
selection.append(id);
|
||||
selection.append(")");
|
||||
final Cursor c = context.getContentResolver().query(
|
||||
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, projection, selection.toString(),
|
||||
null, null);
|
||||
|
||||
if (c == null) {
|
||||
return null;
|
||||
}
|
||||
c.moveToFirst();
|
||||
|
||||
|
||||
try {
|
||||
|
||||
Uri uri = Uri.parse(c.getString(1));
|
||||
c.close();
|
||||
|
||||
return uri;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static int getStatusBarHeight() {
|
||||
int result = 0;
|
||||
int resourceId = App.Companion.getContext().getResources().getIdentifier("status_bar_height", "dimen", "android");
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
package code.name.monkey.retromusic.util;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||
|
||||
|
@ -22,7 +24,7 @@ public class SwipeAndDragHelper extends ItemTouchHelper.Callback {
|
|||
|
||||
private ActionCompletionContract contract;
|
||||
|
||||
public SwipeAndDragHelper(ActionCompletionContract contract) {
|
||||
public SwipeAndDragHelper(@NonNull ActionCompletionContract contract) {
|
||||
this.contract = contract;
|
||||
}
|
||||
|
||||
|
|
|
@ -28,41 +28,18 @@ import android.view.View
|
|||
import android.view.animation.PathInterpolator
|
||||
import android.widget.ProgressBar
|
||||
import android.widget.SeekBar
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.core.view.ViewCompat
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||
import code.name.monkey.retromusic.R
|
||||
import com.google.android.material.card.MaterialCardView
|
||||
import com.google.android.material.shape.CornerFamily
|
||||
import com.google.android.material.shape.MaterialShapeDrawable
|
||||
import com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView
|
||||
|
||||
|
||||
object ViewUtil {
|
||||
|
||||
const val RETRO_MUSIC_ANIM_TIME = 1000
|
||||
fun cardViewTopCorners(cardView: MaterialCardView) {
|
||||
|
||||
val radius = PreferenceUtil.getInstance(cardView.context).dialogCorner
|
||||
(cardView.background as? MaterialShapeDrawable).let {
|
||||
it?.shapeAppearanceModel?.apply {
|
||||
toBuilder()
|
||||
.setTopLeftCorner(CornerFamily.CUT, 10f)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun createTextColorTransition(v: TextView, @ColorInt startColor: Int, @ColorInt endColor: Int): Animator {
|
||||
return createColorAnimator(v, "textColor", startColor, endColor)
|
||||
}
|
||||
|
||||
fun createBackgroundColorTransition(v: View, @ColorInt startColor: Int, @ColorInt endColor: Int): Animator {
|
||||
return createColorAnimator(v, "backgroundColor", startColor, endColor)
|
||||
}
|
||||
|
||||
fun setProgressDrawable(progressSlider: SeekBar, newColor: Int, thumbTint: Boolean = false) {
|
||||
|
||||
|
@ -103,21 +80,6 @@ object ViewUtil {
|
|||
return animator
|
||||
}
|
||||
|
||||
fun setStatusBarHeight(context: Context, statusBar: View) {
|
||||
val lp = statusBar.layoutParams
|
||||
lp.height = getStatusBarHeight(context)
|
||||
statusBar.requestLayout()
|
||||
}
|
||||
|
||||
fun getStatusBarHeight(context: Context): Int {
|
||||
var result = 0
|
||||
val resourceId = context.resources.getIdentifier("status_bar_height", "dimen", "android")
|
||||
if (resourceId > 0) {
|
||||
result = context.resources.getDimensionPixelSize(resourceId)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
fun hitTest(v: View, x: Int, y: Int): Boolean {
|
||||
val tx = (ViewCompat.getTranslationX(v) + 0.5f).toInt()
|
||||
val ty = (ViewCompat.getTranslationY(v) + 0.5f).toInt()
|
||||
|
|
|
@ -1,91 +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.color;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.ColorMatrix;
|
||||
import android.graphics.ColorMatrixColorFilter;
|
||||
import android.graphics.LinearGradient;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffXfermode;
|
||||
import android.graphics.Shader;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
/**
|
||||
* A utility class to colorize bitmaps with a color gradient and a special blending mode
|
||||
*/
|
||||
public class ImageGradientColorizer {
|
||||
|
||||
public Bitmap colorize(Drawable drawable, int backgroundColor, boolean isRtl) {
|
||||
int width = drawable.getIntrinsicWidth();
|
||||
int height = drawable.getIntrinsicHeight();
|
||||
int size = Math.min(width, height);
|
||||
int widthInset = (width - size) / 2;
|
||||
int heightInset = (height - size) / 2;
|
||||
drawable = drawable.mutate();
|
||||
drawable.setBounds(-widthInset, -heightInset, width - widthInset, height - heightInset);
|
||||
Bitmap newBitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
|
||||
Canvas canvas = new Canvas(newBitmap);
|
||||
// Values to calculate the luminance of a color
|
||||
float lr = 0.2126f;
|
||||
float lg = 0.7152f;
|
||||
float lb = 0.0722f;
|
||||
// Extract the red, green, blue components of the color extraction color in
|
||||
// float and int form
|
||||
int tri = Color.red(backgroundColor);
|
||||
int tgi = Color.green(backgroundColor);
|
||||
int tbi = Color.blue(backgroundColor);
|
||||
float tr = tri / 255f;
|
||||
float tg = tgi / 255f;
|
||||
float tb = tbi / 255f;
|
||||
// Calculate the luminance of the color extraction color
|
||||
float cLum = (tr * lr + tg * lg + tb * lb) * 255;
|
||||
ColorMatrix m = new ColorMatrix(new float[]{
|
||||
lr, lg, lb, 0, tri - cLum,
|
||||
lr, lg, lb, 0, tgi - cLum,
|
||||
lr, lg, lb, 0, tbi - cLum,
|
||||
0, 0, 0, 1, 0,
|
||||
});
|
||||
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
LinearGradient linearGradient = new LinearGradient(0, 0, size, 0,
|
||||
new int[]{0, Color.argb(0.5f, 1, 1, 1), Color.BLACK},
|
||||
new float[]{0.0f, 0.4f, 1.0f}, Shader.TileMode.CLAMP);
|
||||
paint.setShader(linearGradient);
|
||||
Bitmap fadeIn = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
|
||||
Canvas fadeInCanvas = new Canvas(fadeIn);
|
||||
drawable.clearColorFilter();
|
||||
drawable.draw(fadeInCanvas);
|
||||
if (isRtl) {
|
||||
// Let's flip the gradient
|
||||
fadeInCanvas.translate(size, 0);
|
||||
fadeInCanvas.scale(-1, 1);
|
||||
}
|
||||
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
|
||||
fadeInCanvas.drawPaint(paint);
|
||||
Paint coloredPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
coloredPaint.setColorFilter(new ColorMatrixColorFilter(m));
|
||||
coloredPaint.setAlpha((int) (0.5f * 255));
|
||||
canvas.drawBitmap(fadeIn, 0, 0, coloredPaint);
|
||||
linearGradient = new LinearGradient(0, 0, size, 0,
|
||||
new int[]{0, Color.argb(0.5f, 1, 1, 1), Color.BLACK},
|
||||
new float[]{0.0f, 0.6f, 1.0f}, Shader.TileMode.CLAMP);
|
||||
paint.setShader(linearGradient);
|
||||
fadeInCanvas.drawPaint(paint);
|
||||
canvas.drawBitmap(fadeIn, 0, 0, null);
|
||||
return newBitmap;
|
||||
}
|
||||
}
|
|
@ -20,6 +20,7 @@ import android.graphics.Canvas;
|
|||
import android.graphics.Color;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.palette.graphics.Palette;
|
||||
|
||||
|
@ -57,7 +58,6 @@ public class MediaNotificationProcessor {
|
|||
private static final float BLACK_MAX_LIGHTNESS = 0.08f;
|
||||
private static final float WHITE_MIN_LIGHTNESS = 0.90f;
|
||||
private static final int RESIZE_BITMAP_AREA = 150 * 150;
|
||||
private final ImageGradientColorizer mColorizer;
|
||||
private final Context mContext;
|
||||
/**
|
||||
* The context of the notification. This is the app context of the package posting the
|
||||
|
@ -70,16 +70,14 @@ public class MediaNotificationProcessor {
|
|||
private onColorThing onColorThing;
|
||||
|
||||
public MediaNotificationProcessor(Context context, Context packageContext, onColorThing thing) {
|
||||
this(context, packageContext, new ImageGradientColorizer());
|
||||
this(context, packageContext);
|
||||
onColorThing = thing;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
MediaNotificationProcessor(Context context, Context packageContext,
|
||||
ImageGradientColorizer colorizer) {
|
||||
MediaNotificationProcessor(Context context, Context packageContext) {
|
||||
mContext = context;
|
||||
mPackageContext = packageContext;
|
||||
mColorizer = colorizer;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -292,228 +290,4 @@ public class MediaNotificationProcessor {
|
|||
public interface onColorThing {
|
||||
void bothColor(int i, int i2);
|
||||
}
|
||||
|
||||
/**
|
||||
* The fraction below which we select the vibrant instead of the light/dark vibrant color
|
||||
*//*
|
||||
private static final float POPULATION_FRACTION_FOR_MORE_VIBRANT = 1.0f;
|
||||
*//**
|
||||
* Minimum saturation that a muted color must have if there exists if deciding between two colors
|
||||
*//*
|
||||
private static final float MIN_SATURATION_WHEN_DECIDING = 0.19f;
|
||||
*//**
|
||||
* Minimum fraction that any color must have to be picked up as a text color
|
||||
*//*
|
||||
private static final double MINIMUM_IMAGE_FRACTION = 0.002;
|
||||
*//**
|
||||
* The population fraction to select the dominant color as the text color over a the colored
|
||||
* ones.
|
||||
*//*
|
||||
private static final float POPULATION_FRACTION_FOR_DOMINANT = 0.01f;
|
||||
*//**
|
||||
* The population fraction to select a white or black color as the background over a color.
|
||||
*//*
|
||||
private static final float POPULATION_FRACTION_FOR_WHITE_OR_BLACK = 2.5f;
|
||||
|
||||
private static final float BLACK_MAX_LIGHTNESS = 0.08f;
|
||||
private static final float WHITE_MIN_LIGHTNESS = 0.90f;
|
||||
private static final int RESIZE_BITMAP_AREA = 150 * 150;
|
||||
private static float[] mFilteredBackgroundHsl = null;
|
||||
private final ImageGradientColorizer mColorizer;
|
||||
private final Context mContext;
|
||||
*//**
|
||||
* The context of the notification. This is the app context of the package posting the
|
||||
* notification.
|
||||
*//*
|
||||
private final Context mPackageContext;
|
||||
private static Palette.Filter mBlackWhiteFilter = (rgb, hsl) -> !isWhiteOrBlack(hsl);
|
||||
private boolean mIsLowPriority;
|
||||
|
||||
public MediaNotificationProcessor(Context context, Context packageContext) {
|
||||
this(context, packageContext, new ImageGradientColorizer());
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
MediaNotificationProcessor(Context context, Context packageContext,
|
||||
ImageGradientColorizer colorizer) {
|
||||
mContext = context;
|
||||
mPackageContext = packageContext;
|
||||
mColorizer = colorizer;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Palette.Builder generatePalette(Bitmap bitmap) {
|
||||
return bitmap == null ? null : Palette.from(bitmap).clearFilters().resizeBitmapArea(RESIZE_BITMAP_AREA);
|
||||
}
|
||||
|
||||
public static int getBackgroundColor(Palette.Builder builder) {
|
||||
return findBackgroundColorAndFilter(builder.generate());
|
||||
}
|
||||
|
||||
public static int getTextColor(Palette.Builder builder) {
|
||||
int backgroundColor = 0;
|
||||
if (mFilteredBackgroundHsl != null) {
|
||||
builder.addFilter((rgb, hsl) -> {
|
||||
// at least 10 degrees hue difference
|
||||
float diff = Math.abs(hsl[0] - mFilteredBackgroundHsl[0]);
|
||||
return diff > 10 && diff < 350;
|
||||
});
|
||||
}
|
||||
builder.addFilter(mBlackWhiteFilter);
|
||||
Palette palette = builder.generate();
|
||||
return selectForegroundColor(backgroundColor, palette);
|
||||
}
|
||||
|
||||
private static int selectForegroundColor(int backgroundColor, Palette palette) {
|
||||
if (ColorUtil.isColorLight(backgroundColor)) {
|
||||
return selectForegroundColorForSwatches(palette.getDarkVibrantSwatch(),
|
||||
palette.getVibrantSwatch(),
|
||||
palette.getDarkMutedSwatch(),
|
||||
palette.getMutedSwatch(),
|
||||
palette.getDominantSwatch(),
|
||||
Color.BLACK);
|
||||
} else {
|
||||
return selectForegroundColorForSwatches(palette.getLightVibrantSwatch(),
|
||||
palette.getVibrantSwatch(),
|
||||
palette.getLightMutedSwatch(),
|
||||
palette.getMutedSwatch(),
|
||||
palette.getDominantSwatch(),
|
||||
Color.WHITE);
|
||||
}
|
||||
}
|
||||
|
||||
private static int selectForegroundColorForSwatches(Palette.Swatch moreVibrant,
|
||||
Palette.Swatch vibrant, Palette.Swatch moreMutedSwatch, Palette.Swatch mutedSwatch,
|
||||
Palette.Swatch dominantSwatch, int fallbackColor) {
|
||||
Palette.Swatch coloredCandidate = selectVibrantCandidate(moreVibrant, vibrant);
|
||||
if (coloredCandidate == null) {
|
||||
coloredCandidate = selectMutedCandidate(mutedSwatch, moreMutedSwatch);
|
||||
}
|
||||
if (coloredCandidate != null) {
|
||||
if (dominantSwatch == coloredCandidate) {
|
||||
return coloredCandidate.getRgb();
|
||||
} else if ((float) coloredCandidate.getPopulation() / dominantSwatch.getPopulation()
|
||||
< POPULATION_FRACTION_FOR_DOMINANT
|
||||
&& dominantSwatch.getHsl()[1] > MIN_SATURATION_WHEN_DECIDING) {
|
||||
return dominantSwatch.getRgb();
|
||||
} else {
|
||||
return coloredCandidate.getRgb();
|
||||
}
|
||||
} else if (hasEnoughPopulation(dominantSwatch)) {
|
||||
return dominantSwatch.getRgb();
|
||||
} else {
|
||||
return fallbackColor;
|
||||
}
|
||||
}
|
||||
|
||||
private static Palette.Swatch selectMutedCandidate(Palette.Swatch first,
|
||||
Palette.Swatch second) {
|
||||
boolean firstValid = hasEnoughPopulation(first);
|
||||
boolean secondValid = hasEnoughPopulation(second);
|
||||
if (firstValid && secondValid) {
|
||||
float firstSaturation = first.getHsl()[1];
|
||||
float secondSaturation = second.getHsl()[1];
|
||||
float populationFraction = first.getPopulation() / (float) second.getPopulation();
|
||||
if (firstSaturation * populationFraction > secondSaturation) {
|
||||
return first;
|
||||
} else {
|
||||
return second;
|
||||
}
|
||||
} else if (firstValid) {
|
||||
return first;
|
||||
} else if (secondValid) {
|
||||
return second;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Palette.Swatch selectVibrantCandidate(Palette.Swatch first,
|
||||
Palette.Swatch second) {
|
||||
boolean firstValid = hasEnoughPopulation(first);
|
||||
boolean secondValid = hasEnoughPopulation(second);
|
||||
if (firstValid && secondValid) {
|
||||
int firstPopulation = first.getPopulation();
|
||||
int secondPopulation = second.getPopulation();
|
||||
if (firstPopulation / (float) secondPopulation
|
||||
< POPULATION_FRACTION_FOR_MORE_VIBRANT) {
|
||||
return second;
|
||||
} else {
|
||||
return first;
|
||||
}
|
||||
} else if (firstValid) {
|
||||
return first;
|
||||
} else if (secondValid) {
|
||||
return second;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean hasEnoughPopulation(Palette.Swatch swatch) {
|
||||
// We want a fraction that is at least 1% of the image
|
||||
return swatch != null
|
||||
&& (swatch.getPopulation() / (float) RESIZE_BITMAP_AREA > MINIMUM_IMAGE_FRACTION);
|
||||
}
|
||||
|
||||
public static int findBackgroundColorAndFilter(Palette palette) {
|
||||
// by default we use the dominant palette
|
||||
Palette.Swatch dominantSwatch = palette.getDominantSwatch();
|
||||
if (dominantSwatch == null) {
|
||||
// We're not filtering on white or black
|
||||
mFilteredBackgroundHsl = null;
|
||||
return Color.WHITE;
|
||||
}
|
||||
if (!isWhiteOrBlack(dominantSwatch.getHsl())) {
|
||||
mFilteredBackgroundHsl = dominantSwatch.getHsl();
|
||||
return dominantSwatch.getRgb();
|
||||
}
|
||||
// Oh well, we selected black or white. Lets look at the second color!
|
||||
List<Swatch> swatches = palette.getSwatches();
|
||||
float highestNonWhitePopulation = -1;
|
||||
Palette.Swatch second = null;
|
||||
for (Palette.Swatch swatch : swatches) {
|
||||
if (swatch != dominantSwatch
|
||||
&& swatch.getPopulation() > highestNonWhitePopulation
|
||||
&& !isWhiteOrBlack(swatch.getHsl())) {
|
||||
second = swatch;
|
||||
highestNonWhitePopulation = swatch.getPopulation();
|
||||
}
|
||||
}
|
||||
if (second == null) {
|
||||
// We're not filtering on white or black
|
||||
mFilteredBackgroundHsl = null;
|
||||
return dominantSwatch.getRgb();
|
||||
}
|
||||
if (dominantSwatch.getPopulation() / highestNonWhitePopulation
|
||||
> POPULATION_FRACTION_FOR_WHITE_OR_BLACK) {
|
||||
// The dominant swatch is very dominant, lets take it!
|
||||
// We're not filtering on white or black
|
||||
mFilteredBackgroundHsl = null;
|
||||
return dominantSwatch.getRgb();
|
||||
} else {
|
||||
mFilteredBackgroundHsl = second.getHsl();
|
||||
return second.getRgb();
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isWhiteOrBlack(float[] hsl) {
|
||||
return isBlack(hsl) || isWhite(hsl);
|
||||
}
|
||||
|
||||
*//**
|
||||
* @return true if the color represents a color which is close to black.
|
||||
*//*
|
||||
private static boolean isBlack(float[] hslColor) {
|
||||
return hslColor[2] <= BLACK_MAX_LIGHTNESS;
|
||||
}
|
||||
|
||||
*//**
|
||||
* @return true if the color represents a color which is close to white.
|
||||
*//*
|
||||
private static boolean isWhite(float[] hslColor) {
|
||||
return hslColor[2] >= WHITE_MIN_LIGHTNESS;
|
||||
}
|
||||
|
||||
public void setIsLowPriority(boolean isLowPriority) {
|
||||
mIsLowPriority = isLowPriority;
|
||||
}*/
|
||||
}
|
||||
|
|
|
@ -1,33 +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.schedulers;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import io.reactivex.Scheduler;
|
||||
|
||||
/**
|
||||
* Created by hemanths on 12/08/17.
|
||||
*/
|
||||
|
||||
public interface BaseSchedulerProvider {
|
||||
@NonNull
|
||||
Scheduler computation();
|
||||
|
||||
@NonNull
|
||||
Scheduler io();
|
||||
|
||||
@NonNull
|
||||
Scheduler ui();
|
||||
}
|
|
@ -1,58 +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.schedulers;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import io.reactivex.Scheduler;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
|
||||
/**
|
||||
* Created by hemanths on 12/08/17.
|
||||
*/
|
||||
|
||||
public class SchedulerProvider implements BaseSchedulerProvider {
|
||||
@NonNull
|
||||
private static SchedulerProvider INSTANCE;
|
||||
|
||||
public SchedulerProvider() {
|
||||
}
|
||||
|
||||
public static synchronized SchedulerProvider getInstance() {
|
||||
if (INSTANCE == null) {
|
||||
INSTANCE = new SchedulerProvider();
|
||||
}
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public Scheduler computation() {
|
||||
return Schedulers.computation();
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public Scheduler io() {
|
||||
return Schedulers.io();
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public Scheduler ui() {
|
||||
return AndroidSchedulers.mainThread();
|
||||
}
|
||||
}
|
|
@ -1,12 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
|
||||
<gradient
|
||||
android:angle="0"
|
||||
android:centerColor="#80ffffff"
|
||||
android:endColor="@android:color/transparent"
|
||||
android:startColor="@android:color/white"
|
||||
android:type="linear"/>
|
||||
android:shape="rectangle">
|
||||
|
||||
<gradient
|
||||
android:angle="0"
|
||||
android:centerColor="#80ffffff"
|
||||
android:endColor="@android:color/transparent"
|
||||
android:startColor="?colorPrimary"
|
||||
android:type="linear" />
|
||||
</shape>
|
|
@ -1,157 +1,159 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/background"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
tools:ignore="ContentDescription">
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/image"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_alignBottom="@+id/content"
|
||||
android:layout_alignParentEnd="true">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/largeIcon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:adjustViewBounds="true"
|
||||
android:scaleType="centerCrop"
|
||||
/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/foregroundImage"
|
||||
android:layout_width="96dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_alignStart="@id/largeIcon"
|
||||
android:src="@drawable/background_image"
|
||||
tools:tint="@color/md_black_1000"/>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/app"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:layout_marginEnd="48dp"
|
||||
android:paddingTop="6dp"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/smallIcon"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_marginStart="8dp"
|
||||
tools:src="@drawable/ic_audiotrack_black_24dp"
|
||||
tools:tint="@color/md_black_1000"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/appName"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:ellipsize="end"
|
||||
android:lines="1"
|
||||
android:singleLine="true"
|
||||
android:textSize="12sp"
|
||||
tools:text="@string/app_name"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/arrow"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_marginStart="2dp"
|
||||
android:scaleType="centerInside"
|
||||
android:src="@drawable/ic_keyboard_arrow_down_black_24dp"
|
||||
android:tint="@color/md_black_1000"
|
||||
tools:ignore="VectorDrawableCompat"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/content"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/background"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_below="@id/app"
|
||||
android:layout_toStartOf="@id/actions"
|
||||
android:paddingBottom="12dp"
|
||||
android:paddingStart="0dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:orientation="vertical">
|
||||
tools:ignore="ContentDescription">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:textStyle="bold"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="0dp"
|
||||
android:ellipsize="end"
|
||||
android:lines="1"
|
||||
android:singleLine="true"
|
||||
tools:text="@string/title_dashboard"/>
|
||||
<RelativeLayout
|
||||
android:id="@+id/image"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_alignBottom="@+id/content"
|
||||
android:layout_alignParentEnd="true">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/subtitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="0dp"
|
||||
android:ellipsize="end"
|
||||
android:lines="1"
|
||||
android:singleLine="true"
|
||||
tools:text="@string/title_dashboard"/>
|
||||
<ImageView
|
||||
android:id="@+id/largeIcon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:adjustViewBounds="true"
|
||||
android:scaleType="centerCrop"
|
||||
tools:src="@tools:sample/avatars" />
|
||||
|
||||
</LinearLayout>
|
||||
<ImageView
|
||||
android:id="@+id/foregroundImage"
|
||||
android:layout_width="96dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_alignStart="@id/largeIcon"
|
||||
android:src="@drawable/background_image"
|
||||
tools:tint="?colorPrimary" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/actions"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="48dp"
|
||||
android:layout_alignBottom="@id/content"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignTop="@id/content"
|
||||
android:layout_gravity="bottom"
|
||||
android:orientation="horizontal">
|
||||
</RelativeLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/action_prev"
|
||||
android:layout_width="42dp"
|
||||
android:layout_height="42dp"
|
||||
android:scaleType="centerInside"/>
|
||||
<LinearLayout
|
||||
android:id="@+id/app"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="48dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingTop="4dp"
|
||||
android:paddingEnd="8dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/action_play_pause"
|
||||
android:layout_width="42dp"
|
||||
android:layout_height="42dp"
|
||||
android:scaleType="centerInside"/>
|
||||
<ImageView
|
||||
android:id="@+id/smallIcon"
|
||||
android:layout_width="12dp"
|
||||
android:layout_height="12dp"
|
||||
android:layout_marginStart="8dp"
|
||||
tools:src="@drawable/ic_audiotrack_black_24dp"
|
||||
tools:tint="@color/md_black_1000" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/action_next"
|
||||
android:layout_width="42dp"
|
||||
android:layout_height="42dp"
|
||||
android:scaleType="centerInside"/>
|
||||
<TextView
|
||||
android:id="@+id/appName"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:ellipsize="end"
|
||||
android:lines="1"
|
||||
android:singleLine="true"
|
||||
android:textSize="10sp"
|
||||
tools:text="@string/app_name" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/action_quit"
|
||||
android:layout_width="42dp"
|
||||
android:layout_height="42dp"
|
||||
android:scaleType="centerInside"
|
||||
android:visibility="gone"/>
|
||||
<ImageView
|
||||
android:id="@+id/arrow"
|
||||
android:layout_width="12dp"
|
||||
android:layout_height="12dp"
|
||||
android:layout_marginStart="2dp"
|
||||
android:scaleType="centerInside"
|
||||
android:src="@drawable/ic_keyboard_arrow_down_black_24dp"
|
||||
android:tint="@color/md_black_1000"
|
||||
tools:ignore="VectorDrawableCompat" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/fifth"
|
||||
android:layout_width="42dp"
|
||||
android:layout_height="42dp"
|
||||
android:scaleType="centerInside"/>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/app"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_toStartOf="@id/actions"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:paddingBottom="4dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:lines="1"
|
||||
android:singleLine="true"
|
||||
android:textStyle="bold"
|
||||
tools:text="@tools:sample/lorem/random" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/subtitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:lines="1"
|
||||
android:singleLine="true"
|
||||
android:textSize="12sp"
|
||||
tools:text="@tools:sample/lorem/random" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/actions"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignTop="@id/content"
|
||||
android:layout_alignBottom="@id/content"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_gravity="bottom"
|
||||
android:layout_marginEnd="24dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/action_prev"
|
||||
android:layout_width="38dp"
|
||||
android:layout_height="38dp"
|
||||
android:scaleType="centerInside"
|
||||
android:src="@drawable/ic_skip_previous_round_white_32dp"
|
||||
tools:tint="?colorOnPrimary" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/action_play_pause"
|
||||
android:layout_width="38dp"
|
||||
android:layout_height="38dp"
|
||||
android:scaleType="centerInside"
|
||||
android:src="@drawable/ic_pause_white_24dp"
|
||||
tools:tint="?colorOnPrimary" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/action_next"
|
||||
android:layout_width="38dp"
|
||||
android:layout_height="38dp"
|
||||
android:scaleType="centerInside"
|
||||
android:src="@drawable/ic_skip_next_round_white_32dp"
|
||||
tools:tint="?colorOnPrimary" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/action_quit"
|
||||
android:layout_width="38dp"
|
||||
android:layout_height="38dp"
|
||||
android:scaleType="centerInside"
|
||||
android:visibility="gone" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/fifth"
|
||||
android:layout_width="38dp"
|
||||
android:layout_height="38dp"
|
||||
android:scaleType="centerInside" />
|
||||
</LinearLayout>
|
||||
</RelativeLayout>
|
|
@ -1,146 +1,135 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/background"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/background"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/image"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_alignBottom="@+id/content"
|
||||
android:layout_alignParentEnd="true">
|
||||
android:id="@+id/image"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignBottom="@+id/content"
|
||||
android:layout_alignParentEnd="true">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/largeIcon"
|
||||
android:layout_width="@dimen/notification_big_image_size"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:scaleType="centerCrop"
|
||||
android:contentDescription="TODO" />
|
||||
android:id="@+id/largeIcon"
|
||||
android:layout_width="@dimen/notification_big_image_size"
|
||||
android:layout_height="@dimen/notification_big_image_size"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:scaleType="centerCrop"
|
||||
tools:src="@tools:sample/avatars" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/foregroundImage"
|
||||
android:layout_width="96dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_alignStart="@id/largeIcon"
|
||||
android:src="@drawable/background_image"
|
||||
android:tint="@color/md_black_1000"
|
||||
android:contentDescription="TODO" />
|
||||
android:id="@+id/foregroundImage"
|
||||
android:layout_width="96dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_alignStart="@id/largeIcon"
|
||||
android:src="@drawable/background_image" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentStart="true"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="12dp"
|
||||
android:paddingStart="0dp"
|
||||
android:paddingEnd="144dp"
|
||||
android:orientation="vertical">
|
||||
android:id="@+id/content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentStart="true"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="0dp"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingEnd="144dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingEnd="0dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/smallIcon"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_marginStart="8dp"
|
||||
tools:src="@drawable/ic_audiotrack_black_24dp"
|
||||
tools:tint="@color/md_black_1000"
|
||||
android:contentDescription="TODO" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/appName"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:ellipsize="end"
|
||||
android:lines="1"
|
||||
android:singleLine="true"
|
||||
android:textSize="13sp"
|
||||
tools:text="@string/app_name"/>
|
||||
android:layout_marginBottom="4dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/arrow"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_marginStart="2dp"
|
||||
android:scaleType="centerInside"
|
||||
android:src="@drawable/ic_keyboard_arrow_up_24dp"
|
||||
tools:ignore="VectorDrawableCompat"
|
||||
android:contentDescription="TODO" />
|
||||
android:id="@+id/smallIcon"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_marginStart="8dp"
|
||||
tools:src="@drawable/ic_audiotrack_black_24dp"
|
||||
tools:tint="@color/md_black_1000" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/appName"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:ellipsize="end"
|
||||
android:lines="1"
|
||||
android:singleLine="true"
|
||||
android:textSize="13sp"
|
||||
tools:text="@string/app_name" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/arrow"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_marginStart="2dp"
|
||||
android:scaleType="centerInside"
|
||||
android:src="@drawable/ic_keyboard_arrow_up_24dp"
|
||||
android:tint="@color/dark_grey"
|
||||
tools:ignore="VectorDrawableCompat" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:textStyle="bold"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="0dp"
|
||||
android:ellipsize="end"
|
||||
android:lines="1"
|
||||
android:singleLine="true"
|
||||
tools:text="@string/title_dashboard"/>
|
||||
android:id="@+id/title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:lines="1"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="0dp"
|
||||
android:singleLine="true"
|
||||
android:textStyle="bold"
|
||||
tools:text="@tools:sample/lorem/random" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/subtitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="0dp"
|
||||
android:ellipsize="end"
|
||||
android:lines="1"
|
||||
android:singleLine="true"
|
||||
tools:text="@string/title_dashboard"/>
|
||||
android:id="@+id/subtitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:lines="1"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="0dp"
|
||||
android:singleLine="true"
|
||||
tools:text="@tools:sample/lorem/random" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:orientation="horizontal"
|
||||
tools:ignore="ContentDescription">
|
||||
android:paddingEnd="0dp"
|
||||
android:paddingStart="12dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/action_prev"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:scaleType="centerInside"
|
||||
tools:tint="@color/md_black_1000"/>
|
||||
android:id="@+id/action_prev"
|
||||
android:layout_width="42dp"
|
||||
android:layout_height="42dp"
|
||||
android:scaleType="centerInside" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/action_play_pause"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:scaleType="centerInside"/>
|
||||
android:id="@+id/action_play_pause"
|
||||
android:layout_width="42dp"
|
||||
android:layout_height="42dp"
|
||||
android:scaleType="centerInside" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/action_next"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:scaleType="centerInside"/>
|
||||
android:id="@+id/action_next"
|
||||
android:layout_width="42dp"
|
||||
android:layout_height="42dp"
|
||||
android:scaleType="centerInside" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/action_quit"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:scaleType="centerInside"
|
||||
tools:ignore="ContentDescription"/>
|
||||
|
||||
android:id="@+id/action_quit"
|
||||
android:layout_width="42dp"
|
||||
android:layout_height="42dp"
|
||||
android:scaleType="centerInside" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</RelativeLayout>
|
|
@ -19,7 +19,8 @@
|
|||
android:layout_height="match_parent"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:adjustViewBounds="true"
|
||||
android:scaleType="centerCrop" />
|
||||
android:scaleType="centerCrop"
|
||||
tools:src="@tools:sample/avatars" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/foregroundImage"
|
||||
|
@ -27,7 +28,7 @@
|
|||
android:layout_height="match_parent"
|
||||
android:layout_alignStart="@id/largeIcon"
|
||||
android:src="@drawable/background_image"
|
||||
tools:tint="@color/md_black_1000" />
|
||||
tools:tint="?colorPrimary" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
|
@ -36,11 +37,10 @@
|
|||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="48dp"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingTop="6dp"
|
||||
android:paddingTop="4dp"
|
||||
android:paddingEnd="8dp">
|
||||
|
||||
<ImageView
|
||||
|
@ -82,9 +82,8 @@
|
|||
android:layout_alignParentStart="true"
|
||||
android:layout_toStartOf="@id/actions"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="0dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:paddingBottom="12dp">
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="12dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
|
@ -92,11 +91,9 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:lines="1"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="0dp"
|
||||
android:singleLine="true"
|
||||
android:textStyle="bold"
|
||||
tools:text="@string/title_dashboard" />
|
||||
tools:text="@tools:sample/lorem/random" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/subtitle"
|
||||
|
@ -104,10 +101,8 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:lines="1"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="0dp"
|
||||
android:singleLine="true"
|
||||
tools:text="@string/title_dashboard" />
|
||||
tools:text="@tools:sample/lorem/random" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
@ -124,33 +119,39 @@
|
|||
|
||||
<ImageView
|
||||
android:id="@+id/action_prev"
|
||||
android:layout_width="42dp"
|
||||
android:layout_height="42dp"
|
||||
android:scaleType="centerInside" />
|
||||
android:layout_width="38dp"
|
||||
android:layout_height="38dp"
|
||||
android:scaleType="centerInside"
|
||||
android:src="@drawable/ic_skip_previous_round_white_32dp"
|
||||
android:tint="?colorOnPrimary" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/action_play_pause"
|
||||
android:layout_width="42dp"
|
||||
android:layout_height="42dp"
|
||||
android:scaleType="centerInside" />
|
||||
android:layout_width="38dp"
|
||||
android:layout_height="38dp"
|
||||
android:scaleType="centerInside"
|
||||
android:src="@drawable/ic_pause_white_24dp"
|
||||
android:tint="?colorOnPrimary" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/action_next"
|
||||
android:layout_width="42dp"
|
||||
android:layout_height="42dp"
|
||||
android:scaleType="centerInside" />
|
||||
android:layout_width="38dp"
|
||||
android:layout_height="38dp"
|
||||
android:scaleType="centerInside"
|
||||
android:src="@drawable/ic_skip_next_round_white_32dp"
|
||||
android:tint="?colorOnPrimary" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/action_quit"
|
||||
android:layout_width="42dp"
|
||||
android:layout_height="42dp"
|
||||
android:layout_width="38dp"
|
||||
android:layout_height="38dp"
|
||||
android:scaleType="centerInside"
|
||||
android:visibility="gone" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/fifth"
|
||||
android:layout_width="42dp"
|
||||
android:layout_height="42dp"
|
||||
android:layout_width="38dp"
|
||||
android:layout_height="38dp"
|
||||
android:scaleType="centerInside" />
|
||||
</LinearLayout>
|
||||
</RelativeLayout>
|
|
@ -1,142 +1,133 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/background"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/image"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/background"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_alignBottom="@+id/content"
|
||||
android:layout_alignParentEnd="true">
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/largeIcon"
|
||||
android:layout_width="@dimen/notification_big_image_size"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:scaleType="centerCrop"
|
||||
android:contentDescription="TODO" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/foregroundImage"
|
||||
android:layout_width="96dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_alignStart="@id/largeIcon"
|
||||
android:src="@drawable/background_image"
|
||||
tools:tint="@color/md_black_1000"
|
||||
android:contentDescription="TODO" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentStart="true"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="12dp"
|
||||
android:paddingStart="0dp"
|
||||
android:paddingEnd="144dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/smallIcon"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_marginStart="8dp"
|
||||
tools:src="@drawable/ic_audiotrack_black_24dp"
|
||||
tools:tint="@color/md_black_1000"
|
||||
android:contentDescription="TODO" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/appName"
|
||||
android:layout_width="wrap_content"
|
||||
<RelativeLayout
|
||||
android:id="@+id/image"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:ellipsize="end"
|
||||
android:lines="1"
|
||||
android:singleLine="true"
|
||||
android:textSize="13sp"
|
||||
tools:text="@string/app_name"/>
|
||||
android:layout_alignBottom="@+id/content"
|
||||
android:layout_alignParentEnd="true">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/arrow"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_marginStart="2dp"
|
||||
android:scaleType="centerInside"
|
||||
android:src="@drawable/ic_keyboard_arrow_up_24dp"
|
||||
tools:ignore="VectorDrawableCompat"
|
||||
android:contentDescription="TODO" />
|
||||
<ImageView
|
||||
android:id="@+id/largeIcon"
|
||||
android:layout_width="@dimen/notification_big_image_size"
|
||||
android:layout_height="@dimen/notification_big_image_size"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:scaleType="centerCrop"
|
||||
tools:src="@tools:sample/avatars" />
|
||||
|
||||
</LinearLayout>
|
||||
<ImageView
|
||||
android:id="@+id/foregroundImage"
|
||||
android:layout_width="96dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_alignStart="@id/largeIcon"
|
||||
android:src="@drawable/background_image" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:textStyle="bold"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="0dp"
|
||||
android:ellipsize="end"
|
||||
android:lines="1"
|
||||
android:singleLine="true"
|
||||
tools:text="@string/title_dashboard"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/subtitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="0dp"
|
||||
android:ellipsize="end"
|
||||
android:lines="1"
|
||||
android:singleLine="true"
|
||||
tools:text="@string/title_dashboard"/>
|
||||
</RelativeLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="horizontal">
|
||||
android:id="@+id/content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentStart="true"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="0dp"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingEnd="144dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/action_prev"
|
||||
android:layout_width="42dp"
|
||||
android:layout_height="42dp"
|
||||
android:scaleType="centerInside"
|
||||
android:contentDescription="TODO" />
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/action_play_pause"
|
||||
android:layout_width="42dp"
|
||||
android:layout_height="42dp"
|
||||
android:scaleType="centerInside"
|
||||
android:contentDescription="TODO" />
|
||||
<ImageView
|
||||
android:id="@+id/smallIcon"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_marginStart="8dp"
|
||||
tools:src="@drawable/ic_audiotrack_black_24dp"
|
||||
tools:tint="@color/md_black_1000" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/action_next"
|
||||
android:layout_width="42dp"
|
||||
android:layout_height="42dp"
|
||||
android:scaleType="centerInside"
|
||||
android:contentDescription="TODO" />
|
||||
<TextView
|
||||
android:id="@+id/appName"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:ellipsize="end"
|
||||
android:lines="1"
|
||||
android:singleLine="true"
|
||||
android:textSize="13sp"
|
||||
tools:text="@string/app_name" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/action_quit"
|
||||
android:layout_width="42dp"
|
||||
android:layout_height="42dp"
|
||||
android:scaleType="centerInside"
|
||||
android:contentDescription="TODO" />
|
||||
<ImageView
|
||||
android:id="@+id/arrow"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_marginStart="2dp"
|
||||
android:scaleType="centerInside"
|
||||
android:src="@drawable/ic_keyboard_arrow_up_24dp"
|
||||
android:tint="@color/dark_grey"
|
||||
tools:ignore="VectorDrawableCompat" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:lines="1"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="0dp"
|
||||
android:singleLine="true"
|
||||
android:textStyle="bold"
|
||||
tools:text="@tools:sample/lorem/random" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/subtitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:lines="1"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="0dp"
|
||||
android:singleLine="true"
|
||||
tools:text="@tools:sample/lorem/random" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/action_prev"
|
||||
android:layout_width="42dp"
|
||||
android:layout_height="42dp"
|
||||
android:scaleType="centerInside" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/action_play_pause"
|
||||
android:layout_width="42dp"
|
||||
android:layout_height="42dp"
|
||||
android:scaleType="centerInside" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/action_next"
|
||||
android:layout_width="42dp"
|
||||
android:layout_height="42dp"
|
||||
android:scaleType="centerInside" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/action_quit"
|
||||
android:layout_width="42dp"
|
||||
android:layout_height="42dp"
|
||||
android:scaleType="centerInside" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</RelativeLayout>
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
<dimen name="empty_text_size">20sp</dimen>
|
||||
|
||||
<dimen name="notification_big_image_size">134dp</dimen>
|
||||
<dimen name="notification_big_image_size">112dp</dimen>
|
||||
|
||||
<!-- MUST BE THE RESULT OF WIDTH PLUS INSET-->
|
||||
|
||||
|
|
Loading…
Reference in a new issue