My initial commit
Removed Google play dependencies
This commit is contained in:
parent
fd582fff69
commit
301ac10570
430 changed files with 2210 additions and 3137 deletions
|
@ -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 io.github.muntashirakon.music.helper
|
||||
|
||||
import android.content.Context
|
||||
import android.view.ViewGroup
|
||||
import io.github.muntashirakon.music.R
|
||||
|
||||
|
||||
object HorizontalAdapterHelper {
|
||||
|
||||
const val LAYOUT_RES = R.layout.item_image
|
||||
|
||||
private const val TYPE_FIRST = 1
|
||||
private const val TYPE_MIDDLE = 2
|
||||
private const val TYPE_LAST = 3
|
||||
|
||||
fun applyMarginToLayoutParams(
|
||||
context: Context,
|
||||
layoutParams: ViewGroup.MarginLayoutParams, viewType: Int
|
||||
) {
|
||||
val listMargin = context.resources
|
||||
.getDimensionPixelSize(R.dimen.now_playing_top_margin)
|
||||
if (viewType == TYPE_FIRST) {
|
||||
layoutParams.leftMargin = listMargin
|
||||
} else if (viewType == TYPE_LAST) {
|
||||
layoutParams.rightMargin = listMargin
|
||||
}
|
||||
}
|
||||
|
||||
fun getItemViewtype(position: Int, itemCount: Int): Int {
|
||||
return when (position) {
|
||||
0 -> TYPE_FIRST
|
||||
itemCount - 1 -> TYPE_LAST
|
||||
else -> TYPE_MIDDLE
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* 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 io.github.muntashirakon.music.helper;
|
||||
|
||||
public interface M3UConstants {
|
||||
String EXTENSION = "m3u";
|
||||
String HEADER = "#EXTM3U";
|
||||
String ENTRY = "#EXTINF:";
|
||||
String DURATION_SEPARATOR = ",";
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* 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 io.github.muntashirakon.music.helper
|
||||
|
||||
import android.content.Context
|
||||
import io.github.muntashirakon.music.model.Playlist
|
||||
import java.io.BufferedWriter
|
||||
import java.io.File
|
||||
import java.io.FileWriter
|
||||
import java.io.IOException
|
||||
|
||||
object M3UWriter : M3UConstants {
|
||||
@JvmStatic
|
||||
@Throws(IOException::class)
|
||||
fun write(
|
||||
context: Context,
|
||||
dir: File,
|
||||
playlist: Playlist
|
||||
): File? {
|
||||
if (!dir.exists()) dir.mkdirs()
|
||||
val file = File(dir, playlist.name + "." + M3UConstants.EXTENSION)
|
||||
val songs = playlist.getSongs(context)
|
||||
if (songs.size > 0) {
|
||||
val bw = BufferedWriter(FileWriter(file))
|
||||
bw.write(M3UConstants.HEADER)
|
||||
for (song in songs) {
|
||||
bw.newLine()
|
||||
bw.write(M3UConstants.ENTRY + song.duration + M3UConstants.DURATION_SEPARATOR + song.artistName + " - " + song.title)
|
||||
bw.newLine()
|
||||
bw.write(song.data)
|
||||
}
|
||||
bw.close()
|
||||
}
|
||||
return file
|
||||
}
|
||||
}
|
|
@ -0,0 +1,484 @@
|
|||
/*
|
||||
* 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 io.github.muntashirakon.music.helper
|
||||
|
||||
import android.annotation.TargetApi
|
||||
import android.app.Activity
|
||||
import android.content.*
|
||||
import android.database.Cursor
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Environment
|
||||
import android.os.IBinder
|
||||
import android.provider.DocumentsContract
|
||||
import android.provider.MediaStore
|
||||
import android.widget.Toast
|
||||
import androidx.core.content.ContextCompat
|
||||
import io.github.muntashirakon.music.R
|
||||
import io.github.muntashirakon.music.loaders.SongLoader
|
||||
import io.github.muntashirakon.music.model.Song
|
||||
import io.github.muntashirakon.music.service.MusicService
|
||||
|
||||
import io.github.muntashirakon.music.util.PreferenceUtil
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
|
||||
object MusicPlayerRemote {
|
||||
val TAG: String = MusicPlayerRemote::class.java.simpleName
|
||||
private val mConnectionMap = WeakHashMap<Context, ServiceBinder>()
|
||||
var musicService: MusicService? = null
|
||||
|
||||
@JvmStatic
|
||||
val isPlaying: Boolean
|
||||
get() = musicService != null && musicService!!.isPlaying
|
||||
|
||||
fun isPlaying(song: Song): Boolean {
|
||||
return if (!isPlaying) {
|
||||
false
|
||||
} else song.id == currentSong.id
|
||||
}
|
||||
|
||||
val currentSong: Song
|
||||
get() = if (musicService != null) {
|
||||
musicService!!.currentSong
|
||||
} else Song.emptySong
|
||||
|
||||
/**
|
||||
* Async
|
||||
*/
|
||||
var position: Int
|
||||
get() = if (musicService != null) {
|
||||
musicService!!.position
|
||||
} else -1
|
||||
set(position) {
|
||||
if (musicService != null) {
|
||||
musicService!!.position = position
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
val playingQueue: List<Song>
|
||||
get() = if (musicService != null) {
|
||||
musicService?.playingQueue as List<Song>
|
||||
} else listOf<Song>()
|
||||
|
||||
val songProgressMillis: Int
|
||||
get() = if (musicService != null) {
|
||||
musicService!!.songProgressMillis
|
||||
} else -1
|
||||
|
||||
val songDurationMillis: Int
|
||||
get() = if (musicService != null) {
|
||||
musicService!!.songDurationMillis
|
||||
} else -1
|
||||
|
||||
val repeatMode: Int
|
||||
get() = if (musicService != null) {
|
||||
musicService!!.repeatMode
|
||||
} else MusicService.REPEAT_MODE_NONE
|
||||
|
||||
@JvmStatic
|
||||
val shuffleMode: Int
|
||||
get() = if (musicService != null) {
|
||||
musicService!!.shuffleMode
|
||||
} else MusicService.SHUFFLE_MODE_NONE
|
||||
|
||||
val audioSessionId: Int
|
||||
get() = if (musicService != null) {
|
||||
musicService!!.audioSessionId
|
||||
} else -1
|
||||
|
||||
val isServiceConnected: Boolean
|
||||
get() = musicService != null
|
||||
|
||||
fun bindToService(context: Context, callback: ServiceConnection): ServiceToken? {
|
||||
|
||||
var realActivity: Activity? = (context as Activity).parent
|
||||
if (realActivity == null) {
|
||||
realActivity = context
|
||||
}
|
||||
|
||||
val contextWrapper = ContextWrapper(realActivity)
|
||||
val intent = Intent(contextWrapper, MusicService::class.java)
|
||||
try {
|
||||
contextWrapper.startService(intent)
|
||||
} catch (ignored: IllegalStateException) {
|
||||
ContextCompat.startForegroundService(context, intent)
|
||||
}
|
||||
val binder = ServiceBinder(callback)
|
||||
|
||||
if (contextWrapper.bindService(
|
||||
Intent().setClass(contextWrapper, MusicService::class.java),
|
||||
binder,
|
||||
Context.BIND_AUTO_CREATE
|
||||
)
|
||||
) {
|
||||
mConnectionMap[contextWrapper] = binder
|
||||
return ServiceToken(contextWrapper)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun unbindFromService(token: ServiceToken?) {
|
||||
if (token == null) {
|
||||
return
|
||||
}
|
||||
val mContextWrapper = token.mWrappedContext
|
||||
val mBinder = mConnectionMap.remove(mContextWrapper) ?: return
|
||||
mContextWrapper.unbindService(mBinder)
|
||||
if (mConnectionMap.isEmpty()) {
|
||||
musicService = null
|
||||
}
|
||||
}
|
||||
|
||||
private fun getFilePathFromUri(context: Context, uri: Uri): String? {
|
||||
var cursor: Cursor? = null
|
||||
val column = "_data"
|
||||
val projection = arrayOf(column)
|
||||
|
||||
try {
|
||||
cursor = context.contentResolver.query(uri, projection, null, null, null)
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
val columnIndex = cursor.getColumnIndexOrThrow(column)
|
||||
return cursor.getString(columnIndex)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
println(e.message)
|
||||
} finally {
|
||||
cursor?.close()
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun getQueueDurationSongs(): Int {
|
||||
return musicService?.playingQueue?.size ?: -1
|
||||
}
|
||||
|
||||
/**
|
||||
* Async
|
||||
*/
|
||||
fun playSongAt(position: Int) {
|
||||
musicService?.playSongAt(position)
|
||||
}
|
||||
|
||||
fun pauseSong() {
|
||||
musicService?.pause()
|
||||
}
|
||||
|
||||
/**
|
||||
* Async
|
||||
*/
|
||||
fun playNextSong() {
|
||||
musicService?.playNextSong(true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Async
|
||||
*/
|
||||
fun playPreviousSong() {
|
||||
musicService?.playPreviousSong(true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Async
|
||||
*/
|
||||
fun back() {
|
||||
musicService?.back(true)
|
||||
}
|
||||
|
||||
fun resumePlaying() {
|
||||
musicService?.play()
|
||||
}
|
||||
|
||||
/**
|
||||
* Async
|
||||
*/
|
||||
@JvmStatic
|
||||
fun openQueue(queue: List<Song>, startPosition: Int, startPlaying: Boolean) {
|
||||
if (!tryToHandleOpenPlayingQueue(
|
||||
queue,
|
||||
startPosition,
|
||||
startPlaying
|
||||
) && musicService != null
|
||||
) {
|
||||
musicService?.openQueue(queue, startPosition, startPlaying)
|
||||
if (PreferenceUtil.isShuffleModeOn)
|
||||
setShuffleMode(MusicService.SHUFFLE_MODE_NONE)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Async
|
||||
*/
|
||||
@JvmStatic
|
||||
fun openAndShuffleQueue(queue: List<Song>, startPlaying: Boolean) {
|
||||
var startPosition = 0
|
||||
if (queue.isNotEmpty()) {
|
||||
startPosition = Random().nextInt(queue.size)
|
||||
}
|
||||
|
||||
if (!tryToHandleOpenPlayingQueue(
|
||||
queue,
|
||||
startPosition,
|
||||
startPlaying
|
||||
) && musicService != null
|
||||
) {
|
||||
openQueue(queue, startPosition, startPlaying)
|
||||
setShuffleMode(MusicService.SHUFFLE_MODE_SHUFFLE)
|
||||
}
|
||||
}
|
||||
|
||||
private fun tryToHandleOpenPlayingQueue(
|
||||
queue: List<Song>,
|
||||
startPosition: Int,
|
||||
startPlaying: Boolean
|
||||
): Boolean {
|
||||
if (playingQueue === queue) {
|
||||
if (startPlaying) {
|
||||
playSongAt(startPosition)
|
||||
} else {
|
||||
position = startPosition
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun getQueueDurationMillis(position: Int): Long {
|
||||
return if (musicService != null) {
|
||||
musicService!!.getQueueDurationMillis(position)
|
||||
} else -1
|
||||
}
|
||||
|
||||
fun seekTo(millis: Int): Int {
|
||||
return if (musicService != null) {
|
||||
musicService!!.seek(millis)
|
||||
} else -1
|
||||
}
|
||||
|
||||
fun cycleRepeatMode(): Boolean {
|
||||
if (musicService != null) {
|
||||
musicService?.cycleRepeatMode()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun toggleShuffleMode(): Boolean {
|
||||
if (musicService != null) {
|
||||
musicService?.toggleShuffle()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun setShuffleMode(shuffleMode: Int): Boolean {
|
||||
if (musicService != null) {
|
||||
musicService!!.shuffleMode = shuffleMode
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun playNext(song: Song): Boolean {
|
||||
if (musicService != null) {
|
||||
if (playingQueue.size > 0) {
|
||||
musicService?.addSong(position + 1, song)
|
||||
} else {
|
||||
val queue = ArrayList<Song>()
|
||||
queue.add(song)
|
||||
openQueue(queue, 0, false)
|
||||
}
|
||||
Toast.makeText(
|
||||
musicService,
|
||||
musicService!!.resources.getString(R.string.added_title_to_playing_queue),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun playNext(songs: List<Song>): Boolean {
|
||||
if (musicService != null) {
|
||||
if (playingQueue.size > 0) {
|
||||
musicService?.addSongs(position + 1, songs)
|
||||
} else {
|
||||
openQueue(songs, 0, false)
|
||||
}
|
||||
val toast =
|
||||
if (songs.size == 1) musicService!!.resources.getString(R.string.added_title_to_playing_queue) else musicService!!.resources.getString(
|
||||
R.string.added_x_titles_to_playing_queue,
|
||||
songs.size
|
||||
)
|
||||
Toast.makeText(musicService, toast, Toast.LENGTH_SHORT).show()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun enqueue(song: Song): Boolean {
|
||||
if (musicService != null) {
|
||||
if (playingQueue.size > 0) {
|
||||
musicService?.addSong(song)
|
||||
} else {
|
||||
val queue = ArrayList<Song>()
|
||||
queue.add(song)
|
||||
openQueue(queue, 0, false)
|
||||
}
|
||||
Toast.makeText(
|
||||
musicService,
|
||||
musicService!!.resources.getString(R.string.added_title_to_playing_queue),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun enqueue(songs: List<Song>): Boolean {
|
||||
if (musicService != null) {
|
||||
if (playingQueue.size > 0) {
|
||||
musicService?.addSongs(songs)
|
||||
} else {
|
||||
openQueue(songs, 0, false)
|
||||
}
|
||||
val toast =
|
||||
if (songs.size == 1) musicService!!.resources.getString(R.string.added_title_to_playing_queue) else musicService!!.resources.getString(
|
||||
R.string.added_x_titles_to_playing_queue,
|
||||
songs.size
|
||||
)
|
||||
Toast.makeText(musicService, toast, Toast.LENGTH_SHORT).show()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun removeFromQueue(song: Song): Boolean {
|
||||
if (musicService != null) {
|
||||
musicService!!.removeSong(song)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun removeFromQueue(position: Int): Boolean {
|
||||
if (musicService != null && position >= 0 && position < playingQueue.size) {
|
||||
musicService!!.removeSong(position)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun moveSong(from: Int, to: Int): Boolean {
|
||||
if (musicService != null && from >= 0 && to >= 0 && from < playingQueue.size && to < playingQueue.size) {
|
||||
musicService!!.moveSong(from, to)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun clearQueue(): Boolean {
|
||||
if (musicService != null) {
|
||||
musicService!!.clearQueue()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun playFromUri(uri: Uri) {
|
||||
if (musicService != null) {
|
||||
|
||||
var songs: List<Song>? = null
|
||||
if (uri.scheme != null && uri.authority != null) {
|
||||
if (uri.scheme == ContentResolver.SCHEME_CONTENT) {
|
||||
var songId: String? = null
|
||||
if (uri.authority == "com.android.providers.media.documents") {
|
||||
songId = getSongIdFromMediaProvider(uri)
|
||||
} else if (uri.authority == "media") {
|
||||
songId = uri.lastPathSegment
|
||||
}
|
||||
if (songId != null) {
|
||||
songs = SongLoader.getSongs(
|
||||
SongLoader.makeSongCursor(
|
||||
musicService!!,
|
||||
MediaStore.Audio.AudioColumns._ID + "=?",
|
||||
arrayOf(songId)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (songs == null) {
|
||||
var songFile: File? = null
|
||||
if (uri.authority != null && uri.authority == "com.android.externalstorage.documents") {
|
||||
songFile =
|
||||
File(
|
||||
Environment.getExternalStorageDirectory(),
|
||||
uri.path?.split(":".toRegex(), 2)?.get(1)
|
||||
)
|
||||
}
|
||||
if (songFile == null) {
|
||||
val path = getFilePathFromUri(musicService!!, uri)
|
||||
if (path != null)
|
||||
songFile = File(path)
|
||||
}
|
||||
if (songFile == null && uri.path != null) {
|
||||
songFile = File(uri.path)
|
||||
}
|
||||
if (songFile != null) {
|
||||
songs = SongLoader.getSongs(
|
||||
SongLoader.makeSongCursor(
|
||||
musicService!!,
|
||||
MediaStore.Audio.AudioColumns.DATA + "=?",
|
||||
arrayOf(songFile.absolutePath)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
if (songs != null && songs.isNotEmpty()) {
|
||||
openQueue(songs, 0, true)
|
||||
} else {
|
||||
//TODO the file is not listed in the media store
|
||||
println("The file is not listed in the media store")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.KITKAT)
|
||||
private fun getSongIdFromMediaProvider(uri: Uri): String {
|
||||
return DocumentsContract.getDocumentId(uri).split(":".toRegex())
|
||||
.dropLastWhile { it.isEmpty() }.toTypedArray()[1]
|
||||
}
|
||||
|
||||
class ServiceBinder internal constructor(private val mCallback: ServiceConnection?) :
|
||||
ServiceConnection {
|
||||
|
||||
override fun onServiceConnected(className: ComponentName, service: IBinder) {
|
||||
val binder = service as MusicService.MusicBinder
|
||||
musicService = binder.service
|
||||
mCallback?.onServiceConnected(className, service)
|
||||
}
|
||||
|
||||
override fun onServiceDisconnected(className: ComponentName) {
|
||||
mCallback?.onServiceDisconnected(className)
|
||||
musicService = null
|
||||
}
|
||||
}
|
||||
|
||||
class ServiceToken internal constructor(internal var mWrappedContext: ContextWrapper)
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* 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 io.github.muntashirakon.music.helper
|
||||
|
||||
import android.os.Handler
|
||||
import android.os.Message
|
||||
|
||||
class MusicProgressViewUpdateHelper : Handler {
|
||||
|
||||
private var callback: Callback? = null
|
||||
private var intervalPlaying: Int = 0
|
||||
private var intervalPaused: Int = 0
|
||||
|
||||
fun start() {
|
||||
queueNextRefresh(1)
|
||||
}
|
||||
|
||||
fun stop() {
|
||||
removeMessages(CMD_REFRESH_PROGRESS_VIEWS)
|
||||
}
|
||||
|
||||
constructor(callback: Callback) {
|
||||
this.callback = callback
|
||||
this.intervalPlaying = UPDATE_INTERVAL_PLAYING
|
||||
this.intervalPaused = UPDATE_INTERVAL_PAUSED
|
||||
}
|
||||
|
||||
constructor(callback: Callback, intervalPlaying: Int, intervalPaused: Int) {
|
||||
this.callback = callback
|
||||
this.intervalPlaying = intervalPlaying
|
||||
this.intervalPaused = intervalPaused
|
||||
}
|
||||
|
||||
override fun handleMessage(msg: Message) {
|
||||
super.handleMessage(msg)
|
||||
if (msg.what == CMD_REFRESH_PROGRESS_VIEWS) {
|
||||
queueNextRefresh(refreshProgressViews().toLong())
|
||||
}
|
||||
}
|
||||
|
||||
private fun refreshProgressViews(): Int {
|
||||
val progressMillis = MusicPlayerRemote.songProgressMillis
|
||||
val totalMillis = MusicPlayerRemote.songDurationMillis
|
||||
if (totalMillis > 0)
|
||||
callback?.onUpdateProgressViews(progressMillis, totalMillis)
|
||||
|
||||
if (!MusicPlayerRemote.isPlaying) {
|
||||
return intervalPaused
|
||||
}
|
||||
|
||||
val remainingMillis = intervalPlaying - progressMillis % intervalPlaying
|
||||
|
||||
return Math.max(MIN_INTERVAL, remainingMillis)
|
||||
}
|
||||
|
||||
private fun queueNextRefresh(delay: Long) {
|
||||
val message = obtainMessage(CMD_REFRESH_PROGRESS_VIEWS)
|
||||
removeMessages(CMD_REFRESH_PROGRESS_VIEWS)
|
||||
sendMessageDelayed(message, delay)
|
||||
}
|
||||
|
||||
interface Callback {
|
||||
fun onUpdateProgressViews(progress: Int, total: Int)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val CMD_REFRESH_PROGRESS_VIEWS = 1
|
||||
private const val MIN_INTERVAL = 20
|
||||
private const val UPDATE_INTERVAL_PLAYING = 1000
|
||||
private const val UPDATE_INTERVAL_PAUSED = 500
|
||||
}
|
||||
}
|
|
@ -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 io.github.muntashirakon.music.helper
|
||||
|
||||
import android.view.View
|
||||
|
||||
|
||||
class PlayPauseButtonOnClickHandler : View.OnClickListener {
|
||||
override fun onClick(v: View) {
|
||||
if (MusicPlayerRemote.isPlaying) {
|
||||
MusicPlayerRemote.pauseSong()
|
||||
} else {
|
||||
MusicPlayerRemote.resumePlaying()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,149 @@
|
|||
/*
|
||||
* 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 io.github.muntashirakon.music.helper
|
||||
|
||||
import android.app.SearchManager
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.provider.MediaStore
|
||||
import io.github.muntashirakon.music.loaders.SongLoader
|
||||
import io.github.muntashirakon.music.model.Song
|
||||
import java.util.*
|
||||
|
||||
object SearchQueryHelper {
|
||||
private const val TITLE_SELECTION = "lower(" + MediaStore.Audio.AudioColumns.TITLE + ") = ?"
|
||||
private const val ALBUM_SELECTION = "lower(" + MediaStore.Audio.AudioColumns.ALBUM + ") = ?"
|
||||
private const val ARTIST_SELECTION = "lower(" + MediaStore.Audio.AudioColumns.ARTIST + ") = ?"
|
||||
private const val AND = " AND "
|
||||
var songs = ArrayList<Song>()
|
||||
|
||||
@JvmStatic
|
||||
fun getSongs(context: Context, extras: Bundle): ArrayList<Song> {
|
||||
val query = extras.getString(SearchManager.QUERY, null)
|
||||
val artistName = extras.getString(MediaStore.EXTRA_MEDIA_ARTIST, null)
|
||||
val albumName = extras.getString(MediaStore.EXTRA_MEDIA_ALBUM, null)
|
||||
val titleName = extras.getString(MediaStore.EXTRA_MEDIA_TITLE, null)
|
||||
|
||||
var songs = ArrayList<Song>()
|
||||
if (artistName != null && albumName != null && titleName != null) {
|
||||
songs = SongLoader.getSongs(
|
||||
SongLoader.makeSongCursor(
|
||||
context,
|
||||
ARTIST_SELECTION + AND + ALBUM_SELECTION + AND + TITLE_SELECTION,
|
||||
arrayOf(
|
||||
artistName.toLowerCase(),
|
||||
albumName.toLowerCase(),
|
||||
titleName.toLowerCase()
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
if (songs.isNotEmpty()) {
|
||||
return songs
|
||||
}
|
||||
if (artistName != null && titleName != null) {
|
||||
songs = SongLoader.getSongs(
|
||||
SongLoader.makeSongCursor(
|
||||
context,
|
||||
ARTIST_SELECTION + AND + TITLE_SELECTION,
|
||||
arrayOf(artistName.toLowerCase(), titleName.toLowerCase())
|
||||
)
|
||||
)
|
||||
}
|
||||
if (songs.isNotEmpty()) {
|
||||
return songs
|
||||
}
|
||||
if (albumName != null && titleName != null) {
|
||||
songs = SongLoader.getSongs(
|
||||
SongLoader.makeSongCursor(
|
||||
context,
|
||||
ALBUM_SELECTION + AND + TITLE_SELECTION,
|
||||
arrayOf(albumName.toLowerCase(), titleName.toLowerCase())
|
||||
)
|
||||
)
|
||||
}
|
||||
if (songs.isNotEmpty()) {
|
||||
return songs
|
||||
}
|
||||
if (artistName != null) {
|
||||
songs = SongLoader.getSongs(
|
||||
SongLoader.makeSongCursor(
|
||||
context,
|
||||
ARTIST_SELECTION,
|
||||
arrayOf(artistName.toLowerCase())
|
||||
)
|
||||
)
|
||||
}
|
||||
if (songs.isNotEmpty()) {
|
||||
return songs
|
||||
}
|
||||
if (albumName != null) {
|
||||
songs = SongLoader.getSongs(
|
||||
SongLoader.makeSongCursor(
|
||||
context,
|
||||
ALBUM_SELECTION,
|
||||
arrayOf(albumName.toLowerCase())
|
||||
)
|
||||
)
|
||||
}
|
||||
if (songs.isNotEmpty()) {
|
||||
return songs
|
||||
}
|
||||
if (titleName != null) {
|
||||
songs = SongLoader.getSongs(
|
||||
SongLoader.makeSongCursor(
|
||||
context,
|
||||
TITLE_SELECTION,
|
||||
arrayOf(titleName.toLowerCase())
|
||||
)
|
||||
)
|
||||
}
|
||||
if (songs.isNotEmpty()) {
|
||||
return songs
|
||||
}
|
||||
songs =
|
||||
SongLoader.getSongs(
|
||||
SongLoader.makeSongCursor(
|
||||
context,
|
||||
ARTIST_SELECTION,
|
||||
arrayOf(query.toLowerCase())
|
||||
)
|
||||
)
|
||||
|
||||
if (songs.isNotEmpty()) {
|
||||
return songs
|
||||
}
|
||||
songs = SongLoader.getSongs(
|
||||
SongLoader.makeSongCursor(
|
||||
context,
|
||||
ALBUM_SELECTION,
|
||||
arrayOf(query.toLowerCase())
|
||||
)
|
||||
)
|
||||
if (songs.isNotEmpty()) {
|
||||
return songs
|
||||
}
|
||||
songs = SongLoader.getSongs(
|
||||
SongLoader.makeSongCursor(
|
||||
context,
|
||||
TITLE_SELECTION,
|
||||
arrayOf(query.toLowerCase())
|
||||
)
|
||||
)
|
||||
return if (songs.isNotEmpty()) {
|
||||
songs
|
||||
} else ArrayList()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* 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 io.github.muntashirakon.music.helper
|
||||
|
||||
import io.github.muntashirakon.music.model.Song
|
||||
|
||||
|
||||
object ShuffleHelper {
|
||||
|
||||
fun makeShuffleList(listToShuffle: MutableList<Song>, current: Int) {
|
||||
if (listToShuffle.isEmpty()) return
|
||||
if (current >= 0) {
|
||||
val song = listToShuffle.removeAt(current)
|
||||
listToShuffle.shuffle()
|
||||
listToShuffle.add(0, song)
|
||||
} else {
|
||||
listToShuffle.shuffle()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,186 @@
|
|||
/*
|
||||
* 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 io.github.muntashirakon.music.helper
|
||||
|
||||
import android.provider.MediaStore
|
||||
|
||||
class SortOrder {
|
||||
|
||||
/**
|
||||
* Artist sort order entries.
|
||||
*/
|
||||
interface ArtistSortOrder {
|
||||
|
||||
companion object {
|
||||
|
||||
/* Artist sort order A-Z */
|
||||
const val ARTIST_A_Z = MediaStore.Audio.Artists.DEFAULT_SORT_ORDER
|
||||
|
||||
/* Artist sort order Z-A */
|
||||
const val ARTIST_Z_A = "$ARTIST_A_Z DESC"
|
||||
|
||||
/* Artist sort order number of songs */
|
||||
const val ARTIST_NUMBER_OF_SONGS = MediaStore.Audio.Artists.NUMBER_OF_TRACKS + " DESC"
|
||||
|
||||
/* Artist sort order number of albums */
|
||||
const val ARTIST_NUMBER_OF_ALBUMS = MediaStore.Audio.Artists.NUMBER_OF_ALBUMS + " DESC"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Album sort order entries.
|
||||
*/
|
||||
interface AlbumSortOrder {
|
||||
|
||||
companion object {
|
||||
|
||||
/* Album sort order A-Z */
|
||||
const val ALBUM_A_Z = MediaStore.Audio.Albums.DEFAULT_SORT_ORDER
|
||||
|
||||
/* Album sort order Z-A */
|
||||
const val ALBUM_Z_A = "$ALBUM_A_Z DESC"
|
||||
|
||||
/* Album sort order songs */
|
||||
const val ALBUM_NUMBER_OF_SONGS = MediaStore.Audio.Albums.NUMBER_OF_SONGS + " DESC"
|
||||
|
||||
/* Album sort order artist */
|
||||
const val ALBUM_ARTIST = (MediaStore.Audio.Artists.DEFAULT_SORT_ORDER
|
||||
+ ", " + MediaStore.Audio.Albums.DEFAULT_SORT_ORDER)
|
||||
|
||||
/* Album sort order year */
|
||||
const val ALBUM_YEAR = MediaStore.Audio.Media.YEAR + " DESC"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Song sort order entries.
|
||||
*/
|
||||
interface SongSortOrder {
|
||||
|
||||
companion object {
|
||||
|
||||
/* Song sort order A-Z */
|
||||
const val SONG_A_Z = MediaStore.Audio.Media.DEFAULT_SORT_ORDER
|
||||
|
||||
/* Song sort order Z-A */
|
||||
const val SONG_Z_A = "$SONG_A_Z DESC"
|
||||
|
||||
/* Song sort order artist */
|
||||
const val SONG_ARTIST = MediaStore.Audio.Artists.DEFAULT_SORT_ORDER
|
||||
|
||||
/* Song sort order album */
|
||||
const val SONG_ALBUM = MediaStore.Audio.Albums.DEFAULT_SORT_ORDER
|
||||
|
||||
/* Song sort order year */
|
||||
const val SONG_YEAR = MediaStore.Audio.Media.YEAR + " DESC"
|
||||
|
||||
/* Song sort order duration */
|
||||
const val SONG_DURATION = MediaStore.Audio.Media.DURATION + " DESC"
|
||||
|
||||
/* Song sort order date */
|
||||
const val SONG_DATE = MediaStore.Audio.Media.DATE_ADDED + " DESC"
|
||||
|
||||
/* Song sort modified date */
|
||||
const val SONG_DATE_MODIFIED = MediaStore.Audio.Media.DATE_MODIFIED + " DESC"
|
||||
|
||||
/* Song sort order composer*/
|
||||
const val COMPOSER = MediaStore.Audio.Media.COMPOSER
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Album song sort order entries.
|
||||
*/
|
||||
interface AlbumSongSortOrder {
|
||||
|
||||
companion object {
|
||||
|
||||
/* Album song sort order A-Z */
|
||||
const val SONG_A_Z = MediaStore.Audio.Media.DEFAULT_SORT_ORDER
|
||||
|
||||
/* Album song sort order Z-A */
|
||||
const val SONG_Z_A = "$SONG_A_Z DESC"
|
||||
|
||||
/* Album song sort order track list */
|
||||
const val SONG_TRACK_LIST = (MediaStore.Audio.Media.TRACK + ", "
|
||||
+ MediaStore.Audio.Media.DEFAULT_SORT_ORDER)
|
||||
|
||||
/* Album song sort order duration */
|
||||
const val SONG_DURATION = SongSortOrder.SONG_DURATION
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Artist song sort order entries.
|
||||
*/
|
||||
interface ArtistSongSortOrder {
|
||||
|
||||
companion object {
|
||||
|
||||
/* Artist song sort order A-Z */
|
||||
const val SONG_A_Z = MediaStore.Audio.Media.DEFAULT_SORT_ORDER
|
||||
|
||||
/* Artist song sort order Z-A */
|
||||
const val SONG_Z_A = "$SONG_A_Z DESC"
|
||||
|
||||
/* Artist song sort order album */
|
||||
const val SONG_ALBUM = MediaStore.Audio.Media.ALBUM
|
||||
|
||||
/* Artist song sort order year */
|
||||
const val SONG_YEAR = MediaStore.Audio.Media.YEAR + " DESC"
|
||||
|
||||
/* Artist song sort order duration */
|
||||
const val SONG_DURATION = MediaStore.Audio.Media.DURATION + " DESC"
|
||||
|
||||
/* Artist song sort order date */
|
||||
const val SONG_DATE = MediaStore.Audio.Media.DATE_ADDED + " DESC"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Artist album sort order entries.
|
||||
*/
|
||||
interface ArtistAlbumSortOrder {
|
||||
|
||||
companion object {
|
||||
|
||||
/* Artist album sort order A-Z */
|
||||
const val ALBUM_A_Z = MediaStore.Audio.Albums.DEFAULT_SORT_ORDER
|
||||
|
||||
/* Artist album sort order Z-A */
|
||||
const val ALBUM_Z_A = "$ALBUM_A_Z DESC"
|
||||
|
||||
/* Artist album sort order year */
|
||||
const val ALBUM_YEAR = MediaStore.Audio.Media.YEAR + " DESC"
|
||||
|
||||
/* Artist album sort order year */
|
||||
const val ALBUM_YEAR_ASC = MediaStore.Audio.Media.YEAR + " ASC"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Genre sort order entries.
|
||||
*/
|
||||
interface GenreSortOrder {
|
||||
|
||||
companion object {
|
||||
|
||||
/* Genre sort order A-Z */
|
||||
const val GENRE_A_Z = MediaStore.Audio.Genres.DEFAULT_SORT_ORDER
|
||||
|
||||
/* Genre sort order Z-A */
|
||||
const val ALBUM_Z_A = "$GENRE_A_Z DESC"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,333 @@
|
|||
package io.github.muntashirakon.music.helper;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
/**
|
||||
* Blur using Java code.
|
||||
* <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 remove the leftmost color. The remaining
|
||||
* colors on the topmost layer of the stack are either added on
|
||||
* or reduced by one, depending on if they are on the right or
|
||||
* on the left side of the stack.
|
||||
*
|
||||
* @author Enrique López Mañas <eenriquelopez@gmail.com>
|
||||
* http://www.neo-tech.es
|
||||
* <p/>
|
||||
* Author of the original algorithm: Mario Klingemann <mario.quasimondo.com>
|
||||
* <p/>
|
||||
* Based heavily on http://vitiy.info/Code/stackblur.cpp
|
||||
* See http://vitiy.info/stackblur-algorithm-multi-threaded-blur-for-cpp/
|
||||
* @copyright: Enrique López Mañas
|
||||
* @license: Apache License 2.0
|
||||
*/
|
||||
public class StackBlur {
|
||||
|
||||
static final int EXECUTOR_THREADS = Runtime.getRuntime().availableProcessors();
|
||||
static final ExecutorService EXECUTOR = Executors.newFixedThreadPool(EXECUTOR_THREADS);
|
||||
|
||||
private static final short[] stackblur_mul = {
|
||||
512, 512, 456, 512, 328, 456, 335, 512, 405, 328, 271, 456, 388, 335, 292, 512,
|
||||
454, 405, 364, 328, 298, 271, 496, 456, 420, 388, 360, 335, 312, 292, 273, 512,
|
||||
482, 454, 428, 405, 383, 364, 345, 328, 312, 298, 284, 271, 259, 496, 475, 456,
|
||||
437, 420, 404, 388, 374, 360, 347, 335, 323, 312, 302, 292, 282, 273, 265, 512,
|
||||
497, 482, 468, 454, 441, 428, 417, 405, 394, 383, 373, 364, 354, 345, 337, 328,
|
||||
320, 312, 305, 298, 291, 284, 278, 271, 265, 259, 507, 496, 485, 475, 465, 456,
|
||||
446, 437, 428, 420, 412, 404, 396, 388, 381, 374, 367, 360, 354, 347, 341, 335,
|
||||
329, 323, 318, 312, 307, 302, 297, 292, 287, 282, 278, 273, 269, 265, 261, 512,
|
||||
505, 497, 489, 482, 475, 468, 461, 454, 447, 441, 435, 428, 422, 417, 411, 405,
|
||||
399, 394, 389, 383, 378, 373, 368, 364, 359, 354, 350, 345, 341, 337, 332, 328,
|
||||
324, 320, 316, 312, 309, 305, 301, 298, 294, 291, 287, 284, 281, 278, 274, 271,
|
||||
268, 265, 262, 259, 257, 507, 501, 496, 491, 485, 480, 475, 470, 465, 460, 456,
|
||||
451, 446, 442, 437, 433, 428, 424, 420, 416, 412, 408, 404, 400, 396, 392, 388,
|
||||
385, 381, 377, 374, 370, 367, 363, 360, 357, 354, 350, 347, 344, 341, 338, 335,
|
||||
332, 329, 326, 323, 320, 318, 315, 312, 310, 307, 304, 302, 299, 297, 294, 292,
|
||||
289, 287, 285, 282, 280, 278, 275, 273, 271, 269, 267, 265, 263, 261, 259
|
||||
};
|
||||
|
||||
private static final byte[] stackblur_shr = {
|
||||
9, 11, 12, 13, 13, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17,
|
||||
17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19,
|
||||
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20,
|
||||
20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21,
|
||||
21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
|
||||
21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22,
|
||||
22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
|
||||
22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23,
|
||||
23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
|
||||
23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
|
||||
23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
|
||||
23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
|
||||
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
|
||||
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
|
||||
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
|
||||
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24
|
||||
};
|
||||
|
||||
public static Bitmap blur(Bitmap original, float radius) {
|
||||
int w = original.getWidth();
|
||||
int h = original.getHeight();
|
||||
int[] currentPixels = new int[w * h];
|
||||
original.getPixels(currentPixels, 0, w, 0, 0, w, h);
|
||||
int cores = EXECUTOR_THREADS;
|
||||
|
||||
ArrayList<BlurTask> horizontal = new ArrayList<BlurTask>(cores);
|
||||
ArrayList<BlurTask> vertical = new ArrayList<BlurTask>(cores);
|
||||
for (int i = 0; i < cores; i++) {
|
||||
horizontal.add(new BlurTask(currentPixels, w, h, (int) radius, cores, i, 1));
|
||||
vertical.add(new BlurTask(currentPixels, w, h, (int) radius, cores, i, 2));
|
||||
}
|
||||
|
||||
try {
|
||||
EXECUTOR.invokeAll(horizontal);
|
||||
} catch (InterruptedException e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
EXECUTOR.invokeAll(vertical);
|
||||
} catch (InterruptedException e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Bitmap.createBitmap(currentPixels, w, h, Bitmap.Config.ARGB_8888);
|
||||
}
|
||||
|
||||
private static void blurIteration(int[] src, int w, int h, int radius, int cores, int core, int step) {
|
||||
int x, y, xp, yp, i;
|
||||
int sp;
|
||||
int stack_start;
|
||||
int stack_i;
|
||||
|
||||
int src_i;
|
||||
int dst_i;
|
||||
|
||||
long sum_r, sum_g, sum_b,
|
||||
sum_in_r, sum_in_g, sum_in_b,
|
||||
sum_out_r, sum_out_g, sum_out_b;
|
||||
|
||||
int wm = w - 1;
|
||||
int hm = h - 1;
|
||||
int div = (radius * 2) + 1;
|
||||
int mul_sum = stackblur_mul[radius];
|
||||
byte shr_sum = stackblur_shr[radius];
|
||||
int[] stack = new int[div];
|
||||
|
||||
if (step == 1) {
|
||||
int minY = core * h / cores;
|
||||
int maxY = (core + 1) * h / cores;
|
||||
|
||||
for (y = minY; y < maxY; y++) {
|
||||
sum_r = sum_g = sum_b =
|
||||
sum_in_r = sum_in_g = sum_in_b =
|
||||
sum_out_r = sum_out_g = sum_out_b = 0;
|
||||
|
||||
src_i = w * y; // start of line (0,y)
|
||||
|
||||
for (i = 0; i <= radius; i++) {
|
||||
stack_i = i;
|
||||
stack[stack_i] = src[src_i];
|
||||
sum_r += ((src[src_i] >>> 16) & 0xff) * (i + 1);
|
||||
sum_g += ((src[src_i] >>> 8) & 0xff) * (i + 1);
|
||||
sum_b += (src[src_i] & 0xff) * (i + 1);
|
||||
sum_out_r += ((src[src_i] >>> 16) & 0xff);
|
||||
sum_out_g += ((src[src_i] >>> 8) & 0xff);
|
||||
sum_out_b += (src[src_i] & 0xff);
|
||||
}
|
||||
|
||||
|
||||
for (i = 1; i <= radius; i++) {
|
||||
if (i <= wm) src_i += 1;
|
||||
stack_i = i + radius;
|
||||
stack[stack_i] = src[src_i];
|
||||
sum_r += ((src[src_i] >>> 16) & 0xff) * (radius + 1 - i);
|
||||
sum_g += ((src[src_i] >>> 8) & 0xff) * (radius + 1 - i);
|
||||
sum_b += (src[src_i] & 0xff) * (radius + 1 - i);
|
||||
sum_in_r += ((src[src_i] >>> 16) & 0xff);
|
||||
sum_in_g += ((src[src_i] >>> 8) & 0xff);
|
||||
sum_in_b += (src[src_i] & 0xff);
|
||||
}
|
||||
|
||||
|
||||
sp = radius;
|
||||
xp = radius;
|
||||
if (xp > wm) xp = wm;
|
||||
src_i = xp + y * w; // img.pix_ptr(xp, y);
|
||||
dst_i = y * w; // img.pix_ptr(0, y);
|
||||
for (x = 0; x < w; x++) {
|
||||
src[dst_i] = (int)
|
||||
((src[dst_i] & 0xFFFFFFFF) |
|
||||
((((sum_r * mul_sum) >>> shr_sum) & 0xff) << 16) |
|
||||
((((sum_g * mul_sum) >>> shr_sum) & 0xff) << 8) |
|
||||
((((sum_b * mul_sum) >>> shr_sum) & 0xff)));
|
||||
dst_i += 1;
|
||||
|
||||
sum_r -= sum_out_r;
|
||||
sum_g -= sum_out_g;
|
||||
sum_b -= sum_out_b;
|
||||
|
||||
stack_start = sp + div - radius;
|
||||
if (stack_start >= div) stack_start -= div;
|
||||
stack_i = stack_start;
|
||||
|
||||
sum_out_r -= ((stack[stack_i] >>> 16) & 0xff);
|
||||
sum_out_g -= ((stack[stack_i] >>> 8) & 0xff);
|
||||
sum_out_b -= (stack[stack_i] & 0xff);
|
||||
|
||||
if (xp < wm) {
|
||||
src_i += 1;
|
||||
++xp;
|
||||
}
|
||||
|
||||
stack[stack_i] = src[src_i];
|
||||
|
||||
sum_in_r += ((src[src_i] >>> 16) & 0xff);
|
||||
sum_in_g += ((src[src_i] >>> 8) & 0xff);
|
||||
sum_in_b += (src[src_i] & 0xff);
|
||||
sum_r += sum_in_r;
|
||||
sum_g += sum_in_g;
|
||||
sum_b += sum_in_b;
|
||||
|
||||
++sp;
|
||||
if (sp >= div) sp = 0;
|
||||
stack_i = sp;
|
||||
|
||||
sum_out_r += ((stack[stack_i] >>> 16) & 0xff);
|
||||
sum_out_g += ((stack[stack_i] >>> 8) & 0xff);
|
||||
sum_out_b += (stack[stack_i] & 0xff);
|
||||
sum_in_r -= ((stack[stack_i] >>> 16) & 0xff);
|
||||
sum_in_g -= ((stack[stack_i] >>> 8) & 0xff);
|
||||
sum_in_b -= (stack[stack_i] & 0xff);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// step 2
|
||||
else if (step == 2) {
|
||||
int minX = core * w / cores;
|
||||
int maxX = (core + 1) * w / cores;
|
||||
|
||||
for (x = minX; x < maxX; x++) {
|
||||
sum_r = sum_g = sum_b =
|
||||
sum_in_r = sum_in_g = sum_in_b =
|
||||
sum_out_r = sum_out_g = sum_out_b = 0;
|
||||
|
||||
src_i = x; // x,0
|
||||
for (i = 0; i <= radius; i++) {
|
||||
stack_i = i;
|
||||
stack[stack_i] = src[src_i];
|
||||
sum_r += ((src[src_i] >>> 16) & 0xff) * (i + 1);
|
||||
sum_g += ((src[src_i] >>> 8) & 0xff) * (i + 1);
|
||||
sum_b += (src[src_i] & 0xff) * (i + 1);
|
||||
sum_out_r += ((src[src_i] >>> 16) & 0xff);
|
||||
sum_out_g += ((src[src_i] >>> 8) & 0xff);
|
||||
sum_out_b += (src[src_i] & 0xff);
|
||||
}
|
||||
for (i = 1; i <= radius; i++) {
|
||||
if (i <= hm) src_i += w; // +stride
|
||||
|
||||
stack_i = i + radius;
|
||||
stack[stack_i] = src[src_i];
|
||||
sum_r += ((src[src_i] >>> 16) & 0xff) * (radius + 1 - i);
|
||||
sum_g += ((src[src_i] >>> 8) & 0xff) * (radius + 1 - i);
|
||||
sum_b += (src[src_i] & 0xff) * (radius + 1 - i);
|
||||
sum_in_r += ((src[src_i] >>> 16) & 0xff);
|
||||
sum_in_g += ((src[src_i] >>> 8) & 0xff);
|
||||
sum_in_b += (src[src_i] & 0xff);
|
||||
}
|
||||
|
||||
sp = radius;
|
||||
yp = radius;
|
||||
if (yp > hm) yp = hm;
|
||||
src_i = x + yp * w; // img.pix_ptr(x, yp);
|
||||
dst_i = x; // img.pix_ptr(x, 0);
|
||||
for (y = 0; y < h; y++) {
|
||||
src[dst_i] = (int)
|
||||
((src[dst_i] & 0xFFFFFFFF) |
|
||||
((((sum_r * mul_sum) >>> shr_sum) & 0xff) << 16) |
|
||||
((((sum_g * mul_sum) >>> shr_sum) & 0xff) << 8) |
|
||||
((((sum_b * mul_sum) >>> shr_sum) & 0xff)));
|
||||
dst_i += w;
|
||||
|
||||
sum_r -= sum_out_r;
|
||||
sum_g -= sum_out_g;
|
||||
sum_b -= sum_out_b;
|
||||
|
||||
stack_start = sp + div - radius;
|
||||
if (stack_start >= div) stack_start -= div;
|
||||
stack_i = stack_start;
|
||||
|
||||
sum_out_r -= ((stack[stack_i] >>> 16) & 0xff);
|
||||
sum_out_g -= ((stack[stack_i] >>> 8) & 0xff);
|
||||
sum_out_b -= (stack[stack_i] & 0xff);
|
||||
|
||||
if (yp < hm) {
|
||||
src_i += w; // stride
|
||||
++yp;
|
||||
}
|
||||
|
||||
stack[stack_i] = src[src_i];
|
||||
|
||||
sum_in_r += ((src[src_i] >>> 16) & 0xff);
|
||||
sum_in_g += ((src[src_i] >>> 8) & 0xff);
|
||||
sum_in_b += (src[src_i] & 0xff);
|
||||
sum_r += sum_in_r;
|
||||
sum_g += sum_in_g;
|
||||
sum_b += sum_in_b;
|
||||
|
||||
++sp;
|
||||
if (sp >= div) sp = 0;
|
||||
stack_i = sp;
|
||||
|
||||
sum_out_r += ((stack[stack_i] >>> 16) & 0xff);
|
||||
sum_out_g += ((stack[stack_i] >>> 8) & 0xff);
|
||||
sum_out_b += (stack[stack_i] & 0xff);
|
||||
sum_in_r -= ((stack[stack_i] >>> 16) & 0xff);
|
||||
sum_in_g -= ((stack[stack_i] >>> 8) & 0xff);
|
||||
sum_in_b -= (stack[stack_i] & 0xff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class BlurTask implements Callable<Void> {
|
||||
private final int[] _src;
|
||||
private final int _w;
|
||||
private final int _h;
|
||||
private final int _radius;
|
||||
private final int _totalCores;
|
||||
private final int _coreIndex;
|
||||
private final int _round;
|
||||
|
||||
public BlurTask(int[] src, int w, int h, int radius, int totalCores, int coreIndex, int round) {
|
||||
_src = src;
|
||||
_w = w;
|
||||
_h = h;
|
||||
_radius = radius;
|
||||
_totalCores = totalCores;
|
||||
_coreIndex = coreIndex;
|
||||
_round = round;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void call() throws Exception {
|
||||
blurIteration(_src, _w, _h, _radius, _totalCores, _coreIndex, _round);
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* 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 io.github.muntashirakon.music.helper
|
||||
|
||||
/**
|
||||
* Simple thread safe stop watch.
|
||||
*
|
||||
* @author Karim Abou Zeid (kabouzeid)
|
||||
*/
|
||||
class StopWatch {
|
||||
|
||||
/**
|
||||
* The time the stop watch was last started.
|
||||
*/
|
||||
private var startTime: Long = 0
|
||||
|
||||
/**
|
||||
* The time elapsed before the current [.startTime].
|
||||
*/
|
||||
private var previousElapsedTime: Long = 0
|
||||
|
||||
/**
|
||||
* Whether the stop watch is currently running or not.
|
||||
*/
|
||||
private var isRunning: Boolean = false
|
||||
|
||||
/**
|
||||
* @return the total elapsed time in milliseconds
|
||||
*/
|
||||
val elapsedTime: Long
|
||||
get() = synchronized(this) {
|
||||
var currentElapsedTime: Long = 0
|
||||
if (isRunning) {
|
||||
currentElapsedTime = System.currentTimeMillis() - startTime
|
||||
}
|
||||
return previousElapsedTime + currentElapsedTime
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts or continues the stop watch.
|
||||
*
|
||||
* @see .pause
|
||||
* @see .reset
|
||||
*/
|
||||
fun start() {
|
||||
synchronized(this) {
|
||||
startTime = System.currentTimeMillis()
|
||||
isRunning = true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pauses the stop watch. It can be continued later from [.start].
|
||||
*
|
||||
* @see .start
|
||||
* @see .reset
|
||||
*/
|
||||
fun pause() {
|
||||
synchronized(this) {
|
||||
previousElapsedTime += System.currentTimeMillis() - startTime
|
||||
isRunning = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops and resets the stop watch to zero milliseconds.
|
||||
*
|
||||
* @see .start
|
||||
* @see .pause
|
||||
*/
|
||||
fun reset() {
|
||||
synchronized(this) {
|
||||
startTime = 0
|
||||
previousElapsedTime = 0
|
||||
isRunning = false
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return String.format("%d millis", elapsedTime)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* 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 io.github.muntashirakon.music.helper.menu
|
||||
|
||||
import android.app.Activity
|
||||
import android.view.MenuItem
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
|
||||
import io.github.muntashirakon.music.R
|
||||
import io.github.muntashirakon.music.dialogs.AddToPlaylistDialog
|
||||
import io.github.muntashirakon.music.helper.MusicPlayerRemote
|
||||
import io.github.muntashirakon.music.loaders.GenreLoader
|
||||
import io.github.muntashirakon.music.model.Genre
|
||||
import io.github.muntashirakon.music.model.Song
|
||||
import java.util.*
|
||||
|
||||
object GenreMenuHelper {
|
||||
fun handleMenuClick(activity: AppCompatActivity, genre: Genre, item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
R.id.action_play -> {
|
||||
MusicPlayerRemote.openQueue(getGenreSongs(activity, genre), 0, true)
|
||||
return true
|
||||
}
|
||||
R.id.action_play_next -> {
|
||||
MusicPlayerRemote.playNext(getGenreSongs(activity, genre))
|
||||
return true
|
||||
}
|
||||
R.id.action_add_to_playlist -> {
|
||||
AddToPlaylistDialog.create(getGenreSongs(activity, genre))
|
||||
.show(activity.supportFragmentManager, "ADD_PLAYLIST")
|
||||
return true
|
||||
}
|
||||
R.id.action_add_to_current_playing -> {
|
||||
MusicPlayerRemote.enqueue(getGenreSongs(activity, genre))
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun getGenreSongs(activity: Activity, genre: Genre): ArrayList<Song> {
|
||||
return GenreLoader.getSongs(activity, genre.id)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* 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 io.github.muntashirakon.music.helper.menu
|
||||
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.view.MenuItem
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import io.github.muntashirakon.music.App
|
||||
import io.github.muntashirakon.music.R
|
||||
import io.github.muntashirakon.music.dialogs.AddToPlaylistDialog
|
||||
import io.github.muntashirakon.music.dialogs.DeletePlaylistDialog
|
||||
import io.github.muntashirakon.music.dialogs.RenamePlaylistDialog
|
||||
import io.github.muntashirakon.music.helper.MusicPlayerRemote
|
||||
import io.github.muntashirakon.music.loaders.PlaylistSongsLoader
|
||||
import io.github.muntashirakon.music.misc.WeakContextAsyncTask
|
||||
import io.github.muntashirakon.music.model.AbsCustomPlaylist
|
||||
import io.github.muntashirakon.music.model.Playlist
|
||||
import io.github.muntashirakon.music.model.Song
|
||||
import io.github.muntashirakon.music.util.PlaylistsUtil
|
||||
import java.util.*
|
||||
|
||||
|
||||
object PlaylistMenuHelper {
|
||||
|
||||
fun handleMenuClick(
|
||||
activity: AppCompatActivity,
|
||||
playlist: Playlist, item: MenuItem
|
||||
): Boolean {
|
||||
when (item.itemId) {
|
||||
R.id.action_play -> {
|
||||
MusicPlayerRemote.openQueue(getPlaylistSongs(activity, playlist), 9, true)
|
||||
return true
|
||||
}
|
||||
R.id.action_play_next -> {
|
||||
MusicPlayerRemote.playNext(getPlaylistSongs(activity, playlist))
|
||||
return true
|
||||
}
|
||||
R.id.action_add_to_playlist -> {
|
||||
AddToPlaylistDialog.create(getPlaylistSongs(activity, playlist))
|
||||
.show(activity.supportFragmentManager, "ADD_PLAYLIST")
|
||||
return true
|
||||
}
|
||||
R.id.action_add_to_current_playing -> {
|
||||
MusicPlayerRemote.enqueue(getPlaylistSongs(activity, playlist))
|
||||
return true
|
||||
}
|
||||
R.id.action_rename_playlist -> {
|
||||
RenamePlaylistDialog.create(playlist.id.toLong())
|
||||
.show(activity.supportFragmentManager, "RENAME_PLAYLIST")
|
||||
return true
|
||||
}
|
||||
R.id.action_delete_playlist -> {
|
||||
DeletePlaylistDialog.create(playlist)
|
||||
.show(activity.supportFragmentManager, "DELETE_PLAYLIST")
|
||||
return true
|
||||
}
|
||||
R.id.action_save_playlist -> {
|
||||
SavePlaylistAsyncTask(activity).execute(playlist)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun getPlaylistSongs(
|
||||
activity: Activity,
|
||||
playlist: Playlist
|
||||
): ArrayList<Song> {
|
||||
return if (playlist is AbsCustomPlaylist) {
|
||||
playlist.getSongs(activity)
|
||||
} else {
|
||||
PlaylistSongsLoader.getPlaylistSongList(activity, playlist)
|
||||
}
|
||||
}
|
||||
|
||||
private class SavePlaylistAsyncTask internal constructor(context: Context) :
|
||||
WeakContextAsyncTask<Playlist, String, String>(context) {
|
||||
|
||||
override fun doInBackground(vararg params: Playlist): String {
|
||||
return String.format(
|
||||
App.getContext().getString(
|
||||
R.string
|
||||
.saved_playlist_to
|
||||
), PlaylistsUtil.savePlaylist(App.getContext(), params[0])
|
||||
)
|
||||
}
|
||||
|
||||
override fun onPostExecute(string: String) {
|
||||
super.onPostExecute(string)
|
||||
val context = context
|
||||
if (context != null) {
|
||||
Toast.makeText(context, string, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* 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 io.github.muntashirakon.music.helper.menu
|
||||
|
||||
import android.content.Intent
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.widget.PopupMenu
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import io.github.muntashirakon.music.R
|
||||
import io.github.muntashirakon.music.activities.tageditor.AbsTagEditorActivity
|
||||
import io.github.muntashirakon.music.activities.tageditor.SongTagEditorActivity
|
||||
import io.github.muntashirakon.music.dialogs.AddToPlaylistDialog
|
||||
import io.github.muntashirakon.music.dialogs.DeleteSongsDialog
|
||||
import io.github.muntashirakon.music.dialogs.SongDetailDialog
|
||||
import io.github.muntashirakon.music.helper.MusicPlayerRemote
|
||||
import io.github.muntashirakon.music.interfaces.PaletteColorHolder
|
||||
import io.github.muntashirakon.music.model.Song
|
||||
import io.github.muntashirakon.music.util.MusicUtil
|
||||
import io.github.muntashirakon.music.util.NavigationUtil
|
||||
import io.github.muntashirakon.music.util.RingtoneManager
|
||||
|
||||
object SongMenuHelper {
|
||||
const val MENU_RES = R.menu.menu_item_song
|
||||
|
||||
fun handleMenuClick(activity: FragmentActivity, song: Song, menuItemId: Int): Boolean {
|
||||
when (menuItemId) {
|
||||
R.id.action_set_as_ringtone -> {
|
||||
if (RingtoneManager.requiresDialog(activity)) {
|
||||
RingtoneManager.getDialog(activity)
|
||||
} else {
|
||||
val ringtoneManager = RingtoneManager(activity)
|
||||
ringtoneManager.setRingtone(song)
|
||||
}
|
||||
return true
|
||||
}
|
||||
R.id.action_share -> {
|
||||
activity.startActivity(
|
||||
Intent.createChooser(
|
||||
MusicUtil.createShareSongFileIntent(song, activity),
|
||||
null
|
||||
)
|
||||
)
|
||||
return true
|
||||
}
|
||||
R.id.action_delete_from_device -> {
|
||||
DeleteSongsDialog.create(song).show(activity.supportFragmentManager, "DELETE_SONGS")
|
||||
return true
|
||||
}
|
||||
R.id.action_add_to_playlist -> {
|
||||
AddToPlaylistDialog.create(song)
|
||||
.show(activity.supportFragmentManager, "ADD_PLAYLIST")
|
||||
return true
|
||||
}
|
||||
R.id.action_play_next -> {
|
||||
MusicPlayerRemote.playNext(song)
|
||||
return true
|
||||
}
|
||||
R.id.action_add_to_current_playing -> {
|
||||
MusicPlayerRemote.enqueue(song)
|
||||
return true
|
||||
}
|
||||
R.id.action_tag_editor -> {
|
||||
val tagEditorIntent = Intent(activity, SongTagEditorActivity::class.java)
|
||||
tagEditorIntent.putExtra(AbsTagEditorActivity.EXTRA_ID, song.id)
|
||||
if (activity is PaletteColorHolder)
|
||||
tagEditorIntent.putExtra(
|
||||
AbsTagEditorActivity.EXTRA_PALETTE,
|
||||
(activity as PaletteColorHolder).paletteColor
|
||||
)
|
||||
activity.startActivity(tagEditorIntent)
|
||||
return true
|
||||
}
|
||||
R.id.action_details -> {
|
||||
SongDetailDialog.create(song).show(activity.supportFragmentManager, "SONG_DETAILS")
|
||||
return true
|
||||
}
|
||||
R.id.action_go_to_album -> {
|
||||
NavigationUtil.goToAlbum(activity, song.albumId)
|
||||
return true
|
||||
}
|
||||
R.id.action_go_to_artist -> {
|
||||
NavigationUtil.goToArtist(activity, song.artistId)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
abstract class OnClickSongMenu protected constructor(private val activity: AppCompatActivity) :
|
||||
View.OnClickListener, PopupMenu.OnMenuItemClickListener {
|
||||
|
||||
open val menuRes: Int
|
||||
get() = MENU_RES
|
||||
|
||||
abstract val song: Song
|
||||
|
||||
override fun onClick(v: View) {
|
||||
val popupMenu = PopupMenu(activity, v)
|
||||
popupMenu.inflate(menuRes)
|
||||
popupMenu.setOnMenuItemClickListener(this)
|
||||
popupMenu.show()
|
||||
}
|
||||
|
||||
override fun onMenuItemClick(item: MenuItem): Boolean {
|
||||
return handleMenuClick(activity, song, item.itemId)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* 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 io.github.muntashirakon.music.helper.menu
|
||||
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
|
||||
import io.github.muntashirakon.music.R
|
||||
import io.github.muntashirakon.music.dialogs.AddToPlaylistDialog
|
||||
import io.github.muntashirakon.music.dialogs.DeleteSongsDialog
|
||||
import io.github.muntashirakon.music.helper.MusicPlayerRemote
|
||||
import io.github.muntashirakon.music.model.Song
|
||||
import java.util.*
|
||||
|
||||
|
||||
object SongsMenuHelper {
|
||||
fun handleMenuClick(
|
||||
activity: FragmentActivity,
|
||||
songs: ArrayList<Song>,
|
||||
menuItemId: Int
|
||||
): Boolean {
|
||||
when (menuItemId) {
|
||||
R.id.action_play_next -> {
|
||||
MusicPlayerRemote.playNext(songs)
|
||||
return true
|
||||
}
|
||||
R.id.action_add_to_current_playing -> {
|
||||
MusicPlayerRemote.enqueue(songs)
|
||||
return true
|
||||
}
|
||||
R.id.action_add_to_playlist -> {
|
||||
AddToPlaylistDialog.create(songs)
|
||||
.show(activity.supportFragmentManager, "ADD_PLAYLIST")
|
||||
return true
|
||||
}
|
||||
R.id.action_delete_from_device -> {
|
||||
DeleteSongsDialog.create(songs)
|
||||
.show(activity.supportFragmentManager, "DELETE_SONGS")
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue