github.popeen.dsub.activity.SubsonicActivity.java Source code

Java tutorial

Introduction

Here is the source code for github.popeen.dsub.activity.SubsonicActivity.java

Source

/*
 This file is part of Subsonic.
    
 Subsonic 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.
    
 Subsonic 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 Subsonic.  If not, see <http://www.gnu.org/licenses/>.
    
 Copyright 2009 (C) Sindre Mehus
 */
package github.popeen.dsub.activity;

import android.app.UiModeManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.Manifest.permission;
import android.media.AudioManager;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.support.design.widget.NavigationView;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.app.AppCompatDelegate;
import android.support.v7.widget.Toolbar;
import android.text.Html;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.view.animation.AnimationUtils;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;

import org.json.JSONObject;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.MalformedURLException;
import java.net.Socket;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import github.popeen.dsub.R;
import github.popeen.dsub.domain.ServerInfo;
import github.popeen.dsub.fragments.AdminFragment;
import github.popeen.dsub.fragments.SubsonicFragment;
import github.popeen.dsub.fragments.UserFragment;
import github.popeen.dsub.service.DownloadService;
import github.popeen.dsub.service.HeadphoneListenerService;
import github.popeen.dsub.service.MusicService;
import github.popeen.dsub.service.MusicServiceFactory;
import github.popeen.dsub.service.RESTMusicService;
import github.popeen.dsub.updates.UpdateApp;
import github.popeen.dsub.util.Constants;
import github.popeen.dsub.util.DrawableTint;
import github.popeen.dsub.util.ImageLoader;
import github.popeen.dsub.util.KakaduaUtil;
import github.popeen.dsub.util.SilentBackgroundTask;
import github.popeen.dsub.util.ThemeUtil;
import github.popeen.dsub.util.Util;
import github.popeen.dsub.view.UpdateView;
import github.popeen.dsub.util.UserUtil;

public class SubsonicActivity extends AppCompatActivity implements OnItemSelectedListener, SensorEventListener {
    private static final String TAG = SubsonicActivity.class.getSimpleName();
    private static ImageLoader IMAGE_LOADER;
    protected static String theme;
    protected static boolean fullScreen;
    protected static boolean actionbarColored;
    private static final int MENU_GROUP_SERVER = 10;
    private static final int MENU_ITEM_SERVER_BASE = 100;
    public static final int PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE = 1;
    public static final int PERMISSIONS_REQUEST_LOCATION = 2;

    private final List<Runnable> afterServiceAvailable = new ArrayList<>();
    private boolean drawerIdle = true;
    private boolean destroyed = false;
    private boolean finished = false;
    protected List<SubsonicFragment> backStack = new ArrayList<SubsonicFragment>();
    protected SubsonicFragment currentFragment;
    protected View primaryContainer;
    protected View secondaryContainer;
    protected boolean tv = false;
    protected boolean touchscreen = true;
    protected Handler handler = new Handler();
    Spinner actionBarSpinner;
    ArrayAdapter<CharSequence> spinnerAdapter;
    ViewGroup rootView;
    DrawerLayout drawer;
    ActionBarDrawerToggle drawerToggle;
    NavigationView drawerList;
    View drawerHeader;
    ImageView drawerUserAvatar;
    ImageView drawerHeaderToggle;
    TextView drawerServerName;
    TextView drawerUserName;
    int lastSelectedPosition = 0;
    boolean showingTabs = true;
    boolean drawerOpen = false;
    SharedPreferences.OnSharedPreferenceChangeListener preferencesListener;
    private SensorManager sensorManager;
    float x, y, z;
    boolean checkShake = false;

