mirror of
https://github.com/antebudimir/tempus.git
synced 2026-01-01 18:03:33 +00:00
Refactor project structure
This commit is contained in:
parent
a26c127356
commit
2d886b67ff
46 changed files with 158 additions and 361 deletions
|
|
@ -1,58 +1,26 @@
|
|||
/*
|
||||
* Copyright (C) 2017 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 com.cappielloantonio.play.service;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.provider.MediaStore;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
|
||||
import com.cappielloantonio.play.App;
|
||||
import com.cappielloantonio.play.model.Song;
|
||||
import com.cappielloantonio.play.repository.SongRepository;
|
||||
import com.cappielloantonio.play.util.MusicUtil;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.MediaItem;
|
||||
import com.google.android.exoplayer2.RenderersFactory;
|
||||
import com.google.android.exoplayer2.drm.DrmInitData;
|
||||
import com.google.android.exoplayer2.drm.DrmSession;
|
||||
import com.google.android.exoplayer2.drm.DrmSessionEventListener;
|
||||
import com.google.android.exoplayer2.drm.OfflineLicenseHelper;
|
||||
import com.google.android.exoplayer2.offline.Download;
|
||||
import com.google.android.exoplayer2.offline.DownloadCursor;
|
||||
import com.google.android.exoplayer2.offline.DownloadHelper;
|
||||
import com.google.android.exoplayer2.offline.DownloadHelper.LiveContentUnsupportedException;
|
||||
import com.google.android.exoplayer2.offline.DownloadIndex;
|
||||
import com.google.android.exoplayer2.offline.DownloadManager;
|
||||
import com.google.android.exoplayer2.offline.DownloadRequest;
|
||||
import com.google.android.exoplayer2.offline.DownloadService;
|
||||
import com.google.android.exoplayer2.source.TrackGroup;
|
||||
import com.google.android.exoplayer2.source.TrackGroupArray;
|
||||
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
|
||||
import com.google.android.exoplayer2.trackselection.MappingTrackSelector.MappedTrackInfo;
|
||||
import com.google.android.exoplayer2.upstream.HttpDataSource;
|
||||
import com.google.android.exoplayer2.util.Log;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
|
|
@ -62,9 +30,6 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
|||
import static com.google.android.exoplayer2.util.Assertions.checkNotNull;
|
||||
import static com.google.android.exoplayer2.util.Assertions.checkStateNotNull;
|
||||
|
||||
/**
|
||||
* Tracks media that has been downloaded.
|
||||
*/
|
||||
public class DownloadTracker {
|
||||
|
||||
private static final String TAG = "DownloadTracker";
|
||||
|
|
@ -116,10 +81,10 @@ public class DownloadTracker {
|
|||
|
||||
if (download != null && download.state != Download.STATE_FAILED) {
|
||||
song.setOffline(false);
|
||||
DownloadService.sendRemoveDownload(context, PlayDownloadService.class, download.request.id, false);
|
||||
DownloadService.sendRemoveDownload(context, DownloaderService.class, download.request.id, false);
|
||||
} else {
|
||||
song.setOffline(true);
|
||||
DownloadService.sendAddDownload(context, PlayDownloadService.class, getDownloadRequest(mediaItem.playbackProperties.uri),false);
|
||||
DownloadService.sendAddDownload(context, DownloaderService.class, getDownloadRequest(mediaItem.playbackProperties.uri),false);
|
||||
}
|
||||
|
||||
songRepository.setOfflineStatus(song);
|
||||
|
|
@ -129,7 +94,7 @@ public class DownloadTracker {
|
|||
public void removeAllDownloads() {
|
||||
SongRepository songRepository = new SongRepository(App.getInstance());
|
||||
songRepository.setAllOffline();
|
||||
DownloadService.sendRemoveAllDownloads(context, PlayDownloadService.class, false);
|
||||
DownloadService.sendRemoveAllDownloads(context, DownloaderService.class, false);
|
||||
}
|
||||
|
||||
private void loadDownloads() {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,84 @@
|
|||
package com.cappielloantonio.play.service;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.cappielloantonio.play.R;
|
||||
import com.cappielloantonio.play.util.DownloadUtil;
|
||||
import com.google.android.exoplayer2.offline.Download;
|
||||
import com.google.android.exoplayer2.offline.DownloadManager;
|
||||
import com.google.android.exoplayer2.offline.DownloadService;
|
||||
import com.google.android.exoplayer2.scheduler.Scheduler;
|
||||
import com.google.android.exoplayer2.ui.DownloadNotificationHelper;
|
||||
import com.google.android.exoplayer2.util.NotificationUtil;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class DownloaderService extends DownloadService {
|
||||
|
||||
private static final int FOREGROUND_NOTIFICATION_ID = 1;
|
||||
|
||||
public DownloaderService() {
|
||||
super(FOREGROUND_NOTIFICATION_ID, DEFAULT_FOREGROUND_NOTIFICATION_UPDATE_INTERVAL, DownloadUtil.DOWNLOAD_NOTIFICATION_CHANNEL_ID, R.string.exo_download_notification_channel_name, 0);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected Scheduler getScheduler() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
protected DownloadManager getDownloadManager() {
|
||||
DownloadManager downloadManager = DownloadUtil.getDownloadManager(this);
|
||||
DownloadNotificationHelper downloadNotificationHelper = DownloadUtil.getDownloadNotificationHelper(this);
|
||||
downloadManager.addListener(new TerminalStateNotificationHelper(this, downloadNotificationHelper, FOREGROUND_NOTIFICATION_ID + 1));
|
||||
return downloadManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
protected Notification getForegroundNotification(@NonNull List<Download> downloads) {
|
||||
return DownloadUtil.getDownloadNotificationHelper(this).buildProgressNotification(this, R.drawable.ic_downloading,null, null, downloads);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and displays notifications for downloads when they complete or fail.
|
||||
*
|
||||
* <p>This helper will outlive the lifespan of a single instance of DemoDownloadService.
|
||||
* It is static to avoid leaking the first DemoDownloadService instance.
|
||||
*/
|
||||
private static final class TerminalStateNotificationHelper implements DownloadManager.Listener {
|
||||
|
||||
private final Context context;
|
||||
private final DownloadNotificationHelper notificationHelper;
|
||||
|
||||
private int nextNotificationId;
|
||||
|
||||
public TerminalStateNotificationHelper(Context context, DownloadNotificationHelper notificationHelper, int firstNotificationId) {
|
||||
this.context = context.getApplicationContext();
|
||||
this.notificationHelper = notificationHelper;
|
||||
nextNotificationId = firstNotificationId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDownloadChanged(DownloadManager downloadManager, Download download, @Nullable Exception finalException) {
|
||||
Notification notification;
|
||||
|
||||
if (download.state == Download.STATE_COMPLETED) {
|
||||
notification = notificationHelper.buildDownloadCompletedNotification(context, R.drawable.ic_done,null, Util.fromUtf8Bytes(download.request.data));
|
||||
} else if (download.state == Download.STATE_FAILED) {
|
||||
notification = notificationHelper.buildDownloadFailedNotification(context, R.drawable.ic_error, null, Util.fromUtf8Bytes(download.request.data));
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
NotificationUtil.setNotification(context, nextNotificationId++, notification);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,219 +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 com.cappielloantonio.play.service;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
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 androidx.core.content.ContextCompat;
|
||||
|
||||
import com.cappielloantonio.play.BuildConfig;
|
||||
|
||||
/**
|
||||
* Used to control headset playback.
|
||||
* Single press: pause/resume
|
||||
* Double press: next track
|
||||
* Triple press: previous track
|
||||
*/
|
||||
public class MediaButtonIntentReceiver extends BroadcastReceiver {
|
||||
private static final boolean DEBUG = BuildConfig.DEBUG;
|
||||
public static final String TAG = MediaButtonIntentReceiver.class.getSimpleName();
|
||||
|
||||
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")
|
||||
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 = MusicService.ACTION_TOGGLE;
|
||||
break;
|
||||
case 2:
|
||||
command = MusicService.ACTION_SKIP;
|
||||
break;
|
||||
case 3:
|
||||
command = MusicService.ACTION_REWIND;
|
||||
break;
|
||||
default:
|
||||
command = null;
|
||||
break;
|
||||
}
|
||||
|
||||
if (command != null) {
|
||||
final Context context = (Context) msg.obj;
|
||||
startService(context, command);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
releaseWakeLockIfHandlerIdle();
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void onReceive(final Context context, final Intent intent) {
|
||||
if (DEBUG) Log.v(TAG, "Received intent: " + intent);
|
||||
if (handleIntent(context, intent) && isOrderedBroadcast()) {
|
||||
abortBroadcast();
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
// fallback to system time if event time is not available
|
||||
final long eventTime = event.getEventTime() != 0
|
||||
? event.getEventTime()
|
||||
: System.currentTimeMillis();
|
||||
|
||||
String command = null;
|
||||
switch (keycode) {
|
||||
case KeyEvent.KEYCODE_MEDIA_STOP:
|
||||
command = MusicService.ACTION_STOP;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_HEADSETHOOK:
|
||||
case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
|
||||
command = MusicService.ACTION_TOGGLE;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_MEDIA_NEXT:
|
||||
command = MusicService.ACTION_SKIP;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
|
||||
command = MusicService.ACTION_REWIND;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_MEDIA_PAUSE:
|
||||
command = MusicService.ACTION_PAUSE;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_MEDIA_PLAY:
|
||||
command = MusicService.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);
|
||||
try {
|
||||
// IMPORTANT NOTE: (kind of a hack)
|
||||
// on Android O and above the following crashes when the app is not running
|
||||
// there is no good way to check whether the app is running so we catch the exception
|
||||
// we do not always want to use startForegroundService() because then one gets an ANR
|
||||
// if no notification is displayed via startForeground()
|
||||
// according to Play analytics this happens a lot, I suppose for example if command = PAUSE
|
||||
context.startService(intent);
|
||||
} catch (IllegalStateException ignored) {
|
||||
ContextCompat.startForegroundService(context, 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);
|
||||
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, context.getClass().getName());
|
||||
mWakeLock.setReferenceCounted(false);
|
||||
}
|
||||
|
||||
if (DEBUG) Log.v(TAG, "Acquiring wake lock and sending " + msg.what);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -7,7 +7,7 @@ import android.widget.Toast;
|
|||
|
||||
import com.cappielloantonio.play.R;
|
||||
import com.cappielloantonio.play.model.Song;
|
||||
import com.cappielloantonio.play.service.playback.Playback;
|
||||
import com.cappielloantonio.play.interfaces.Playback;
|
||||
import com.cappielloantonio.play.util.DownloadUtil;
|
||||
import com.cappielloantonio.play.util.MusicUtil;
|
||||
import com.cappielloantonio.play.util.PreferenceUtil;
|
||||
|
|
@ -18,7 +18,6 @@ import com.google.android.exoplayer2.MediaItem;
|
|||
import com.google.android.exoplayer2.SimpleExoPlayer;
|
||||
import com.google.android.exoplayer2.database.ExoDatabaseProvider;
|
||||
import com.google.android.exoplayer2.source.DefaultMediaSourceFactory;
|
||||
import com.google.android.exoplayer2.source.MediaSourceFactory;
|
||||
import com.google.android.exoplayer2.upstream.DataSource;
|
||||
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
|
||||
import com.google.android.exoplayer2.upstream.FileDataSource;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,331 @@
|
|||
package com.cappielloantonio.play.service;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.ContextWrapper;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.os.IBinder;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.cappielloantonio.play.App;
|
||||
import com.cappielloantonio.play.model.Song;
|
||||
import com.cappielloantonio.play.repository.QueueRepository;
|
||||
import com.cappielloantonio.play.service.MusicService;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
public class MusicPlayerRemote {
|
||||
private static final String TAG = "MusicPlayerRemote";
|
||||
|
||||
public static MusicService musicService;
|
||||
|
||||
private static final WeakHashMap<Context, ServiceBinder> mConnectionMap = new WeakHashMap<>();
|
||||
|
||||
public static ServiceToken bindToService(@NonNull final Context context, final ServiceConnection callback) {
|
||||
Activity realActivity = ((Activity) context).getParent();
|
||||
if (realActivity == null) {
|
||||
realActivity = (Activity) context;
|
||||
}
|
||||
|
||||
final ContextWrapper contextWrapper = new ContextWrapper(realActivity);
|
||||
contextWrapper.startService(new Intent(contextWrapper, MusicService.class));
|
||||
|
||||
final ServiceBinder binder = new ServiceBinder(callback);
|
||||
|
||||
if (contextWrapper.bindService(new Intent().setClass(contextWrapper, MusicService.class), binder, Context.BIND_AUTO_CREATE)) {
|
||||
mConnectionMap.put(contextWrapper, binder);
|
||||
return new ServiceToken(contextWrapper);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void unbindFromService(@Nullable final ServiceToken token) {
|
||||
if (token == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final ContextWrapper mContextWrapper = token.mWrappedContext;
|
||||
final ServiceBinder mBinder = mConnectionMap.remove(mContextWrapper);
|
||||
if (mBinder == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
mContextWrapper.unbindService(mBinder);
|
||||
if (mConnectionMap.isEmpty()) {
|
||||
musicService = null;
|
||||
}
|
||||
}
|
||||
|
||||
public static final class ServiceBinder implements ServiceConnection {
|
||||
private final ServiceConnection mCallback;
|
||||
|
||||
public ServiceBinder(final ServiceConnection callback) {
|
||||
mCallback = callback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceConnected(final ComponentName className, final IBinder service) {
|
||||
MusicService.MusicBinder binder = (MusicService.MusicBinder) service;
|
||||
musicService = binder.getService();
|
||||
if (mCallback != null) {
|
||||
mCallback.onServiceConnected(className, service);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(final ComponentName className) {
|
||||
if (mCallback != null) {
|
||||
mCallback.onServiceDisconnected(className);
|
||||
}
|
||||
|
||||
musicService = null;
|
||||
}
|
||||
}
|
||||
|
||||
public static final class ServiceToken {
|
||||
public ContextWrapper mWrappedContext;
|
||||
|
||||
public ServiceToken(final ContextWrapper context) {
|
||||
mWrappedContext = context;
|
||||
}
|
||||
}
|
||||
|
||||
public static void playSongAt(final int position) {
|
||||
if (musicService != null) {
|
||||
musicService.playSongAt(position);
|
||||
}
|
||||
}
|
||||
|
||||
public static void setPosition(final int position) {
|
||||
if (musicService != null) {
|
||||
musicService.setPosition(position);
|
||||
}
|
||||
}
|
||||
|
||||
public static void pauseSong() {
|
||||
if (musicService != null) {
|
||||
musicService.pause();
|
||||
}
|
||||
}
|
||||
|
||||
public static void playNextSong() {
|
||||
if (musicService != null) {
|
||||
musicService.playNextSong();
|
||||
}
|
||||
}
|
||||
|
||||
public static void playPreviousSong() {
|
||||
if (musicService != null) {
|
||||
musicService.playPreviousSong(true);
|
||||
}
|
||||
}
|
||||
|
||||
public static void back() {
|
||||
if (musicService != null) {
|
||||
musicService.back(true);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isPlaying() {
|
||||
return musicService != null && musicService.isPlaying();
|
||||
}
|
||||
|
||||
public static boolean isLoading() {
|
||||
return musicService != null && musicService.isLoading();
|
||||
}
|
||||
|
||||
public static void quitPlaying() {
|
||||
if (musicService != null) {
|
||||
musicService.quitPlaying();
|
||||
}
|
||||
}
|
||||
|
||||
public static void resumePlaying() {
|
||||
if (musicService != null) {
|
||||
musicService.play();
|
||||
}
|
||||
}
|
||||
|
||||
public static void openQueue(final List<Song> queue, final int startPosition, final boolean startPlaying) {
|
||||
Log.d(TAG, "MusicPlayerRemote - openQueue: S " + queue.size() + " P " + startPosition + " SP " + startPlaying);
|
||||
|
||||
if (!tryToHandleOpenPlayingQueue(queue, startPosition, startPlaying) && musicService != null) {
|
||||
musicService.openQueue(queue, startPosition, startPlaying);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean tryToHandleOpenPlayingQueue(final List<Song> queue, final int startPosition, final boolean startPlaying) {
|
||||
if (getPlayingQueue() == queue) {
|
||||
if (startPlaying) {
|
||||
playSongAt(startPosition);
|
||||
} else {
|
||||
setPosition(startPosition);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static Song getCurrentSong() {
|
||||
if (musicService != null) {
|
||||
return musicService.getCurrentSong();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static int getPosition() {
|
||||
if (musicService != null) {
|
||||
return musicService.getPosition();
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static List<Song> getPlayingQueue() {
|
||||
if (musicService != null) {
|
||||
return musicService.getPlayingQueue();
|
||||
}
|
||||
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
public static int getSongProgressMillis() {
|
||||
if (musicService != null) {
|
||||
return musicService.getSongProgressMillis();
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static int getSongDurationMillis() {
|
||||
if (musicService != null) {
|
||||
return musicService.getSongDurationMillis();
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static int seekTo(int millis) {
|
||||
if (musicService != null) {
|
||||
return musicService.seek(millis);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static boolean playNext(Song song) {
|
||||
if (musicService != null) {
|
||||
QueueRepository queueRepository = new QueueRepository(App.getInstance());
|
||||
|
||||
if (getPlayingQueue().size() > 0) {
|
||||
musicService.addSong(getPosition() + 1, song);
|
||||
queueRepository.insertAllAndStartNew(getPlayingQueue());
|
||||
} else {
|
||||
List<Song> songToEnqueue = new ArrayList<>();
|
||||
songToEnqueue.add(song);
|
||||
queueRepository.insertAllAndStartNew(songToEnqueue);
|
||||
openQueue(songToEnqueue, 0, true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean playNext(@NonNull List<Song> songs) {
|
||||
if (musicService != null) {
|
||||
QueueRepository queueRepository = new QueueRepository(App.getInstance());
|
||||
|
||||
if (getPlayingQueue().size() > 0) {
|
||||
musicService.addSongs(getPosition() + 1, songs);
|
||||
queueRepository.insertAllAndStartNew(getPlayingQueue());
|
||||
} else {
|
||||
List<Song> songToEnqueue = new ArrayList<>();
|
||||
songToEnqueue.addAll(songs);
|
||||
queueRepository.insertAllAndStartNew(songToEnqueue);
|
||||
openQueue(songToEnqueue, 0, true);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean enqueue(Song song) {
|
||||
if (musicService != null) {
|
||||
QueueRepository queueRepository = new QueueRepository(App.getInstance());
|
||||
|
||||
if (getPlayingQueue().size() > 0) {
|
||||
musicService.addSong(song);
|
||||
queueRepository.insertAllAndStartNew(getPlayingQueue());
|
||||
} else {
|
||||
List<Song> songToEnqueue = new ArrayList<>();
|
||||
songToEnqueue.add(song);
|
||||
queueRepository.insertAllAndStartNew(songToEnqueue);
|
||||
openQueue(songToEnqueue, 0, true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean enqueue(@NonNull List<Song> songs) {
|
||||
if (musicService != null) {
|
||||
QueueRepository queueRepository = new QueueRepository(App.getInstance());
|
||||
|
||||
if (getPlayingQueue().size() > 0) {
|
||||
musicService.addSongs(songs);
|
||||
queueRepository.insertAllAndStartNew(getPlayingQueue());
|
||||
} else {
|
||||
List<Song> songToEnqueue = new ArrayList<>();
|
||||
songToEnqueue.addAll(songs);
|
||||
queueRepository.insertAllAndStartNew(songToEnqueue);
|
||||
openQueue(songToEnqueue, 0, true);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean removeFromQueue(int position) {
|
||||
if (musicService != null && position >= 0 && position < getPlayingQueue().size()) {
|
||||
musicService.removeSong(position);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean moveSong(int from, int to) {
|
||||
if (musicService != null && from >= 0 && to >= 0 && from < getPlayingQueue().size() && to < getPlayingQueue().size()) {
|
||||
musicService.moveSong(from, to);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean clearQueue() {
|
||||
if (musicService != null) {
|
||||
musicService.clearQueue();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -17,25 +17,23 @@ import android.os.Looper;
|
|||
import android.os.Message;
|
||||
import android.os.PowerManager;
|
||||
import android.os.Process;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.v4.media.MediaMetadataCompat;
|
||||
import android.support.v4.media.session.MediaSessionCompat;
|
||||
import android.support.v4.media.session.PlaybackStateCompat;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.preference.Preference;
|
||||
|
||||
import com.cappielloantonio.play.App;
|
||||
import com.cappielloantonio.play.R;
|
||||
import com.cappielloantonio.play.broadcast.receiver.MediaButtonIntentReceiver;
|
||||
import com.cappielloantonio.play.model.Playlist;
|
||||
import com.cappielloantonio.play.model.Song;
|
||||
import com.cappielloantonio.play.repository.QueueRepository;
|
||||
import com.cappielloantonio.play.repository.SongRepository;
|
||||
import com.cappielloantonio.play.service.notification.PlayingNotification;
|
||||
import com.cappielloantonio.play.service.playback.Playback;
|
||||
import com.cappielloantonio.play.ui.notification.PlayingNotification;
|
||||
import com.cappielloantonio.play.interfaces.Playback;
|
||||
import com.cappielloantonio.play.util.PreferenceUtil;
|
||||
|
||||
import org.jellyfin.apiclient.interaction.EmptyResponse;
|
||||
|
|
|
|||
|
|
@ -1,127 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2017 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 com.cappielloantonio.play.service;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.cappielloantonio.play.R;
|
||||
import com.cappielloantonio.play.util.DownloadUtil;
|
||||
import com.google.android.exoplayer2.offline.Download;
|
||||
import com.google.android.exoplayer2.offline.DownloadManager;
|
||||
import com.google.android.exoplayer2.offline.DownloadService;
|
||||
import com.google.android.exoplayer2.scheduler.Scheduler;
|
||||
import com.google.android.exoplayer2.ui.DownloadNotificationHelper;
|
||||
import com.google.android.exoplayer2.util.NotificationUtil;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A service for downloading media.
|
||||
*/
|
||||
public class PlayDownloadService extends DownloadService {
|
||||
|
||||
private static final int JOB_ID = 1;
|
||||
private static final int FOREGROUND_NOTIFICATION_ID = 1;
|
||||
|
||||
public PlayDownloadService() {
|
||||
super(
|
||||
FOREGROUND_NOTIFICATION_ID,
|
||||
DEFAULT_FOREGROUND_NOTIFICATION_UPDATE_INTERVAL,
|
||||
DownloadUtil.DOWNLOAD_NOTIFICATION_CHANNEL_ID,
|
||||
R.string.exo_download_notification_channel_name,
|
||||
0);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected Scheduler getScheduler() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
protected DownloadManager getDownloadManager() {
|
||||
|
||||
DownloadManager downloadManager = DownloadUtil.getDownloadManager(/* context= */ this);
|
||||
DownloadNotificationHelper downloadNotificationHelper = DownloadUtil.getDownloadNotificationHelper(this);
|
||||
downloadManager.addListener(
|
||||
new TerminalStateNotificationHelper(
|
||||
this, downloadNotificationHelper, FOREGROUND_NOTIFICATION_ID + 1));
|
||||
return downloadManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
protected Notification getForegroundNotification(@NonNull List<Download> downloads) {
|
||||
return DownloadUtil.getDownloadNotificationHelper(/* context= */ this)
|
||||
.buildProgressNotification(
|
||||
this,
|
||||
R.drawable.ic_downloading,
|
||||
null,
|
||||
null,
|
||||
downloads);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and displays notifications for downloads when they complete or fail.
|
||||
*
|
||||
* <p>This helper will outlive the lifespan of a single instance of DemoDownloadService.
|
||||
* It is static to avoid leaking the first DemoDownloadService instance.
|
||||
*/
|
||||
private static final class TerminalStateNotificationHelper implements DownloadManager.Listener {
|
||||
|
||||
private final Context context;
|
||||
private final DownloadNotificationHelper notificationHelper;
|
||||
|
||||
private int nextNotificationId;
|
||||
|
||||
public TerminalStateNotificationHelper(
|
||||
Context context, DownloadNotificationHelper notificationHelper, int firstNotificationId) {
|
||||
this.context = context.getApplicationContext();
|
||||
this.notificationHelper = notificationHelper;
|
||||
nextNotificationId = firstNotificationId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDownloadChanged(
|
||||
DownloadManager downloadManager, Download download, @Nullable Exception finalException) {
|
||||
Notification notification;
|
||||
if (download.state == Download.STATE_COMPLETED) {
|
||||
notification =
|
||||
notificationHelper.buildDownloadCompletedNotification(
|
||||
context,
|
||||
R.drawable.ic_done,
|
||||
/* contentIntent= */ null,
|
||||
Util.fromUtf8Bytes(download.request.data));
|
||||
} else if (download.state == Download.STATE_FAILED) {
|
||||
notification =
|
||||
notificationHelper.buildDownloadFailedNotification(
|
||||
context,
|
||||
R.drawable.ic_done,
|
||||
/* contentIntent= */ null,
|
||||
Util.fromUtf8Bytes(download.request.data));
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
NotificationUtil.setNotification(context, nextNotificationId++, notification);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,72 +0,0 @@
|
|||
package com.cappielloantonio.play.service
|
||||
|
||||
import com.google.android.exoplayer2.MediaItem
|
||||
import com.google.android.exoplayer2.drm.DrmSessionManager
|
||||
import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory
|
||||
import com.google.android.exoplayer2.source.MediaSource
|
||||
import com.google.android.exoplayer2.source.MediaSourceFactory
|
||||
import com.google.android.exoplayer2.source.ProgressiveMediaSource
|
||||
import com.google.android.exoplayer2.source.hls.HlsMediaSource
|
||||
import com.google.android.exoplayer2.upstream.*
|
||||
|
||||
import kotlinx.coroutines.*
|
||||
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.URL
|
||||
|
||||
class UnknownMediaSourceFactory(dataSourceFactory: DataSource.Factory) : MediaSourceFactory {
|
||||
private val hlsMediaSource : HlsMediaSource.Factory
|
||||
private val progressiveMediaSource : ProgressiveMediaSource.Factory
|
||||
|
||||
private var loadErrorHandlingPolicy: LoadErrorHandlingPolicy
|
||||
override fun setDrmSessionManager(drmSessionManager: DrmSessionManager?): MediaSourceFactory {
|
||||
return this
|
||||
}
|
||||
|
||||
override fun setDrmHttpDataSourceFactory(drmHttpDataSourceFactory: HttpDataSource.Factory?): MediaSourceFactory {
|
||||
return this
|
||||
}
|
||||
|
||||
override fun setDrmUserAgent(drmUserAgent: String?): MediaSourceFactory {
|
||||
return this
|
||||
}
|
||||
|
||||
override fun setLoadErrorHandlingPolicy(loadErrorHandlingPolicy: LoadErrorHandlingPolicy?): MediaSourceFactory {
|
||||
this.loadErrorHandlingPolicy = loadErrorHandlingPolicy!!
|
||||
return this
|
||||
}
|
||||
|
||||
override fun getSupportedTypes(): IntArray {
|
||||
return intArrayOf()
|
||||
}
|
||||
|
||||
override fun createMediaSource(mediaItem: MediaItem): MediaSource {
|
||||
val type: String? = runBlocking {
|
||||
httpGet(mediaItem.playbackProperties!!.uri.toString())
|
||||
}
|
||||
|
||||
val sourceFactory: MediaSourceFactory = if (type == "application/x-mpegURL") {
|
||||
hlsMediaSource
|
||||
} else {
|
||||
progressiveMediaSource
|
||||
}
|
||||
|
||||
return sourceFactory.createMediaSource(mediaItem)
|
||||
}
|
||||
|
||||
private suspend fun httpGet(url: String?): String? {
|
||||
return withContext(Dispatchers.IO) {
|
||||
val request = URL(url)
|
||||
val conn = request.openConnection() as HttpURLConnection
|
||||
|
||||
return@withContext conn.getHeaderField("Content-Type")
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
hlsMediaSource = HlsMediaSource.Factory(dataSourceFactory)
|
||||
progressiveMediaSource = ProgressiveMediaSource.Factory(dataSourceFactory, DefaultExtractorsFactory())
|
||||
|
||||
loadErrorHandlingPolicy = DefaultLoadErrorHandlingPolicy()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,173 +0,0 @@
|
|||
package com.cappielloantonio.play.service.notification;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.media.app.NotificationCompat.MediaStyle;
|
||||
|
||||
import com.cappielloantonio.play.R;
|
||||
import com.cappielloantonio.play.glide.CustomGlideRequest;
|
||||
import com.cappielloantonio.play.model.Song;
|
||||
import com.cappielloantonio.play.ui.activities.MainActivity;
|
||||
import com.cappielloantonio.play.service.MusicService;
|
||||
|
||||
import com.bumptech.glide.request.target.CustomTarget;
|
||||
import com.bumptech.glide.request.transition.Transition;
|
||||
|
||||
import static android.content.Context.NOTIFICATION_SERVICE;
|
||||
import static com.cappielloantonio.play.service.MusicService.ACTION_REWIND;
|
||||
import static com.cappielloantonio.play.service.MusicService.ACTION_SKIP;
|
||||
import static com.cappielloantonio.play.service.MusicService.ACTION_TOGGLE;
|
||||
|
||||
public class PlayingNotification {
|
||||
|
||||
private static final int NOTIFICATION_ID = 1;
|
||||
static final String NOTIFICATION_CHANNEL_ID = "playing_notification";
|
||||
|
||||
private static final int NOTIFY_MODE_FOREGROUND = 1;
|
||||
private static final int NOTIFY_MODE_BACKGROUND = 0;
|
||||
|
||||
private int notifyMode = NOTIFY_MODE_BACKGROUND;
|
||||
|
||||
private NotificationManager notificationManager;
|
||||
protected MusicService service;
|
||||
boolean stopped;
|
||||
|
||||
public synchronized void init(MusicService service) {
|
||||
this.service = service;
|
||||
notificationManager = (NotificationManager) service.getSystemService(NOTIFICATION_SERVICE);
|
||||
createNotificationChannel();
|
||||
}
|
||||
|
||||
public synchronized void update() {
|
||||
stopped = false;
|
||||
|
||||
final Song song = service.getCurrentSong();
|
||||
final boolean isPlaying = service.isPlaying();
|
||||
final int playButtonResId = isPlaying ? R.drawable.ic_pause_white_24dp : R.drawable.ic_play_arrow_white_24dp;
|
||||
|
||||
Intent action = new Intent(service, MainActivity.class);
|
||||
action.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
final PendingIntent clickIntent = PendingIntent.getActivity(service, 0, action, 0);
|
||||
|
||||
final ComponentName serviceName = new ComponentName(service, MusicService.class);
|
||||
Intent intent = new Intent(MusicService.ACTION_QUIT);
|
||||
intent.setComponent(serviceName);
|
||||
final PendingIntent deleteIntent = PendingIntent.getService(service, 0, intent, 0);
|
||||
|
||||
final int bigNotificationImageSize = service.getResources().getDimensionPixelSize(R.dimen.notification_big_image_size);
|
||||
service.runOnUiThread(() -> CustomGlideRequest.Builder
|
||||
.from(service, song.getPrimary(), song.getBlurHash(), CustomGlideRequest.PRIMARY, CustomGlideRequest.TOP_QUALITY, CustomGlideRequest.SONG_PIC)
|
||||
.bitmap()
|
||||
.build()
|
||||
.into(new CustomTarget<Bitmap>(bigNotificationImageSize, bigNotificationImageSize) {
|
||||
@Override
|
||||
public void onResourceReady(@NonNull Bitmap resource, Transition<? super Bitmap> glideAnimation) {
|
||||
update(resource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadFailed(Drawable drawable) {
|
||||
update(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadCleared(Drawable drawable) {
|
||||
update(null);
|
||||
}
|
||||
|
||||
void update(Bitmap bitmap) {
|
||||
if (bitmap == null) {
|
||||
bitmap = BitmapFactory.decodeResource(service.getResources(), R.drawable.default_album_art);
|
||||
}
|
||||
|
||||
NotificationCompat.Action playPauseAction = new NotificationCompat.Action(playButtonResId, service.getString(R.string.action_play_pause), retrievePlaybackAction(ACTION_TOGGLE));
|
||||
NotificationCompat.Action previousAction = new NotificationCompat.Action(R.drawable.ic_skip_previous_white_24dp, service.getString(R.string.action_previous), retrievePlaybackAction(ACTION_REWIND));
|
||||
NotificationCompat.Action nextAction = new NotificationCompat.Action(R.drawable.ic_skip_next_white_24dp, service.getString(R.string.action_next), retrievePlaybackAction(ACTION_SKIP));
|
||||
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(service, NOTIFICATION_CHANNEL_ID)
|
||||
.setSmallIcon(R.drawable.ic_notification)
|
||||
.setSubText(song.getAlbumName())
|
||||
.setLargeIcon(bitmap)
|
||||
.setContentIntent(clickIntent)
|
||||
.setDeleteIntent(deleteIntent)
|
||||
.setContentTitle(song.getTitle())
|
||||
.setContentText(song.getArtistName())
|
||||
.setOngoing(isPlaying)
|
||||
.setShowWhen(false)
|
||||
.addAction(previousAction)
|
||||
.addAction(playPauseAction)
|
||||
.addAction(nextAction);
|
||||
|
||||
builder.setStyle(new MediaStyle().setMediaSession(service.getMediaSession().getSessionToken()).setShowActionsInCompactView(0, 1, 2))
|
||||
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
|
||||
|
||||
// notification has been stopped before loading was finished
|
||||
if (stopped) return;
|
||||
|
||||
updateNotifyModeAndPostNotification(builder.build());
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
public synchronized void stop() {
|
||||
stopped = true;
|
||||
service.stopForeground(true);
|
||||
notificationManager.cancel(NOTIFICATION_ID);
|
||||
}
|
||||
|
||||
private PendingIntent retrievePlaybackAction(final String action) {
|
||||
final ComponentName serviceName = new ComponentName(service, MusicService.class);
|
||||
Intent intent = new Intent(action);
|
||||
intent.setComponent(serviceName);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
return PendingIntent.getService(service, 0, intent, 0);
|
||||
}
|
||||
|
||||
void updateNotifyModeAndPostNotification(Notification notification) {
|
||||
int newNotifyMode;
|
||||
|
||||
if (service.isPlaying()) {
|
||||
newNotifyMode = NOTIFY_MODE_FOREGROUND;
|
||||
} else {
|
||||
newNotifyMode = NOTIFY_MODE_BACKGROUND;
|
||||
}
|
||||
|
||||
if (notifyMode != newNotifyMode && newNotifyMode == NOTIFY_MODE_BACKGROUND) {
|
||||
service.stopForeground(false);
|
||||
}
|
||||
|
||||
if (newNotifyMode == NOTIFY_MODE_FOREGROUND) {
|
||||
service.startForeground(NOTIFICATION_ID, notification);
|
||||
} else if (newNotifyMode == NOTIFY_MODE_BACKGROUND) {
|
||||
notificationManager.notify(NOTIFICATION_ID, notification);
|
||||
}
|
||||
|
||||
notifyMode = newNotifyMode;
|
||||
}
|
||||
|
||||
@RequiresApi(26)
|
||||
private void createNotificationChannel() {
|
||||
NotificationChannel notificationChannel = notificationManager.getNotificationChannel(NOTIFICATION_CHANNEL_ID);
|
||||
if (notificationChannel == null) {
|
||||
notificationChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, service.getString(R.string.playing_notification_name), NotificationManager.IMPORTANCE_LOW);
|
||||
notificationChannel.setDescription(service.getString(R.string.playing_notification_description));
|
||||
notificationChannel.enableLights(false);
|
||||
notificationChannel.enableVibration(false);
|
||||
|
||||
notificationManager.createNotificationChannel(notificationChannel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
package com.cappielloantonio.play.service.playback;
|
||||
|
||||
import com.cappielloantonio.play.model.Song;
|
||||
|
||||
public interface Playback {
|
||||
void setDataSource(Song song);
|
||||
|
||||
void queueDataSource(Song song);
|
||||
|
||||
void setCallbacks(PlaybackCallbacks callbacks);
|
||||
|
||||
boolean isReady();
|
||||
|
||||
boolean isPlaying();
|
||||
|
||||
boolean isLoading();
|
||||
|
||||
void start();
|
||||
|
||||
void pause();
|
||||
|
||||
void stop();
|
||||
|
||||
int getProgress();
|
||||
|
||||
int getDuration();
|
||||
|
||||
void setProgress(int progress);
|
||||
|
||||
void setVolume(int volume);
|
||||
|
||||
int getVolume();
|
||||
|
||||
interface PlaybackCallbacks {
|
||||
void onStateChanged(int state);
|
||||
|
||||
void onReadyChanged(boolean ready, int reason);
|
||||
|
||||
void onTrackChanged(int reason);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue