systems.byteswap.publicstream.MainActivity.java Source code

Java tutorial

Introduction

Here is the source code for systems.byteswap.publicstream.MainActivity.java

Source

/**
    Copyright:
    2015/2016 Benjamin Aigner
    
    This file is part of AustrianPublicStream.
    
    AustrianPublicStream 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.
    
    AustrianPublicStream 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 AustrianPublicStream.  If not, see <http://www.gnu.org/licenses/>.
    
    
    Contribution:
    A big "thank you" to following projects/webpages providing sourcecode/libraries/information:
    
    ToxicBakery for its transformer library (https://github.com/ToxicBakery/ViewPagerTransforms)
    VideoLAN for its VLC library (https://wiki.videolan.org/Libvlc/)
    RomanNurik for the Android Asset Studio (https://romannurik.github.io/AndroidAssetStudio/)
    
    and of course all the experts on stackoverflow for helpful hints!
**/

package systems.byteswap.publicstream;

import android.annotation.TargetApi;
import android.app.AlertDialog;
import android.app.FragmentManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.media.AudioManager;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;
import android.preference.PreferenceManager;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.support.v4.app.NotificationCompat;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ListView;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;

import com.ToxicBakery.viewpager.transforms.CubeOutTransformer;

import org.videolan.libvlc.util.AndroidUtil;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Timer;
import java.util.TimerTask;

//TODO: ordentlich kommentieren & clean-up

public class MainActivity extends AppCompatActivity {
    /** debug tags **/
    public static String TAG_REMOTELIST = "REMOTELIST";
    public static String TAG_ADAPTER = "ADAPTERPROGS";

    /** ID for the download notification, unique to differ the notifications for the update
     * Note: this number should be the highest amongst all notification, because it will be
     * increased for each concurrent download!!! **/
    public static int NOTIFICATION_DOWNLOAD_ID = 2;
    /** ID for the play notification, unique to differ the notifications for the update **/
    public static int NOTIFICATION_PLAY_ID = 1;

    //Handler to process all postDelayed operations (timer replacement for Android)
    private Handler handler = new Handler();

    MediaService mService;
    int currentTime;
    int currentDuration;

    /** Storage provider to access the SQLite DB */
    public static StorageProvider store;

    static ProgramExpandableAdapter adapter;
    static HeaderAdapter headerAdapter;

    ORFParser.ORFProgram currentProgram;
    int currentPosition;

    boolean useHWAccel = true;

    //Timer instance for the remotelist (not working with handler -> NetworkOnMainThread exception)
    Timer programDataTimer;

    //Runnable instance for the seek update: time in the main activity & notification (will be posted to the handler)
    Runnable mRunnableSeek = new Runnable() {
        public void run() {
            TimerMethodSeek();
        }
    };

    /**
     The {@link android.support.v4.view.PagerAdapter} that will provide
     fragments for each of the sections. We use a
     {@link FragmentPagerAdapter} derivative, which will keep every
     loaded fragment in memory. If this becomes too memory intensive, it
     may be best to switch to a
     {@link android.support.v4.app.FragmentStatePagerAdapter}.
     */
    SectionsPagerAdapter mSectionsPagerAdapter;

    private MainFragment dataFragment;
    private static ArrayList<ORFParser.ORFProgram> programListToday;
    private static ArrayList<ORFParser.ORFProgram> programListTodayMinus1;
    private static ArrayList<ORFParser.ORFProgram> programListTodayMinus2;
    private static ArrayList<ORFParser.ORFProgram> programListTodayMinus3;
    private static ArrayList<ORFParser.ORFProgram> programListTodayMinus4;
    private static ArrayList<ORFParser.ORFProgram> programListTodayMinus5;
    private static ArrayList<ORFParser.ORFProgram> programListTodayMinus6;
    private static ArrayList<ORFParser.ORFProgram> programListTodayMinus7;
    private static ArrayList<ORFParser.ORFProgram> programListOffline;
    /** boolean flag to show if a notification was already created. If paused, only one notification is issued */
    boolean isPausedNotified = false;

    /** flag (from the settings) to show if a notification is issued during playback */
    boolean showPausedNotification = true;

    /** flag (from the settings) to show if a notification is issued if paused */
    boolean showPlayNotification = true;

    /** flag (from the settings) to show if a notification is issued on the lockscreen */
    boolean showLockscreenNotification = true;

    private ServiceConnection mConnection;

    int currentDownloadNotificationId = NOTIFICATION_DOWNLOAD_ID;

    public final static int NOTIFICATION_COMMAND_CANCEL = 1;
    public final static int NOTIFICATION_COMMAND_UPDATE_TEXT = 2;
    public final static int NOTIFICATION_COMMAND_UPDATE_ICON = 3;
    public final static int NOTIFICATION_COMMAND_NOTHING = 4;

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
        FragmentManager fm = getFragmentManager();
        dataFragment = (MainFragment) fm.findFragmentByTag("data");
        if (dataFragment == null) {
            createWithoutFragment();
        } else {
            restoreFromFragment();
        }
    }

    void createWithoutFragment() {
        Intent mMediaServiceIntent;

        // add the fragment
        dataFragment = new MainFragment();

        mConnection = new ServiceConnection() {
            public void onServiceConnected(ComponentName className, IBinder service) {
                //mService = ((LocalBinder<MediaService>) service).getService();
                mService = MediaService.getService(service);
                dataFragment.setMediaService(mService);
            }

            public void onServiceDisconnected(ComponentName className) {
            }
        };

        dataFragment.setMediaConnection(mConnection);

        //start the mediaplayer service
        mMediaServiceIntent = new Intent(this, MediaService.class);
        startService(mMediaServiceIntent);
        bindService(mMediaServiceIntent, mConnection, Context.BIND_IMPORTANT);
        dataFragment.setMediaServiceIntent(mMediaServiceIntent);
    }

    void restoreFromFragment() {
        if (dataFragment == null)
            return;

        Intent mMediaServiceIntent;

        //Restore everything necessary from the dataFragment (if available)
        programListToday = dataFragment.getProgramListToday();
        programListTodayMinus1 = dataFragment.getProgramListTodayMinus1();
        programListTodayMinus2 = dataFragment.getProgramListTodayMinus2();
        programListTodayMinus3 = dataFragment.getProgramListTodayMinus3();
        programListTodayMinus4 = dataFragment.getProgramListTodayMinus4();
        programListTodayMinus5 = dataFragment.getProgramListTodayMinus5();
        programListTodayMinus6 = dataFragment.getProgramListTodayMinus6();
        programListTodayMinus7 = dataFragment.getProgramListTodayMinus7();
        currentDuration = dataFragment.getCurrentDuration();
        currentTime = dataFragment.getCurrentTime();
        programListOffline = dataFragment.getProgramListOffline();
        mService = dataFragment.getMediaService();
        mConnection = dataFragment.getMediaConnection();
        mMediaServiceIntent = dataFragment.getMediaServiceIntent();
        currentDownloadNotificationId = dataFragment.getCurrentDownloadNotificationId();
        currentPosition = dataFragment.getCurrentPosition();
        currentProgram = dataFragment.getCurrentProgram();

        //if something went wrong, restart the connection...
        if (mMediaServiceIntent == null || mConnection == null || mService == null) {
            Log.w("Restore activity", "Something went wrong with the service connection, restarting: ");
            Log.w("Restore activity", "mMediaServiceIntent:" + mMediaServiceIntent);
            Log.w("Restore activity", "mConnection:" + mConnection);
            Log.w("Restore activity", "mService:" + mService);
            mMediaServiceIntent = new Intent(this, MediaService.class);
            startService(mMediaServiceIntent);

            mConnection = new ServiceConnection() {
                public void onServiceConnected(ComponentName className, IBinder service) {
                    //mService = ((LocalBinder<MediaService>) service).getService();
                    mService = MediaService.getService(service);
                    dataFragment.setMediaService(mService);
                }

                public void onServiceDisconnected(ComponentName className) {
                    // As our service is in the same process, this should never be called
                }
            };
            dataFragment.setMediaConnection(mConnection);
            dataFragment.setMediaServiceIntent(mMediaServiceIntent);
        }

        bindService(mMediaServiceIntent, mConnection, Context.BIND_AUTO_CREATE);
        TextView textViewCurrentStream = (TextView) findViewById(R.id.textViewCurrentStream);
        if (dataFragment.getTextPlayButton() != null)
            textViewCurrentStream.setText(dataFragment.getTextPlayButton());
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        store = new StorageProvider(this);

        // find the retained fragment on activity restarts
        FragmentManager fm = getFragmentManager();
        dataFragment = (MainFragment) fm.findFragmentByTag("data");
        // create the fragment and data the first time
        // or load existing data...
        if (dataFragment == null) {
            //create everything necessary new
            createWithoutFragment();
            //commit our fragment (containing data) to the FragmentManager
            fm.beginTransaction().add(dataFragment, "data").commit();
        } else {
            //Existing fragment -> restore everything from there
            restoreFromFragment();
        }

        mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());

        IntentFilter filter = new IntentFilter();
        filter.setPriority(Integer.MAX_VALUE);
        filter.addAction(MediaService.ACTION_PLAY_PAUSE);
        registerReceiver(mReceiver, filter);
        registerV21();

        /**
        Set up the ViewPager with the sections adapter.
        The {@link ViewPager} that will host the section contents.
        */
        ViewPager mViewPager = (ViewPager) findViewById(R.id.container);
        mViewPager.setAdapter(mSectionsPagerAdapter);
        mViewPager.setPageTransformer(true, new CubeOutTransformer());

        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        addGUIListener();
    }

    /** add all callbacks to the corresponding GUI elements:
     * -) Play the live stream
     * -) Play/Pause button
     * -) Seekbar
     */
    public void addGUIListener() {
        //add a click listener to the "Live" button
        Button buttonLive = (Button) findViewById(R.id.buttonLive);
        buttonLive.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mService.setUseHWAcceleration(useHWAccel);
                mService.onCommand(MediaService.ACTION_LOAD, ORFParser.ORF_LIVE_URL);
                TextView text = (TextView) findViewById(R.id.textViewCurrentStream);
                if (dataFragment != null)
                    dataFragment.setTextPlayButton("LIVE");
                text.setText("LIVE");
                handler.removeCallbacks(mRunnableSeek);
                handler.postDelayed(mRunnableSeek, 1000);
                updateGUIElementsVisibility();
            }
        });

        //add a click listener to the "Play/Pause" button
        final ImageButton buttonPlayPause = (ImageButton) findViewById(R.id.buttonPause);
        buttonPlayPause.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mService != null) {
                    mService.setUseHWAcceleration(useHWAccel);
                    mService.onCommand(MediaService.ACTION_PLAY_PAUSE, "");
                }
                handler.removeCallbacks(mRunnableSeek);
                handler.postDelayed(mRunnableSeek, 1000);
                updateGUIElementsVisibility();
            }
        });
        updateGUIElementsVisibility();

        SeekBar seekBar = (SeekBar) findViewById(R.id.seekBar);
        seekBar.setMax(1000);
        seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
                if (mService != null) {
                    mService.onCommand(MediaService.ACTION_SETTIME,
                            String.valueOf((float) seekBar.getProgress() / 1000));
                    updateGUIElementsVisibility();
                }
            }
        });
    }

    /**
     * Update the visibility settings for the play/pause button in the main activity
     */
    private void updateGUIElementsVisibility() {
        ImageButton buttonPlayPause = (ImageButton) findViewById(R.id.buttonPause);
        TextView time = (TextView) findViewById(R.id.textViewTime);
        SeekBar seekBar = (SeekBar) findViewById(R.id.seekBar);
        buttonPlayPause.setImageResource(R.drawable.ic_av_pause_circle_outline);
        if (mService != null) {
            switch (mService.getState()) {
            case MediaService.MEDIA_STATE_PLAYING:
                seekBar.setVisibility(View.VISIBLE);
                time.setVisibility(View.VISIBLE);
                buttonPlayPause.setVisibility(View.VISIBLE);
                break;
            case MediaService.MEDIA_STATE_IDLE:
                seekBar.setVisibility(View.INVISIBLE);
                time.setVisibility(View.INVISIBLE);
                buttonPlayPause.setVisibility(View.INVISIBLE);
                break;
            case MediaService.MEDIA_STATE_PAUSED:
                seekBar.setVisibility(View.VISIBLE);
                time.setVisibility(View.VISIBLE);
                buttonPlayPause.setVisibility(View.VISIBLE);
                buttonPlayPause.setImageResource(R.drawable.ic_av_play_circle_outline);
                break;
            default:
                break;
            }
        } else {
            seekBar.setVisibility(View.INVISIBLE);
            time.setVisibility(View.INVISIBLE);
            buttonPlayPause.setVisibility(View.INVISIBLE);
        }
    }

    /** callback listener for short clicks
     * Used to play the selected programs
     */
    public void programClickListener(ORFParser.ORFProgram child, int position) {
        currentProgram = child;
        dataFragment.setCurrentProgram(child);
        currentPosition = position;
        dataFragment.setCurrentPosition(position);
        TextView streamtext = (TextView) findViewById(R.id.textViewCurrentStream);
        streamtext.setText(child.title);
        if (dataFragment != null)
            dataFragment.setTextPlayButton(child.title);
        Toast.makeText(MainActivity.this, "Play", Toast.LENGTH_SHORT).show();
        mService.setUseHWAcceleration(useHWAccel);
        mService.onCommand(MediaService.ACTION_LOAD, child.url);
        updateGUIElementsVisibility();
        //schedule the perdiodic seek time / notification update
        handler.removeCallbacks(mRunnableSeek);
        handler.postDelayed(mRunnableSeek, 1000);
    }

    /** callback listener for long clicks.
     * Used to delete an already loaded program
     * or download a remote one
    */
    public void programLongClickListener(final ORFParser.ORFProgram child, boolean toDelete) {
        if (toDelete) {
            AlertDialog.Builder builder = new AlertDialog.Builder(this);
            builder.setMessage("Wollen Sie den Beitrag " + child.shortTitle + " wirklich lschen?")
                    .setTitle("Lschen");
            builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int id) {
                    //delete the list entry & update the list
                    store.deleteOffline(String.valueOf(child.id));
                    Toast.makeText(MainActivity.this, "Beitrag gelscht", Toast.LENGTH_SHORT).show();
                    //Update the list
                    ArrayList<ORFParser.ORFProgram> temp = store.getOffline();
                    if (temp != null) {
                        programListOffline = temp;
                        dataFragment.setProgramListOffline(temp);
                    }
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            ViewPager mViewPager = (ViewPager) findViewById(R.id.container);
                            mViewPager.getAdapter().notifyDataSetChanged();
                        }
                    });
                }
            });
            builder.setNegativeButton("Abbrechen", new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int id) {
                    // User cancelled the dialog
                }
            });

            builder.create().show();

        } else {
            programDownloadClickListener(child);
        }
    }

    private class DownloadNotificationCancelTask implements Runnable {
        int notificationId;

        public void setId(int id) {
            this.notificationId = id;
        }

        @Override
        public void run() {
            //fetch the notification manager
            NotificationManager mNotificationManager = (NotificationManager) getSystemService(
                    Context.NOTIFICATION_SERVICE);
            //cancel the given notificaiton
            mNotificationManager.cancel(this.notificationId);
        }
    }

    private class DownloadProgramTask extends AsyncTask<ORFParser.ORFProgram, Integer, Void> {
        int notificationId = NOTIFICATION_DOWNLOAD_ID;

        public DownloadProgramTask(int notificationId) {
            this.notificationId = notificationId;
        }

        /** The system calls this to perform work in a worker thread and
         * delivers it the parameters given to AsyncTask.execute() */
        //Download according to: https://stackoverflow.com/questions/6407324/how-to-get-image-from-url-in-android/13174188#13174188
        protected Void doInBackground(ORFParser.ORFProgram... childs) {
            ORFParser.ORFProgram child = childs[0];
            String fileName;
            try {
                ORFParser parser = new ORFParser();
                //setup the connection
                URL orfURL = new URL(child.url);
                HttpURLConnection urlConnection = (HttpURLConnection) orfURL.openConnection();
                urlConnection.setRequestMethod("GET");
                urlConnection.setDoOutput(true);
                urlConnection.connect();

                //create the file
                SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
                //TODO: external storage? is jetzt nicht die SD Karte...
                String settingsPath = settings.getString(getString(R.string.SETTINGS_DOWNLOADFOLDER),
                        Environment.getExternalStorageDirectory().toString());
                if (settingsPath.equals(""))
                    settingsPath = Environment.getExternalStorageDirectory().toString();

                File folder = new File(settingsPath + "/1-Beitrge");
                fileName = child.dayLabel + "-" + child.time.replace(':', '.') + "-"
                        + child.shortTitle.replace('/', '_') + ".mp3";
                folder.mkdirs();

                File file = new File(folder, fileName);
                if (file.createNewFile()) {
                    file.createNewFile();
                }

                //Setup the streams
                FileOutputStream fileOutput = new FileOutputStream(file);
                InputStream inputStream = urlConnection.getInputStream();

                //this is the total size of the file
                int totalSize = urlConnection.getContentLength();
                //variable to store total downloaded bytes
                int downloadedSize = 0;

                //create a buffer...
                byte[] buffer = new byte[1024];
                int bufferLength; //used to store a temporary size of the buffer

                //Create a notification to show the download progress
                NotificationManager mNotificationManager = (NotificationManager) getSystemService(
                        Context.NOTIFICATION_SERVICE);
                int progress = 0;
                int progresstemp;
                NotificationCompat.Builder mNotifyBuilder = new NotificationCompat.Builder(getBaseContext())
                        .setContentTitle("Download").setContentText(child.title + "0%")
                        .setSmallIcon(R.drawable.notification_download);

                //now, read through the input buffer and write the contents to the file
                while ((bufferLength = inputStream.read(buffer)) > 0) {
                    //add the data in the buffer to the file in the file output stream (the file on the sd card
                    fileOutput.write(buffer, 0, bufferLength);
                    //add up the size so we know how much is downloaded
                    downloadedSize += bufferLength;
                    //show every progress change in the notification
                    progresstemp = (int) (((float) downloadedSize / (float) totalSize) * 100);
                    if (progresstemp != progress) {
                        mNotifyBuilder.setContentText(child.title + " " + progresstemp + "%");
                        mNotifyBuilder.setProgress(100, progresstemp, false);
                        mNotificationManager.notify(this.notificationId, mNotifyBuilder.build());
                        progress = progresstemp;
                    }

                    if (isCancelled()) {
                        mNotificationManager.cancelAll();
                        break;
                    }
                }
                //close the output stream when done

                fileOutput.close();

                if (downloadedSize == totalSize) {
                    runOnUiThread(new Runnable() {
                        public void run() {
                            Toast.makeText(getBaseContext(), "Download abgeschlossen", Toast.LENGTH_SHORT).show();
                        }
                    });
                    //Finally: add the downloaded program to the offline list and update the UI...
                    child.url = folder + "/" + fileName;
                    store.addOffline(child);
                    programListOffline = store.getOffline();
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            try {
                                ViewPager mViewPager = (ViewPager) findViewById(R.id.container);
                                mViewPager.getAdapter().notifyDataSetChanged();
                            } catch (IllegalStateException e) {
                                Log.w("DOWNLOAD", e.getMessage());
                            }
                        }
                    });

                    //start a timer to cancel the notification after a few minutes
                    DownloadNotificationCancelTask cancelTask = new DownloadNotificationCancelTask();
                    cancelTask.setId(this.notificationId);
                    handler.postDelayed(cancelTask,
                            Integer.valueOf(
                                    settings.getString(getString(R.string.SETTINGS_DOWNLOADNOTECANCEL), "20"))
                                    * 1000);
                } else {
                    runOnUiThread(new Runnable() {
                        public void run() {
                            Toast.makeText(getBaseContext(), "Fehler: unvollstndiger Download",
                                    Toast.LENGTH_SHORT).show();
                        }
                    });
                }
            } catch (final IOException e) {
                runOnUiThread(new Runnable() {
                    public void run() {
                        Log.e("DOWNLOAD", e.getMessage());
                        Toast.makeText(getBaseContext(),
                                "Fehler: keine Berechtigung (bitte in den Einstellungen freischalten)",
                                Toast.LENGTH_LONG).show();
                    }
                });
            } catch (final Exception e) {
                runOnUiThread(new Runnable() {
                    public void run() {
                        Log.e("DOWNLOAD", e.getMessage());
                        Toast.makeText(getBaseContext(), "Fehler: " + e.getMessage(), Toast.LENGTH_SHORT).show();

                    }
                });
            }
            return null;
        }

        protected void onProgressUpdate(Integer... progress) {
        }

        /** The system calls this to perform work in the UI thread and delivers
         * the result from doInBackground() */
        protected void onPostExecute(Void result) {

        }
    }

    /** listener for download item clicks **/
    public void programDownloadClickListener(final ORFParser.ORFProgram child) {
        Toast.makeText(getBaseContext(), "Download: " + child.shortTitle, Toast.LENGTH_SHORT).show();
        //increment the download notification id for each download (it is reset if the app is completely closed)
        currentDownloadNotificationId++;
        //store the current id to the fragment
        dataFragment.setCurrentDownloadNotificationId(currentDownloadNotificationId);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
            new DownloadProgramTask(currentDownloadNotificationId).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,
                    child);
        else
            new DownloadProgramTask(currentDownloadNotificationId).execute(child);
    }

    @Override
    public void onResume() {
        super.onResume();
        //load settings from preferences (interval of the refetch and notification settings)
        SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this);
        int interval = Integer.valueOf(settings.getString(getString(R.string.SETTINGS_REFETCH_INTERVAL), "5"));
        showPausedNotification = settings.getBoolean(getString(R.string.SETTINGS_SHOW_PAUSED_NOTIFICATION), true);
        showPlayNotification = settings.getBoolean(getString(R.string.SETTINGS_SHOW_PLAY_NOTIFICATION), true);
        showLockscreenNotification = settings.getBoolean(getString(R.string.SETTINGS_SHOW_LOCKSCREEN_NOTIFICATION),
                true);
        useHWAccel = settings.getBoolean(getString(R.string.SETTINGS_HW_ACCELERATOR), true);

        //schedule the regular update of the remote list
        programDataTimer = new Timer();
        programDataTimer.schedule(new TimerTask() {
            @Override
            public void run() {
                TimerMethodRemoteList();
            }
        }, 0, interval * 60 * 1000);

        //Create the regular update timer for the notifications and the progress bar in the GUI
        attachTimerMethodSeek();
        //Update the Play/Pause button in the main activity
        updateGUIElementsVisibility();
    }

    public void attachTimerMethodSeek() {
        handler.removeCallbacks(mRunnableSeek);
        handler.post(mRunnableSeek);
    }

    /**
     * Regular called timer method, fetching the remote lists in 9 concurrent threads
     */
    private void TimerMethodRemoteList() {
        //fetch all offline programs first

        ArrayList<ORFParser.ORFProgram> temp = store.getOffline();
        if (temp != null) {
            if (!temp.equals(programListOffline)) {
                programListOffline = temp;
                try {
                    dataFragment.setProgramListOffline(temp);
                } catch (Exception e) {
                    Log.e(MainActivity.TAG_REMOTELIST, "Exception while saving list to fragment");
                }
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        ViewPager mViewPager = (ViewPager) findViewById(R.id.container);
                        try {
                            mViewPager.getAdapter().notifyDataSetChanged();
                        } catch (IllegalStateException e) {
                            Log.w(MainActivity.TAG_REMOTELIST, "Exception while updating list: " + e.getMessage());
                        }
                    }
                });
            }
        }

        //post all fetch actions (for each day since today-1week)
        new Thread(new Runnable() {
            @Override
            public void run() {
                //Create calendar object (today)
                Calendar today = new GregorianCalendar();
                //create parser object
                ORFParser parser = new ORFParser();

                ArrayList<ORFParser.ORFProgram> temp;
                temp = parser.getProgramsForDay(today.getTime(), store);
                if (temp != null && !temp.equals(programListToday)) {
                    programListToday = temp;
                    try {
                        dataFragment.setProgramListToday(temp);
                    } catch (Exception e) {
                        Log.e(MainActivity.TAG_REMOTELIST, "Exception while saving list to fragment");
                    }
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            ViewPager mViewPager = (ViewPager) findViewById(R.id.container);
                            try {
                                mViewPager.getAdapter().notifyDataSetChanged();
                            } catch (IllegalStateException e) {
                                Log.w(MainActivity.TAG_REMOTELIST,
                                        "Exception while updating list: " + e.getMessage());
                            }
                        }
                    });
                }
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                //Create calendar object (today)
                Calendar today = new GregorianCalendar();
                //create parser object
                ORFParser parser = new ORFParser();

                ArrayList<ORFParser.ORFProgram> temp;

                today.add(Calendar.DAY_OF_MONTH, -1);
                temp = parser.getProgramsForDay(today.getTime(), store);
                if (temp != null && !temp.equals(programListTodayMinus1)) {
                    programListTodayMinus1 = temp;
                    try {
                        dataFragment.setProgramListTodayMinus1(temp);
                    } catch (Exception e) {
                        Log.e(MainActivity.TAG_REMOTELIST, "Exception while saving list to fragment");
                    }
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            ViewPager mViewPager = (ViewPager) findViewById(R.id.container);
                            try {
                                mViewPager.getAdapter().notifyDataSetChanged();
                            } catch (IllegalStateException e) {
                                Log.w(MainActivity.TAG_REMOTELIST,
                                        "Exception while updating list: " + e.getMessage());
                            }
                        }
                    });
                }
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                //Create calendar object (today)
                Calendar today = new GregorianCalendar();
                //create parser object
                ORFParser parser = new ORFParser();

                ArrayList<ORFParser.ORFProgram> temp;

                today.add(Calendar.DAY_OF_MONTH, -2);
                temp = parser.getProgramsForDay(today.getTime(), store);
                if (temp != null && !temp.equals(programListTodayMinus2)) {
                    programListTodayMinus2 = temp;
                    try {
                        dataFragment.setProgramListTodayMinus2(temp);
                    } catch (Exception e) {
                        Log.e(MainActivity.TAG_REMOTELIST, "Exception while saving list to fragment");
                    }
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            ViewPager mViewPager = (ViewPager) findViewById(R.id.container);
                            try {
                                mViewPager.getAdapter().notifyDataSetChanged();
                            } catch (IllegalStateException e) {
                                Log.w(MainActivity.TAG_REMOTELIST,
                                        "Exception while updating list: " + e.getMessage());
                            }
                        }
                    });
                }
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                //Create calendar object (today)
                Calendar today = new GregorianCalendar();
                //create parser object
                ORFParser parser = new ORFParser();

                ArrayList<ORFParser.ORFProgram> temp;

                today.add(Calendar.DAY_OF_MONTH, -3);
                temp = parser.getProgramsForDay(today.getTime(), store);
                if (temp != null && !temp.equals(programListTodayMinus3)) {
                    programListTodayMinus3 = temp;
                    try {
                        dataFragment.setProgramListTodayMinus3(temp);
                    } catch (Exception e) {
                        Log.e(MainActivity.TAG_REMOTELIST, "Exception while saving list to fragment");
                    }
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            ViewPager mViewPager = (ViewPager) findViewById(R.id.container);
                            try {
                                mViewPager.getAdapter().notifyDataSetChanged();
                            } catch (IllegalStateException e) {
                                Log.w(MainActivity.TAG_REMOTELIST,
                                        "Exception while updating list: " + e.getMessage());
                            }
                        }
                    });
                }
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                //Create calendar object (today)
                Calendar today = new GregorianCalendar();
                //create parser object
                ORFParser parser = new ORFParser();

                ArrayList<ORFParser.ORFProgram> temp;

                today.add(Calendar.DAY_OF_MONTH, -4);
                temp = parser.getProgramsForDay(today.getTime(), store);
                if (temp != null && !temp.equals(programListTodayMinus4)) {
                    programListTodayMinus4 = temp;
                    try {
                        dataFragment.setProgramListTodayMinus4(temp);
                    } catch (Exception e) {
                        Log.e(MainActivity.TAG_REMOTELIST, "Exception while saving list to fragment");
                    }
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            ViewPager mViewPager = (ViewPager) findViewById(R.id.container);
                            try {
                                mViewPager.getAdapter().notifyDataSetChanged();
                            } catch (IllegalStateException e) {
                                Log.w(MainActivity.TAG_REMOTELIST,
                                        "Exception while updating list: " + e.getMessage());
                            }
                        }
                    });
                }
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                //Create calendar object (today)
                Calendar today = new GregorianCalendar();
                //create parser object
                ORFParser parser = new ORFParser();

                ArrayList<ORFParser.ORFProgram> temp;

                today.add(Calendar.DAY_OF_MONTH, -5);
                temp = parser.getProgramsForDay(today.getTime(), store);
                if (temp != null && !temp.equals(programListTodayMinus5)) {
                    programListTodayMinus5 = temp;
                    try {
                        dataFragment.setProgramListTodayMinus5(temp);
                    } catch (Exception e) {
                        Log.e(MainActivity.TAG_REMOTELIST, "Exception while saving list to fragment");
                    }
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            ViewPager mViewPager = (ViewPager) findViewById(R.id.container);
                            try {
                                mViewPager.getAdapter().notifyDataSetChanged();
                            } catch (IllegalStateException e) {
                                Log.w(MainActivity.TAG_REMOTELIST,
                                        "Exception while updating list: " + e.getMessage());
                            }
                        }
                    });
                }
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                //Create calendar object (today)
                Calendar today = new GregorianCalendar();
                //create parser object
                ORFParser parser = new ORFParser();

                ArrayList<ORFParser.ORFProgram> temp;

                today.add(Calendar.DAY_OF_MONTH, -6);
                temp = parser.getProgramsForDay(today.getTime(), store);
                if (temp != null && !temp.equals(programListTodayMinus6)) {
                    programListTodayMinus6 = temp;
                    try {
                        dataFragment.setProgramListTodayMinus6(temp);
                    } catch (Exception e) {
                        Log.e(MainActivity.TAG_REMOTELIST, "Exception while saving list to fragment");
                    }
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            ViewPager mViewPager = (ViewPager) findViewById(R.id.container);
                            try {
                                mViewPager.getAdapter().notifyDataSetChanged();
                            } catch (IllegalStateException e) {
                                Log.w(MainActivity.TAG_REMOTELIST,
                                        "Exception while updating list: " + e.getMessage());
                            }
                        }
                    });
                }
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                //Create calendar object (today)
                Calendar today = new GregorianCalendar();
                //create parser object
                ORFParser parser = new ORFParser();

                ArrayList<ORFParser.ORFProgram> temp;

                today.add(Calendar.DAY_OF_MONTH, -7);
                temp = parser.getProgramsForDay(today.getTime(), store);
                if (temp != null && !temp.equals(programListTodayMinus7)) {
                    programListTodayMinus7 = temp;
                    try {
                        dataFragment.setProgramListTodayMinus7(temp);
                    } catch (Exception e) {
                        Log.e(MainActivity.TAG_REMOTELIST, "Exception while saving list to fragment");
                    }
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            ViewPager mViewPager = (ViewPager) findViewById(R.id.container);
                            try {
                                mViewPager.getAdapter().notifyDataSetChanged();
                            } catch (IllegalStateException e) {
                                Log.w(MainActivity.TAG_REMOTELIST,
                                        "Exception while updating list: " + e.getMessage());
                            }
                        }
                    });
                }
            }
        }).start();
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    private void registerV21() {
        final IntentFilter intentFilter = new IntentFilter(AudioManager.ACTION_HDMI_AUDIO_PLUG);
        registerReceiver(mReceiverV21, intentFilter);
    }

    private final BroadcastReceiver mReceiverV21 = AndroidUtil.isLolliPopOrLater() ? new BroadcastReceiver() {
        @TargetApi(Build.VERSION_CODES.LOLLIPOP)
        @Override
        public void onReceive(Context context, Intent intent) {
            //final String action = intent.getAction();
            //if (action == null)
        }
    } : null;

    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (mService == null) {
                Log.w("TAG", "Intent received, but MediaService is not loaded, skipping.");
                return;
            }

            if (action.equalsIgnoreCase(MediaService.ACTION_PLAY_PAUSE)) {
                mService.onCommand(MediaService.ACTION_PLAY_PAUSE, "");
                attachTimerMethodSeek();
            }
        }
    };

    /**
     * Regular scheduled method, updating following information:
     * Current seek time / full time of the program in the notification
     * Current seek time / full time / progress bar in the main activity
     */
    private void TimerMethodSeek() {
        if (mService != null) {
            /** manage time formats, depending on play state **/
            SimpleDateFormat formatter = new SimpleDateFormat("mm:ss");
            int timeStamp = mService.getCurrentPosition();
            String dateString;

            switch (timeStamp) {
            //stopped/preparing... -> time: 0
            case -1:
                dateString = formatter.format(new Date(0));
                break;
            //pause: no change...
            case -2:
                dateString = formatter.format(new Date(currentTime));
                break;
            //Playing...
            default:
                dateString = formatter.format(new Date(timeStamp));
                currentTime = timeStamp;
                dataFragment.setCurrentTime(timeStamp);
                break;
            }
            //add the separator
            dateString += "/";

            timeStamp = mService.getDuration();
            switch (timeStamp) {
            case -1:
                dateString += "00:00";
                break;
            case -2:
                dateString += formatter.format(new Date(currentDuration));
                break;
            default:
                dateString += formatter.format(new Date(timeStamp));
                currentDuration = timeStamp;
                dataFragment.setCurrentDuration(currentDuration);
                break;
            }

            SeekBar seekbar = (SeekBar) findViewById(R.id.seekBar);
            if (!mService.isLive()) {
                try {
                    seekbar.setProgress((int) (((float) currentTime / (float) currentDuration) * 1000));
                } catch (ArithmeticException e) {
                    Log.d("PUBLICSTREAM", "Progressbar: Div by 0");
                }
            } else {
                currentProgram = null;
                seekbar.setProgress(0);
            }

            /** set the corresponding notification **/
            switch (timeStamp) {
            case -1:
                //cancel() if the playback is stopped
                mService.stopForeground(true);
                updateScreenNotification(NOTIFICATION_COMMAND_CANCEL, "");
                isPausedNotified = false;
                Log.i("NOTE", "STOPPED -> cancel");
                handler.removeCallbacks(mRunnableSeek);

                //if we reached this block either the current program was completely played
                //or we are at the activity start-up
                //so we need to store the current program as "listened" in following places:
                //-) currentProgram
                //-) StorageProvider
                //-) The containing ArrayList of programs
                if (currentProgram != null) {
                    store.setListened(String.valueOf(currentProgram.id), currentProgram.dayLabel);
                    currentProgram.isListened = true;

                    if (programListOffline.size() > currentPosition
                            && programListOffline.get(currentPosition).id == currentProgram.id)
                        programListOffline.set(currentPosition, currentProgram);
                    if (programListToday.size() > currentPosition
                            && programListToday.get(currentPosition).id == currentProgram.id)
                        programListToday.set(currentPosition, currentProgram);
                    if (programListTodayMinus1.size() > currentPosition
                            && programListTodayMinus1.get(currentPosition).id == currentProgram.id)
                        programListTodayMinus1.set(currentPosition, currentProgram);
                    if (programListTodayMinus2.size() > currentPosition
                            && programListTodayMinus2.get(currentPosition).id == currentProgram.id)
                        programListTodayMinus2.set(currentPosition, currentProgram);
                    if (programListTodayMinus3.size() > currentPosition
                            && programListTodayMinus3.get(currentPosition).id == currentProgram.id)
                        programListTodayMinus3.set(currentPosition, currentProgram);
                    if (programListTodayMinus4.size() > currentPosition
                            && programListTodayMinus4.get(currentPosition).id == currentProgram.id)
                        programListTodayMinus4.set(currentPosition, currentProgram);
                    if (programListTodayMinus5.size() > currentPosition
                            && programListTodayMinus5.get(currentPosition).id == currentProgram.id)
                        programListTodayMinus5.set(currentPosition, currentProgram);
                    if (programListTodayMinus6.size() > currentPosition
                            && programListTodayMinus6.get(currentPosition).id == currentProgram.id)
                        programListTodayMinus6.set(currentPosition, currentProgram);
                    if (programListTodayMinus7.size() > currentPosition
                            && programListTodayMinus7.get(currentPosition).id == currentProgram.id)
                        programListTodayMinus7.set(currentPosition, currentProgram);
                    ViewPager mViewPager = (ViewPager) findViewById(R.id.container);
                    mViewPager.getAdapter().notifyDataSetChanged();
                    currentProgram = null;
                }
                break;
            case -2:
                mService.stopForeground(true);
                //if paused && not notified already (done only once)
                if (!isPausedNotified && showPausedNotification) {
                    isPausedNotified = true;
                    updateScreenNotification(NOTIFICATION_COMMAND_UPDATE_TEXT, "Pause: " + dateString);
                    Log.i("NOTE", "PAUSED -> new notification");
                }
                if (!showPausedNotification)
                    updateScreenNotification(NOTIFICATION_COMMAND_CANCEL, "");
                handler.removeCallbacks(mRunnableSeek);
                Log.i("NOTE", "PAUSED -> already notified");
                break;
            default:
                //if the playback is active, display the current time
                if (showPlayNotification) {
                    isPausedNotified = false;
                    Log.i("NOTE", "PLAY -> update...");
                    mService.startForeground(MainActivity.NOTIFICATION_PLAY_ID,
                            updateScreenNotification(NOTIFICATION_COMMAND_UPDATE_TEXT, "Abspielen: " + dateString));
                }
                handler.postDelayed(mRunnableSeek, 1000);
                break;
            }

            //Update the time in the text view (GUI, bottom right)
            TextView time = (TextView) findViewById(R.id.textViewTime);
            time.setText(dateString);
            updateGUIElementsVisibility();
        }
    }

    @Override
    public void onPause() {
        super.onPause();
        //Stop the regular list update
        programDataTimer.cancel();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(mConnection);
    }

    /**
     * @param item Menu item, given by Android
     * @return true, if no valid item was found
     *
     * Handling the menu for the main activity, by now:
     * -) SettingsActivity
     */
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {

        int id = item.getItemId();
        switch (id) {
        case R.id.action_settings:
            Intent intent = new Intent(this, SettingsActivity.class);
            startActivity(intent);
            return true;
        default:
            break;
        }
        return super.onOptionsItemSelected(item);
    }

    private Notification updateScreenNotification(int command, String parameter) {
        /** create the notification **/
        Intent notificationIntent = new Intent(getApplicationContext(), MainActivity.class)
                .setFlags(Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
        PendingIntent contentIntent = PendingIntent.getActivity(getApplicationContext(), 0, notificationIntent, 0);
        NotificationManager mNotificationManager = (NotificationManager) getSystemService(
                Context.NOTIFICATION_SERVICE);
        Notification mNotification;

        NotificationCompat.Builder mNotifyBuilder = new NotificationCompat.Builder(mService)
                .setContentTitle("1 - PublicStream").setSmallIcon(R.drawable.notification_play)
                .setVisibility(NotificationCompat.VISIBILITY_PUBLIC).setContentIntent(contentIntent);

        //PLay Pause
        PendingIntent piPlayPause = PendingIntent.getBroadcast(mService, 0,
                new Intent(MediaService.ACTION_PLAY_PAUSE), PendingIntent.FLAG_UPDATE_CURRENT);

        switch (command) {
        case NOTIFICATION_COMMAND_CANCEL:
            mNotificationManager.cancel(MainActivity.NOTIFICATION_PLAY_ID);
            return null;
        case NOTIFICATION_COMMAND_UPDATE_ICON:
            return null;
        case NOTIFICATION_COMMAND_UPDATE_TEXT:
            mNotifyBuilder.setContentText(parameter);
            if (mService.getState().equals(MediaService.MEDIA_STATE_PLAYING)) {
                mNotifyBuilder.addAction(R.drawable.ic_av_pause_circle_outline, "Pause", piPlayPause);
            } else {
                mNotifyBuilder.addAction(R.drawable.ic_av_play_circle_outline, "Play", piPlayPause);
            }
            mNotification = mNotifyBuilder.build();
            mNotificationManager.notify(MainActivity.NOTIFICATION_PLAY_ID, mNotification);

            return mNotification;
        case NOTIFICATION_COMMAND_NOTHING:
            return mNotifyBuilder.build();
        default:
            return null;
        }
    }

    /**
     * A placeholder fragment containing a simple view.
     */
    public static class PlaceholderFragment extends Fragment {
        /**
         * The fragment argument representing the section number for this
         * fragment.
         */
        private static final String ARG_SECTION_DATE = "sectionDate";

        public PlaceholderFragment() {
        }

        /**
         * Returns a new instance of this fragment for the given section
         * number.
         */
        public static PlaceholderFragment newInstance(int sectionNumber) {
            PlaceholderFragment fragment = new PlaceholderFragment();
            Bundle args = new Bundle();
            args.putInt(ARG_SECTION_DATE, sectionNumber);
            fragment.setArguments(args);
            return fragment;
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            View rootView = inflater.inflate(R.layout.fragment_main, container, false);

            ListView expandableList = (ListView) rootView.findViewById(R.id.programList);
            ListView headerList = (ListView) rootView.findViewById(R.id.headerList);
            headerAdapter = new HeaderAdapter(getArguments().getInt(ARG_SECTION_DATE));
            headerAdapter.setInflater(getActivity().getLayoutInflater());

            switch (getArguments().getInt(ARG_SECTION_DATE)) {
            case 1: //Offline
                adapter = new ProgramExpandableAdapter(true);
                adapter.setInflater(getActivity().getLayoutInflater(), getContext());
                adapter.setListPrograms(programListOffline);
                break;
            case 2: //Today
                adapter = new ProgramExpandableAdapter(false);
                adapter.setInflater(getActivity().getLayoutInflater(), getContext());
                adapter.setListPrograms(programListToday);
                break;
            case 3: //Today -1 day
                adapter = new ProgramExpandableAdapter(false);
                adapter.setInflater(getActivity().getLayoutInflater(), getContext());
                adapter.setListPrograms(programListTodayMinus1);
                break;
            case 4: //Today -2 day
                adapter = new ProgramExpandableAdapter(false);
                adapter.setInflater(getActivity().getLayoutInflater(), getContext());
                adapter.setListPrograms(programListTodayMinus2);
                break;
            case 5: //Today -3 day
                adapter = new ProgramExpandableAdapter(false);
                adapter.setInflater(getActivity().getLayoutInflater(), getContext());
                adapter.setListPrograms(programListTodayMinus3);
                break;
            case 6: //Today -4 day
                adapter = new ProgramExpandableAdapter(false);
                adapter.setInflater(getActivity().getLayoutInflater(), getContext());
                adapter.setListPrograms(programListTodayMinus4);
                break;
            case 7: //Today -5 day
                adapter = new ProgramExpandableAdapter(false);
                adapter.setInflater(getActivity().getLayoutInflater(), getContext());
                adapter.setListPrograms(programListTodayMinus5);
                break;
            case 8: //Today -6 day
                adapter = new ProgramExpandableAdapter(false);
                adapter.setInflater(getActivity().getLayoutInflater(), getContext());
                adapter.setListPrograms(programListTodayMinus6);
                break;
            case 9: //Today -7 day
                adapter = new ProgramExpandableAdapter(false);
                adapter.setInflater(getActivity().getLayoutInflater(), getContext());
                adapter.setListPrograms(programListTodayMinus7);
                break;
            }
            expandableList.setAdapter(adapter);
            headerList.setAdapter(headerAdapter);
            return rootView;
        }
    }

    /**
     * A {@link FragmentPagerAdapter} that returns a fragment corresponding to
     * one of the sections/tabs/pages.
     */
    public class SectionsPagerAdapter extends FragmentPagerAdapter {

        public SectionsPagerAdapter(android.support.v4.app.FragmentManager fm) {
            super(fm);
        }

        @Override
        public Fragment getItem(int position) {
            // getItem is called to instantiate the fragment for the given page.
            // Return a PlaceholderFragment (defined as a static inner class below).
            return PlaceholderFragment.newInstance(position + 1);
        }

        @Override
        public int getItemPosition(Object object) {
            return POSITION_NONE;
        }

        @Override
        public int getCount() {
            // Show 9 total pages (7days in the past, today and the offline parts)
            return 9;
        }

        @Override
        public CharSequence getPageTitle(int position) {
            return null;
        }
    }
}