    static {
        AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_AUTO);
    }

    @Override
    protected void onCreate(Bundle bundle) {

        sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
        sensorManager.registerListener(this, sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
                SensorManager.SENSOR_DELAY_NORMAL);

        UiModeManager uiModeManager = (UiModeManager) getSystemService(UI_MODE_SERVICE);
        if (uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION) {
            // tv = true;
        }
        PackageManager pm = getPackageManager();
        if (!pm.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN)) {
            touchscreen = false;
        }

        setUncaughtExceptionHandler();
        applyTheme();
        applyFullscreen();
        super.onCreate(bundle);
        startService(new Intent(this, DownloadService.class));
        setVolumeControlStream(AudioManager.STREAM_MUSIC);

        if (getIntent().hasExtra(Constants.FRAGMENT_POSITION)) {
            lastSelectedPosition = getIntent().getIntExtra(Constants.FRAGMENT_POSITION, 0);
        }

        if (preferencesListener == null) {
            preferencesListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
                @Override
                public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
                    // When changing drawer settings change visibility
                    switch (key) {
                    case Constants.PREFERENCES_KEY_PODCASTS_ENABLED:
                        setDrawerItemVisible(R.id.drawer_podcasts, false);
                        break;
                    case Constants.PREFERENCES_KEY_BOOKMARKS_ENABLED:
                        setDrawerItemVisible(R.id.drawer_bookmarks, false);
                        break;
                    case Constants.PREFERENCES_KEY_INTERNET_RADIO_ENABLED:
                        setDrawerItemVisible(R.id.drawer_internet_radio_stations, false);
                        break;
                    case Constants.PREFERENCES_KEY_SHARED_ENABLED:
                        setDrawerItemVisible(R.id.drawer_shares, false);
                        break;
                    case Constants.PREFERENCES_KEY_CHAT_ENABLED:
                        setDrawerItemVisible(R.id.drawer_chat, false);
                        break;
                    case Constants.PREFERENCES_KEY_ADMIN_ENABLED:
                        setDrawerItemVisible(R.id.drawer_admin, false);
                        break;
                    }
                }
            };
            Util.getPreferences(this).registerOnSharedPreferenceChangeListener(preferencesListener);
        }

        if (ContextCompat.checkSelfPermission(this,
                permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, new String[] { permission.WRITE_EXTERNAL_STORAGE },
                    PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE);
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
        switch (requestCode) {
        case PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE: {
            // If request is cancelled, the result arrays are empty.
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

            } else {
                Util.toast(this, R.string.permission_external_storage_failed);
                finish();
            }
        }
        }
    }

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

        if (spinnerAdapter == null) {
            createCustomActionBarView();
        }
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
        getSupportActionBar().setHomeButtonEnabled(true);

        // Sync the toggle state after onRestoreInstanceState has occurred.
        if (drawerToggle != null) {
            drawerToggle.syncState();
        }

        if (Util.shouldStartOnHeadphones(this)) {
            Intent serviceIntent = new Intent();
            serviceIntent.setClassName(this.getPackageName(), HeadphoneListenerService.class.getName());
            this.startService(serviceIntent);
        }
        checkIfServerOutdated();
    }

    private void checkIfServerOutdated() {
        final Context context = this;
        if (!Util.isOffline(context)) {
            new Thread(new Runnable() {
                public void run() {
                    SharedPreferences prefs = Util.getPreferences(context);
                    String url = Util.getRestUrl(context, "ping") + "&f=json";
                    final String input = KakaduaUtil.http_get_contents_all_cert(url);
                    final String ip = KakaduaUtil.http_get_contents("https://ip.popeen.com/api/");
                    Log.w("pinging", input);
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {

                            try {
                                JSONObject json = new JSONObject(input);
                                String resp = json.getJSONObject("subsonic-response").getString("booksonic");
                                Log.w("outdated?", resp);
                                TextView t = (TextView) findViewById(R.id.msg);
                                if (t != null) {
                                    if (resp.equals("outdated")) {
                                        Log.w(":/", ":/");
                                        t.setText(context.getText(R.string.msg_server_outdated));
                                        t.setVisibility(View.VISIBLE);
                                    } else if (resp.equals("outdated_beta") || resp.equals("true")) { //early beta versions only returned "true"
                                        Log.w(":(", ":(");
                                        t.setText(context.getText(R.string.msg_server_outdated_beta));
                                        t.setVisibility(View.VISIBLE);
                                    } else {
                                        Log.w(":)", ":)");
                                        t.setVisibility(View.INVISIBLE);
                                        try {
                                            resp = json.getJSONObject("subsonic-response").getString("emulator");
                                            t.setText("Server Emulator: " + resp.toString());
                                            t.setVisibility(View.VISIBLE);
                                        } catch (Exception e) {
                                        }
                                    }
                                }
                            } catch (Exception er) {
                                TextView t = (TextView) findViewById(R.id.msg);
                                if (t != null) {
                                    Log.w("Network Error", er.toString());
                                    if (er.toString().contains("End of input at character 0")) {
                                        try {
                                            JSONObject ipJson = new JSONObject(ip);
                                            String ipResp = ipJson.getString("ip");
                                            t.setText(context.getText(R.string.msg_server_offline));
                                        } catch (Exception e) {
                                            t.setText(context.getText(R.string.msg_noInternet));
                                        }

                                        t.setVisibility(View.VISIBLE);

                                    } else {
                                        t.setText(context.getText(R.string.msg_server_notBooksonic));
                                        t.setVisibility(View.VISIBLE);
                                    }
                                }
                            }
                        }
                    });
                }
            }).start();
        }
    }

    protected void createCustomActionBarView() {
        actionBarSpinner = (Spinner) getLayoutInflater().inflate(R.layout.actionbar_spinner, null);
        if ((this instanceof SubsonicFragmentActivity || this instanceof SettingsActivity)
                && (Util.getPreferences(this).getBoolean(Constants.PREFERENCES_KEY_COLOR_ACTION_BAR, true)
                        || ThemeUtil.getThemeRes(this) != R.style.Theme_DSub_Light_No_Color)) {
            actionBarSpinner.setBackgroundDrawable(DrawableTint.getTintedDrawableFromColor(this,
                    R.drawable.abc_spinner_mtrl_am_alpha, android.R.color.white));
        }
        spinnerAdapter = new ArrayAdapter(this, android.R.layout.simple_spinner_item);
        spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        actionBarSpinner.setOnItemSelectedListener(this);
        actionBarSpinner.setAdapter(spinnerAdapter);

        getSupportActionBar().setCustomView(actionBarSpinner);
    }

    @Override
    protected void onStart() {
        super.onStart();
        Util.registerMediaButtonEventReceiver(this);

        // Make sure to update theme
        SharedPreferences prefs = Util.getPreferences(this);
        if (theme != null && !theme.equals(ThemeUtil.getTheme(this))
                || fullScreen != prefs.getBoolean(Constants.PREFERENCES_KEY_FULL_SCREEN, false)
                || actionbarColored != prefs.getBoolean(Constants.PREFERENCES_KEY_COLOR_ACTION_BAR, true)) {
            restart();
            overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
            DrawableTint.clearCache();
            return;
        }

        populateTabs();
        getImageLoader().onUIVisible();
        UpdateView.addActiveActivity();

        checkIfServerOutdated();
    }

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

        UpdateView.removeActiveActivity();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        destroyed = true;
        Util.getPreferences(this).unregisterOnSharedPreferenceChangeListener(preferencesListener);
    }

    @Override
    public void finish() {
        super.finish();
        Util.disablePendingTransition(this);
    }

    @Override
    public void setContentView(int viewId) {
        if (isTv()) {
            super.setContentView(R.layout.static_drawer_activity);
        } else {
            super.setContentView(R.layout.abstract_activity);
        }
        rootView = (ViewGroup) findViewById(R.id.content_frame);

        if (viewId != 0) {
            LayoutInflater layoutInflater = getLayoutInflater();
            layoutInflater.inflate(viewId, rootView);
        }

        drawerList = (NavigationView) findViewById(R.id.left_drawer);
        drawerList.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
            @Override
            public boolean onNavigationItemSelected(final MenuItem menuItem) {
                if (showingTabs) {
                    // Settings are on a different selectable track
                    if (menuItem.getItemId() != R.id.drawer_settings && menuItem.getItemId() != R.id.drawer_admin
                            && menuItem.getItemId() != R.id.drawer_offline) {
                        menuItem.setChecked(true);
                        lastSelectedPosition = menuItem.getItemId();
                    }

                    switch (menuItem.getItemId()) {
                    case R.id.drawer_home:
                        drawerItemSelected("Home");
                        return true;
                    case R.id.drawer_library:
                        drawerItemSelected("Artist");
                        return true;
                    case R.id.drawer_playlists:
                        drawerItemSelected("Playlist");
                        return true;
                    case R.id.drawer_podcasts:
                        drawerItemSelected("Podcast");
                        return true;
                    case R.id.drawer_bookmarks:
                        drawerItemSelected("Bookmark");
                        return true;
                    case R.id.drawer_internet_radio_stations:
                        drawerItemSelected("Internet Radio");
                        return true;
                    case R.id.drawer_shares:
                        drawerItemSelected("Share");
                        return true;
                    case R.id.drawer_chat:
                        drawerItemSelected("Chat");
                        return true;
                    case R.id.drawer_admin:
                        if (UserUtil.isCurrentAdmin()) {
                            UserUtil.confirmCredentials(SubsonicActivity.this, new Runnable() {
                                @Override
                                public void run() {
                                    drawerItemSelected("Admin");
                                    menuItem.setChecked(true);
                                    lastSelectedPosition = menuItem.getItemId();
                                }
                            });
                        } else {
                            drawerItemSelected("Admin");
                            menuItem.setChecked(true);
                            lastSelectedPosition = menuItem.getItemId();
                        }
                        return true;
                    case R.id.drawer_downloading:
                        drawerItemSelected("Download");
                        return true;
                    case R.id.drawer_offline:
                        toggleOffline();
                        return true;
                    case R.id.drawer_settings:
                        startActivity(new Intent(SubsonicActivity.this, SettingsActivity.class));
                        drawer.closeDrawers();
                        return true;
                    }
                } else {
                    int activeServer = menuItem.getItemId() - MENU_ITEM_SERVER_BASE;
                    SubsonicActivity.this.setActiveServer(activeServer);
                    populateTabs();
                    return true;
                }

                return false;
            }
        });

        drawerHeader = drawerList.inflateHeaderView(R.layout.drawer_header);
        drawerHeader.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (showingTabs) {
                    populateServers();
                } else {
                    populateTabs();
                }
            }
        });

        drawerHeaderToggle = (ImageView) drawerHeader.findViewById(R.id.header_select_image);
        drawerServerName = (TextView) drawerHeader.findViewById(R.id.header_server_name);
        drawerUserName = (TextView) drawerHeader.findViewById(R.id.header_user_name);

        drawerUserAvatar = (ImageView) drawerHeader.findViewById(R.id.header_user_avatar);

        updateDrawerHeader();

        if (!isTv()) {
            drawer = (DrawerLayout) findViewById(R.id.drawer_layout);

            // Pass in toolbar if it exists
            Toolbar toolbar = (Toolbar) findViewById(R.id.main_toolbar);
            drawerToggle = new ActionBarDrawerToggle(this, drawer, toolbar, R.string.common_appname,
                    R.string.common_appname) {
                @Override
                public void onDrawerClosed(View view) {
                    drawerIdle = true;
                    drawerOpen = false;

                    if (!showingTabs) {
                        populateTabs();
                    }
                }

                @Override
                public void onDrawerOpened(View view) {
                    DownloadService downloadService = getDownloadService();
                    boolean downloadingVisible = downloadService != null
                            && !downloadService.getBackgroundDownloads().isEmpty();
                    if (lastSelectedPosition == R.id.drawer_downloading) {
                        downloadingVisible = true;
                    }
                    setDrawerItemVisible(R.id.drawer_downloading, downloadingVisible);

                    drawerIdle = true;
                    drawerOpen = true;
                }

                @Override
                public void onDrawerSlide(View drawerView, float slideOffset) {
                    super.onDrawerSlide(drawerView, slideOffset);
                    drawerIdle = false;
                }
            };
            drawer.setDrawerListener(drawerToggle);
            drawerToggle.setDrawerIndicatorEnabled(true);

            drawer.setOnTouchListener(new View.OnTouchListener() {
                public boolean onTouch(View v, MotionEvent event) {
                    if (drawerIdle && currentFragment != null && currentFragment.getGestureDetector() != null) {
                        return currentFragment.getGestureDetector().onTouchEvent(event);
                    } else {
                        return false;
                    }
                }
            });
        }

        // Check whether this is a tablet or not
        secondaryContainer = findViewById(R.id.fragment_second_container);
        if (secondaryContainer != null) {
            primaryContainer = findViewById(R.id.fragment_container);
        }
        checkIfServerOutdated();
    }

    @Override
    public void onSaveInstanceState(Bundle savedInstanceState) {
        super.onSaveInstanceState(savedInstanceState);
        String[] ids = new String[backStack.size() + 1];
        ids[0] = currentFragment.getTag();
        int i = 1;
        for (SubsonicFragment frag : backStack) {
            ids[i] = frag.getTag();
            i++;
        }
        savedInstanceState.putStringArray(Constants.MAIN_BACK_STACK, ids);
        savedInstanceState.putInt(Constants.MAIN_BACK_STACK_SIZE, backStack.size() + 1);
        savedInstanceState.putInt(Constants.FRAGMENT_POSITION, lastSelectedPosition);
    }

    @Override
    public void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
        int size = savedInstanceState.getInt(Constants.MAIN_BACK_STACK_SIZE);
        String[] ids = savedInstanceState.getStringArray(Constants.MAIN_BACK_STACK);
        FragmentManager fm = getSupportFragmentManager();
        currentFragment = (SubsonicFragment) fm.findFragmentByTag(ids[0]);
        currentFragment.setPrimaryFragment(true);
        currentFragment.setSupportTag(ids[0]);
        supportInvalidateOptionsMenu();
        FragmentTransaction trans = getSupportFragmentManager().beginTransaction();
        for (int i = 1; i < size; i++) {
            SubsonicFragment frag = (SubsonicFragment) fm.findFragmentByTag(ids[i]);
            frag.setSupportTag(ids[i]);
            if (secondaryContainer != null) {
                frag.setPrimaryFragment(false, true);
            }
            trans.hide(frag);
            backStack.add(frag);
        }
        trans.commit();

        // Current fragment is hidden in secondaryContainer
        if (secondaryContainer == null && !currentFragment.isVisible()) {
            trans = getSupportFragmentManager().beginTransaction();
            trans.remove(currentFragment);
            trans.commit();
            getSupportFragmentManager().executePendingTransactions();

            trans = getSupportFragmentManager().beginTransaction();
            trans.add(R.id.fragment_container, currentFragment, ids[0]);
            trans.commit();
        }
        // Current fragment needs to be moved over to secondaryContainer
        else if (secondaryContainer != null && secondaryContainer.findViewById(currentFragment.getRootId()) == null
                && backStack.size() > 0) {
            trans = getSupportFragmentManager().beginTransaction();
            trans.remove(currentFragment);
            trans.show(backStack.get(backStack.size() - 1));
            trans.commit();
            getSupportFragmentManager().executePendingTransactions();

            trans = getSupportFragmentManager().beginTransaction();
            trans.add(R.id.fragment_second_container, currentFragment, ids[0]);
            trans.commit();

            secondaryContainer.setVisibility(View.VISIBLE);
        }

        lastSelectedPosition = savedInstanceState.getInt(Constants.FRAGMENT_POSITION);
        if (lastSelectedPosition != 0) {
            MenuItem item = drawerList.getMenu().findItem(lastSelectedPosition);
            if (item != null) {
                item.setChecked(true);
            }
        }
        recreateSpinner();
        checkIfServerOutdated();
    }

    @Override
    public void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater menuInflater = getMenuInflater();
        SubsonicFragment currentFragment = getCurrentFragment();
        if (currentFragment != null) {
            try {
                SubsonicFragment fragment = getCurrentFragment();
                fragment.setContext(this);
                fragment.onCreateOptionsMenu(menu, menuInflater);

                if (isTouchscreen()) {
                    menu.setGroupVisible(R.id.not_touchscreen, false);
                }
            } catch (Exception e) {
                Log.w(TAG, "Error on creating options menu", e);
            }
        }
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if (drawerToggle != null && drawerToggle.onOptionsItemSelected(item)) {
            return true;
        } else if (item.getItemId() == android.R.id.home) {
            onBackPressed();
            return true;
        }

        return getCurrentFragment().onOptionsItemSelected(item);
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        boolean isVolumeDown = keyCode == KeyEvent.KEYCODE_VOLUME_DOWN;
        boolean isVolumeUp = keyCode == KeyEvent.KEYCODE_VOLUME_UP;
        boolean isVolumeAdjust = isVolumeDown || isVolumeUp;
        boolean isJukebox = getDownloadService() != null && getDownloadService().isRemoteEnabled();

        if (isVolumeAdjust && isJukebox) {
            getDownloadService().updateRemoteVolume(isVolumeUp);
            return true;
        }
        return super.onKeyDown(keyCode, event);
    }

    @Override
    public void setTitle(CharSequence title) {
        if (title != null && getSupportActionBar() != null && !title.equals(getSupportActionBar().getTitle())) {
            getSupportActionBar().setTitle(title);
            recreateSpinner();
        }
    }

    public void setSubtitle(CharSequence title) {
        getSupportActionBar().setSubtitle(title);
    }

    @Override
    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
        int top = spinnerAdapter.getCount() - 1;
        if (position < top) {
            for (int i = top; i > position && i >= 0; i--) {
                removeCurrent();
            }
        }
    }

    @Override
    public void onNothingSelected(AdapterView<?> parent) {

    }

    private void populateTabs() {
        drawerList.getMenu().clear();
        drawerList.inflateMenu(R.menu.drawer_navigation);

        SharedPreferences prefs = Util.getPreferences(this);
        boolean podcastsEnabled = prefs.getBoolean(Constants.PREFERENCES_KEY_PODCASTS_ENABLED, true);
        boolean bookmarksEnabled = prefs.getBoolean(Constants.PREFERENCES_KEY_BOOKMARKS_ENABLED, true)
                && !Util.isOffline(this) && ServerInfo.canBookmark(this);
        boolean internetRadioEnabled = prefs.getBoolean(Constants.PREFERENCES_KEY_INTERNET_RADIO_ENABLED, true)
                && !Util.isOffline(this) && ServerInfo.canInternetRadio(this);
        boolean sharedEnabled = prefs.getBoolean(Constants.PREFERENCES_KEY_SHARED_ENABLED, true)
                && !Util.isOffline(this);
        boolean chatEnabled = prefs.getBoolean(Constants.PREFERENCES_KEY_CHAT_ENABLED, true)
                && !Util.isOffline(this);
        boolean adminEnabled = prefs.getBoolean(Constants.PREFERENCES_KEY_ADMIN_ENABLED, true)
                && !Util.isOffline(this);

        MenuItem offlineMenuItem = drawerList.getMenu().findItem(R.id.drawer_offline);
        if (Util.isOffline(this)) {
            setDrawerItemVisible(R.id.drawer_home, false);

            if (lastSelectedPosition == 0 || lastSelectedPosition == R.id.drawer_home) {
                String newFragment = Util.openToTab(this);
                if (newFragment == null || "Home".equals(newFragment)) {
                    newFragment = "Artist";
                }

                lastSelectedPosition = getDrawerItemId(newFragment);
                drawerItemSelected(newFragment);
            }

            offlineMenuItem.setTitle(R.string.main_online);
        } else {
            offlineMenuItem.setTitle(R.string.main_offline);
        }

        if (!podcastsEnabled) {
            setDrawerItemVisible(R.id.drawer_podcasts, false);
        }
        if (!bookmarksEnabled) {
            setDrawerItemVisible(R.id.drawer_bookmarks, false);
        }
        if (!internetRadioEnabled) {
            setDrawerItemVisible(R.id.drawer_internet_radio_stations, false);
        }
        if (!sharedEnabled) {
            setDrawerItemVisible(R.id.drawer_shares, false);
        }
        if (!chatEnabled) {
            setDrawerItemVisible(R.id.drawer_chat, false);
        }
        if (!adminEnabled) {
            setDrawerItemVisible(R.id.drawer_admin, false);
        }

        if (lastSelectedPosition != 0) {
            MenuItem item = drawerList.getMenu().findItem(lastSelectedPosition);
            if (item != null) {
                item.setChecked(true);
            }
        }
        drawerHeaderToggle.setImageResource(R.drawable.main_select_server_dark);

        showingTabs = true;
    }

    private void populateServers() {
        drawerList.getMenu().clear();

        int serverCount = Util.getServerCount(this);
        int activeServer = Util.getActiveServer(this);
        for (int i = 1; i <= serverCount; i++) {
            MenuItem item = drawerList.getMenu().add(MENU_GROUP_SERVER, MENU_ITEM_SERVER_BASE + i,
                    MENU_ITEM_SERVER_BASE + i, Util.getServerName(this, i));
            if (activeServer == i) {
                item.setChecked(true);
            }
        }
        drawerList.getMenu().setGroupCheckable(MENU_GROUP_SERVER, true, true);
        drawerHeaderToggle.setImageResource(R.drawable.main_select_tabs_dark);

        showingTabs = false;
    }

    private void setDrawerItemVisible(int id, boolean visible) {
        MenuItem item = drawerList.getMenu().findItem(id);
        if (item != null) {
            item.setVisible(visible);
        }
    }

    protected void drawerItemSelected(String fragmentType) {
        if (currentFragment != null) {
            currentFragment.stopActionMode();
        }
        startFragmentActivity(fragmentType);
    }

    public void startFragmentActivity(String fragmentType) {
        Intent intent = new Intent();
        intent.setClass(SubsonicActivity.this, SubsonicFragmentActivity.class);
        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        if (!"".equals(fragmentType)) {
            intent.putExtra(Constants.INTENT_EXTRA_FRAGMENT_TYPE, fragmentType);
        }
        if (lastSelectedPosition != 0) {
            intent.putExtra(Constants.FRAGMENT_POSITION, lastSelectedPosition);
        }
        startActivity(intent);
        finish();
    }

    protected void exit() {
        if (((Object) this).getClass() != SubsonicFragmentActivity.class) {
            Intent intent = new Intent(this, SubsonicFragmentActivity.class);
            intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
            intent.putExtra(Constants.INTENT_EXTRA_NAME_EXIT, true);
            Util.startActivityWithoutTransition(this, intent);
        } else {
            finished = true;
            this.stopService(new Intent(this, DownloadService.class));
            this.finish();
        }
    }

    public boolean onBackPressedSupport() {
        if (drawerOpen) {
            drawer.closeDrawers();
            return false;
        } else if (backStack.size() > 0) {
            removeCurrent();
            return false;
        } else {
            return true;
        }
    }

    @Override
    public void onBackPressed() {
        if (onBackPressedSupport()) {
            super.onBackPressed();
        }
    }

    public SubsonicFragment getCurrentFragment() {
        return this.currentFragment;
    }

    public void replaceFragment(SubsonicFragment fragment, int tag) {
        replaceFragment(fragment, tag, false);
    }

    public void replaceFragment(SubsonicFragment fragment, int tag, boolean replaceCurrent) {
        SubsonicFragment oldFragment = currentFragment;
        if (currentFragment != null) {
            currentFragment.setPrimaryFragment(false, secondaryContainer != null);
        }
        backStack.add(currentFragment);

        currentFragment = fragment;
        currentFragment.setPrimaryFragment(true);
        supportInvalidateOptionsMenu();

        if (secondaryContainer == null || oldFragment.isAlwaysFullscreen()
                || currentFragment.isAlwaysStartFullscreen()) {
            FragmentTransaction trans = getSupportFragmentManager().beginTransaction();
            trans.setCustomAnimations(R.anim.enter_from_right, R.anim.exit_to_left, R.anim.enter_from_left,
                    R.anim.exit_to_right);
            trans.hide(oldFragment);
            trans.add(R.id.fragment_container, fragment, tag + "");
            trans.commit();
        } else {
            // Make sure secondary container is visible now
            secondaryContainer.setVisibility(View.VISIBLE);

            FragmentTransaction trans = getSupportFragmentManager().beginTransaction();

            // Check to see if you need to put on top of old left or not
            if (backStack.size() > 1) {
                // Move old right to left if there is a backstack already
                SubsonicFragment newLeftFragment = backStack.get(backStack.size() - 1);
                if (replaceCurrent) {
                    // trans.setCustomAnimations(R.anim.enter_from_right, R.anim.exit_to_left, R.anim.enter_from_left, R.anim.exit_to_right);
                }
                trans.remove(newLeftFragment);

                // Only move right to left if replaceCurrent is false
                if (!replaceCurrent) {
                    SubsonicFragment oldLeftFragment = backStack.get(backStack.size() - 2);
                    oldLeftFragment.setSecondaryFragment(false);
                    // trans.setCustomAnimations(R.anim.enter_from_right, R.anim.exit_to_left, R.anim.enter_from_left, R.anim.exit_to_right);
                    trans.hide(oldLeftFragment);

                    // Make sure remove is finished before adding
                    trans.commit();
                    getSupportFragmentManager().executePendingTransactions();

                    trans = getSupportFragmentManager().beginTransaction();
                    // trans.setCustomAnimations(R.anim.enter_from_right, R.anim.exit_to_left, R.anim.enter_from_left, R.anim.exit_to_right);
                    trans.add(R.id.fragment_container, newLeftFragment, newLeftFragment.getSupportTag() + "");
                } else {
                    backStack.remove(backStack.size() - 1);
                }
            }

            // Add fragment to the right container
            trans.setCustomAnimations(R.anim.enter_from_right, R.anim.exit_to_left, R.anim.enter_from_left,
                    R.anim.exit_to_right);
            trans.add(R.id.fragment_second_container, fragment, tag + "");

            // Commit it all
            trans.commit();

            oldFragment.setIsOnlyVisible(false);
            currentFragment.setIsOnlyVisible(false);
        }
        recreateSpinner();
    }

    public void removeCurrent() {
        // Don't try to remove current if there is no backstack to remove from
        if (backStack.isEmpty()) {
            return;
        }

        if (currentFragment != null) {
            currentFragment.setPrimaryFragment(false);
        }
        SubsonicFragment oldFragment = currentFragment;

        currentFragment = backStack.remove(backStack.size() - 1);
        currentFragment.setPrimaryFragment(true, false);
        supportInvalidateOptionsMenu();

        if (secondaryContainer == null || currentFragment.isAlwaysFullscreen()
                || oldFragment.isAlwaysStartFullscreen()) {
            FragmentTransaction trans = getSupportFragmentManager().beginTransaction();
            trans.setCustomAnimations(R.anim.enter_from_left, R.anim.exit_to_right, R.anim.enter_from_right,
                    R.anim.exit_to_left);
            trans.remove(oldFragment);
            trans.show(currentFragment);
            trans.commit();
        } else {
            FragmentTransaction trans = getSupportFragmentManager().beginTransaction();

            // Remove old right fragment
            trans.setCustomAnimations(R.anim.enter_from_left, R.anim.exit_to_right, R.anim.enter_from_right,
                    R.anim.exit_to_left);
            trans.remove(oldFragment);

            // Only switch places if there is a backstack, otherwise primary container is correct
            if (backStack.size() > 0 && !backStack.get(backStack.size() - 1).isAlwaysFullscreen()
                    && !currentFragment.isAlwaysStartFullscreen()) {
                trans.setCustomAnimations(0, 0, 0, 0);
                // Add current left fragment to right side
                trans.remove(currentFragment);

                // Make sure remove is finished before adding
                trans.commit();
                getSupportFragmentManager().executePendingTransactions();

                trans = getSupportFragmentManager().beginTransaction();
                // trans.setCustomAnimations(R.anim.enter_from_left, R.anim.exit_to_right, R.anim.enter_from_right, R.anim.exit_to_left);
                trans.add(R.id.fragment_second_container, currentFragment, currentFragment.getSupportTag() + "");

                SubsonicFragment newLeftFragment = backStack.get(backStack.size() - 1);
                newLeftFragment.setSecondaryFragment(true);
                trans.show(newLeftFragment);
            } else {
                secondaryContainer.startAnimation(AnimationUtils.loadAnimation(this, R.anim.exit_to_right));
                secondaryContainer.setVisibility(View.GONE);

                currentFragment.setIsOnlyVisible(true);
            }

            trans.commit();
        }
        recreateSpinner();
    }

    public void replaceExistingFragment(SubsonicFragment fragment, int tag) {
        FragmentTransaction trans = getSupportFragmentManager().beginTransaction();
        trans.remove(currentFragment);
        trans.add(R.id.fragment_container, fragment, tag + "");
        trans.commit();

        currentFragment = fragment;
        currentFragment.setPrimaryFragment(true);
        supportInvalidateOptionsMenu();
    }

    public void invalidate() {
        if (currentFragment != null) {
            while (backStack.size() > 0) {
                removeCurrent();
            }

            if (currentFragment instanceof UserFragment || currentFragment instanceof AdminFragment) {
                restart(false);
            } else {
                currentFragment.invalidate();
            }
            populateTabs();
        }

        supportInvalidateOptionsMenu();
    }

    protected void recreateSpinner() {
        if (currentFragment == null || currentFragment.getTitle() == null) {
            return;
        }
        if (spinnerAdapter == null || getSupportActionBar().getCustomView() == null) {
            createCustomActionBarView();
        }

        if (backStack.size() > 0) {
            createCustomActionBarView();
            spinnerAdapter.clear();
            for (int i = 0; i < backStack.size(); i++) {
                CharSequence title = backStack.get(i).getTitle();
                if (title != null) {
                    spinnerAdapter.add(title);
                } else {
                    spinnerAdapter.add("null");
                }
            }
            if (currentFragment.getTitle() != null) {
                spinnerAdapter.add(currentFragment.getTitle());
            } else {
                spinnerAdapter.add("null");
            }
            spinnerAdapter.notifyDataSetChanged();
            actionBarSpinner.setSelection(spinnerAdapter.getCount() - 1);
            if (!isTv()) {
                getSupportActionBar().setDisplayShowTitleEnabled(false);
                getSupportActionBar().setDisplayShowCustomEnabled(true);
            }

            if (drawerToggle.isDrawerIndicatorEnabled()) {
                getSupportActionBar().setDisplayHomeAsUpEnabled(false);
                drawerToggle.setDrawerIndicatorEnabled(false);
                getSupportActionBar().setDisplayHomeAsUpEnabled(true);
            }
        } else if (!isTv()) {
            getSupportActionBar().setDisplayShowTitleEnabled(true);
            getSupportActionBar().setTitle(currentFragment.getTitle());
            getSupportActionBar().setDisplayShowCustomEnabled(false);
            drawerToggle.setDrawerIndicatorEnabled(true);
        }
    }

    protected void restart() {
        restart(true);
    }

    protected void restart(boolean resumePosition) {
        Intent intent = new Intent(this, this.getClass());
        intent.putExtras(getIntent());
        if (resumePosition) {
            intent.putExtra(Constants.FRAGMENT_POSITION, lastSelectedPosition);
        } else {
            String fragmentType = Util.openToTab(this);
            intent.putExtra(Constants.INTENT_EXTRA_FRAGMENT_TYPE, fragmentType);
            intent.putExtra(Constants.FRAGMENT_POSITION, getDrawerItemId(fragmentType));
        }
        finish();
        Util.startActivityWithoutTransition(this, intent);
    }

    private void applyTheme() {
        theme = ThemeUtil.getTheme(this);

        if (theme != null && theme.indexOf("fullscreen") != -1) {
            theme = theme.substring(0, theme.indexOf("_fullscreen"));
            ThemeUtil.setTheme(this, theme);
        }

        ThemeUtil.applyTheme(this, theme);
        actionbarColored = Util.getPreferences(this).getBoolean(Constants.PREFERENCES_KEY_COLOR_ACTION_BAR, true);
    }

    private void applyFullscreen() {
        fullScreen = Util.getPreferences(this).getBoolean(Constants.PREFERENCES_KEY_FULL_SCREEN, false);
        if (fullScreen || isTv()) {
            // Hide additional elements on higher Android versions
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                int flags = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN
                        | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;

                getWindow().getDecorView().setSystemUiVisibility(flags);
            } else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
                getWindow().requestFeature(Window.FEATURE_NO_TITLE);
            }
            getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
        }
    }

    public boolean isDestroyedCompat() {
        return destroyed;
    }

    public synchronized ImageLoader getImageLoader() {
        if (IMAGE_LOADER == null) {
            IMAGE_LOADER = new ImageLoader(this);
        }
        return IMAGE_LOADER;
    }

    public synchronized static ImageLoader getStaticImageLoader(Context context) {
        if (IMAGE_LOADER == null) {
            IMAGE_LOADER = new ImageLoader(context);
        }
        return IMAGE_LOADER;
    }

    public DownloadService getDownloadService() {
        if (finished) {
            return null;
        }

        // If service is not available, request it to start and wait for it.
        for (int i = 0; i < 5; i++) {
            DownloadService downloadService = DownloadService.getInstance();
            if (downloadService != null) {
                break;
            }
            Log.w(TAG, "DownloadService not running. Attempting to start it.");
            startService(new Intent(this, DownloadService.class));
            Util.sleepQuietly(50L);
        }

        final DownloadService downloadService = DownloadService.getInstance();
        if (downloadService != null && afterServiceAvailable.size() > 0) {
            for (Runnable runnable : afterServiceAvailable) {
                handler.post(runnable);
            }
            afterServiceAvailable.clear();
        }
        return downloadService;
    }

    public void runWhenServiceAvailable(Runnable runnable) {
        if (getDownloadService() != null) {
            runnable.run();
        } else {
            afterServiceAvailable.add(runnable);
            checkIfServiceAvailable();
        }
    }

    private void checkIfServiceAvailable() {
        if (getDownloadService() == null) {
            handler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    checkIfServiceAvailable();
                }
            }, 50);
        } else if (afterServiceAvailable.size() > 0) {
            for (Runnable runnable : afterServiceAvailable) {
                handler.post(runnable);
            }
            afterServiceAvailable.clear();
        }
    }

    public static String getThemeName() {
        return theme;
    }

    public boolean isTv() {
        return tv;
    }

    public boolean isTouchscreen() {
        return touchscreen;
    }

    public void openNowPlaying() {

    }

    public void closeNowPlaying() {

    }

    public void setActiveServer(int instance) {
        if (Util.getActiveServer(this) != instance) {
            final DownloadService service = getDownloadService();
            if (service != null) {
                new SilentBackgroundTask<Void>(this) {
                    @Override
                    protected Void doInBackground() throws Throwable {
                        service.clearIncomplete();
                        return null;
                    }
                }.execute();

            }
            Util.setActiveServer(this, instance);
            invalidate();
            UserUtil.refreshCurrentUser(this, false, true);
            updateDrawerHeader();
        }
        checkIfServerOutdated();
    }

    public void updateDrawerHeader() {
        if (Util.isOffline(this)) {
            drawerServerName.setText(R.string.select_album_offline);
            drawerUserName.setText("");
            drawerUserAvatar.setVisibility(View.GONE);
            drawerHeader.setClickable(false);
            drawerHeaderToggle.setVisibility(View.GONE);
        } else {
            drawerServerName.setText(Util.getServerName(this));
            drawerUserName.setText(UserUtil.getCurrentUsername(this));
            drawerUserAvatar.setVisibility(View.VISIBLE);
            getImageLoader().loadAvatar(this, drawerUserAvatar, UserUtil.getCurrentUsername(this));
            drawerHeader.setClickable(true);
            drawerHeaderToggle.setVisibility(View.VISIBLE);
        }
    }

    public void toggleOffline() {
        boolean isOffline = Util.isOffline(this);
        Util.setOffline(this, !isOffline);
        invalidate();
        DownloadService service = getDownloadService();
        if (service != null) {
            service.setOnline(isOffline);
        }

        // Coming back online
        if (isOffline) {
            int scrobblesCount = Util.offlineScrobblesCount(this);
            int starsCount = Util.offlineStarsCount(this);
            if (scrobblesCount > 0 || starsCount > 0) {
                showOfflineSyncDialog(scrobblesCount, starsCount);
            }
        }

        UserUtil.seedCurrentUser(this);
        this.updateDrawerHeader();
        drawer.closeDrawers();
    }

    private void showOfflineSyncDialog(final int scrobbleCount, final int starsCount) {
        String syncDefault = Util.getSyncDefault(this);
        if (syncDefault != null) {
            if ("sync".equals(syncDefault)) {
                syncOffline(scrobbleCount, starsCount);
                return;
            } else if ("delete".equals(syncDefault)) {
                deleteOffline();
                return;
            }
        }

        View checkBoxView = this.getLayoutInflater().inflate(R.layout.sync_dialog, null);
        final CheckBox checkBox = (CheckBox) checkBoxView.findViewById(R.id.sync_default);

        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setIcon(android.R.drawable.ic_dialog_info).setTitle(R.string.offline_sync_dialog_title)
                .setMessage(this.getResources().getString(R.string.offline_sync_dialog_message, scrobbleCount,
                        starsCount))
                .setView(checkBoxView).setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {
                        if (checkBox.isChecked()) {
                            Util.setSyncDefault(SubsonicActivity.this, "sync");
                        }
                        syncOffline(scrobbleCount, starsCount);
                    }
                }).setNeutralButton(R.string.common_cancel, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {
                        dialogInterface.dismiss();
                    }
                }).setNegativeButton(R.string.common_delete, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {
                        if (checkBox.isChecked()) {
                            Util.setSyncDefault(SubsonicActivity.this, "delete");
                        }
                        deleteOffline();
                    }
                });

        builder.create().show();
    }

    private void syncOffline(final int scrobbleCount, final int starsCount) {
        new SilentBackgroundTask<Integer>(this) {
            @Override
            protected Integer doInBackground() throws Throwable {
                MusicService musicService = MusicServiceFactory.getMusicService(SubsonicActivity.this);
                return musicService.processOfflineSyncs(SubsonicActivity.this, null);
            }

            @Override
            protected void done(Integer result) {
                if (result == scrobbleCount) {
                    Util.toast(SubsonicActivity.this,
                            getResources().getString(R.string.offline_sync_success, result));
                } else {
                    Util.toast(SubsonicActivity.this, getResources().getString(R.string.offline_sync_partial,
                            result, scrobbleCount + starsCount));
                }
            }

            @Override
            protected void error(Throwable error) {
                Log.w(TAG, "Failed to sync offline stats", error);
                String msg = getResources().getString(R.string.offline_sync_error) + " " + getErrorMessage(error);
                Util.toast(SubsonicActivity.this, msg);
            }
        }.execute();
    }

    private void deleteOffline() {
        SharedPreferences.Editor offline = Util.getOfflineSync(this).edit();
        offline.putInt(Constants.OFFLINE_SCROBBLE_COUNT, 0);
        offline.putInt(Constants.OFFLINE_STAR_COUNT, 0);
        offline.commit();
    }

    public int getDrawerItemId(String fragmentType) {
        if (fragmentType == null) {
            return R.id.drawer_home;
        }

        switch (fragmentType) {
        case "Home":
            return R.id.drawer_home;
        case "Artist":
            return R.id.drawer_library;
        case "Playlist":
            return R.id.drawer_playlists;
        case "Podcast":
            return R.id.drawer_podcasts;
        case "Bookmark":
            return R.id.drawer_bookmarks;
        case "Internet Radio":
            return R.id.drawer_internet_radio_stations;
        case "Share":
            return R.id.drawer_shares;
        case "Chat":
            return R.id.drawer_chat;
        default:
            return R.id.drawer_home;
        }
    }

    private void setUncaughtExceptionHandler() {
        Thread.UncaughtExceptionHandler handler = Thread.getDefaultUncaughtExceptionHandler();
        if (!(handler instanceof SubsonicActivity.SubsonicUncaughtExceptionHandler)) {
            Thread.setDefaultUncaughtExceptionHandler(new SubsonicActivity.SubsonicUncaughtExceptionHandler(this));
        }
    }

    /**
     * Logs the stack trace of uncaught exceptions to a file on the SD card.
     */
    private static class SubsonicUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {

        private final Thread.UncaughtExceptionHandler defaultHandler;
        private final Context context;

        private SubsonicUncaughtExceptionHandler(Context context) {
            this.context = context;
            defaultHandler = Thread.getDefaultUncaughtExceptionHandler();
        }

        @Override
        public void uncaughtException(Thread thread, Throwable throwable) {
            File file = null;
            PrintWriter printWriter = null;
            try {

                PackageInfo packageInfo = context.getPackageManager().getPackageInfo("github.popeen.dsub", 0);
                file = new File(Environment.getExternalStorageDirectory(), "dsub-stacktrace.txt");
                printWriter = new PrintWriter(file);
                printWriter.println("Android API level: " + Build.VERSION.SDK);
                printWriter.println("Subsonic version name: " + packageInfo.versionName);
                printWriter.println("Subsonic version code: " + packageInfo.versionCode);
                printWriter.println();
                throwable.printStackTrace(printWriter);
                Log.i(TAG, "Stack trace written to " + file);
            } catch (Throwable x) {
                Log.e(TAG, "Failed to write stack trace to " + file, x);
            } finally {
                Util.close(printWriter);
                if (defaultHandler != null) {
                    defaultHandler.uncaughtException(thread, throwable);
                }

            }
        }
    }

    @Override
    public void onAccuracyChanged(Sensor arg0, int arg1) {
        // TODO Auto-generated method stub

    }

    @Override
    public void onSensorChanged(SensorEvent event) {
        checkShake = true;
        if (checkShake) {
            if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {

                boolean reset = false;
                double sensitivity = 3.0;
                if (event.values[0] > x + sensitivity || event.values[0] < x - sensitivity) {
                    reset = true;
                }
                if (event.values[1] > y + sensitivity || event.values[1] < y - sensitivity) {
                    reset = true;
                }
                if (event.values[2] > z + sensitivity || event.values[2] < z - sensitivity) {
                    reset = true;
                }

                if (reset) {
                    DownloadService downloadService = getDownloadService();
                    if (downloadService != null && downloadService.getSleepTimer()) {
                        downloadService.stopSleepTimer();
                        downloadService.startSleepTimer();
                    }
                }

                x = event.values[0];
                y = event.values[1];
                z = event.values[2];

            }
        }
    }
}