kotlin conversion
This commit is contained in:
parent
8e6ab40d93
commit
b2c15ef186
316 changed files with 13055 additions and 22983 deletions
|
@ -1,211 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project Licensed under the Apache
|
||||
* License, Version 2.0 (the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law
|
||||
* or agreed to in writing, software distributed under the License is
|
||||
* distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the specific language
|
||||
* governing permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
// Modified for Phonograph by Karim Abou Zeid (kabouzeid).
|
||||
|
||||
package code.name.monkey.retromusic.service;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.os.PowerManager;
|
||||
import android.os.PowerManager.WakeLock;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
|
||||
import code.name.monkey.retromusic.BuildConfig;
|
||||
|
||||
import static code.name.monkey.retromusic.Constants.ACTION_PAUSE;
|
||||
import static code.name.monkey.retromusic.Constants.ACTION_PLAY;
|
||||
import static code.name.monkey.retromusic.Constants.ACTION_REWIND;
|
||||
import static code.name.monkey.retromusic.Constants.ACTION_SKIP;
|
||||
import static code.name.monkey.retromusic.Constants.ACTION_STOP;
|
||||
import static code.name.monkey.retromusic.Constants.ACTION_TOGGLE_PAUSE;
|
||||
|
||||
/**
|
||||
* Used to control headset playback.
|
||||
* Single press: pause/resume
|
||||
* Double press: next track
|
||||
* Triple press: previous track
|
||||
*/
|
||||
public class MediaButtonIntentReceiver extends BroadcastReceiver {
|
||||
public static final String TAG = MediaButtonIntentReceiver.class.getSimpleName();
|
||||
private static final boolean DEBUG = BuildConfig.DEBUG;
|
||||
private static final int MSG_HEADSET_DOUBLE_CLICK_TIMEOUT = 2;
|
||||
|
||||
private static final int DOUBLE_CLICK = 400;
|
||||
|
||||
private static WakeLock mWakeLock = null;
|
||||
private static int mClickCounter = 0;
|
||||
private static long mLastClickTime = 0;
|
||||
|
||||
@SuppressLint("HandlerLeak") // false alarm, handler is already static
|
||||
private static Handler mHandler = new Handler() {
|
||||
|
||||
@Override
|
||||
public void handleMessage(final Message msg) {
|
||||
switch (msg.what) {
|
||||
case MSG_HEADSET_DOUBLE_CLICK_TIMEOUT:
|
||||
final int clickCount = msg.arg1;
|
||||
final String command;
|
||||
|
||||
if (DEBUG) Log.v(TAG, "Handling headset click, count = " + clickCount);
|
||||
switch (clickCount) {
|
||||
case 1:
|
||||
command = ACTION_TOGGLE_PAUSE;
|
||||
break;
|
||||
case 2:
|
||||
command = ACTION_SKIP;
|
||||
break;
|
||||
case 3:
|
||||
command = ACTION_REWIND;
|
||||
break;
|
||||
default:
|
||||
command = null;
|
||||
break;
|
||||
}
|
||||
|
||||
if (command != null) {
|
||||
final Context context = (Context) msg.obj;
|
||||
startService(context, command);
|
||||
}
|
||||
break;
|
||||
}
|
||||
releaseWakeLockIfHandlerIdle();
|
||||
}
|
||||
};
|
||||
|
||||
public static boolean handleIntent(final Context context, final Intent intent) {
|
||||
final String intentAction = intent.getAction();
|
||||
if (Intent.ACTION_MEDIA_BUTTON.equals(intentAction)) {
|
||||
final KeyEvent event = intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
|
||||
if (event == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final int keycode = event.getKeyCode();
|
||||
final int action = event.getAction();
|
||||
final long eventTime = event.getEventTime() != 0 ?
|
||||
event.getEventTime() : System.currentTimeMillis();
|
||||
|
||||
String command = null;
|
||||
switch (keycode) {
|
||||
case KeyEvent.KEYCODE_MEDIA_STOP:
|
||||
command = ACTION_STOP;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_HEADSETHOOK:
|
||||
case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
|
||||
command = ACTION_TOGGLE_PAUSE;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_MEDIA_NEXT:
|
||||
command = ACTION_SKIP;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
|
||||
command = ACTION_REWIND;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_MEDIA_PAUSE:
|
||||
command = ACTION_PAUSE;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_MEDIA_PLAY:
|
||||
command = ACTION_PLAY;
|
||||
break;
|
||||
}
|
||||
if (command != null) {
|
||||
if (action == KeyEvent.ACTION_DOWN) {
|
||||
if (event.getRepeatCount() == 0) {
|
||||
// Only consider the first event in a sequence, not the repeat events,
|
||||
// so that we don't trigger in cases where the first event went to
|
||||
// a different app (e.g. when the user ends a phone call by
|
||||
// long pressing the headset button)
|
||||
|
||||
// The service may or may not be running, but we need to send it
|
||||
// a command.
|
||||
if (keycode == KeyEvent.KEYCODE_HEADSETHOOK || keycode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE) {
|
||||
if (eventTime - mLastClickTime >= DOUBLE_CLICK) {
|
||||
mClickCounter = 0;
|
||||
}
|
||||
|
||||
mClickCounter++;
|
||||
if (DEBUG) Log.v(TAG, "Got headset click, count = " + mClickCounter);
|
||||
mHandler.removeMessages(MSG_HEADSET_DOUBLE_CLICK_TIMEOUT);
|
||||
|
||||
Message msg = mHandler.obtainMessage(
|
||||
MSG_HEADSET_DOUBLE_CLICK_TIMEOUT, mClickCounter, 0, context);
|
||||
|
||||
long delay = mClickCounter < 3 ? DOUBLE_CLICK : 0;
|
||||
if (mClickCounter >= 3) {
|
||||
mClickCounter = 0;
|
||||
}
|
||||
mLastClickTime = eventTime;
|
||||
acquireWakeLockAndSendMessage(context, msg, delay);
|
||||
} else {
|
||||
startService(context, command);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void startService(Context context, String command) {
|
||||
final Intent intent = new Intent(context, MusicService.class);
|
||||
intent.setAction(command);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
context.startForegroundService(intent);
|
||||
} else {
|
||||
context.startService(intent);
|
||||
}
|
||||
}
|
||||
|
||||
private static void acquireWakeLockAndSendMessage(Context context, Message msg, long delay) {
|
||||
if (mWakeLock == null) {
|
||||
Context appContext = context.getApplicationContext();
|
||||
PowerManager pm = (PowerManager) appContext.getSystemService(Context.POWER_SERVICE);
|
||||
if (pm != null) {
|
||||
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
|
||||
"RetroMusicApp:Wakelock headset button");
|
||||
}
|
||||
mWakeLock.setReferenceCounted(false);
|
||||
}
|
||||
if (DEBUG) Log.v(TAG, "Acquiring wake lock and sending " + msg.what);
|
||||
// Make sure we don't indefinitely hold the wake lock under any circumstances
|
||||
mWakeLock.acquire(10000);
|
||||
|
||||
mHandler.sendMessageDelayed(msg, delay);
|
||||
}
|
||||
|
||||
private static void releaseWakeLockIfHandlerIdle() {
|
||||
if (mHandler.hasMessages(MSG_HEADSET_DOUBLE_CLICK_TIMEOUT)) {
|
||||
if (DEBUG) Log.v(TAG, "Handler still has messages pending, not releasing wake lock");
|
||||
return;
|
||||
}
|
||||
|
||||
if (mWakeLock != null) {
|
||||
if (DEBUG) Log.v(TAG, "Releasing wake lock");
|
||||
mWakeLock.release();
|
||||
mWakeLock = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceive(final Context context, final Intent intent) {
|
||||
if (DEBUG) Log.v(TAG, "Received intent: " + intent);
|
||||
if (handleIntent(context, intent) && isOrderedBroadcast()) {
|
||||
abortBroadcast();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,186 @@
|
|||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project Licensed under the Apache
|
||||
* License, Version 2.0 (the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law
|
||||
* or agreed to in writing, software distributed under the License is
|
||||
* distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the specific language
|
||||
* governing permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
package code.name.monkey.retromusic.service
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.Handler
|
||||
import android.os.Message
|
||||
import android.os.PowerManager
|
||||
import android.os.PowerManager.WakeLock
|
||||
import android.util.Log
|
||||
import android.view.KeyEvent
|
||||
|
||||
import code.name.monkey.retromusic.BuildConfig
|
||||
import code.name.monkey.retromusic.Constants.ACTION_PAUSE
|
||||
import code.name.monkey.retromusic.Constants.ACTION_PLAY
|
||||
import code.name.monkey.retromusic.Constants.ACTION_REWIND
|
||||
import code.name.monkey.retromusic.Constants.ACTION_SKIP
|
||||
import code.name.monkey.retromusic.Constants.ACTION_STOP
|
||||
import code.name.monkey.retromusic.Constants.ACTION_TOGGLE_PAUSE
|
||||
|
||||
/**
|
||||
* Used to control headset playback.
|
||||
* Single press: pause/resume
|
||||
* Double press: actionNext track
|
||||
* Triple press: previous track
|
||||
*/
|
||||
class MediaButtonIntentReceiver : BroadcastReceiver() {
|
||||
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
if (DEBUG) Log.v(TAG, "Received intent: $intent")
|
||||
if (handleIntent(context, intent) && isOrderedBroadcast) {
|
||||
abortBroadcast()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val TAG = MediaButtonIntentReceiver::class.java.simpleName
|
||||
private val DEBUG = BuildConfig.DEBUG
|
||||
private val MSG_HEADSET_DOUBLE_CLICK_TIMEOUT = 2
|
||||
|
||||
private val DOUBLE_CLICK = 400
|
||||
|
||||
private var wakeLock: WakeLock? = null
|
||||
private var mClickCounter = 0
|
||||
private var mLastClickTime: Long = 0
|
||||
|
||||
@SuppressLint("HandlerLeak") // false alarm, handler is already static
|
||||
private val mHandler = object : Handler() {
|
||||
|
||||
override fun handleMessage(msg: Message) {
|
||||
when (msg.what) {
|
||||
MSG_HEADSET_DOUBLE_CLICK_TIMEOUT -> {
|
||||
val clickCount = msg.arg1
|
||||
val command: String?
|
||||
|
||||
if (DEBUG) Log.v(TAG, "Handling headset click, count = $clickCount")
|
||||
when (clickCount) {
|
||||
1 -> command = ACTION_TOGGLE_PAUSE
|
||||
2 -> command = ACTION_SKIP
|
||||
3 -> command = ACTION_REWIND
|
||||
else -> command = null
|
||||
}
|
||||
|
||||
if (command != null) {
|
||||
val context = msg.obj as Context
|
||||
startService(context, command)
|
||||
}
|
||||
}
|
||||
}
|
||||
releaseWakeLockIfHandlerIdle()
|
||||
}
|
||||
}
|
||||
|
||||
fun handleIntent(context: Context, intent: Intent): Boolean {
|
||||
val intentAction = intent.action
|
||||
if (Intent.ACTION_MEDIA_BUTTON == intentAction) {
|
||||
val event = intent.getParcelableExtra<KeyEvent>(Intent.EXTRA_KEY_EVENT)
|
||||
?: return false
|
||||
|
||||
val keycode = event.keyCode
|
||||
val action = event.action
|
||||
val eventTime = if (event.eventTime != 0L)
|
||||
event.eventTime
|
||||
else
|
||||
System.currentTimeMillis()
|
||||
|
||||
var command: String? = null
|
||||
when (keycode) {
|
||||
KeyEvent.KEYCODE_MEDIA_STOP -> command = ACTION_STOP
|
||||
KeyEvent.KEYCODE_HEADSETHOOK, KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE -> command = ACTION_TOGGLE_PAUSE
|
||||
KeyEvent.KEYCODE_MEDIA_NEXT -> command = ACTION_SKIP
|
||||
KeyEvent.KEYCODE_MEDIA_PREVIOUS -> command = ACTION_REWIND
|
||||
KeyEvent.KEYCODE_MEDIA_PAUSE -> command = ACTION_PAUSE
|
||||
KeyEvent.KEYCODE_MEDIA_PLAY -> command = ACTION_PLAY
|
||||
}
|
||||
if (command != null) {
|
||||
if (action == KeyEvent.ACTION_DOWN) {
|
||||
if (event.repeatCount == 0) {
|
||||
// Only consider the first event in a sequence, not the repeat events,
|
||||
// so that we don't trigger in cases where the first event went to
|
||||
// a different app (e.g. when the user ends a phone call by
|
||||
// long pressing the headset button)
|
||||
|
||||
// The service may or may not be running, but we need to send it
|
||||
// a command.
|
||||
if (keycode == KeyEvent.KEYCODE_HEADSETHOOK || keycode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE) {
|
||||
if (eventTime - mLastClickTime >= DOUBLE_CLICK) {
|
||||
mClickCounter = 0
|
||||
}
|
||||
|
||||
mClickCounter++
|
||||
if (DEBUG) Log.v(TAG, "Got headset click, count = $mClickCounter")
|
||||
mHandler.removeMessages(MSG_HEADSET_DOUBLE_CLICK_TIMEOUT)
|
||||
|
||||
val msg = mHandler.obtainMessage(
|
||||
MSG_HEADSET_DOUBLE_CLICK_TIMEOUT, mClickCounter, 0, context)
|
||||
|
||||
val delay = (if (mClickCounter < 3) DOUBLE_CLICK else 0).toLong()
|
||||
if (mClickCounter >= 3) {
|
||||
mClickCounter = 0
|
||||
}
|
||||
mLastClickTime = eventTime
|
||||
acquireWakeLockAndSendMessage(context, msg, delay)
|
||||
} else {
|
||||
startService(context, command)
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun startService(context: Context, command: String?) {
|
||||
val intent = Intent(context, MusicService::class.java)
|
||||
intent.action = command
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
context.startForegroundService(intent)
|
||||
} else {
|
||||
context.startService(intent)
|
||||
}
|
||||
}
|
||||
|
||||
private fun acquireWakeLockAndSendMessage(context: Context, msg: Message, delay: Long) {
|
||||
if (wakeLock == null) {
|
||||
val appContext = context.applicationContext
|
||||
val pm = appContext.getSystemService(Context.POWER_SERVICE) as PowerManager
|
||||
wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "RetroMusicApp:Wakelock headset button")
|
||||
wakeLock!!.setReferenceCounted(false)
|
||||
}
|
||||
if (DEBUG) Log.v(TAG, "Acquiring wake lock and sending " + msg.what)
|
||||
// Make sure we don't indefinitely hold the wake lock under any circumstances
|
||||
wakeLock!!.acquire(10000)
|
||||
|
||||
mHandler.sendMessageDelayed(msg, delay)
|
||||
}
|
||||
|
||||
private fun releaseWakeLockIfHandlerIdle() {
|
||||
if (mHandler.hasMessages(MSG_HEADSET_DOUBLE_CLICK_TIMEOUT)) {
|
||||
if (DEBUG) Log.v(TAG, "Handler still has messages pending, not releasing wake lock")
|
||||
return
|
||||
}
|
||||
|
||||
if (wakeLock != null) {
|
||||
if (DEBUG) Log.v(TAG, "Releasing wake lock")
|
||||
wakeLock!!.release()
|
||||
wakeLock = null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,334 +0,0 @@
|
|||
package code.name.monkey.retromusic.service;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.media.AudioManager;
|
||||
import android.media.MediaPlayer;
|
||||
import android.media.audiofx.AudioEffect;
|
||||
import android.net.Uri;
|
||||
import android.os.PowerManager;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import code.name.monkey.retromusic.R;
|
||||
import code.name.monkey.retromusic.service.playback.Playback;
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil;
|
||||
|
||||
/**
|
||||
* @author Andrew Neal, Karim Abou Zeid (kabouzeid)
|
||||
*/
|
||||
public class MultiPlayer implements Playback, MediaPlayer.OnErrorListener, MediaPlayer.OnCompletionListener {
|
||||
public static final String TAG = MultiPlayer.class.getSimpleName();
|
||||
|
||||
private MediaPlayer mCurrentMediaPlayer = new MediaPlayer();
|
||||
private MediaPlayer mNextMediaPlayer;
|
||||
|
||||
private Context context;
|
||||
@Nullable
|
||||
private Playback.PlaybackCallbacks callbacks;
|
||||
|
||||
private boolean mIsInitialized = false;
|
||||
|
||||
/**
|
||||
* Constructor of <code>MultiPlayer</code>
|
||||
*/
|
||||
MultiPlayer(final Context context) {
|
||||
this.context = context;
|
||||
mCurrentMediaPlayer.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param path The path of the file, or the http/rtsp URL of the stream
|
||||
* you want to play
|
||||
* @return True if the <code>player</code> has been prepared and is
|
||||
* ready to play, false otherwise
|
||||
*/
|
||||
@Override
|
||||
public boolean setDataSource(@NonNull final String path) {
|
||||
mIsInitialized = false;
|
||||
mIsInitialized = setDataSourceImpl(mCurrentMediaPlayer, path);
|
||||
if (mIsInitialized) {
|
||||
setNextDataSource(null);
|
||||
}
|
||||
return mIsInitialized;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param player The {@link MediaPlayer} to use
|
||||
* @param path The path of the file, or the http/rtsp URL of the stream
|
||||
* you want to play
|
||||
* @return True if the <code>player</code> has been prepared and is
|
||||
* ready to play, false otherwise
|
||||
*/
|
||||
private boolean setDataSourceImpl(@NonNull final MediaPlayer player, @NonNull final String path) {
|
||||
if (context == null) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
player.reset();
|
||||
player.setOnPreparedListener(null);
|
||||
if (path.startsWith("content://")) {
|
||||
player.setDataSource(context, Uri.parse(path));
|
||||
} else {
|
||||
player.setDataSource(path);
|
||||
}
|
||||
player.setAudioStreamType(AudioManager.STREAM_MUSIC);
|
||||
player.prepare();
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
player.setOnCompletionListener(this);
|
||||
player.setOnErrorListener(this);
|
||||
final Intent intent = new Intent(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION);
|
||||
intent.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, getAudioSessionId());
|
||||
intent.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, context.getPackageName());
|
||||
intent.putExtra(AudioEffect.EXTRA_CONTENT_TYPE, AudioEffect.CONTENT_TYPE_MUSIC);
|
||||
context.sendBroadcast(intent);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the MediaPlayer to start when this MediaPlayer finishes playback.
|
||||
*
|
||||
* @param path The path of the file, or the http/rtsp URL of the stream
|
||||
* you want to play
|
||||
*/
|
||||
@Override
|
||||
public void setNextDataSource(@Nullable final String path) {
|
||||
if (context == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
mCurrentMediaPlayer.setNextMediaPlayer(null);
|
||||
} catch (IllegalArgumentException e) {
|
||||
Log.i(TAG, "Next media player is current one, continuing");
|
||||
} catch (IllegalStateException e) {
|
||||
Log.e(TAG, "Media player not initialized!");
|
||||
return;
|
||||
}
|
||||
if (mNextMediaPlayer != null) {
|
||||
mNextMediaPlayer.release();
|
||||
mNextMediaPlayer = null;
|
||||
}
|
||||
if (path == null) {
|
||||
return;
|
||||
}
|
||||
if (PreferenceUtil.getInstance().gaplessPlayback()) {
|
||||
mNextMediaPlayer = new MediaPlayer();
|
||||
mNextMediaPlayer.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK);
|
||||
mNextMediaPlayer.setAudioSessionId(getAudioSessionId());
|
||||
if (setDataSourceImpl(mNextMediaPlayer, path)) {
|
||||
try {
|
||||
mCurrentMediaPlayer.setNextMediaPlayer(mNextMediaPlayer);
|
||||
} catch (@NonNull IllegalArgumentException | IllegalStateException e) {
|
||||
Log.e(TAG, "setNextDataSource: setNextMediaPlayer()", e);
|
||||
if (mNextMediaPlayer != null) {
|
||||
mNextMediaPlayer.release();
|
||||
mNextMediaPlayer = null;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (mNextMediaPlayer != null) {
|
||||
mNextMediaPlayer.release();
|
||||
mNextMediaPlayer = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the callbacks
|
||||
*
|
||||
* @param callbacks The callbacks to use
|
||||
*/
|
||||
@Override
|
||||
public void setCallbacks(@Nullable final Playback.PlaybackCallbacks callbacks) {
|
||||
this.callbacks = callbacks;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True if the player is ready to go, false otherwise
|
||||
*/
|
||||
@Override
|
||||
public boolean isInitialized() {
|
||||
return mIsInitialized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts or resumes playback.
|
||||
*/
|
||||
@Override
|
||||
public boolean start() {
|
||||
try {
|
||||
mCurrentMediaPlayer.start();
|
||||
return true;
|
||||
} catch (IllegalStateException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the MediaPlayer to its uninitialized state.
|
||||
*/
|
||||
@Override
|
||||
public void stop() {
|
||||
mCurrentMediaPlayer.reset();
|
||||
mIsInitialized = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases resources associated with this MediaPlayer object.
|
||||
*/
|
||||
@Override
|
||||
public void release() {
|
||||
stop();
|
||||
mCurrentMediaPlayer.release();
|
||||
if (mNextMediaPlayer != null) {
|
||||
mNextMediaPlayer.release();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pauses playback. Call start() to resume.
|
||||
*/
|
||||
@Override
|
||||
public boolean pause() {
|
||||
try {
|
||||
mCurrentMediaPlayer.pause();
|
||||
return true;
|
||||
} catch (IllegalStateException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the MultiPlayer is playing.
|
||||
*/
|
||||
@Override
|
||||
public boolean isPlaying() {
|
||||
return mIsInitialized && mCurrentMediaPlayer.isPlaying();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the duration of the file.
|
||||
*
|
||||
* @return The duration in milliseconds
|
||||
*/
|
||||
@Override
|
||||
public int duration() {
|
||||
if (!mIsInitialized) {
|
||||
return -1;
|
||||
}
|
||||
try {
|
||||
return mCurrentMediaPlayer.getDuration();
|
||||
} catch (IllegalStateException e) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current playback position.
|
||||
*
|
||||
* @return The current position in milliseconds
|
||||
*/
|
||||
@Override
|
||||
public int position() {
|
||||
if (!mIsInitialized) {
|
||||
return -1;
|
||||
}
|
||||
try {
|
||||
return mCurrentMediaPlayer.getCurrentPosition();
|
||||
} catch (IllegalStateException e) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current playback position.
|
||||
*
|
||||
* @param whereto The offset in milliseconds from the start to seek to
|
||||
* @return The offset in milliseconds from the start to seek to
|
||||
*/
|
||||
@Override
|
||||
public int seek(final int whereto) {
|
||||
try {
|
||||
mCurrentMediaPlayer.seekTo(whereto);
|
||||
return whereto;
|
||||
} catch (IllegalStateException e) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setVolume(final float vol) {
|
||||
try {
|
||||
mCurrentMediaPlayer.setVolume(vol, vol);
|
||||
return true;
|
||||
} catch (IllegalStateException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the audio session ID.
|
||||
*
|
||||
* @param sessionId The audio session ID
|
||||
*/
|
||||
@Override
|
||||
public boolean setAudioSessionId(final int sessionId) {
|
||||
try {
|
||||
mCurrentMediaPlayer.setAudioSessionId(sessionId);
|
||||
return true;
|
||||
} catch (@NonNull IllegalArgumentException | IllegalStateException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the audio session ID.
|
||||
*
|
||||
* @return The current audio session ID.
|
||||
*/
|
||||
@Override
|
||||
public int getAudioSessionId() {
|
||||
return mCurrentMediaPlayer.getAudioSessionId();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean onError(final MediaPlayer mp, final int what, final int extra) {
|
||||
mIsInitialized = false;
|
||||
mCurrentMediaPlayer.release();
|
||||
mCurrentMediaPlayer = new MediaPlayer();
|
||||
mCurrentMediaPlayer.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK);
|
||||
if (context != null) {
|
||||
Toast.makeText(context, context.getResources().getString(R.string.unplayable_file), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void onCompletion(final MediaPlayer mp) {
|
||||
if (mp == mCurrentMediaPlayer && mNextMediaPlayer != null) {
|
||||
mIsInitialized = false;
|
||||
mCurrentMediaPlayer.release();
|
||||
mCurrentMediaPlayer = mNextMediaPlayer;
|
||||
mIsInitialized = true;
|
||||
mNextMediaPlayer = null;
|
||||
if (callbacks != null)
|
||||
callbacks.onTrackWentToNext();
|
||||
} else {
|
||||
if (callbacks != null)
|
||||
callbacks.onTrackEnded();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,333 @@
|
|||
package code.name.monkey.retromusic.service
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.media.AudioManager
|
||||
import android.media.MediaPlayer
|
||||
import android.media.audiofx.AudioEffect
|
||||
import android.net.Uri
|
||||
import android.os.PowerManager
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.service.playback.Playback
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
|
||||
/**
|
||||
* @author Andrew Neal, Karim Abou Zeid (kabouzeid)
|
||||
*/
|
||||
class MultiPlayer
|
||||
/**
|
||||
* Constructor of `MultiPlayer`
|
||||
*/
|
||||
internal constructor(private val context: Context?) : Playback, MediaPlayer.OnErrorListener, MediaPlayer.OnCompletionListener {
|
||||
|
||||
private var mCurrentMediaPlayer = MediaPlayer()
|
||||
private var mNextMediaPlayer: MediaPlayer? = null
|
||||
private var callbacks: Playback.PlaybackCallbacks? = null
|
||||
|
||||
private var mIsInitialized = false
|
||||
|
||||
init {
|
||||
mCurrentMediaPlayer.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param path The path of the file, or the http/rtsp URL of the stream
|
||||
* you want to play
|
||||
* @return True if the `player` has been prepared and is
|
||||
* ready to play, false otherwise
|
||||
*/
|
||||
override fun setDataSource(path: String): Boolean {
|
||||
mIsInitialized = false
|
||||
mIsInitialized = setDataSourceImpl(mCurrentMediaPlayer, path)
|
||||
if (mIsInitialized) {
|
||||
setNextDataSource(null)
|
||||
}
|
||||
return mIsInitialized
|
||||
}
|
||||
|
||||
/**
|
||||
* @param player The [MediaPlayer] to use
|
||||
* @param path The path of the file, or the http/rtsp URL of the stream
|
||||
* you want to play
|
||||
* @return True if the `player` has been prepared and is
|
||||
* ready to play, false otherwise
|
||||
*/
|
||||
private fun setDataSourceImpl(player: MediaPlayer, path: String): Boolean {
|
||||
if (context == null) {
|
||||
return false
|
||||
}
|
||||
try {
|
||||
player.reset()
|
||||
player.setOnPreparedListener(null)
|
||||
if (path.startsWith("content://")) {
|
||||
player.setDataSource(context, Uri.parse(path))
|
||||
} else {
|
||||
player.setDataSource(path)
|
||||
}
|
||||
player.setAudioStreamType(AudioManager.STREAM_MUSIC)
|
||||
player.prepare()
|
||||
} catch (e: Exception) {
|
||||
return false
|
||||
}
|
||||
|
||||
player.setOnCompletionListener(this)
|
||||
player.setOnErrorListener(this)
|
||||
val intent = Intent(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION)
|
||||
intent.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, audioSessionId)
|
||||
intent.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, context.packageName)
|
||||
intent.putExtra(AudioEffect.EXTRA_CONTENT_TYPE, AudioEffect.CONTENT_TYPE_MUSIC)
|
||||
context.sendBroadcast(intent)
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the MediaPlayer to start when this MediaPlayer finishes playback.
|
||||
*
|
||||
* @param path The path of the file, or the http/rtsp URL of the stream
|
||||
* you want to play
|
||||
*/
|
||||
override fun setNextDataSource(path: String?) {
|
||||
if (context == null) {
|
||||
return
|
||||
}
|
||||
try {
|
||||
mCurrentMediaPlayer.setNextMediaPlayer(null)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
Log.i(TAG, "Next media player is current one, continuing")
|
||||
} catch (e: IllegalStateException) {
|
||||
Log.e(TAG, "Media player not initialized!")
|
||||
return
|
||||
}
|
||||
|
||||
if (mNextMediaPlayer != null) {
|
||||
mNextMediaPlayer!!.release()
|
||||
mNextMediaPlayer = null
|
||||
}
|
||||
if (path == null) {
|
||||
return
|
||||
}
|
||||
if (PreferenceUtil.getInstance().gaplessPlayback()) {
|
||||
mNextMediaPlayer = MediaPlayer()
|
||||
mNextMediaPlayer!!.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK)
|
||||
mNextMediaPlayer!!.audioSessionId = audioSessionId
|
||||
if (setDataSourceImpl(mNextMediaPlayer!!, path)) {
|
||||
try {
|
||||
mCurrentMediaPlayer.setNextMediaPlayer(mNextMediaPlayer)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
Log.e(TAG, "setNextDataSource: setNextMediaPlayer()", e)
|
||||
if (mNextMediaPlayer != null) {
|
||||
mNextMediaPlayer!!.release()
|
||||
mNextMediaPlayer = null
|
||||
}
|
||||
} catch (e: IllegalStateException) {
|
||||
Log.e(TAG, "setNextDataSource: setNextMediaPlayer()", e)
|
||||
if (mNextMediaPlayer != null) {
|
||||
mNextMediaPlayer!!.release()
|
||||
mNextMediaPlayer = null
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
if (mNextMediaPlayer != null) {
|
||||
mNextMediaPlayer!!.release()
|
||||
mNextMediaPlayer = null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the callbacks
|
||||
*
|
||||
* @param callbacks The callbacks to use
|
||||
*/
|
||||
override fun setCallbacks(callbacks: Playback.PlaybackCallbacks?) {
|
||||
this.callbacks = callbacks
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True if the player is ready to go, false otherwise
|
||||
*/
|
||||
override fun isInitialized(): Boolean {
|
||||
return mIsInitialized
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts or resumes playback.
|
||||
*/
|
||||
override fun start(): Boolean {
|
||||
try {
|
||||
mCurrentMediaPlayer.start()
|
||||
return true
|
||||
} catch (e: IllegalStateException) {
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the MediaPlayer to its uninitialized state.
|
||||
*/
|
||||
override fun stop() {
|
||||
mCurrentMediaPlayer.reset()
|
||||
mIsInitialized = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases resources associated with this MediaPlayer object.
|
||||
*/
|
||||
override fun release() {
|
||||
stop()
|
||||
mCurrentMediaPlayer.release()
|
||||
if (mNextMediaPlayer != null) {
|
||||
mNextMediaPlayer!!.release()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pauses playback. Call start() to resume.
|
||||
*/
|
||||
override fun pause(): Boolean {
|
||||
try {
|
||||
mCurrentMediaPlayer.pause()
|
||||
return true
|
||||
} catch (e: IllegalStateException) {
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the MultiPlayer is playing.
|
||||
*/
|
||||
override fun isPlaying(): Boolean {
|
||||
return mIsInitialized && mCurrentMediaPlayer.isPlaying
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the duration of the file.
|
||||
*
|
||||
* @return The duration in milliseconds
|
||||
*/
|
||||
override fun duration(): Int {
|
||||
if (!mIsInitialized) {
|
||||
return -1
|
||||
}
|
||||
try {
|
||||
return mCurrentMediaPlayer.duration
|
||||
} catch (e: IllegalStateException) {
|
||||
return -1
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current playback position.
|
||||
*
|
||||
* @return The current position in milliseconds
|
||||
*/
|
||||
override fun position(): Int {
|
||||
if (!mIsInitialized) {
|
||||
return -1
|
||||
}
|
||||
try {
|
||||
return mCurrentMediaPlayer.currentPosition
|
||||
} catch (e: IllegalStateException) {
|
||||
return -1
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current playback position.
|
||||
*
|
||||
* @param whereto The offset in milliseconds from the start to seek to
|
||||
* @return The offset in milliseconds from the start to seek to
|
||||
*/
|
||||
override fun seek(whereto: Int): Int {
|
||||
try {
|
||||
mCurrentMediaPlayer.seekTo(whereto)
|
||||
return whereto
|
||||
} catch (e: IllegalStateException) {
|
||||
return -1
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun setVolume(vol: Float): Boolean {
|
||||
try {
|
||||
mCurrentMediaPlayer.setVolume(vol, vol)
|
||||
return true
|
||||
} catch (e: IllegalStateException) {
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the audio session ID.
|
||||
*
|
||||
* @param sessionId The audio session ID
|
||||
*/
|
||||
override fun setAudioSessionId(sessionId: Int): Boolean {
|
||||
try {
|
||||
mCurrentMediaPlayer.audioSessionId = sessionId
|
||||
return true
|
||||
} catch (e: IllegalArgumentException) {
|
||||
return false
|
||||
} catch (e: IllegalStateException) {
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the audio session ID.
|
||||
*
|
||||
* @return The current audio session ID.
|
||||
*/
|
||||
override fun getAudioSessionId(): Int {
|
||||
return mCurrentMediaPlayer.audioSessionId
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
override fun onError(mp: MediaPlayer, what: Int, extra: Int): Boolean {
|
||||
mIsInitialized = false
|
||||
mCurrentMediaPlayer.release()
|
||||
mCurrentMediaPlayer = MediaPlayer()
|
||||
mCurrentMediaPlayer.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK)
|
||||
if (context != null) {
|
||||
Toast.makeText(context, context.resources.getString(R.string.unplayable_file), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
override fun onCompletion(mp: MediaPlayer) {
|
||||
if (mp === mCurrentMediaPlayer && mNextMediaPlayer != null) {
|
||||
mIsInitialized = false
|
||||
mCurrentMediaPlayer.release()
|
||||
mCurrentMediaPlayer = mNextMediaPlayer as MediaPlayer
|
||||
mIsInitialized = true
|
||||
mNextMediaPlayer = null
|
||||
if (callbacks != null)
|
||||
callbacks!!.onTrackWentToNext()
|
||||
} else {
|
||||
if (callbacks != null)
|
||||
callbacks!!.onTrackEnded()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val TAG: String = MultiPlayer::class.java.simpleName
|
||||
}
|
||||
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -194,9 +194,9 @@ public class WearBrowserService extends MediaBrowserService {
|
|||
} else {
|
||||
switch (Integer.parseInt(Character.toString(parentId.charAt(0)))) {
|
||||
case TYPE_ARTIST:
|
||||
List<Artist> artistList = ArtistLoader.getAllArtists(mContext).blockingFirst();
|
||||
List<Artist> artistList = ArtistLoader.INSTANCE.getAllArtists(mContext).blockingFirst();
|
||||
for (Artist artist : artistList) {
|
||||
String albumNmber = String.format("%d %s", artist.albums.size(), artist.albums.size() > 1 ? "Albums" : "Album");
|
||||
String albumNmber = String.format("%d %s", artist.getAlbums().size(), artist.getAlbums().size() > 1 ? "Albums" : "Album");
|
||||
String songCount = String.format("%d %s", artist.getSongs().size(), artist.getSongs().size() > 1 ? "Songs" : "Song");
|
||||
fillMediaItems(mediaItems,
|
||||
Integer.toString(TYPE_ARTIST_SONG_ALBUMS) + Long.toString(artist.getId()),
|
||||
|
@ -215,9 +215,9 @@ public class WearBrowserService extends MediaBrowserService {
|
|||
Uri.parse("android.resource://code.name.monkey.retromusic/drawable/default_artist_art"),
|
||||
MediaBrowser.MediaItem.FLAG_BROWSABLE);
|
||||
|
||||
List<Album> artistAlbums = ArtistLoader.getArtist(mContext, Integer.parseInt(parentId.substring(1))).blockingFirst().albums; //ArtistAlbumLoader.getAlbumsForArtist(mContext, Long.parseLong(parentId.substring(1)));
|
||||
List<Album> artistAlbums = ArtistLoader.INSTANCE.getArtist(mContext, Integer.parseInt(parentId.substring(1))).blockingFirst().getAlbums(); //ArtistAlbumLoader.getAlbumsForArtist(mContext, Long.parseLong(parentId.substring(1)));
|
||||
for (Album album : artistAlbums) {
|
||||
String songCount = String.format("%d %s", album.songs.size(), album.songs.size() > 1 ? "Songs" : "Song");
|
||||
String songCount = String.format("%d %s", album.getSongs().size(), album.getSongs().size() > 1 ? "Songs" : "Song");
|
||||
fillMediaItems(mediaItems,
|
||||
Integer.toString(TYPE_ALBUM_SONGS) + Long.toString(album.getId()),
|
||||
album.getTitle(),
|
||||
|
@ -227,7 +227,7 @@ public class WearBrowserService extends MediaBrowserService {
|
|||
}
|
||||
break;
|
||||
case TYPE_ALBUM:
|
||||
List<Album> albumList = AlbumLoader.getAllAlbums(mContext).blockingFirst();
|
||||
List<Album> albumList = AlbumLoader.Companion.getAllAlbums(mContext).blockingFirst();
|
||||
for (Album album : albumList) {
|
||||
fillMediaItems(mediaItems,
|
||||
Integer.toString(TYPE_ALBUM_SONGS) + Long.toString(album.getId()),
|
||||
|
@ -238,43 +238,43 @@ public class WearBrowserService extends MediaBrowserService {
|
|||
}
|
||||
break;
|
||||
case TYPE_SONG:
|
||||
List<Song> songList = SongLoader.Companion.getAllSongs(mContext).blockingFirst();
|
||||
List<Song> songList = SongLoader.INSTANCE.getAllSongs(mContext).blockingFirst();
|
||||
for (Song song : songList) {
|
||||
fillMediaItems(mediaItems,
|
||||
String.valueOf(song.id),
|
||||
song.title,
|
||||
song.artistName,
|
||||
String.valueOf(song.getId()),
|
||||
song.getTitle(),
|
||||
song.getArtistName(),
|
||||
Uri.parse("android.resource://code.name.monkey.retromusic/drawable/default_album_art"),
|
||||
MediaBrowser.MediaItem.FLAG_PLAYABLE);
|
||||
}
|
||||
break;
|
||||
|
||||
case TYPE_ALBUM_SONGS:
|
||||
List<Song> albumSongList = AlbumLoader.getAlbum(mContext, Integer.parseInt(parentId.substring(1))).blockingFirst().songs;
|
||||
List<Song> albumSongList = AlbumLoader.Companion.getAlbum(mContext, Integer.parseInt(parentId.substring(1))).blockingFirst().getSongs();
|
||||
for (Song song : albumSongList) {
|
||||
fillMediaItems(mediaItems,
|
||||
String.valueOf(song.id),
|
||||
song.title,
|
||||
song.artistName,
|
||||
String.valueOf(song.getId()),
|
||||
song.getTitle(),
|
||||
song.getArtistName(),
|
||||
Uri.parse("android.resource://code.name.monkey.retromusic/drawable/default_album_art"),
|
||||
MediaBrowser.MediaItem.FLAG_PLAYABLE);
|
||||
}
|
||||
break;
|
||||
case TYPE_ARTIST_ALL_SONGS:
|
||||
List<Song> artistSongs = ArtistLoader.getArtist(mContext, Integer.parseInt(parentId.substring(1))).blockingFirst().getSongs();
|
||||
List<Song> artistSongs = ArtistLoader.INSTANCE.getArtist(mContext, Integer.parseInt(parentId.substring(1))).blockingFirst().getSongs();
|
||||
for (Song song : artistSongs) {
|
||||
fillMediaItems(mediaItems,
|
||||
String.valueOf(song.id),
|
||||
song.title,
|
||||
song.albumName,
|
||||
String.valueOf(song.getId()),
|
||||
song.getTitle(),
|
||||
song.getAlbumName(),
|
||||
Uri.parse("android.resource://code.name.monkey.retromusic/drawable/default_album_art"),
|
||||
MediaBrowser.MediaItem.FLAG_PLAYABLE);
|
||||
}
|
||||
break;
|
||||
case TYPE_PLAYLIST:
|
||||
List<Playlist> playlistList = PlaylistLoader.getAllPlaylists(mContext).blockingFirst();
|
||||
List<Playlist> playlistList = PlaylistLoader.INSTANCE.getAllPlaylists(mContext).blockingFirst();
|
||||
for (Playlist playlist : playlistList) {
|
||||
int size = PlaylistSongsLoader.getPlaylistSongList(mContext, playlist).blockingFirst().size();
|
||||
int size = PlaylistSongsLoader.INSTANCE.getPlaylistSongList(mContext, playlist).blockingFirst().size();
|
||||
String songCount = String.format("%d %s", size, size > 1 ? "Songs" : "Song");
|
||||
fillMediaItems(mediaItems,
|
||||
Integer.toString(TYPE_PLAYLIST_ALL_SONGS) + Long.toString(playlist.id),
|
||||
|
@ -285,12 +285,12 @@ public class WearBrowserService extends MediaBrowserService {
|
|||
}
|
||||
break;
|
||||
case TYPE_PLAYLIST_ALL_SONGS:
|
||||
List<Song> playlistSongs = PlaylistSongsLoader.getPlaylistSongList(mContext, Integer.parseInt(parentId.substring(1))).blockingFirst();
|
||||
List<Song> playlistSongs = PlaylistSongsLoader.INSTANCE.getPlaylistSongList(mContext, Integer.parseInt(parentId.substring(1))).blockingFirst();
|
||||
for (Song song : playlistSongs) {
|
||||
fillMediaItems(mediaItems,
|
||||
String.valueOf(song.id),
|
||||
song.title,
|
||||
song.albumName,
|
||||
String.valueOf(song.getId()),
|
||||
song.getTitle(),
|
||||
song.getAlbumName(),
|
||||
Uri.parse("android.resource://code.name.monkey.retromusic/drawable/default_album_art"),
|
||||
MediaBrowser.MediaItem.FLAG_PLAYABLE);
|
||||
}
|
||||
|
@ -326,8 +326,8 @@ public class WearBrowserService extends MediaBrowserService {
|
|||
long songId = Long.parseLong(mediaId);
|
||||
setSessionActive();
|
||||
ArrayList<Song> songs = new ArrayList<>();
|
||||
songs.add(SongLoader.Companion.getSong(mContext, Integer.parseInt(mediaId)).blockingFirst());
|
||||
MusicPlayerRemote.openQueue(songs, 0, true);
|
||||
songs.add(SongLoader.INSTANCE.getSong(mContext, Integer.parseInt(mediaId)).blockingFirst());
|
||||
MusicPlayerRemote.INSTANCE.openQueue(songs, 0, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,186 +0,0 @@
|
|||
package code.name.monkey.retromusic.service.daydream;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.service.dreams.DreamService;
|
||||
import androidx.transition.TransitionManager;
|
||||
import androidx.recyclerview.widget.DefaultItemAnimator;
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import code.name.monkey.retromusic.loaders.SongLoader;
|
||||
import code.name.monkey.retromusic.model.Song;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.Unbinder;
|
||||
import code.name.monkey.retromusic.R;
|
||||
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget;
|
||||
import code.name.monkey.retromusic.glide.SongGlideRequest;
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote;
|
||||
import code.name.monkey.retromusic.ui.adapter.base.MediaEntryViewHolder;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.ObservableSource;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
import io.reactivex.functions.Function;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
|
||||
/**
|
||||
* Created by hemanths on 25/09/17.
|
||||
*/
|
||||
|
||||
public class RetroMusicAlbums extends DreamService {
|
||||
@BindView(R.id.recycler_view)
|
||||
RecyclerView mRecyclerView;
|
||||
@BindView(R.id.title)
|
||||
TextView mTitle;
|
||||
@BindView(R.id.text)
|
||||
TextView mText;
|
||||
@BindView(R.id.title_container)
|
||||
ViewGroup mViewGroup;
|
||||
|
||||
private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (intent == null) {
|
||||
return;
|
||||
}
|
||||
String artist = intent.getStringExtra("artist");
|
||||
String track = intent.getStringExtra("track");
|
||||
if (mViewGroup != null) {
|
||||
mViewGroup.setVisibility(View.VISIBLE);
|
||||
TransitionManager.beginDelayedTransition(mViewGroup);
|
||||
if (mTitle != null) {
|
||||
mTitle.setText(track);
|
||||
}
|
||||
if (mText != null) {
|
||||
mText.setText(artist);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
private Unbinder unbinder;
|
||||
private CompositeDisposable mDisposable;
|
||||
|
||||
@Override
|
||||
public void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
|
||||
View view = LayoutInflater.from(this).inflate(R.layout.dream_service, null);
|
||||
setContentView(view);
|
||||
unbinder = ButterKnife.bind(this, view);
|
||||
|
||||
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
|
||||
GridLayoutManager layoutManager = new GridLayoutManager(this, 4, LinearLayoutManager.VERTICAL, false);
|
||||
mRecyclerView.setLayoutManager(layoutManager);
|
||||
|
||||
|
||||
mDisposable.add(getPlayingQueue()
|
||||
.subscribeOn(Schedulers.computation())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.flatMap((Function<ArrayList<Song>, ObservableSource<ArrayList<Song>>>) songs -> Observable.create(e -> {
|
||||
if (songs.isEmpty()) {
|
||||
e.onNext(SongLoader.Companion.getAllSongs(RetroMusicAlbums.this).blockingFirst());
|
||||
} else {
|
||||
e.onNext(songs);
|
||||
}
|
||||
e.onComplete();
|
||||
}))
|
||||
.subscribe(songs -> {
|
||||
if (songs.size() > 0) {
|
||||
ImagesAdapter imagesAdapter = new ImagesAdapter(songs);
|
||||
mRecyclerView.setAdapter(imagesAdapter);
|
||||
}
|
||||
}));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
setInteractive(true);
|
||||
setFullscreen(true);
|
||||
|
||||
mDisposable = new CompositeDisposable();
|
||||
|
||||
IntentFilter iF = new IntentFilter();
|
||||
iF.addAction("com.android.music.musicservicecommand");
|
||||
iF.addAction("com.android.music.metachanged");
|
||||
registerReceiver(mBroadcastReceiver, iF);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
unbinder.unbind();
|
||||
mDisposable.clear();
|
||||
unregisterReceiver(mBroadcastReceiver);
|
||||
}
|
||||
|
||||
private Observable<ArrayList<Song>> getPlayingQueue() {
|
||||
return Observable.just(MusicPlayerRemote.getPlayingQueue());
|
||||
}
|
||||
|
||||
class ImagesAdapter extends RecyclerView.Adapter<ImagesAdapter.ViewHolder> {
|
||||
|
||||
private final ArrayList<Song> list;
|
||||
|
||||
public ImagesAdapter(ArrayList<Song> songs) {
|
||||
this.list = songs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
|
||||
return new ViewHolder(LayoutInflater.from(getApplicationContext())
|
||||
.inflate(R.layout.image, viewGroup, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(ViewHolder holder, int i) {
|
||||
Song song = list.get(i);
|
||||
SongGlideRequest.Builder.from(Glide.with(getApplicationContext()), song)
|
||||
.checkIgnoreMediaStore(getApplicationContext())
|
||||
.generatePalette(getApplicationContext()).build()
|
||||
.override(400, 400)
|
||||
.into(new RetroMusicColoredTarget(holder.image) {
|
||||
|
||||
@Override
|
||||
public void onColorReady(int color) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return list.size();
|
||||
}
|
||||
|
||||
class ViewHolder extends MediaEntryViewHolder {
|
||||
|
||||
public ViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
super.onClick(v);
|
||||
MusicPlayerRemote.openQueue(list, getAdapterPosition(), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,161 @@
|
|||
package code.name.monkey.retromusic.service.daydream
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.service.dreams.DreamService
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.recyclerview.widget.DefaultItemAnimator
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.transition.TransitionManager
|
||||
import butterknife.BindView
|
||||
import butterknife.ButterKnife
|
||||
import butterknife.Unbinder
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
||||
import code.name.monkey.retromusic.glide.SongGlideRequest
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.loaders.SongLoader
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.ui.adapter.base.MediaEntryViewHolder
|
||||
import com.bumptech.glide.Glide
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Created by hemanths on 25/09/17.
|
||||
*/
|
||||
|
||||
class RetroMusicAlbums : DreamService() {
|
||||
@BindView(R.id.recycler_view)
|
||||
internal var mRecyclerView: RecyclerView? = null
|
||||
@BindView(R.id.title)
|
||||
internal var mTitle: TextView? = null
|
||||
@BindView(R.id.text)
|
||||
internal var mText: TextView? = null
|
||||
@BindView(R.id.title_container)
|
||||
internal var mViewGroup: ViewGroup? = null
|
||||
|
||||
private val mBroadcastReceiver = object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent?) {
|
||||
if (intent == null) {
|
||||
return
|
||||
}
|
||||
val artist = intent.getStringExtra("artist")
|
||||
val track = intent.getStringExtra("track")
|
||||
if (mViewGroup != null) {
|
||||
mViewGroup!!.visibility = View.VISIBLE
|
||||
TransitionManager.beginDelayedTransition(mViewGroup!!)
|
||||
if (mTitle != null) {
|
||||
mTitle!!.text = track
|
||||
}
|
||||
if (mText != null) {
|
||||
mText!!.text = artist
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
private var unbinder: Unbinder? = null
|
||||
private var mDisposable: CompositeDisposable? = null
|
||||
|
||||
private val playingQueue: Observable<ArrayList<Song>>
|
||||
get() = Observable.just(MusicPlayerRemote.playingQueue)
|
||||
|
||||
override fun onAttachedToWindow() {
|
||||
super.onAttachedToWindow()
|
||||
|
||||
val view = LayoutInflater.from(this).inflate(R.layout.dream_service, null)
|
||||
setContentView(view)
|
||||
unbinder = ButterKnife.bind(this, view)
|
||||
|
||||
mRecyclerView!!.itemAnimator = DefaultItemAnimator()
|
||||
val layoutManager = GridLayoutManager(this, 4, RecyclerView.VERTICAL, false)
|
||||
mRecyclerView!!.layoutManager = layoutManager
|
||||
|
||||
|
||||
mDisposable!!.add(playingQueue
|
||||
.subscribeOn(Schedulers.computation())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.flatMap {
|
||||
return@flatMap Observable.create<ArrayList<Song>> { e ->
|
||||
if (it.isEmpty()) {
|
||||
e.onNext(SongLoader.getAllSongs(this@RetroMusicAlbums).blockingFirst())
|
||||
} else {
|
||||
e.onNext(it)
|
||||
}
|
||||
e.onComplete()
|
||||
}
|
||||
}
|
||||
.subscribe { songs ->
|
||||
if (songs.size > 0) {
|
||||
val imagesAdapter = ImagesAdapter(songs)
|
||||
mRecyclerView!!.adapter = imagesAdapter
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
isInteractive = true
|
||||
isFullscreen = true
|
||||
|
||||
mDisposable = CompositeDisposable()
|
||||
|
||||
val iF = IntentFilter()
|
||||
iF.addAction("com.android.music.musicservicecommand")
|
||||
iF.addAction("com.android.music.metachanged")
|
||||
registerReceiver(mBroadcastReceiver, iF)
|
||||
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
unbinder!!.unbind()
|
||||
mDisposable!!.clear()
|
||||
unregisterReceiver(mBroadcastReceiver)
|
||||
}
|
||||
|
||||
internal inner class ImagesAdapter(private val list: ArrayList<Song>) : RecyclerView.Adapter<ImagesAdapter.ViewHolder>() {
|
||||
|
||||
override fun onCreateViewHolder(viewGroup: ViewGroup, i: Int): ViewHolder {
|
||||
return ViewHolder(LayoutInflater.from(applicationContext)
|
||||
.inflate(R.layout.image, viewGroup, false))
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, i: Int) {
|
||||
val song = list[i]
|
||||
SongGlideRequest.Builder.from(Glide.with(applicationContext), song)
|
||||
.checkIgnoreMediaStore(applicationContext)
|
||||
.generatePalette(applicationContext).build()
|
||||
.override(400, 400)
|
||||
.into(object : RetroMusicColoredTarget(holder.image!!) {
|
||||
override fun onColorReady(color: Int) {
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return list.size
|
||||
}
|
||||
|
||||
internal inner class ViewHolder(itemView: View) : MediaEntryViewHolder(itemView) {
|
||||
|
||||
override fun onClick(v: View?) {
|
||||
super.onClick(v)
|
||||
MusicPlayerRemote.openQueue(list, adapterPosition, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -28,7 +28,6 @@ import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper;
|
|||
import code.name.monkey.retromusic.model.Song;
|
||||
import code.name.monkey.retromusic.service.MusicService;
|
||||
import code.name.monkey.retromusic.ui.activities.MainActivity;
|
||||
import code.name.monkey.retromusic.ui.activities.NowPayingActivity;
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil;
|
||||
import code.name.monkey.retromusic.util.RetroColorUtil;
|
||||
import code.name.monkey.retromusic.util.RetroUtil;
|
||||
|
@ -57,27 +56,27 @@ public class PlayingNotificationImpl extends PlayingNotification {
|
|||
final RemoteViews notificationLayoutBig = new RemoteViews(service.getPackageName(),
|
||||
R.layout.notification_big);
|
||||
|
||||
if (TextUtils.isEmpty(song.title) && TextUtils.isEmpty(song.artistName)) {
|
||||
if (TextUtils.isEmpty(song.getTitle()) && TextUtils.isEmpty(song.getArtistName())) {
|
||||
notificationLayout.setViewVisibility(R.id.media_titles, View.INVISIBLE);
|
||||
} else {
|
||||
notificationLayout.setViewVisibility(R.id.media_titles, View.VISIBLE);
|
||||
notificationLayout.setTextViewText(R.id.title, song.title);
|
||||
notificationLayout.setTextViewText(R.id.text, song.artistName);
|
||||
notificationLayout.setTextViewText(R.id.title, song.getTitle());
|
||||
notificationLayout.setTextViewText(R.id.text, song.getArtistName());
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(song.title) && TextUtils.isEmpty(song.artistName) && TextUtils
|
||||
.isEmpty(song.albumName)) {
|
||||
if (TextUtils.isEmpty(song.getTitle()) && TextUtils.isEmpty(song.getArtistName()) && TextUtils
|
||||
.isEmpty(song.getAlbumName())) {
|
||||
notificationLayoutBig.setViewVisibility(R.id.media_titles, View.INVISIBLE);
|
||||
} else {
|
||||
notificationLayoutBig.setViewVisibility(R.id.media_titles, View.VISIBLE);
|
||||
notificationLayoutBig.setTextViewText(R.id.title, song.title);
|
||||
notificationLayoutBig.setTextViewText(R.id.text, song.artistName);
|
||||
notificationLayoutBig.setTextViewText(R.id.text2, song.albumName);
|
||||
notificationLayoutBig.setTextViewText(R.id.title, song.getTitle());
|
||||
notificationLayoutBig.setTextViewText(R.id.text, song.getArtistName());
|
||||
notificationLayoutBig.setTextViewText(R.id.text2, song.getAlbumName());
|
||||
}
|
||||
|
||||
linkButtons(notificationLayout, notificationLayoutBig);
|
||||
|
||||
Intent action = new Intent(service, NowPayingActivity.class);
|
||||
Intent action = new Intent(service, MainActivity.class);
|
||||
action.putExtra("expand", true);
|
||||
action.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
|
||||
|
|
|
@ -16,13 +16,12 @@ import com.bumptech.glide.request.target.SimpleTarget;
|
|||
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.media.app.NotificationCompat.MediaStyle;
|
||||
import code.name.monkey.retromusic.Constants;
|
||||
import code.name.monkey.retromusic.R;
|
||||
import code.name.monkey.retromusic.glide.SongGlideRequest;
|
||||
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper;
|
||||
import code.name.monkey.retromusic.model.Song;
|
||||
import code.name.monkey.retromusic.service.MusicService;
|
||||
import code.name.monkey.retromusic.ui.activities.NowPayingActivity;
|
||||
import code.name.monkey.retromusic.ui.activities.MainActivity;
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil;
|
||||
import code.name.monkey.retromusic.util.RetroColorUtil;
|
||||
|
||||
|
@ -43,14 +42,14 @@ public class PlayingNotificationImpl24 extends PlayingNotification {
|
|||
final int playButtonResId = isPlaying ? R.drawable.ic_pause_white_24dp :
|
||||
R.drawable.ic_play_arrow_white_24dp;
|
||||
|
||||
Intent action = new Intent(service, NowPayingActivity.class);
|
||||
Intent action = new Intent(service, MainActivity.class);
|
||||
action.putExtra("expand", true);
|
||||
action.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
final PendingIntent clickIntent = PendingIntent
|
||||
.getActivity(service, 0, action, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
|
||||
final ComponentName serviceName = new ComponentName(service, MusicService.class);
|
||||
Intent intent = new Intent(Constants.ACTION_QUIT);
|
||||
Intent intent = new Intent(ACTION_QUIT);
|
||||
intent.setComponent(serviceName);
|
||||
final PendingIntent deleteIntent = PendingIntent.getService(service, 0, intent, 0);
|
||||
|
||||
|
@ -106,9 +105,9 @@ public class PlayingNotificationImpl24 extends PlayingNotification {
|
|||
.setLargeIcon(bitmap)
|
||||
.setContentIntent(clickIntent)
|
||||
.setDeleteIntent(deleteIntent)
|
||||
.setContentTitle(Html.fromHtml("<b>" + song.title + "</b>"))
|
||||
.setContentText(song.artistName)
|
||||
.setSubText(Html.fromHtml("<b>" + song.albumName + "</b>"))
|
||||
.setContentTitle(Html.fromHtml("<b>" + song.getTitle() + "</b>"))
|
||||
.setContentText(song.getArtistName())
|
||||
.setSubText(Html.fromHtml("<b>" + song.getAlbumName() + "</b>"))
|
||||
.setOngoing(isPlaying)
|
||||
.setShowWhen(false)
|
||||
.addAction(previousAction)
|
||||
|
|
|
@ -1,248 +0,0 @@
|
|||
package code.name.monkey.retromusic.service.notification;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.widget.RemoteViews;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.request.animation.GlideAnimation;
|
||||
import com.bumptech.glide.request.target.SimpleTarget;
|
||||
import com.bumptech.glide.request.target.Target;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil;
|
||||
import code.name.monkey.appthemehelper.util.MaterialValueHelper;
|
||||
import code.name.monkey.retromusic.R;
|
||||
import code.name.monkey.retromusic.glide.SongGlideRequest;
|
||||
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper;
|
||||
import code.name.monkey.retromusic.model.Song;
|
||||
import code.name.monkey.retromusic.service.MusicService;
|
||||
import code.name.monkey.retromusic.ui.activities.NowPayingActivity;
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil;
|
||||
import code.name.monkey.retromusic.util.RetroUtil;
|
||||
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor;
|
||||
|
||||
import static code.name.monkey.retromusic.Constants.ACTION_QUIT;
|
||||
import static code.name.monkey.retromusic.Constants.ACTION_REWIND;
|
||||
import static code.name.monkey.retromusic.Constants.ACTION_SKIP;
|
||||
import static code.name.monkey.retromusic.Constants.ACTION_TOGGLE_PAUSE;
|
||||
import static code.name.monkey.retromusic.util.RetroUtil.createBitmap;
|
||||
|
||||
/**
|
||||
* @author Hemanth S (h4h13).
|
||||
*/
|
||||
public class PlayingNotificationOreo extends PlayingNotification {
|
||||
|
||||
private Target<BitmapPaletteWrapper> target;
|
||||
|
||||
private RemoteViews getCombinedRemoteViews(boolean collapsed, Song song) {
|
||||
RemoteViews remoteViews = new RemoteViews(service.getPackageName(),
|
||||
collapsed ? R.layout.layout_notification_collapsed : R.layout.layout_notification_expanded);
|
||||
|
||||
remoteViews.setTextViewText(R.id.appName,
|
||||
service.getString(R.string.app_name) + " • " + song.albumName);
|
||||
remoteViews.setTextViewText(R.id.title, song.title);
|
||||
remoteViews.setTextViewText(R.id.subtitle, song.artistName);
|
||||
|
||||
TypedArray typedArray = service
|
||||
.obtainStyledAttributes(new int[]{android.R.attr.selectableItemBackground});
|
||||
int selectableItemBackground = typedArray.getResourceId(0, 0);
|
||||
typedArray.recycle();
|
||||
|
||||
remoteViews.setInt(R.id.content, "setBackgroundResource", selectableItemBackground);
|
||||
|
||||
linkButtons(remoteViews);
|
||||
|
||||
//setNotificationContent(remoteViews, ColorUtil.isColorLight(backgroundColor));
|
||||
return remoteViews;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update() {
|
||||
stopped = false;
|
||||
final Song song = service.getCurrentSong();
|
||||
final boolean isPlaying = service.isPlaying();
|
||||
|
||||
final RemoteViews notificationLayout = getCombinedRemoteViews(true, song);
|
||||
final RemoteViews notificationLayoutBig = getCombinedRemoteViews(false, song);
|
||||
|
||||
Intent action = new Intent(service, NowPayingActivity.class);
|
||||
action.putExtra("expand", true);
|
||||
action.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
|
||||
final PendingIntent clickIntent = PendingIntent
|
||||
.getActivity(service, 0, action, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
final PendingIntent deleteIntent = buildPendingIntent(service, ACTION_QUIT, null);
|
||||
|
||||
final NotificationCompat.Builder builder = new NotificationCompat.Builder(service,
|
||||
NOTIFICATION_CHANNEL_ID)
|
||||
.setSmallIcon(R.drawable.ic_notification)
|
||||
.setContentIntent(clickIntent)
|
||||
.setDeleteIntent(deleteIntent)
|
||||
.setCategory(NotificationCompat.CATEGORY_SERVICE)
|
||||
.setPriority(NotificationCompat.PRIORITY_MAX)
|
||||
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||
.setCustomContentView(notificationLayout)
|
||||
.setCustomBigContentView(notificationLayoutBig)
|
||||
.setOngoing(isPlaying);
|
||||
|
||||
final int bigNotificationImageSize = service.getResources()
|
||||
.getDimensionPixelSize(R.dimen.notification_big_image_size);
|
||||
service.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (target != null) {
|
||||
Glide.clear(target);
|
||||
}
|
||||
target = SongGlideRequest.Builder.from(Glide.with(service), song)
|
||||
.checkIgnoreMediaStore(service)
|
||||
.generatePalette(service).build()
|
||||
.into(new SimpleTarget<BitmapPaletteWrapper>(bigNotificationImageSize,
|
||||
bigNotificationImageSize) {
|
||||
@Override
|
||||
public void onResourceReady(BitmapPaletteWrapper resource,
|
||||
GlideAnimation<? super BitmapPaletteWrapper> glideAnimation) {
|
||||
|
||||
MediaNotificationProcessor mediaNotificationProcessor = new MediaNotificationProcessor(
|
||||
service, service, (i, i2) -> update(resource.getBitmap(), i, i2));
|
||||
mediaNotificationProcessor.processNotification(resource.getBitmap());
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadFailed(Exception e, Drawable errorDrawable) {
|
||||
super.onLoadFailed(e, errorDrawable);
|
||||
update(null, Color.WHITE, Color.BLACK);
|
||||
}
|
||||
|
||||
private void update(@Nullable Bitmap bitmap, int bgColor, int textColor) {
|
||||
if (bitmap != null) {
|
||||
notificationLayout.setImageViewBitmap(R.id.largeIcon, bitmap);
|
||||
notificationLayoutBig.setImageViewBitmap(R.id.largeIcon, bitmap);
|
||||
} else {
|
||||
notificationLayout
|
||||
.setImageViewResource(R.id.largeIcon, R.drawable.default_album_art);
|
||||
notificationLayoutBig
|
||||
.setImageViewResource(R.id.largeIcon, R.drawable.default_album_art);
|
||||
}
|
||||
|
||||
if (!PreferenceUtil.getInstance().coloredNotification()) {
|
||||
bgColor = Color.WHITE;
|
||||
}
|
||||
setBackgroundColor(bgColor);
|
||||
setNotificationContent(ColorUtil.isColorLight(bgColor));
|
||||
|
||||
if (stopped) {
|
||||
return; // notification has been stopped before loading was finished
|
||||
}
|
||||
updateNotifyModeAndPostNotification(builder.build());
|
||||
}
|
||||
|
||||
private void setBackgroundColor(int color) {
|
||||
|
||||
notificationLayout.setInt(R.id.image, "setBackgroundColor", color);
|
||||
notificationLayoutBig.setInt(R.id.image, "setBackgroundColor", color);
|
||||
|
||||
notificationLayout.setInt(R.id.foregroundImage, "setColorFilter", color);
|
||||
notificationLayoutBig.setInt(R.id.foregroundImage, "setColorFilter", color);
|
||||
}
|
||||
|
||||
private void setNotificationContent(boolean dark) {
|
||||
int primary = MaterialValueHelper.getPrimaryTextColor(service, dark);
|
||||
int secondary = MaterialValueHelper.getSecondaryTextColor(service, dark);
|
||||
|
||||
Bitmap close = createBitmap(
|
||||
RetroUtil
|
||||
.getTintedVectorDrawable(service, R.drawable.ic_close_white_24dp, primary),
|
||||
NOTIFICATION_CONTROLS_SIZE_MULTIPLIER);
|
||||
Bitmap prev = createBitmap(
|
||||
RetroUtil
|
||||
.getTintedVectorDrawable(service, R.drawable.ic_skip_previous_white_24dp,
|
||||
primary), NOTIFICATION_CONTROLS_SIZE_MULTIPLIER);
|
||||
Bitmap next = createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(service, R.drawable.ic_skip_next_white_24dp,
|
||||
primary), NOTIFICATION_CONTROLS_SIZE_MULTIPLIER);
|
||||
Bitmap playPause = createBitmap(RetroUtil.getTintedVectorDrawable(service,
|
||||
isPlaying ? R.drawable.ic_pause_white_24dp
|
||||
: R.drawable.ic_play_arrow_white_24dp, primary),
|
||||
NOTIFICATION_CONTROLS_SIZE_MULTIPLIER);
|
||||
|
||||
notificationLayout.setTextColor(R.id.title, primary);
|
||||
notificationLayout.setTextColor(R.id.subtitle, secondary);
|
||||
notificationLayout.setTextColor(R.id.appName, secondary);
|
||||
|
||||
notificationLayout.setImageViewBitmap(R.id.action_prev, prev);
|
||||
notificationLayout.setImageViewBitmap(R.id.action_next, next);
|
||||
notificationLayout.setImageViewBitmap(R.id.action_play_pause, playPause);
|
||||
|
||||
notificationLayoutBig.setTextColor(R.id.title, primary);
|
||||
notificationLayoutBig.setTextColor(R.id.subtitle, secondary);
|
||||
notificationLayoutBig.setTextColor(R.id.appName, secondary);
|
||||
|
||||
notificationLayoutBig.setImageViewBitmap(R.id.action_quit, close);
|
||||
notificationLayoutBig.setImageViewBitmap(R.id.action_prev, prev);
|
||||
notificationLayoutBig.setImageViewBitmap(R.id.action_next, next);
|
||||
notificationLayoutBig.setImageViewBitmap(R.id.action_play_pause, playPause);
|
||||
|
||||
notificationLayout.setImageViewBitmap(R.id.smallIcon,
|
||||
createBitmap(RetroUtil
|
||||
.getTintedVectorDrawable(service, R.drawable.ic_notification, secondary),
|
||||
0.6f));
|
||||
notificationLayoutBig.setImageViewBitmap(R.id.smallIcon,
|
||||
createBitmap(RetroUtil
|
||||
.getTintedVectorDrawable(service, R.drawable.ic_notification, secondary),
|
||||
0.6f));
|
||||
|
||||
notificationLayout.setInt(R.id.arrow, "setColorFilter", secondary);
|
||||
notificationLayoutBig.setInt(R.id.arrow, "setColorFilter", secondary);
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (stopped) {
|
||||
return; // notification has been stopped before loading was finished
|
||||
}
|
||||
updateNotifyModeAndPostNotification(builder.build());
|
||||
}
|
||||
|
||||
|
||||
private PendingIntent buildPendingIntent(Context context, final String action,
|
||||
final ComponentName serviceName) {
|
||||
Intent intent = new Intent(action);
|
||||
intent.setComponent(serviceName);
|
||||
return PendingIntent.getService(context, 0, intent, 0);
|
||||
}
|
||||
|
||||
|
||||
private void linkButtons(final RemoteViews notificationLayout) {
|
||||
PendingIntent pendingIntent;
|
||||
|
||||
final ComponentName serviceName = new ComponentName(service, MusicService.class);
|
||||
|
||||
// Previous track
|
||||
pendingIntent = buildPendingIntent(service, ACTION_REWIND, serviceName);
|
||||
notificationLayout.setOnClickPendingIntent(R.id.action_prev, pendingIntent);
|
||||
|
||||
// Play and pause
|
||||
pendingIntent = buildPendingIntent(service, ACTION_TOGGLE_PAUSE, serviceName);
|
||||
notificationLayout.setOnClickPendingIntent(R.id.action_play_pause, pendingIntent);
|
||||
|
||||
// Next track
|
||||
pendingIntent = buildPendingIntent(service, ACTION_SKIP, serviceName);
|
||||
notificationLayout.setOnClickPendingIntent(R.id.action_next, pendingIntent);
|
||||
|
||||
// Close
|
||||
pendingIntent = buildPendingIntent(service, ACTION_QUIT, serviceName);
|
||||
notificationLayout.setOnClickPendingIntent(R.id.action_quit, pendingIntent);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,221 @@
|
|||
package code.name.monkey.retromusic.service.notification
|
||||
|
||||
import android.app.PendingIntent
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.widget.RemoteViews
|
||||
import androidx.core.app.NotificationCompat
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||
import code.name.monkey.retromusic.Constants.ACTION_QUIT
|
||||
import code.name.monkey.retromusic.Constants.ACTION_REWIND
|
||||
import code.name.monkey.retromusic.Constants.ACTION_SKIP
|
||||
import code.name.monkey.retromusic.Constants.ACTION_TOGGLE_PAUSE
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.glide.SongGlideRequest
|
||||
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.service.MusicService
|
||||
import code.name.monkey.retromusic.ui.activities.MainActivity
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import code.name.monkey.retromusic.util.RetroUtil
|
||||
import code.name.monkey.retromusic.util.RetroUtil.createBitmap
|
||||
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.request.animation.GlideAnimation
|
||||
import com.bumptech.glide.request.target.SimpleTarget
|
||||
import com.bumptech.glide.request.target.Target
|
||||
|
||||
/**
|
||||
* @author Hemanth S (h4h13).
|
||||
*/
|
||||
class PlayingNotificationOreo : PlayingNotification() {
|
||||
|
||||
private var target: Target<BitmapPaletteWrapper>? = null
|
||||
|
||||
private fun getCombinedRemoteViews(collapsed: Boolean, song: Song): RemoteViews {
|
||||
val remoteViews = RemoteViews(service.packageName,
|
||||
if (collapsed) R.layout.layout_notification_collapsed else R.layout.layout_notification_expanded)
|
||||
|
||||
remoteViews.setTextViewText(R.id.appName, service.getString(R.string.app_name) + " • " + song.albumName)
|
||||
remoteViews.setTextViewText(R.id.title, song.title)
|
||||
remoteViews.setTextViewText(R.id.subtitle, song.artistName)
|
||||
|
||||
val typedArray = service.obtainStyledAttributes(intArrayOf(android.R.attr.selectableItemBackground))
|
||||
val selectableItemBackground = typedArray.getResourceId(0, 0)
|
||||
typedArray.recycle()
|
||||
|
||||
remoteViews.setInt(R.id.content, "setBackgroundResource", selectableItemBackground)
|
||||
|
||||
linkButtons(remoteViews)
|
||||
|
||||
//setNotificationContent(remoteViews, ColorUtil.isColorLight(backgroundColor));
|
||||
return remoteViews
|
||||
}
|
||||
|
||||
override fun update() {
|
||||
stopped = false
|
||||
val song = service.currentSong
|
||||
val isPlaying = service.isPlaying
|
||||
|
||||
val notificationLayout = getCombinedRemoteViews(true, song)
|
||||
val notificationLayoutBig = getCombinedRemoteViews(false, song)
|
||||
|
||||
val action = Intent(service, MainActivity::class.java)
|
||||
action.putExtra("expand", true)
|
||||
action.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
|
||||
|
||||
val clickIntent = PendingIntent
|
||||
.getActivity(service, 0, action, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
val deleteIntent = buildPendingIntent(service, ACTION_QUIT, null)
|
||||
|
||||
val builder = NotificationCompat.Builder(service,
|
||||
PlayingNotification.NOTIFICATION_CHANNEL_ID)
|
||||
.setSmallIcon(R.drawable.ic_notification)
|
||||
.setContentIntent(clickIntent)
|
||||
.setDeleteIntent(deleteIntent)
|
||||
.setCategory(NotificationCompat.CATEGORY_SERVICE)
|
||||
.setPriority(NotificationCompat.PRIORITY_MAX)
|
||||
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||
.setCustomContentView(notificationLayout)
|
||||
.setCustomBigContentView(notificationLayoutBig)
|
||||
.setOngoing(isPlaying)
|
||||
|
||||
val bigNotificationImageSize = service.resources
|
||||
.getDimensionPixelSize(R.dimen.notification_big_image_size)
|
||||
service.runOnUiThread(Runnable {
|
||||
if (target != null) {
|
||||
Glide.clear(target!!)
|
||||
}
|
||||
target = SongGlideRequest.Builder.from(Glide.with(service), song)
|
||||
.checkIgnoreMediaStore(service)
|
||||
.generatePalette(service).build()
|
||||
.into(object : SimpleTarget<BitmapPaletteWrapper>(bigNotificationImageSize,
|
||||
bigNotificationImageSize) {
|
||||
override fun onResourceReady(resource: BitmapPaletteWrapper,
|
||||
glideAnimation: GlideAnimation<in BitmapPaletteWrapper>) {
|
||||
|
||||
val mediaNotificationProcessor = MediaNotificationProcessor(
|
||||
service, service) { i, _ -> update(resource.bitmap, i) }
|
||||
mediaNotificationProcessor.processNotification(resource.bitmap)
|
||||
|
||||
}
|
||||
|
||||
override fun onLoadFailed(e: Exception?, errorDrawable: Drawable?) {
|
||||
super.onLoadFailed(e, errorDrawable)
|
||||
update(null, Color.WHITE)
|
||||
}
|
||||
|
||||
private fun update(bitmap: Bitmap?, bgColor: Int) {
|
||||
var bgColorFinal = bgColor
|
||||
if (bitmap != null) {
|
||||
notificationLayout.setImageViewBitmap(R.id.largeIcon, bitmap)
|
||||
notificationLayoutBig.setImageViewBitmap(R.id.largeIcon, bitmap)
|
||||
} else {
|
||||
notificationLayout.setImageViewResource(R.id.largeIcon, R.drawable.default_album_art)
|
||||
notificationLayoutBig.setImageViewResource(R.id.largeIcon, R.drawable.default_album_art)
|
||||
}
|
||||
|
||||
if (!PreferenceUtil.getInstance().coloredNotification()) {
|
||||
bgColorFinal = Color.WHITE
|
||||
}
|
||||
setBackgroundColor(bgColorFinal)
|
||||
setNotificationContent(ColorUtil.isColorLight(bgColorFinal))
|
||||
|
||||
if (stopped) {
|
||||
return // notification has been stopped before loading was finished
|
||||
}
|
||||
updateNotifyModeAndPostNotification(builder.build())
|
||||
}
|
||||
|
||||
private fun setBackgroundColor(color: Int) {
|
||||
|
||||
notificationLayout.setInt(R.id.image, "setBackgroundColor", color)
|
||||
notificationLayoutBig.setInt(R.id.image, "setBackgroundColor", color)
|
||||
|
||||
notificationLayout.setInt(R.id.foregroundImage, "setColorFilter", color)
|
||||
notificationLayoutBig.setInt(R.id.foregroundImage, "setColorFilter", color)
|
||||
}
|
||||
|
||||
private fun setNotificationContent(dark: Boolean) {
|
||||
val primary = MaterialValueHelper.getPrimaryTextColor(service, dark)
|
||||
val secondary = MaterialValueHelper.getSecondaryTextColor(service, dark)
|
||||
|
||||
val close = createBitmap(RetroUtil.getTintedVectorDrawable(service, R.drawable.ic_close_white_24dp, primary)!!, PlayingNotification.NOTIFICATION_CONTROLS_SIZE_MULTIPLIER)
|
||||
val prev = createBitmap(RetroUtil.getTintedVectorDrawable(service, R.drawable.ic_skip_previous_white_24dp, primary)!!, PlayingNotification.NOTIFICATION_CONTROLS_SIZE_MULTIPLIER)
|
||||
val next = createBitmap(RetroUtil.getTintedVectorDrawable(service, R.drawable.ic_skip_next_white_24dp, primary)!!, PlayingNotification.NOTIFICATION_CONTROLS_SIZE_MULTIPLIER)
|
||||
val playPause = createBitmap(RetroUtil.getTintedVectorDrawable(service,
|
||||
if (isPlaying)
|
||||
R.drawable.ic_pause_white_24dp
|
||||
else
|
||||
R.drawable.ic_play_arrow_white_24dp, primary)!!, PlayingNotification.NOTIFICATION_CONTROLS_SIZE_MULTIPLIER)
|
||||
|
||||
notificationLayout.setTextColor(R.id.title, primary)
|
||||
notificationLayout.setTextColor(R.id.subtitle, secondary)
|
||||
notificationLayout.setTextColor(R.id.appName, secondary)
|
||||
|
||||
notificationLayout.setImageViewBitmap(R.id.action_prev, prev)
|
||||
notificationLayout.setImageViewBitmap(R.id.action_next, next)
|
||||
notificationLayout.setImageViewBitmap(R.id.action_play_pause, playPause)
|
||||
|
||||
notificationLayoutBig.setTextColor(R.id.title, primary)
|
||||
notificationLayoutBig.setTextColor(R.id.subtitle, secondary)
|
||||
notificationLayoutBig.setTextColor(R.id.appName, secondary)
|
||||
|
||||
notificationLayoutBig.setImageViewBitmap(R.id.action_quit, close)
|
||||
notificationLayoutBig.setImageViewBitmap(R.id.action_prev, prev)
|
||||
notificationLayoutBig.setImageViewBitmap(R.id.action_next, next)
|
||||
notificationLayoutBig.setImageViewBitmap(R.id.action_play_pause, playPause)
|
||||
|
||||
notificationLayout.setImageViewBitmap(R.id.smallIcon, createBitmap(RetroUtil.getTintedVectorDrawable(service, R.drawable.ic_notification, secondary)!!, 0.6f))
|
||||
notificationLayoutBig.setImageViewBitmap(R.id.smallIcon, createBitmap(RetroUtil.getTintedVectorDrawable(service, R.drawable.ic_notification, secondary)!!, 0.6f))
|
||||
|
||||
notificationLayout.setInt(R.id.arrow, "setColorFilter", secondary)
|
||||
notificationLayoutBig.setInt(R.id.arrow, "setColorFilter", secondary)
|
||||
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
if (stopped) {
|
||||
return // notification has been stopped before loading was finished
|
||||
}
|
||||
updateNotifyModeAndPostNotification(builder.build())
|
||||
}
|
||||
|
||||
|
||||
private fun buildPendingIntent(context: Context, action: String,
|
||||
serviceName: ComponentName?): PendingIntent {
|
||||
val intent = Intent(action)
|
||||
intent.component = serviceName
|
||||
return PendingIntent.getService(context, 0, intent, 0)
|
||||
}
|
||||
|
||||
|
||||
private fun linkButtons(notificationLayout: RemoteViews) {
|
||||
var pendingIntent: PendingIntent
|
||||
|
||||
val serviceName = ComponentName(service, MusicService::class.java)
|
||||
|
||||
// Previous track
|
||||
pendingIntent = buildPendingIntent(service, ACTION_REWIND, serviceName)
|
||||
notificationLayout.setOnClickPendingIntent(R.id.action_prev, pendingIntent)
|
||||
|
||||
// Play and pause
|
||||
pendingIntent = buildPendingIntent(service, ACTION_TOGGLE_PAUSE, serviceName)
|
||||
notificationLayout.setOnClickPendingIntent(R.id.action_play_pause, pendingIntent)
|
||||
|
||||
// Next track
|
||||
pendingIntent = buildPendingIntent(service, ACTION_SKIP, serviceName)
|
||||
notificationLayout.setOnClickPendingIntent(R.id.action_next, pendingIntent)
|
||||
|
||||
// Close
|
||||
pendingIntent = buildPendingIntent(service, ACTION_QUIT, serviceName)
|
||||
notificationLayout.setOnClickPendingIntent(R.id.action_quit, pendingIntent)
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue