Java tutorial
/* * Copyright (C) 2015 Dmitry Kuznetsov aka kudzmi * * This file is part of Rajitaku. * * Rajitaku is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Rajitaku is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Rajitaku. If not, see <http://www.gnu.org/licenses/>. * */ package ru.kudzmi.rajitaku.radioService; import android.annotation.SuppressLint; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.media.AudioManager; import android.media.MediaPlayer; import android.net.wifi.WifiManager; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.PowerManager; import android.support.v4.app.NotificationCompat; import android.support.v4.content.LocalBroadcastManager; import java.io.IOException; import ru.kudzmi.rajitaku.MainActivity; import ru.kudzmi.rajitaku.R; import ru.kudzmi.rajitaku.preferences.Preferences; import ru.kudzmi.rajitaku.preferences.StreamQuality; import wseemann.media.FFmpegMediaMetadataRetriever; import wseemann.media.Metadata; public class RadioService extends Service implements MediaPlayer.OnPreparedListener, MediaPlayer.OnErrorListener { private static final int NOTIFICATION_ID = 39; private MediaPlayer mPlayer; private AudioManager audioManager; private WifiManager.WifiLock wifiLock; private Handler mHandler = new UpdateHandler(); private String lastTrackTitle = ""; AudioManager.OnAudioFocusChangeListener afChangeListener = new AudioManager.OnAudioFocusChangeListener() { boolean isPaused; public void onAudioFocusChange(int focusChange) { switch (focusChange) { case AudioManager.AUDIOFOCUS_GAIN: if (mPlayer == null) initMediaPlayer(); if (isPaused) { mPlayer.start(); startUpdateThread(); isPaused = false; } mPlayer.setVolume(1.0f, 1.0f); break; case AudioManager.AUDIOFOCUS_LOSS: doStop(); break; case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: if (mPlayer != null) { mPlayer.pause(); isPaused = true; } break; case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: if (mPlayer.isPlaying()) mPlayer.setVolume(0.1f, 0.1f); break; } } }; private BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (intent.getAction().equals(BroadcastCommander.CMD_FROM_UI_TO_SERVICE)) { final PlayerCommand playerCommand = (PlayerCommand) intent .getSerializableExtra(BroadcastCommander.MESSAGE_EXTRA); processCommand(playerCommand); } } }; public RadioService() { } @Override public IBinder onBind(Intent intent) { throw new UnsupportedOperationException("Not yet implemented"); } @Override public void onCreate() { super.onCreate(); audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); initMediaPlayer(); IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(BroadcastCommander.CMD_FROM_UI_TO_SERVICE); LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, intentFilter); } @Override public void onDestroy() { super.onDestroy(); if (mReceiver != null) LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver); if (mPlayer != null) { mPlayer.release(); mPlayer = null; } releaseAudioFocus(); releaseWiFiLock(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { final Notification notification = startNotification(false); startForeground(NOTIFICATION_ID, notification); return super.onStartCommand(intent, flags, startId); } private void startUpdateThread() { Thread thread = new Thread() { @Override public void run() { while (mPlayer != null && mPlayer.isPlaying()) { mHandler.sendMessage(Message.obtain()); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }; thread.start(); } private boolean requestAudioFocus() { int result = audioManager.requestAudioFocus(afChangeListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN); return result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED; } private void releaseAudioFocus() { audioManager.abandonAudioFocus(afChangeListener); } private void initMediaPlayer() { mPlayer = new MediaPlayer(); mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); mPlayer.setOnPreparedListener(this); mPlayer.setOnErrorListener(this); mPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK); } private void acquireWiFiLock() { if (wifiLock == null) wifiLock = ((WifiManager) getSystemService(Context.WIFI_SERVICE)) .createWifiLock(WifiManager.WIFI_MODE_FULL, "RadioServiceWiFiLock"); wifiLock.acquire(); } private void releaseWiFiLock() { if (wifiLock != null && wifiLock.isHeld()) wifiLock.release(); } private void sendCurrentPlayerStatus() { final boolean isPlaying = mPlayer != null && mPlayer.isPlaying(); final PlayerStatus playerStatus; String trackTitle = getResources().getString(R.string.message_no_stream); if (isPlaying) { trackTitle = getCurrentTrackTitle(); playerStatus = new PlayerStatus(true, mPlayer.getCurrentPosition(), trackTitle); } else { playerStatus = new PlayerStatus(false, 0, trackTitle); startNotification(false); } BroadcastCommander.sendStatus(this, playerStatus); if (!lastTrackTitle.equals(trackTitle)) { lastTrackTitle = trackTitle; startNotification(true); } } private void sendErrorPlayerStatus() { final String trackTitle = getResources().getString(R.string.message_stream_error); final PlayerStatus playerStatus = new PlayerStatus(false, 0, trackTitle); BroadcastCommander.sendStatus(this, playerStatus); startNotification(false); } private String getCurrentTrackTitle() { final FFmpegMediaMetadataRetriever mediaMetadataRetriever = new FFmpegMediaMetadataRetriever(); mediaMetadataRetriever.setDataSource(Preferences.getStreamQuality(this).getStreamUrl()); final Metadata metadata = mediaMetadataRetriever.getMetadata(); mediaMetadataRetriever.release(); return metadata.getString("StreamTitle"); } private void processCommand(PlayerCommand cmd) { if (cmd == PlayerCommand.STOP) { doStop(); } if (cmd == PlayerCommand.PLAY) { final StreamQuality quality = Preferences.getStreamQuality(this); doPlay(quality.getStreamUrl()); } if (cmd == PlayerCommand.SHUTDOWN) { doStop(); stopForeground(true); BroadcastCommander.sendStatus(this, new PlayerStatus()); this.stopSelf(); } } private void doPlay(String url) { acquireWiFiLock(); if (mPlayer.isPlaying()) { mPlayer.stop(); mPlayer.reset(); } try { mPlayer.setDataSource(url); mPlayer.prepareAsync(); startNotification(true); } catch (IOException e) { sendErrorPlayerStatus(); } } private void doStop() { mPlayer.stop(); mPlayer.reset(); sendCurrentPlayerStatus(); startNotification(false); releaseAudioFocus(); releaseWiFiLock(); } /* Listeners */ @Override public void onPrepared(MediaPlayer mediaPlayer) { requestAudioFocus(); mediaPlayer.start(); startUpdateThread(); } private Notification startNotification(boolean isPlaying) { final NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this) .setSmallIcon(R.drawable.ic_shiza_logo).setContentTitle(getResources().getString(R.string.app_name)) .setContentText(lastTrackTitle).setTicker(lastTrackTitle).setAutoCancel(false); if (isPlaying) { createNotificationAction(mBuilder, NotificationActionReceiver.STOP_STREAM_ACTION, getResources().getString(R.string.notification_action_stop), R.mipmap.ic_stop_black_24dp, 2); } else { createNotificationAction(mBuilder, NotificationActionReceiver.PLAY_STREAM_ACTION, getResources().getString(R.string.notification_action_play), R.mipmap.ic_play_circle_filled_black_24dp, 1); } createNotificationAction(mBuilder, NotificationActionReceiver.SHUTDOWN_ACTION, getResources().getString(R.string.notification_action_shutdown), android.R.drawable.ic_menu_close_clear_cancel, 3); PendingIntent intent = PendingIntent.getActivity(this, 0, new Intent(this, MainActivity.class), 0); mBuilder.setContentIntent(intent); // Gets an instance of the NotificationManager service NotificationManager mNotifyMgr = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); // Build the notification and issues it. final Notification notification = mBuilder.build(); mNotifyMgr.notify(NOTIFICATION_ID, notification); return notification; } private void createNotificationAction(NotificationCompat.Builder builder, String action, String actionTitle, int iconId, int requestId) { final Intent intent = new Intent(); intent.setAction(action); final PendingIntent pendingIntent = PendingIntent.getBroadcast(this, requestId, intent, PendingIntent.FLAG_UPDATE_CURRENT); builder.addAction(iconId, actionTitle, pendingIntent); } @Override public boolean onError(MediaPlayer mp, int what, int extra) { sendErrorPlayerStatus(); return true; } @SuppressLint("HandlerLeak") private final class UpdateHandler extends Handler { @Override public void handleMessage(Message msg) { sendCurrentPlayerStatus(); } } }