ru.kudzmi.rajitaku.radioService.RadioService.java Source code

Java tutorial

Introduction

Here is the source code for ru.kudzmi.rajitaku.radioService.RadioService.java

Source

/*
* 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();
        }
    }

}