/* 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 . Copyright 2009 (C) Sindre Mehus */ package github.daneren2005.subphonic.activity; import java.io.File; import java.io.PrintWriter; import java.util.LinkedList; import java.util.List; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.content.pm.PackageInfo; import android.graphics.Typeface; import android.media.AudioManager; import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.util.Log; import android.view.KeyEvent; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.Window; import android.widget.TextView; import github.daneren2005.subphonic.R; import github.daneren2005.subphonic.domain.MusicDirectory; import github.daneren2005.subphonic.service.DownloadService; import github.daneren2005.subphonic.service.DownloadServiceImpl; import github.daneren2005.subphonic.service.MusicService; import github.daneren2005.subphonic.service.MusicServiceFactory; import github.daneren2005.subphonic.util.Constants; import github.daneren2005.subphonic.util.ImageLoader; import github.daneren2005.subphonic.util.ModalBackgroundTask; import github.daneren2005.subphonic.util.Util; /** * @author Sindre Mehus */ public class SubsonicTabActivity extends Activity { private static final String TAG = SubsonicTabActivity.class.getSimpleName(); private static ImageLoader IMAGE_LOADER; private boolean destroyed; private View homeButton; private View musicButton; private View playlistButton; private View nowPlayingButton; @Override protected void onCreate(Bundle bundle) { setUncaughtExceptionHandler(); applyTheme(); super.onCreate(bundle); requestWindowFeature(Window.FEATURE_NO_TITLE); startService(new Intent(this, DownloadServiceImpl.class)); setVolumeControlStream(AudioManager.STREAM_MUSIC); } @Override protected void onPostCreate(Bundle bundle) { super.onPostCreate(bundle); homeButton = findViewById(R.id.button_bar_home); homeButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent intent = new Intent(SubsonicTabActivity.this, MainActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); Util.startActivityWithoutTransition(SubsonicTabActivity.this, intent); } }); musicButton = findViewById(R.id.button_bar_music); musicButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent intent = new Intent(SubsonicTabActivity.this, SelectArtistActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); Util.startActivityWithoutTransition(SubsonicTabActivity.this, intent); } }); playlistButton = findViewById(R.id.button_bar_playlists); playlistButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent intent = new Intent(SubsonicTabActivity.this, SelectPlaylistActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); Util.startActivityWithoutTransition(SubsonicTabActivity.this, intent); } }); nowPlayingButton = findViewById(R.id.button_bar_now_playing); nowPlayingButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Util.startActivityWithoutTransition(SubsonicTabActivity.this, DownloadActivity.class); } }); if (this instanceof MainActivity) { homeButton.setEnabled(false); } else if (this instanceof SelectAlbumActivity || this instanceof SelectArtistActivity) { musicButton.setEnabled(false); } else if (this instanceof SelectPlaylistActivity) { playlistButton.setEnabled(false); } else if (this instanceof DownloadActivity || this instanceof LyricsActivity) { nowPlayingButton.setEnabled(false); } updateButtonVisibility(); } @Override protected void onResume() { super.onResume(); Util.registerMediaButtonEventReceiver(this); } @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.menu_exit: Intent intent = new Intent(this, MainActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); intent.putExtra(Constants.INTENT_EXTRA_NAME_EXIT, true); Util.startActivityWithoutTransition(this, intent); return true; case R.id.menu_settings: startActivity(new Intent(this, SettingsActivity.class)); return true; case R.id.menu_help: startActivity(new Intent(this, HelpActivity.class)); return true; } return false; } @Override protected void onDestroy() { super.onDestroy(); destroyed = true; getImageLoader().clear(); } @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().isJukeboxEnabled(); if (isVolumeAdjust && isJukebox) { getDownloadService().adjustJukeboxVolume(isVolumeUp); return true; } return super.onKeyDown(keyCode, event); } @Override public void finish() { super.finish(); Util.disablePendingTransition(this); } @Override public void setTitle(CharSequence title) { super.setTitle(title); // Set the font of title in the action bar. TextView text = (TextView) findViewById(R.id.actionbar_title_text); Typeface typeface = Typeface.createFromAsset(getAssets(), "fonts/Storopia.ttf"); text.setTypeface(typeface); text.setText(title); } @Override public void setTitle(int titleId) { setTitle(getString(titleId)); } private void applyTheme() { String theme = Util.getTheme(this); if ("dark".equals(theme)) { setTheme(android.R.style.Theme); } else if ("light".equals(theme)) { setTheme(android.R.style.Theme_Light); } } public boolean isDestroyed() { return destroyed; } private void updateButtonVisibility() { int visibility = Util.isOffline(this) ? View.GONE : View.VISIBLE; } public void setProgressVisible(boolean visible) { View view = findViewById(R.id.tab_progress); if (view != null) { view.setVisibility(visible ? View.VISIBLE : View.GONE); } } public void updateProgress(String message) { TextView view = (TextView) findViewById(R.id.tab_progress_message); if (view != null) { view.setText(message); } } public DownloadService getDownloadService() { // If service is not available, request it to start and wait for it. for (int i = 0; i < 5; i++) { DownloadService downloadService = DownloadServiceImpl.getInstance(); if (downloadService != null) { return downloadService; } Log.w(TAG, "DownloadService not running. Attempting to start it."); startService(new Intent(this, DownloadServiceImpl.class)); Util.sleepQuietly(50L); } return DownloadServiceImpl.getInstance(); } protected void warnIfNetworkOrStorageUnavailable() { if (!Util.isExternalStoragePresent()) { Util.toast(this, R.string.select_album_no_sdcard); } else if (!Util.isOffline(this) && !Util.isNetworkConnected(this)) { Util.toast(this, R.string.select_album_no_network); } } protected synchronized ImageLoader getImageLoader() { if (IMAGE_LOADER == null) { IMAGE_LOADER = new ImageLoader(this); } return IMAGE_LOADER; } protected void downloadRecursively(final String id, final boolean save, final boolean append, final boolean autoplay) { ModalBackgroundTask> task = new ModalBackgroundTask>(this, false) { private static final int MAX_SONGS = 500; @Override protected List doInBackground() throws Throwable { MusicService musicService = MusicServiceFactory.getMusicService(SubsonicTabActivity.this); MusicDirectory root = musicService.getMusicDirectory(id, false, SubsonicTabActivity.this, this); List songs = new LinkedList(); getSongsRecursively(root, songs); return songs; } private void getSongsRecursively(MusicDirectory parent, List songs) throws Exception { if (songs.size() > MAX_SONGS) { return; } for (MusicDirectory.Entry song : parent.getChildren(false, true)) { if (!song.isVideo()) { songs.add(song); } } for (MusicDirectory.Entry dir : parent.getChildren(true, false)) { MusicService musicService = MusicServiceFactory.getMusicService(SubsonicTabActivity.this); getSongsRecursively(musicService.getMusicDirectory(dir.getId(), false, SubsonicTabActivity.this, this), songs); } } @Override protected void done(List songs) { DownloadService downloadService = getDownloadService(); if (!songs.isEmpty() && downloadService != null) { if (!append) { downloadService.clear(); } warnIfNetworkOrStorageUnavailable(); downloadService.download(songs, save, autoplay, false); Util.startActivityWithoutTransition(SubsonicTabActivity.this, DownloadActivity.class); } } }; task.execute(); } private void setUncaughtExceptionHandler() { Thread.UncaughtExceptionHandler handler = Thread.getDefaultUncaughtExceptionHandler(); if (!(handler instanceof SubsonicUncaughtExceptionHandler)) { Thread.setDefaultUncaughtExceptionHandler(new 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.daneren2005.subphonic", 0); file = new File(Environment.getExternalStorageDirectory(), "subsonic-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); } } } } }