com.perm.DoomPlay.PlayingService.java Source code

Java tutorial

Introduction

Here is the source code for com.perm.DoomPlay.PlayingService.java

Source

package com.perm.DoomPlay;

/*
 *    Copyright 2013 Vladislav Krot
 *
 *    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.
 *
 *    You can contact me <DoomPlaye@gmail.com>
 */

import android.annotation.TargetApi;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.AudioManager;
import android.os.*;
import android.preference.PreferenceManager;
import android.support.v4.app.NotificationCompat;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.widget.RemoteViews;
import android.widget.Toast;
import com.api.KException;
import org.json.JSONException;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Random;

public class PlayingService extends Service
        implements BassPlayer.OnCompletionListener, SharedPreferences.OnSharedPreferenceChangeListener {
    public final static String actionTrackChanged = "DoomedTrackChanged";
    public final static String actionIconPlay = "DoomedPlayPlay";
    public final static String actionIconPause = "DoomedPlaPause";

    public final static String actionPlay = "DoomePlay";
    public final static String actionClose = "DoomClose";
    public final static String actionNext = "DoomNext";
    public final static String actionPrevious = "DoomPrevious";
    public final static String actionShuffle = "DoomShuffle";
    public final static String actionLoop = "DoomLooping";

    public final static String actionOffline = "FromlPlayback";
    public static final String actionOnline = "vkOnline";

    final static int nextTrack = 1;
    final static int previousTrack = -1;
    private final static int idForeground = 931;
    public final static int valueIncredible = 519815;

    private BassPlayer bassPlayer;
    public ArrayList<Audio> audios;
    private boolean isPrepared;
    public static int indexCurrentTrack = 0;
    public static boolean isShuffle;
    public static boolean isPlaying;
    public static boolean isLoop;
    public static boolean serviceAlive;
    public static boolean isOnline = false;
    private boolean isLoadingTrack;

    private final MyBinder binder = new MyBinder();
    private static int trackCountTotal = valueIncredible;
    private static int trackCountCurrent = 0;
    private AudioManager audioManager;
    private AFListener afListener;
    private final CallListener callListener = new CallListener();
    private OnLoadingTrackListener loadingListener;
    private final static Random random = new Random();

    public boolean isLoadingTrack() {
        return isLoadingTrack;
    }

    @Override
    public void onCompletion() {
        isPrepared = false;
        countTimer();
        nextSong();
    }

    interface OnLoadingTrackListener {
        void onLoadingTrackStarted();

        void onLoadingTrackEnded();
    }

    void setOnLoadingTrackListener(OnLoadingTrackListener loadingListener) {
        this.loadingListener = loadingListener;
    }

    private OnAlbumArtSave onAlbumArtSaveListener;

    interface OnAlbumArtSave {
        void onAlbumArtSave(long id);
    }

    void setOnAlbumArtSaveListener(OnAlbumArtSave onAlbumArtSaveListener) {
        this.onAlbumArtSaveListener = onAlbumArtSaveListener;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        isLoadingTrack = false;
        isShuffle = false;
        isPlaying = true;
        isLoop = false;

        afListener = new AFListener();
        audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
        audioManager.requestAudioFocus(afListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);

        ComponentName componentName = new ComponentName(this, MediaButtonReceiver.class);
        audioManager.registerMediaButtonEventReceiver(componentName);

        ((TelephonyManager) getSystemService(TELEPHONY_SERVICE)).listen(callListener,
                CallListener.LISTEN_CALL_STATE);
        PreferenceManager.getDefaultSharedPreferences(this).registerOnSharedPreferenceChangeListener(this);

        serviceAlive = true;
        bassPlayer = new BassPlayer();
        bassPlayer.setOnCompletetion(this);

    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        dispose();
        bassPlayer.releaseTotal();
        stopForeground(true);
        serviceAlive = false;
        isPlaying = false;
        sendBroadcast(new Intent(actionIconPlay));
        sendBroadcast(new Intent(SmallWidget.actionUpdateWidget));
        audioManager.abandonAudioFocus(afListener);
        audioManager.unregisterMediaButtonEventReceiver(new ComponentName(this, MediaButtonReceiver.class));
    }

    private void downloadAlbumArt(Audio audio) {
        if (SettingActivity.getPreferences("downloadart") && !AlbumArtGetter.isLoadById(audio.getAid())
                && audio.getTitle() != null && Utils.isOnline(this)) {
            if (!isOnline || SettingActivity.getPreferences("artonline")) {
                new AlbumArtGetter(audio.getAid(), audio.getArtist(), audio.getTitle()) {
                    @Override
                    protected void onBitmapSaved(long albumId) {
                        if (onAlbumArtSaveListener != null)
                            onAlbumArtSaveListener.onAlbumArtSave(albumId);

                        sendBroadcast(new Intent(SmallWidget.actionUpdateWidget));
                        startNotif();
                        sendBroadcast(new Intent(FullPlaybackActivity.actionDataChanged));
                    }
                }.execute();
            }
        }
    }

    private RemoteViews getNotifViews(int layoutId) {
        RemoteViews views = new RemoteViews(getPackageName(), layoutId);

        Audio audio = audios.get(indexCurrentTrack);

        views.setTextViewText(R.id.notifTitle, audio.getTitle());
        views.setTextViewText(R.id.notifArtist, audio.getArtist());

        Bitmap cover = AlbumArtGetter.getBitmapFromStore(audio.getAid(), this);

        if (cover == null) {
            Bitmap tempBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.fallback_cover);
            views.setImageViewBitmap(R.id.notifAlbum, tempBitmap);
            tempBitmap.recycle();
        } else {
            //TODO: java.lang.IllegalArgumentException: RemoteViews for widget update exceeds
            // maximum bitmap memory usage (used: 3240000, max: 2304000)
            // The total memory cannot exceed that required to fill the device's screen once
            try {
                views.setImageViewBitmap(R.id.notifAlbum, cover);
            } catch (IllegalArgumentException e) {
                Bitmap tempBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.fallback_cover);
                views.setImageViewBitmap(R.id.notifAlbum, tempBitmap);
                tempBitmap.recycle();
            } finally {
                cover.recycle();
            }
        }

        views.setImageViewResource(R.id.notifPlay, isPlaying ? R.drawable.widget_pause : R.drawable.widget_play);

        ComponentName componentName = new ComponentName(this, PlayingService.class);

        Intent intentPlay = new Intent(actionPlay);
        intentPlay.setComponent(componentName);
        views.setOnClickPendingIntent(R.id.notifPlay, PendingIntent.getService(this, 0, intentPlay, 0));

        Intent intentNext = new Intent(actionNext);
        intentNext.setComponent(componentName);
        views.setOnClickPendingIntent(R.id.notifNext, PendingIntent.getService(this, 0, intentNext, 0));

        Intent intentPrevious = new Intent(actionPrevious);
        intentPrevious.setComponent(componentName);
        views.setOnClickPendingIntent(R.id.notifPrevious, PendingIntent.getService(this, 0, intentPrevious, 0));

        Intent intentClose = new Intent(actionClose);
        intentClose.setComponent(componentName);
        views.setOnClickPendingIntent(R.id.notifClose, PendingIntent.getService(this, 0, intentClose, 0));

        return views;
    }

    private Notification createNotification() {

        Intent intentActivity;

        if (SettingActivity.getPreferences(SettingActivity.keyOnClickNotif)) {
            intentActivity = new Intent(FullPlaybackActivity.actionReturnFull);
            intentActivity.setClass(this, FullPlaybackActivity.class);
            intentActivity.putExtra(FileSystemActivity.keyMusic, audios);
        } else {
            intentActivity = FullPlaybackActivity.getReturnSmallIntent(this, audios);
        }
        intentActivity.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        Notification notification = new Notification();
        notification.contentView = getNotifViews(R.layout.notif);
        notification.flags |= Notification.FLAG_FOREGROUND_SERVICE;
        notification.contentIntent = PendingIntent.getActivity(this, 0, intentActivity,
                PendingIntent.FLAG_UPDATE_CURRENT);
        notification.icon = isPlaying ? R.drawable.status_icon_pause : R.drawable.status_icon_play;

        return notification;
    }

    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
    private Notification createJellyBeanNotif() {
        RemoteViews views = getNotifViews(R.layout.notif_jelly);
        Notification notification = createNotification();
        views.setTextViewText(R.id.textNotifCount,
                String.valueOf(indexCurrentTrack + 1) + "/" + String.valueOf(audios.size()));

        notification.bigContentView = views;
        notification.priority = Notification.PRIORITY_MAX;
        return notification;
    }

    private void updateWidgets() {
        Intent intent = new Intent(SmallWidget.actionUpdateWidget);
        intent.putExtra(SmallWidget.EXTRA_AUDIO, (Parcelable) audios.get(indexCurrentTrack));
        intent.putExtra(SmallWidget.EXTRA_SIZE, audios.size());
        sendBroadcast(intent);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        String action = intent.getAction();

        assert action != null;

        if (action.equals(actionOffline) || action.equals(actionOnline)) {
            isOnline = action.equals(actionOnline);

            audios = intent.getParcelableArrayListExtra(FullPlaybackActivity.keyService);

            if (intent.getIntExtra(FullPlaybackActivity.keyIndex, 0) != valueIncredible)
                indexCurrentTrack = intent.getIntExtra(FullPlaybackActivity.keyIndex, 0);

            loadMusic();
        } else
            handleNotifControll(action);

        return START_NOT_STICKY;
    }

    private void startNotif() {
        if (MainScreenActivity.isJellyBean)
            startForeground(idForeground, createJellyBeanNotif());
        else if (Build.VERSION.SDK_INT >= 11)
            startForeground(idForeground, createNotification());
        else
            startForeground(idForeground, createOldNotif());
    }

    private Notification createOldNotif() {
        NotificationCompat.Builder builder = new NotificationCompat.Builder(this);

        Intent intentActivity;

        if (SettingActivity.getPreferences(SettingActivity.keyOnClickNotif)) {
            intentActivity = new Intent(FullPlaybackActivity.actionReturnFull);
            intentActivity.setClass(this, FullPlaybackActivity.class);
            intentActivity.putExtra(FileSystemActivity.keyMusic, audios);
        } else {
            intentActivity = FullPlaybackActivity.getReturnSmallIntent(this, audios);
        }
        intentActivity.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        builder.setContentIntent(
                PendingIntent.getActivity(this, 0, intentActivity, PendingIntent.FLAG_UPDATE_CURRENT))
                .setOngoing(true)
                .setSmallIcon(isPlaying ? R.drawable.status_icon_pause : R.drawable.status_icon_play);

        Audio audio = audios.get(indexCurrentTrack);

        builder.setContentTitle(audio.getTitle());
        builder.setContentText(audio.getArtist());

        Bitmap cover = AlbumArtGetter.getBitmapFromStore(audio.getAid(), this);
        if (cover == null) {
            Bitmap tempBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.fallback_cover);
            builder.setLargeIcon(tempBitmap);
            tempBitmap.recycle();
        } else {
            builder.setLargeIcon(cover);
            cover.recycle();
        }

        return builder.build();
    }

    private void handleNotifControll(String action) {
        if (!isLoadingTrack && isPrepared) {
            if (action.equals(actionPlay)) {
                playPause();
            } else if (action.equals(actionClose)) {
                stopSelf();
            } else if (action.equals(actionPrevious)) {
                previousSong();
            } else if (action.equals(actionNext)) {
                nextSong();
            } else if (action.equals(actionShuffle)) {
                setShuffle();
            } else if (action.equals(actionLoop)) {
                setLoop();
            }
        }
    }

    private void setBroadcast() {
        if (isOnline && SettingActivity.getPreferences("broadcast") && MainScreenActivity.api != null) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        MainScreenActivity.api.audioSetBroadcast(audios.get(indexCurrentTrack).getOwner_id() + "_"
                                + audios.get(indexCurrentTrack).getAid());
                    } catch (IOException e) {
                    } catch (JSONException e) {
                    } catch (KException e) {
                    }
                }
            }).start();

        }
    }

    private void loadMusic() {
        dispose();

        //TODO: sometimes it throws  java.lang.IndexOutOfBoundsException: Invalid index 0, size is 0 , it's need to be fixed
        if (audios == null || audios.size() == 0)
            return;
        updateWidgets();
        sendBroadcast(new Intent(actionTrackChanged));
        if (isPlaying)
            sendBroadcast(new Intent(actionIconPause));
        else
            sendBroadcast(new Intent(actionIconPlay));

        startNotif();
        setBroadcast();

        new AsyncTask<Void, String, Void>() {
            @Override
            protected void onPreExecute() {
                super.onPreExecute();
                isLoadingTrack = true;

                if (loadingListener != null)
                    loadingListener.onLoadingTrackStarted();
            }

            @Override
            protected Void doInBackground(Void... params) {
                try {
                    if (isOnline)
                        bassPlayer.prepareNet(audios.get(indexCurrentTrack).getUrl());

                    else
                        bassPlayer.prepareFile(audios.get(indexCurrentTrack).getUrl());
                } catch (IOException e) {
                    cancel(false);
                    e.printStackTrace();
                }

                return null;
            }

            @Override
            protected void onCancelled() {
                super.onCancelled();

                isLoadingTrack = false;
                if (loadingListener != null)
                    loadingListener.onLoadingTrackEnded();

                Toast.makeText(getBaseContext(), getResources().getString(R.string.error), Toast.LENGTH_SHORT)
                        .show();
                isPlaying = false;

                sendBroadcast(new Intent(actionIconPlay));
                sendBroadcast(new Intent(SmallWidget.actionUpdateWidget));
                startNotif();
            }

            @Override
            protected void onPostExecute(Void aVoid) {
                super.onPostExecute(aVoid);
                isPrepared = true;

                isLoadingTrack = false;
                if (loadingListener != null)
                    loadingListener.onLoadingTrackEnded();

                if (isPlaying) {
                    bassPlayer.start();
                }

                Bitmap tempBitmap = AlbumArtGetter.getBitmapFromStore(audios.get(indexCurrentTrack).getAid(),
                        getBaseContext());

                if (tempBitmap == null)
                    downloadAlbumArt(audios.get(indexCurrentTrack));
                else
                    tempBitmap.recycle();
            }
        }.execute();

    }

    public void setLoop() {
        isLoop = !isLoop;
        sendBroadcast(new Intent(SmallWidget.actionUpdateWidget));
    }

    void nextSong() {
        setTrack(nextTrack);
        loadMusic();
    }

    void previousSong() {
        setTrack(previousTrack);
        loadMusic();
    }

    void playTrackFromList(int position) {
        indexCurrentTrack = position;
        loadMusic();
    }

    private void setTrack(int direction) {
        //TODO: sometimes it throws  java.lang.IndexOutOfBoundsException: Invalid index 0, size is 0 , it's need to be fixed
        if (audios == null || audios.size() == 0)
            return;

        if (isShuffle) {
            if (audios.size() != 1 && audios.size() != 0) {
                indexCurrentTrack = random.nextInt(audios.size() - 1);
            }
        } else if (!isLoop)
            changeTrack(direction);

    }

    public void changeTrack(int direction) {
        switch (direction) {
        case previousTrack: {
            indexCurrentTrack--;
            if (indexCurrentTrack == -1)
                indexCurrentTrack = audios.size() - 1;
            break;
        }
        case nextTrack: {
            indexCurrentTrack++;
            if (indexCurrentTrack > audios.size() - 1)
                indexCurrentTrack = 0;

            break;
        }
        default:
            throw new IllegalArgumentException("direction can be 1 or -1");
        }
    }

    public void setShuffle() {
        isShuffle = !isShuffle;
        sendBroadcast(new Intent(SmallWidget.actionUpdateWidget));
    }

    int getDuration() {
        if (isPrepared) {
            return bassPlayer.getTotalTime();
        } else
            return 0;
    }

    int getCurrentPosition() {
        if (isPrepared) {
            return bassPlayer.getCurrentPosition();
        } else
            return 0;
    }

    int getProgressPercentage() {
        if (isPrepared) {
            return bassPlayer.getPercentage();
        } else
            return 0;
    }

    void playPause() {
        if (!isPrepared)
            return;

        if (isPlaying) {
            isPlaying = false;
            bassPlayer.pause();
            sendBroadcast(new Intent(actionIconPlay));

            if (Build.VERSION.SDK_INT < 11)
                stopForeground(true);
            else
                startNotif();

        } else {
            isPlaying = true;
            bassPlayer.start();
            sendBroadcast(new Intent(actionIconPause));

            startNotif();

        }

        sendBroadcast(new Intent(SmallWidget.actionUpdateWidget));
    }

    void setCurrentPosition(int positionMillis) {
        if (isPrepared)
            bassPlayer.seekTo(positionMillis);
    }

    private void dispose() {
        if (isPrepared) {
            isPrepared = false;
            bassPlayer.release();
        }
    }

    private void countTimer() {
        if (trackCountTotal != valueIncredible) {
            trackCountCurrent++;
            if (trackCountCurrent == trackCountTotal)
                sendBroadcast(new Intent(AbstractReceiver.actionKill));
            trackCountTotal = PlayingService.valueIncredible;
        }
    }

    public static void setSleepTrack(int tracksCount) {
        trackCountCurrent = 0;
        trackCountTotal = tracksCount;
    }

    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }

    @Override
    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
        if (serviceAlive && key.equals(SettingActivity.keyOnClickNotif) && audios != null)
            startNotif();
    }

    class MyBinder extends Binder {
        PlayingService getService() {
            return PlayingService.this;
        }
    }

    private class CallListener extends PhoneStateListener {
        boolean wasPlaying = false;

        @Override
        public void onCallStateChanged(int state, String incomingNumber) {
            if ((TelephonyManager.CALL_STATE_RINGING == state && isPlaying)
                    || (TelephonyManager.CALL_STATE_OFFHOOK == state && isPlaying)) {
                if (SettingActivity.getPreferences(SettingActivity.keyOnCall) && isPrepared)
                    playPause();
                wasPlaying = true;
            } else if (TelephonyManager.CALL_STATE_IDLE == state && wasPlaying && !isPlaying) {
                if (SettingActivity.getPreferences(SettingActivity.keyAfterCall) && isPrepared)
                    playPause();
                wasPlaying = false;
            }
        }
    }

    private class AFListener implements AudioManager.OnAudioFocusChangeListener {
        boolean wasPlaying = false;

        @Override
        public void onAudioFocusChange(int focusChange) {
            switch (focusChange) {
            case AudioManager.AUDIOFOCUS_LOSS:
                if (SettingActivity.getPreferences(SettingActivity.keyLongFocus) && isPlaying && isPrepared)
                    playPause();
                wasPlaying = true;

                break;
            case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
            case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:

                if (SettingActivity.getPreferences(SettingActivity.keyShortFocus) && isPlaying && isPrepared)
                    playPause();
                wasPlaying = true;

                break;
            case AudioManager.AUDIOFOCUS_GAIN:

                if (SettingActivity.getPreferences(SettingActivity.keyOnGain) && wasPlaying && !isPlaying
                        && isPrepared)
                    playPause();
                wasPlaying = false;

                break;

            }
        }
    }
}