2013-04-02 03:16:45 +02:00
|
|
|
/*
|
2013-04-24 18:31:42 +02:00
|
|
|
This file is part of UltraSonic.
|
2013-04-02 03:16:45 +02:00
|
|
|
|
|
|
|
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
|
|
|
|
*/
|
2013-04-06 21:47:24 +02:00
|
|
|
package com.thejoshwa.ultrasonic.androidapp.activity;
|
2013-04-02 03:16:45 +02:00
|
|
|
|
|
|
|
import java.io.File;
|
|
|
|
import java.io.PrintWriter;
|
2013-05-16 09:59:55 +02:00
|
|
|
import java.util.ArrayList;
|
2013-04-02 03:16:45 +02:00
|
|
|
import java.util.LinkedList;
|
|
|
|
import java.util.List;
|
|
|
|
|
2013-05-20 08:33:40 +02:00
|
|
|
import android.annotation.SuppressLint;
|
2013-04-02 03:16:45 +02:00
|
|
|
import android.app.Activity;
|
2013-05-16 09:59:55 +02:00
|
|
|
import android.app.AlertDialog;
|
2013-05-20 08:33:40 +02:00
|
|
|
import android.app.Notification;
|
2013-04-02 03:16:45 +02:00
|
|
|
import android.content.Context;
|
2013-05-16 09:59:55 +02:00
|
|
|
import android.content.DialogInterface;
|
2013-04-02 03:16:45 +02:00
|
|
|
import android.content.Intent;
|
|
|
|
import android.content.pm.PackageInfo;
|
2013-04-24 18:31:42 +02:00
|
|
|
import android.graphics.Bitmap;
|
2013-05-20 08:33:40 +02:00
|
|
|
import android.graphics.drawable.Drawable;
|
2013-04-02 03:16:45 +02:00
|
|
|
import android.media.AudioManager;
|
2013-05-18 12:50:54 +02:00
|
|
|
import android.net.Uri;
|
2013-05-20 08:33:40 +02:00
|
|
|
import android.os.AsyncTask;
|
2013-04-02 03:16:45 +02:00
|
|
|
import android.os.Build;
|
|
|
|
import android.os.Bundle;
|
|
|
|
import android.os.Environment;
|
2013-05-20 08:33:40 +02:00
|
|
|
import android.os.Handler;
|
2013-04-02 03:16:45 +02:00
|
|
|
import android.util.Log;
|
|
|
|
import android.view.KeyEvent;
|
|
|
|
import android.view.MenuItem;
|
2013-04-27 11:52:25 +02:00
|
|
|
import android.view.MotionEvent;
|
2013-04-02 03:16:45 +02:00
|
|
|
import android.view.View;
|
|
|
|
import android.view.View.OnClickListener;
|
2013-04-27 11:52:25 +02:00
|
|
|
import android.view.View.OnTouchListener;
|
2013-04-24 18:31:42 +02:00
|
|
|
import android.widget.ImageView;
|
2013-05-22 07:47:40 +02:00
|
|
|
import android.widget.ListAdapter;
|
|
|
|
import android.widget.ListView;
|
2013-05-20 08:33:40 +02:00
|
|
|
import android.widget.RemoteViews;
|
2013-04-02 03:16:45 +02:00
|
|
|
import android.widget.TextView;
|
2013-04-06 21:47:24 +02:00
|
|
|
import com.thejoshwa.ultrasonic.androidapp.R;
|
|
|
|
import com.thejoshwa.ultrasonic.androidapp.domain.MusicDirectory;
|
2013-04-24 18:31:42 +02:00
|
|
|
import com.thejoshwa.ultrasonic.androidapp.domain.MusicDirectory.Entry;
|
|
|
|
import com.thejoshwa.ultrasonic.androidapp.domain.PlayerState;
|
2013-05-16 09:59:55 +02:00
|
|
|
import com.thejoshwa.ultrasonic.androidapp.domain.Playlist;
|
2013-04-24 18:31:42 +02:00
|
|
|
import com.thejoshwa.ultrasonic.androidapp.service.DownloadFile;
|
2013-04-06 21:47:24 +02:00
|
|
|
import com.thejoshwa.ultrasonic.androidapp.service.DownloadService;
|
|
|
|
import com.thejoshwa.ultrasonic.androidapp.service.DownloadServiceImpl;
|
|
|
|
import com.thejoshwa.ultrasonic.androidapp.service.MusicService;
|
|
|
|
import com.thejoshwa.ultrasonic.androidapp.service.MusicServiceFactory;
|
2013-05-16 09:59:55 +02:00
|
|
|
import com.thejoshwa.ultrasonic.androidapp.service.OfflineException;
|
|
|
|
import com.thejoshwa.ultrasonic.androidapp.service.ServerTooOldException;
|
2013-04-06 21:47:24 +02:00
|
|
|
import com.thejoshwa.ultrasonic.androidapp.util.Constants;
|
|
|
|
import com.thejoshwa.ultrasonic.androidapp.util.ImageLoader;
|
2013-05-16 09:59:55 +02:00
|
|
|
import com.thejoshwa.ultrasonic.androidapp.util.LoadingTask;
|
2013-04-06 21:47:24 +02:00
|
|
|
import com.thejoshwa.ultrasonic.androidapp.util.ModalBackgroundTask;
|
2013-05-16 09:59:55 +02:00
|
|
|
import com.thejoshwa.ultrasonic.androidapp.util.SilentBackgroundTask;
|
2013-04-06 21:47:24 +02:00
|
|
|
import com.thejoshwa.ultrasonic.androidapp.util.Util;
|
2013-05-31 10:02:25 +02:00
|
|
|
import com.thejoshwa.ultrasonic.androidapp.util.VideoPlayerType;
|
|
|
|
|
2013-04-24 18:31:42 +02:00
|
|
|
import net.simonvt.menudrawer.MenuDrawer;
|
2013-04-27 11:52:25 +02:00
|
|
|
import net.simonvt.menudrawer.Position;
|
2013-04-24 18:31:42 +02:00
|
|
|
|
2013-04-02 03:16:45 +02:00
|
|
|
/**
|
|
|
|
* @author Sindre Mehus
|
|
|
|
*/
|
|
|
|
public class SubsonicTabActivity extends Activity implements OnClickListener{
|
|
|
|
private static final String TAG = SubsonicTabActivity.class.getSimpleName();
|
|
|
|
private static ImageLoader IMAGE_LOADER;
|
2013-05-16 09:59:55 +02:00
|
|
|
protected static String theme;
|
2013-04-24 18:31:42 +02:00
|
|
|
private static SubsonicTabActivity instance;
|
|
|
|
|
2013-04-02 03:16:45 +02:00
|
|
|
private boolean destroyed;
|
|
|
|
|
2013-04-24 18:31:42 +02:00
|
|
|
private static final String STATE_MENUDRAWER = "com.thejoshwa.ultrasonic.androidapp.menuDrawer";
|
|
|
|
private static final String STATE_ACTIVE_VIEW_ID = "com.thejoshwa.ultrasonic.androidapp.activeViewId";
|
2013-04-27 11:52:25 +02:00
|
|
|
private static final String STATE_ACTIVE_POSITION = "com.thejoshwa.ultrasonic.androidapp.activePosition";
|
2013-04-02 03:16:45 +02:00
|
|
|
|
2013-05-02 08:53:33 +02:00
|
|
|
public MenuDrawer menuDrawer;
|
2013-04-27 11:52:25 +02:00
|
|
|
private int activePosition = 1;
|
2013-04-24 18:31:42 +02:00
|
|
|
private int menuActiveViewId;
|
2013-05-16 09:59:55 +02:00
|
|
|
private View nowPlayingView = null;
|
2013-05-24 10:16:48 +02:00
|
|
|
View chatMenuItem = null;
|
2013-05-29 10:21:09 +02:00
|
|
|
View bookmarksMenuItem = null;
|
2013-04-27 11:52:25 +02:00
|
|
|
View menuMain = null;
|
|
|
|
public static boolean nowPlayingHidden = false;
|
2013-05-20 08:33:40 +02:00
|
|
|
private static Entry currentSong;
|
|
|
|
public Bitmap nowPlayingImage;
|
2013-05-18 12:50:54 +02:00
|
|
|
boolean licenseValid;
|
2013-04-02 03:16:45 +02:00
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void onCreate(Bundle bundle) {
|
|
|
|
setUncaughtExceptionHandler();
|
|
|
|
applyTheme();
|
|
|
|
super.onCreate(bundle);
|
2013-05-20 08:33:40 +02:00
|
|
|
|
2013-04-02 03:16:45 +02:00
|
|
|
startService(new Intent(this, DownloadServiceImpl.class));
|
|
|
|
setVolumeControlStream(AudioManager.STREAM_MUSIC);
|
|
|
|
|
2013-05-16 09:59:55 +02:00
|
|
|
if (bundle != null) {
|
2013-04-27 11:52:25 +02:00
|
|
|
activePosition = bundle.getInt(STATE_ACTIVE_POSITION);
|
2013-04-24 18:31:42 +02:00
|
|
|
menuActiveViewId = bundle.getInt(STATE_ACTIVE_VIEW_ID);
|
|
|
|
}
|
2013-04-02 03:16:45 +02:00
|
|
|
|
2013-04-27 11:52:25 +02:00
|
|
|
menuDrawer = MenuDrawer.attach(this, MenuDrawer.MENU_DRAG_WINDOW, Position.LEFT);
|
|
|
|
menuDrawer.setMenuView(R.layout.menu_main);
|
2013-04-02 03:16:45 +02:00
|
|
|
|
2013-05-24 10:16:48 +02:00
|
|
|
chatMenuItem = findViewById(R.id.menu_chat);
|
2013-05-29 10:21:09 +02:00
|
|
|
bookmarksMenuItem = findViewById(R.id.menu_bookmarks);
|
2013-04-02 08:01:15 +02:00
|
|
|
|
2013-04-24 18:31:42 +02:00
|
|
|
findViewById(R.id.menu_home).setOnClickListener(this);
|
|
|
|
findViewById(R.id.menu_browse).setOnClickListener(this);
|
2013-05-24 10:16:48 +02:00
|
|
|
findViewById(R.id.menu_search).setOnClickListener(this);
|
|
|
|
findViewById(R.id.menu_playlists).setOnClickListener(this);
|
|
|
|
chatMenuItem.setOnClickListener(this);
|
2013-05-29 10:21:09 +02:00
|
|
|
bookmarksMenuItem.setOnClickListener(this);
|
2013-04-24 18:31:42 +02:00
|
|
|
findViewById(R.id.menu_now_playing).setOnClickListener(this);
|
|
|
|
findViewById(R.id.menu_settings).setOnClickListener(this);
|
2013-04-27 11:52:25 +02:00
|
|
|
findViewById(R.id.menu_about).setOnClickListener(this);
|
2013-04-24 18:31:42 +02:00
|
|
|
findViewById(R.id.menu_exit).setOnClickListener(this);
|
|
|
|
|
2013-05-05 03:44:39 +02:00
|
|
|
getActionBar().setDisplayHomeAsUpEnabled(true);
|
2013-04-27 11:52:25 +02:00
|
|
|
|
|
|
|
TextView activeView = (TextView)findViewById(menuActiveViewId);
|
2013-04-02 08:01:15 +02:00
|
|
|
|
2013-04-24 18:31:42 +02:00
|
|
|
if (activeView != null) {
|
|
|
|
menuDrawer.setActiveView(activeView);
|
2013-04-02 03:16:45 +02:00
|
|
|
}
|
2013-05-16 09:59:55 +02:00
|
|
|
}
|
2013-04-24 18:31:42 +02:00
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void onPostCreate(Bundle bundle) {
|
|
|
|
super.onPostCreate(bundle);
|
2013-05-16 09:59:55 +02:00
|
|
|
instance = this;
|
2013-05-24 10:16:48 +02:00
|
|
|
|
|
|
|
int visibility = Util.isOffline(this) ? View.GONE : View.VISIBLE;
|
|
|
|
chatMenuItem.setVisibility(visibility);
|
2013-05-29 10:21:09 +02:00
|
|
|
bookmarksMenuItem.setVisibility(visibility);
|
2013-04-24 18:31:42 +02:00
|
|
|
}
|
2013-04-02 03:16:45 +02:00
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void onResume() {
|
|
|
|
super.onResume();
|
2013-05-02 08:53:33 +02:00
|
|
|
applyTheme();
|
2013-04-24 18:31:42 +02:00
|
|
|
instance = this;
|
2013-05-16 09:59:55 +02:00
|
|
|
|
|
|
|
Util.registerMediaButtonEventReceiver(this);
|
|
|
|
|
|
|
|
// Make sure to update theme
|
|
|
|
if (theme != null && !theme.equals(Util.getTheme(this))) {
|
2013-05-17 10:46:07 +02:00
|
|
|
theme = Util.getTheme(this);
|
2013-05-16 09:59:55 +02:00
|
|
|
restart();
|
|
|
|
}
|
2013-04-27 11:52:25 +02:00
|
|
|
|
|
|
|
if (!nowPlayingHidden) {
|
|
|
|
showNowPlaying();
|
|
|
|
} else {
|
|
|
|
hideNowPlaying();
|
|
|
|
}
|
2013-04-02 03:16:45 +02:00
|
|
|
}
|
2013-04-27 11:52:25 +02:00
|
|
|
|
2013-04-02 03:16:45 +02:00
|
|
|
@Override
|
|
|
|
public boolean onOptionsItemSelected(MenuItem item) {
|
|
|
|
switch (item.getItemId()) {
|
2013-04-24 18:31:42 +02:00
|
|
|
case android.R.id.home:
|
|
|
|
menuDrawer.toggleMenu();
|
|
|
|
return true;
|
2013-04-02 03:16:45 +02:00
|
|
|
}
|
|
|
|
|
2013-04-24 18:31:42 +02:00
|
|
|
return super.onOptionsItemSelected(item);
|
2013-04-02 03:16:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void onDestroy() {
|
|
|
|
Util.unregisterMediaButtonEventReceiver(this);
|
|
|
|
super.onDestroy();
|
|
|
|
destroyed = true;
|
2013-05-20 08:33:40 +02:00
|
|
|
nowPlayingView = null;
|
2013-04-02 03:16:45 +02:00
|
|
|
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);
|
|
|
|
}
|
2013-05-16 09:59:55 +02:00
|
|
|
|
|
|
|
protected void restart() {
|
|
|
|
Intent intent = new Intent(this, this.getClass());
|
|
|
|
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
|
|
|
intent.putExtras(getIntent());
|
|
|
|
Util.startActivityWithoutTransition(this, intent);
|
|
|
|
}
|
2013-04-02 03:16:45 +02:00
|
|
|
|
|
|
|
@Override
|
|
|
|
public void finish() {
|
|
|
|
super.finish();
|
|
|
|
Util.disablePendingTransition(this);
|
|
|
|
}
|
2013-04-24 18:31:42 +02:00
|
|
|
|
2013-05-16 09:59:55 +02:00
|
|
|
public boolean isDestroyed() {
|
|
|
|
return destroyed;
|
|
|
|
}
|
|
|
|
|
2013-05-20 08:33:40 +02:00
|
|
|
public void showNowPlaying() {
|
|
|
|
this.runOnUiThread(new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
new SilentBackgroundTask<Void>(SubsonicTabActivity.this) {
|
|
|
|
@Override
|
|
|
|
protected Void doInBackground() throws Throwable {
|
|
|
|
if (!Util.getShowNowPlayingPreference(SubsonicTabActivity.this)) {
|
|
|
|
hideNowPlaying();
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
nowPlayingView = findViewById(R.id.now_playing);
|
|
|
|
|
|
|
|
if (nowPlayingView != null) {
|
|
|
|
final DownloadService downloadService = DownloadServiceImpl .getInstance();
|
|
|
|
|
|
|
|
if (downloadService != null) {
|
|
|
|
PlayerState playerState = downloadService.getPlayerState();
|
|
|
|
|
|
|
|
if (playerState.equals(PlayerState.PAUSED) || playerState.equals(PlayerState.STARTED)) {
|
|
|
|
DownloadFile file = downloadService.getCurrentPlaying();
|
|
|
|
|
|
|
|
if (file != null) {
|
|
|
|
final Entry song = file.getSong();
|
|
|
|
showNowPlaying(SubsonicTabActivity.this, (DownloadServiceImpl) downloadService, song, playerState);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
hideNowPlaying();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void done(Void result) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}.execute();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
private void applyTheme() {
|
|
|
|
String theme = Util.getTheme(this);
|
|
|
|
|
|
|
|
if ("dark".equalsIgnoreCase(theme) || "fullscreen".equalsIgnoreCase(theme)) {
|
|
|
|
setTheme(R.style.UltraSonicTheme);
|
|
|
|
} else if ("light".equalsIgnoreCase(theme) || "fullscreenlight".equalsIgnoreCase(theme)) {
|
|
|
|
setTheme(R.style.UltraSonicTheme_Light);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public void showNotification(final Handler handler, final MusicDirectory.Entry song, final DownloadServiceImpl downloadService, final Notification notification, final PlayerState playerState) {
|
|
|
|
|
|
|
|
if (Util.isNotificationEnabled(this)) {
|
|
|
|
new AsyncTask<Void, Void, String[]>() {
|
|
|
|
@Override
|
|
|
|
protected void onPostExecute(String[] result) {
|
|
|
|
super.onPostExecute(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
@SuppressLint("NewApi")
|
|
|
|
@Override
|
|
|
|
protected String[] doInBackground(Void... params) {
|
|
|
|
RemoteViews notificationView = notification.contentView;
|
|
|
|
RemoteViews bigNotificationView = null;
|
|
|
|
|
|
|
|
if (Build.VERSION.SDK_INT>= Build.VERSION_CODES.JELLY_BEAN){
|
|
|
|
bigNotificationView = notification.bigContentView;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (playerState == PlayerState.PAUSED) {
|
|
|
|
setImageViewResourceOnUiThread(notificationView, R.id.control_play, R.drawable.media_start_normal_dark);
|
|
|
|
|
|
|
|
if (bigNotificationView != null) {
|
|
|
|
setImageViewResourceOnUiThread(bigNotificationView, R.id.control_play, R.drawable.media_start_normal_dark);
|
|
|
|
}
|
|
|
|
} else if (playerState == PlayerState.STARTED) {
|
|
|
|
setImageViewResourceOnUiThread(notificationView, R.id.control_play, R.drawable.media_pause_normal_dark);
|
|
|
|
|
|
|
|
if (bigNotificationView != null) {
|
|
|
|
setImageViewResourceOnUiThread(bigNotificationView, R.id.control_play, R.drawable.media_pause_normal_dark);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (currentSong != song) {
|
|
|
|
currentSong = song;
|
|
|
|
|
|
|
|
String title = song.getTitle();
|
|
|
|
String text = song.getArtist();
|
|
|
|
String album = song.getAlbum();
|
|
|
|
|
|
|
|
try {
|
|
|
|
if (nowPlayingImage == null) {
|
|
|
|
setImageViewResourceOnUiThread(notificationView, R.id.notification_image, R.drawable.unknown_album);
|
|
|
|
|
|
|
|
if (bigNotificationView != null) {
|
|
|
|
setImageViewResourceOnUiThread(bigNotificationView, R.id.notification_image, R.drawable.unknown_album);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
setImageViewBitmapOnUiThread(notificationView, R.id.notification_image, nowPlayingImage);
|
|
|
|
|
|
|
|
if (bigNotificationView != null) {
|
|
|
|
setImageViewBitmapOnUiThread(bigNotificationView, R.id.notification_image, nowPlayingImage);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (Exception x) {
|
|
|
|
Log.w(TAG, "Failed to get notification cover art", x);
|
|
|
|
setImageViewResourceOnUiThread(notificationView, R.id.notification_image, R.drawable.unknown_album);
|
|
|
|
|
|
|
|
if (bigNotificationView != null) {
|
|
|
|
setImageViewResourceOnUiThread(bigNotificationView, R.id.notification_image, R.drawable.unknown_album);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
setTextViewTextOnUiThread(notificationView, R.id.trackname, title);
|
|
|
|
setTextViewTextOnUiThread(notificationView, R.id.artist, text);
|
|
|
|
setTextViewTextOnUiThread(notificationView, R.id.album, album);
|
|
|
|
|
|
|
|
if (bigNotificationView != null) {
|
|
|
|
setTextViewTextOnUiThread(bigNotificationView, R.id.trackname, title);
|
|
|
|
setTextViewTextOnUiThread(bigNotificationView, R.id.artist, text);
|
|
|
|
setTextViewTextOnUiThread(bigNotificationView, R.id.album, album);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Send the notification and put the service in the foreground.
|
|
|
|
handler.post(new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
downloadService.startForeground(Constants.NOTIFICATION_ID_PLAYING, notification);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}.execute();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public void hidePlayingNotification(final Handler handler, final DownloadServiceImpl downloadService) {
|
|
|
|
|
|
|
|
currentSong = null;
|
|
|
|
|
|
|
|
// Remove notification and remove the service from the foreground
|
|
|
|
handler.post(new Runnable(){
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
downloadService.stopForeground(true);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
private void showNowPlaying(final Context context, final DownloadServiceImpl downloadService, final MusicDirectory.Entry song, final PlayerState playerState) {
|
2013-05-16 09:59:55 +02:00
|
|
|
nowPlayingView = findViewById(R.id.now_playing);
|
2013-04-27 11:52:25 +02:00
|
|
|
|
2013-05-20 08:33:40 +02:00
|
|
|
if (!Util.getShowNowPlayingPreference(SubsonicTabActivity.this)) {
|
|
|
|
hideNowPlaying();
|
2013-04-27 11:52:25 +02:00
|
|
|
return;
|
|
|
|
}
|
2013-05-20 08:33:40 +02:00
|
|
|
|
|
|
|
if (nowPlayingView != null) {
|
|
|
|
try {
|
|
|
|
setVisibilityOnUiThread(nowPlayingView, View.VISIBLE);
|
|
|
|
nowPlayingHidden = false;
|
2013-04-24 18:31:42 +02:00
|
|
|
|
2013-05-20 08:33:40 +02:00
|
|
|
ImageView playButton = (ImageView) nowPlayingView.findViewById(R.id.now_playing_control_play);
|
|
|
|
|
|
|
|
if (playerState == PlayerState.PAUSED) {
|
|
|
|
setImageDrawableOnUiThread(playButton, Util.getDrawableFromAttribute(SubsonicTabActivity.this, R.attr.media_play));
|
|
|
|
} else if (playerState == PlayerState.STARTED) {
|
|
|
|
setImageDrawableOnUiThread(playButton, Util.getDrawableFromAttribute(SubsonicTabActivity.this, R.attr.media_pause));
|
2013-04-24 18:31:42 +02:00
|
|
|
}
|
2013-04-27 11:52:25 +02:00
|
|
|
|
2013-05-20 08:33:40 +02:00
|
|
|
String title = song.getTitle();
|
|
|
|
String artist = song.getArtist();
|
|
|
|
|
|
|
|
final ImageView nowPlayingAlbumArtImage = (ImageView) nowPlayingView.findViewById(R.id.now_playing_image);
|
|
|
|
TextView nowPlayingTrack = (TextView) nowPlayingView.findViewById(R.id.now_playing_trackname);
|
|
|
|
TextView nowPlayingArtist = (TextView) nowPlayingView.findViewById(R.id.now_playing_artist);
|
|
|
|
|
|
|
|
this.runOnUiThread( new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
getImageLoader().loadImage(nowPlayingAlbumArtImage, song, true, Util.getNotificationImageSize(SubsonicTabActivity.this), false, true);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
final Intent intent = new Intent(SubsonicTabActivity.this, SelectAlbumActivity.class);
|
|
|
|
|
|
|
|
if (Util.getShouldUseId3Tags(SubsonicTabActivity.this)) {
|
|
|
|
intent.putExtra(Constants.INTENT_EXTRA_NAME_IS_ALBUM, true);
|
|
|
|
intent.putExtra(Constants.INTENT_EXTRA_NAME_ID, song.getAlbumId());
|
|
|
|
} else {
|
|
|
|
intent.putExtra(Constants.INTENT_EXTRA_NAME_IS_ALBUM, false);
|
|
|
|
intent.putExtra(Constants.INTENT_EXTRA_NAME_ID, song.getParent());
|
|
|
|
}
|
|
|
|
|
|
|
|
intent.putExtra(Constants.INTENT_EXTRA_NAME_NAME, song.getAlbum());
|
|
|
|
|
|
|
|
setOnClickListenerOnUiThread(nowPlayingAlbumArtImage, new View.OnClickListener() {
|
|
|
|
@Override
|
|
|
|
public void onClick(View view) {
|
|
|
|
Util.startActivityWithoutTransition(SubsonicTabActivity.this, intent);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
setTextOnUiThread(nowPlayingTrack, title);
|
|
|
|
setTextOnUiThread(nowPlayingArtist, artist);
|
|
|
|
|
2013-05-16 09:59:55 +02:00
|
|
|
ImageView nowPlayingControlPlay = (ImageView) nowPlayingView.findViewById(R.id.now_playing_control_play);
|
2013-04-27 11:52:25 +02:00
|
|
|
|
|
|
|
SwipeDetector swipeDetector = SwipeDetector.Create(SubsonicTabActivity.this, downloadService);
|
2013-05-20 08:33:40 +02:00
|
|
|
setOnTouchListenerOnUiThread(nowPlayingView, swipeDetector);
|
2013-04-27 11:52:25 +02:00
|
|
|
|
2013-05-20 08:33:40 +02:00
|
|
|
setOnClickListenerOnUiThread(nowPlayingView, new View.OnClickListener() {
|
2013-04-27 11:52:25 +02:00
|
|
|
@Override
|
|
|
|
public void onClick(View v) {
|
|
|
|
}
|
|
|
|
});
|
2013-05-20 08:33:40 +02:00
|
|
|
|
|
|
|
setOnClickListenerOnUiThread(nowPlayingControlPlay, new View.OnClickListener() {
|
2013-04-27 11:52:25 +02:00
|
|
|
@Override
|
|
|
|
public void onClick(View view) {
|
|
|
|
downloadService.togglePlayPause();
|
|
|
|
}
|
2013-05-20 08:33:40 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
} catch (Exception x) {
|
|
|
|
Log.w(TAG, "Failed to get notification cover art", x);
|
2013-04-27 11:52:25 +02:00
|
|
|
}
|
2013-04-24 18:31:42 +02:00
|
|
|
}
|
|
|
|
}
|
2013-04-02 03:16:45 +02:00
|
|
|
|
2013-05-20 08:33:40 +02:00
|
|
|
public void hideNowPlaying() {
|
|
|
|
nowPlayingView = findViewById(R.id.now_playing);
|
|
|
|
setVisibilityOnUiThread(nowPlayingView, View.GONE);
|
2013-04-02 03:16:45 +02:00
|
|
|
}
|
2013-04-24 18:31:42 +02:00
|
|
|
|
2013-05-20 08:33:40 +02:00
|
|
|
public void setTextViewTextOnUiThread(final RemoteViews view, final int id, final String text) {
|
2013-05-16 09:59:55 +02:00
|
|
|
this.runOnUiThread( new Runnable() {
|
2013-05-20 08:33:40 +02:00
|
|
|
@Override
|
2013-05-16 09:59:55 +02:00
|
|
|
public void run() {
|
2013-05-20 08:33:40 +02:00
|
|
|
if (view != null) {
|
|
|
|
view.setTextViewText(id, text);
|
2013-05-16 09:59:55 +02:00
|
|
|
}
|
2013-05-20 08:33:40 +02:00
|
|
|
}
|
2013-05-16 09:59:55 +02:00
|
|
|
});
|
2013-04-24 18:31:42 +02:00
|
|
|
}
|
2013-05-20 08:33:40 +02:00
|
|
|
|
|
|
|
public void setImageViewBitmapOnUiThread(final RemoteViews view, final int id, final Bitmap bitmap) {
|
2013-05-16 09:59:55 +02:00
|
|
|
this.runOnUiThread( new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
2013-05-20 08:33:40 +02:00
|
|
|
if (view != null) {
|
|
|
|
view.setImageViewBitmap(id, bitmap);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setImageViewResourceOnUiThread(final RemoteViews view, final int id, final int resouce) {
|
|
|
|
this.runOnUiThread( new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
if (view != null) {
|
|
|
|
view.setImageViewResource(id, resouce);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2013-05-22 07:47:40 +02:00
|
|
|
public void setListAdapterOnListViewOnUiThread(final ListView view, final ListAdapter adapter) {
|
|
|
|
this.runOnUiThread( new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
if (view != null) {
|
|
|
|
view.setAdapter(adapter);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2013-05-20 08:33:40 +02:00
|
|
|
public void setOnTouchListenerOnUiThread(final View view, final View.OnTouchListener listener) {
|
|
|
|
this.runOnUiThread( new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
if (view != null && view.getVisibility() != View.GONE) {
|
|
|
|
view.setOnTouchListener(listener);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setOnClickListenerOnUiThread(final View view, final View.OnClickListener listener) {
|
|
|
|
this.runOnUiThread( new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
if (view != null && view.getVisibility() != View.GONE) {
|
|
|
|
view.setOnClickListener(listener);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setTextOnUiThread(final TextView view, final String text) {
|
|
|
|
this.runOnUiThread( new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
if (view != null && view.getVisibility() != View.GONE) {
|
|
|
|
view.setText(text);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setImageResourceOnUiThread(final ImageView view, final int resource) {
|
|
|
|
this.runOnUiThread( new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
if (view != null && view.getVisibility() != View.GONE) {
|
|
|
|
view.setImageResource(resource);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setImageDrawableOnUiThread(final ImageView view, final Drawable drawable) {
|
|
|
|
this.runOnUiThread( new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
if (view != null && view.getVisibility() != View.GONE) {
|
|
|
|
view.setImageDrawable(drawable);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setImageBitmapOnUiThread(final ImageView view, final Bitmap bitmap) {
|
|
|
|
this.runOnUiThread( new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
if (view != null && view.getVisibility() != View.GONE) {
|
|
|
|
view.setImageBitmap(bitmap);
|
2013-05-16 09:59:55 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2013-05-20 08:33:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public void setVisibilityOnUiThread(final View view, final int visibility) {
|
|
|
|
this.runOnUiThread( new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
if (view != null && view.getVisibility() != visibility) {
|
|
|
|
view.setVisibility(visibility);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2013-04-24 18:31:42 +02:00
|
|
|
|
|
|
|
public static SubsonicTabActivity getInstance() {
|
|
|
|
return instance;
|
|
|
|
}
|
|
|
|
|
2013-04-02 03:16:45 +02:00
|
|
|
public boolean getIsDestroyed() {
|
|
|
|
return destroyed;
|
|
|
|
}
|
|
|
|
|
|
|
|
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();
|
2013-05-26 07:19:28 +02:00
|
|
|
|
2013-04-02 03:16:45 +02:00
|
|
|
if (downloadService != null) {
|
|
|
|
return downloadService;
|
|
|
|
}
|
2013-05-26 07:19:28 +02:00
|
|
|
|
2013-04-02 03:16:45 +02:00
|
|
|
Log.w(TAG, "DownloadService not running. Attempting to start it.");
|
|
|
|
startService(new Intent(this, DownloadServiceImpl.class));
|
|
|
|
Util.sleepQuietly(50L);
|
|
|
|
}
|
2013-05-26 07:19:28 +02:00
|
|
|
|
2013-04-02 03:16:45 +02:00
|
|
|
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;
|
|
|
|
}
|
2013-05-16 09:59:55 +02:00
|
|
|
|
|
|
|
public synchronized static ImageLoader getStaticImageLoader(Context context) {
|
|
|
|
if (IMAGE_LOADER == null) {
|
|
|
|
IMAGE_LOADER = new ImageLoader(context);
|
|
|
|
}
|
|
|
|
return IMAGE_LOADER;
|
|
|
|
}
|
2013-05-18 12:50:54 +02:00
|
|
|
|
|
|
|
void download(final boolean append, final boolean save, final boolean autoplay, final boolean playNext, final boolean shuffle, final List<Entry> songs) {
|
|
|
|
if (getDownloadService() == null) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Runnable onValid = new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
2013-05-26 07:19:28 +02:00
|
|
|
if (!append && !playNext) {
|
2013-05-18 12:50:54 +02:00
|
|
|
getDownloadService().clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
warnIfNetworkOrStorageUnavailable();
|
2013-05-29 10:21:09 +02:00
|
|
|
getDownloadService().download(songs, save, autoplay, playNext, shuffle, false);
|
2013-05-18 12:50:54 +02:00
|
|
|
String playlistName = getIntent().getStringExtra(Constants.INTENT_EXTRA_NAME_PLAYLIST_NAME);
|
2013-05-29 10:21:09 +02:00
|
|
|
|
2013-05-18 12:50:54 +02:00
|
|
|
if (playlistName != null) {
|
|
|
|
getDownloadService().setSuggestedPlaylistName(playlistName);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (autoplay) {
|
|
|
|
if (Util.getShouldTransitionOnPlaybackPreference(SubsonicTabActivity.this)) {
|
|
|
|
Util.startActivityWithoutTransition(SubsonicTabActivity.this, DownloadActivity.class);
|
|
|
|
}
|
|
|
|
} else if (save) {
|
2013-05-26 08:46:37 +02:00
|
|
|
Util.toast(SubsonicTabActivity.this, getResources().getQuantityString(R.plurals.select_album_n_songs_pinned, songs.size(), songs.size()));
|
2013-05-18 12:50:54 +02:00
|
|
|
} else if (playNext) {
|
|
|
|
Util.toast(SubsonicTabActivity.this, getResources().getQuantityString(R.plurals.select_album_n_songs_play_next, songs.size(), songs.size()));
|
|
|
|
} else if (append) {
|
|
|
|
Util.toast(SubsonicTabActivity.this, getResources().getQuantityString(R.plurals.select_album_n_songs_added, songs.size(), songs.size()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
checkLicenseAndTrialPeriod(onValid);
|
|
|
|
}
|
|
|
|
|
2013-04-02 03:16:45 +02:00
|
|
|
|
2013-05-26 07:19:28 +02:00
|
|
|
protected void downloadRecursively(final String id, final boolean save, final boolean append, final boolean autoplay, final boolean shuffle, final boolean background, final boolean playNext, final boolean unpin) {
|
|
|
|
downloadRecursively(id, "", true, save, append, autoplay, shuffle, background, playNext, unpin);
|
2013-05-16 09:59:55 +02:00
|
|
|
}
|
2013-05-26 07:19:28 +02:00
|
|
|
protected void downloadPlaylist(final String id, final String name, final boolean save, final boolean append, final boolean autoplay, final boolean shuffle, final boolean background, final boolean playNext, final boolean unpin) {
|
|
|
|
downloadRecursively(id, name, false, save, append, autoplay, shuffle, background, playNext, unpin);
|
2013-05-16 09:59:55 +02:00
|
|
|
}
|
2013-05-26 07:19:28 +02:00
|
|
|
protected void downloadRecursively(final String id, final String name, final boolean isDirectory, final boolean save, final boolean append, final boolean autoplay, final boolean shuffle, final boolean background, final boolean playNext, final boolean unpin) {
|
2013-05-16 09:59:55 +02:00
|
|
|
ModalBackgroundTask<List<MusicDirectory.Entry>> task = new ModalBackgroundTask<List<MusicDirectory.Entry>>(this, false) {
|
2013-04-02 03:16:45 +02:00
|
|
|
private static final int MAX_SONGS = 500;
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected List<MusicDirectory.Entry> doInBackground() throws Throwable {
|
|
|
|
MusicService musicService = MusicServiceFactory.getMusicService(SubsonicTabActivity.this);
|
2013-05-16 09:59:55 +02:00
|
|
|
MusicDirectory root;
|
2013-05-18 12:50:54 +02:00
|
|
|
|
|
|
|
if(isDirectory) {
|
|
|
|
if (Util.getShouldUseId3Tags(SubsonicTabActivity.this)) {
|
|
|
|
root = musicService.getAlbum(id, name, false, SubsonicTabActivity.this, this);
|
|
|
|
} else {
|
|
|
|
root = musicService.getMusicDirectory(id, name, false, SubsonicTabActivity.this, this);
|
|
|
|
}
|
|
|
|
} else {
|
2013-05-16 09:59:55 +02:00
|
|
|
root = musicService.getPlaylist(id, name, SubsonicTabActivity.this, this);
|
2013-05-18 12:50:54 +02:00
|
|
|
}
|
|
|
|
|
2013-04-02 03:16:45 +02:00
|
|
|
List<MusicDirectory.Entry> songs = new LinkedList<MusicDirectory.Entry>();
|
|
|
|
getSongsRecursively(root, songs);
|
|
|
|
return songs;
|
|
|
|
}
|
|
|
|
|
|
|
|
private void getSongsRecursively(MusicDirectory parent, List<MusicDirectory.Entry> songs) throws Exception {
|
|
|
|
if (songs.size() > MAX_SONGS) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (MusicDirectory.Entry song : parent.getChildren(false, true)) {
|
|
|
|
if (!song.isVideo()) {
|
|
|
|
songs.add(song);
|
|
|
|
}
|
|
|
|
}
|
2013-05-18 12:50:54 +02:00
|
|
|
|
2013-04-02 03:16:45 +02:00
|
|
|
for (MusicDirectory.Entry dir : parent.getChildren(true, false)) {
|
|
|
|
MusicService musicService = MusicServiceFactory.getMusicService(SubsonicTabActivity.this);
|
2013-05-18 12:50:54 +02:00
|
|
|
MusicDirectory root;
|
|
|
|
|
|
|
|
if (Util.getShouldUseId3Tags(SubsonicTabActivity.this)) {
|
|
|
|
root = musicService.getAlbum(dir.getId(), dir.getTitle(), false, SubsonicTabActivity.this, this);
|
|
|
|
} else {
|
|
|
|
root = musicService.getMusicDirectory(dir.getId(), dir.getTitle(), false, SubsonicTabActivity.this, this);
|
|
|
|
}
|
|
|
|
|
|
|
|
getSongsRecursively(root, songs);
|
2013-04-02 03:16:45 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void done(List<MusicDirectory.Entry> songs) {
|
|
|
|
DownloadService downloadService = getDownloadService();
|
|
|
|
if (!songs.isEmpty() && downloadService != null) {
|
2013-05-26 08:46:37 +02:00
|
|
|
if (!append && !playNext && !unpin && !background) {
|
2013-04-02 03:16:45 +02:00
|
|
|
downloadService.clear();
|
|
|
|
}
|
|
|
|
warnIfNetworkOrStorageUnavailable();
|
2013-05-16 09:59:55 +02:00
|
|
|
if(!background) {
|
2013-05-26 07:19:28 +02:00
|
|
|
if (unpin) {
|
|
|
|
downloadService.unpin(songs);
|
|
|
|
} else {
|
2013-05-29 10:21:09 +02:00
|
|
|
downloadService.download(songs, save, autoplay, playNext, shuffle, false);
|
2013-05-26 07:19:28 +02:00
|
|
|
if (!append && Util.getShouldTransitionOnPlaybackPreference(SubsonicTabActivity.this)) {
|
|
|
|
Util.startActivityWithoutTransition(SubsonicTabActivity.this, DownloadActivity.class);
|
|
|
|
}
|
2013-05-16 09:59:55 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2013-05-26 07:19:28 +02:00
|
|
|
if (unpin) {
|
|
|
|
downloadService.unpin(songs);
|
|
|
|
} else {
|
|
|
|
downloadService.downloadBackground(songs, save);
|
|
|
|
}
|
2013-05-16 09:59:55 +02:00
|
|
|
}
|
2013-04-02 03:16:45 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
task.execute();
|
|
|
|
}
|
2013-05-16 09:59:55 +02:00
|
|
|
|
2013-05-31 10:02:25 +02:00
|
|
|
protected void playVideo(MusicDirectory.Entry entry) {
|
|
|
|
if (!Util.isNetworkConnected(this)) {
|
|
|
|
Util.toast(this, R.string.select_album_no_network);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
VideoPlayerType player = Util.getVideoPlayerType(this);
|
|
|
|
|
|
|
|
try {
|
|
|
|
player.playVideo(this, entry);
|
|
|
|
} catch (Exception e) {
|
|
|
|
Util.toast(this, e.getMessage(), false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-05-16 09:59:55 +02:00
|
|
|
protected void addToPlaylist(final List<MusicDirectory.Entry> songs) {
|
|
|
|
if(songs.isEmpty()) {
|
|
|
|
Util.toast(this, "No songs selected");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
new LoadingTask<List<Playlist>>(this, true) {
|
|
|
|
@Override
|
|
|
|
protected List<Playlist> doInBackground() throws Throwable {
|
|
|
|
MusicService musicService = MusicServiceFactory.getMusicService(SubsonicTabActivity.this);
|
|
|
|
return musicService.getPlaylists(false, SubsonicTabActivity.this, this);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void done(final List<Playlist> playlists) {
|
|
|
|
List<String> names = new ArrayList<String>();
|
|
|
|
for(Playlist playlist: playlists) {
|
|
|
|
names.add(playlist.getName());
|
|
|
|
}
|
|
|
|
|
|
|
|
AlertDialog.Builder builder = new AlertDialog.Builder(SubsonicTabActivity.this);
|
|
|
|
builder.setTitle("Add to Playlist")
|
|
|
|
.setItems(names.toArray(new CharSequence[names.size()]), new DialogInterface.OnClickListener() {
|
|
|
|
public void onClick(DialogInterface dialog, int which) {
|
|
|
|
addToPlaylist(playlists.get(which), songs);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
AlertDialog dialog = builder.create();
|
|
|
|
dialog.show();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void error(Throwable error) {
|
|
|
|
String msg;
|
|
|
|
if (error instanceof OfflineException || error instanceof ServerTooOldException) {
|
|
|
|
msg = getErrorMessage(error);
|
|
|
|
} else {
|
|
|
|
msg = getResources().getString(R.string.playlist_error) + " " + getErrorMessage(error);
|
|
|
|
}
|
|
|
|
|
|
|
|
Util.toast(SubsonicTabActivity.this, msg, false);
|
|
|
|
}
|
|
|
|
}.execute();
|
|
|
|
}
|
|
|
|
|
|
|
|
private void addToPlaylist(final Playlist playlist, final List<MusicDirectory.Entry> songs) {
|
|
|
|
new SilentBackgroundTask<Void>(this) {
|
|
|
|
@Override
|
|
|
|
protected Void doInBackground() throws Throwable {
|
|
|
|
MusicService musicService = MusicServiceFactory.getMusicService(SubsonicTabActivity.this);
|
2013-05-18 12:50:54 +02:00
|
|
|
musicService.updatePlaylist(playlist.getId(), songs, SubsonicTabActivity.this, null);
|
2013-05-16 09:59:55 +02:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void done(Void result) {
|
|
|
|
Util.toast(SubsonicTabActivity.this, getResources().getString(R.string.updated_playlist, songs.size(), playlist.getName()));
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void error(Throwable error) {
|
|
|
|
String msg;
|
|
|
|
if (error instanceof OfflineException || error instanceof ServerTooOldException) {
|
|
|
|
msg = getErrorMessage(error);
|
|
|
|
} else {
|
|
|
|
msg = getResources().getString(R.string.updated_playlist_error, playlist.getName()) + " " + getErrorMessage(error);
|
|
|
|
}
|
|
|
|
|
|
|
|
Util.toast(SubsonicTabActivity.this, msg, false);
|
|
|
|
}
|
|
|
|
}.execute();
|
|
|
|
}
|
2013-05-18 12:50:54 +02:00
|
|
|
|
|
|
|
protected void checkLicenseAndTrialPeriod(Runnable onValid) {
|
|
|
|
if (licenseValid) {
|
|
|
|
onValid.run();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
int trialDaysLeft = Util.getRemainingTrialDays(this);
|
|
|
|
Log.i(TAG, trialDaysLeft + " trial days left.");
|
|
|
|
|
|
|
|
if (trialDaysLeft == 0) {
|
|
|
|
showDonationDialog(trialDaysLeft, null);
|
|
|
|
} else if (trialDaysLeft < Constants.FREE_TRIAL_DAYS / 2) {
|
|
|
|
showDonationDialog(trialDaysLeft, onValid);
|
|
|
|
} else {
|
|
|
|
Util.toast(this, getResources().getString(R.string.select_album_not_licensed, trialDaysLeft));
|
|
|
|
onValid.run();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void showDonationDialog(int trialDaysLeft, final Runnable onValid) {
|
|
|
|
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
|
|
|
builder.setIcon(android.R.drawable.ic_dialog_info);
|
|
|
|
|
|
|
|
if (trialDaysLeft == 0) {
|
|
|
|
builder.setTitle(R.string.select_album_donate_dialog_0_trial_days_left);
|
|
|
|
} else {
|
|
|
|
builder.setTitle(getResources().getQuantityString(R.plurals.select_album_donate_dialog_n_trial_days_left,
|
|
|
|
trialDaysLeft, trialDaysLeft));
|
|
|
|
}
|
|
|
|
|
|
|
|
builder.setMessage(R.string.select_album_donate_dialog_message);
|
|
|
|
|
|
|
|
builder.setPositiveButton(R.string.select_album_donate_dialog_now,
|
|
|
|
new DialogInterface.OnClickListener() {
|
|
|
|
@Override
|
|
|
|
public void onClick(DialogInterface dialogInterface, int i) {
|
|
|
|
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(Constants.DONATION_URL)));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
builder.setNegativeButton(R.string.select_album_donate_dialog_later,
|
|
|
|
new DialogInterface.OnClickListener() {
|
|
|
|
@Override
|
|
|
|
public void onClick(DialogInterface dialogInterface, int i) {
|
|
|
|
dialogInterface.dismiss();
|
|
|
|
if (onValid != null) {
|
|
|
|
onValid.run();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
builder.create().show();
|
|
|
|
}
|
|
|
|
|
2013-04-02 03:16:45 +02:00
|
|
|
|
|
|
|
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 {
|
|
|
|
|
2013-04-06 21:47:24 +02:00
|
|
|
PackageInfo packageInfo = context.getPackageManager().getPackageInfo("com.thejoshwa.ultrasonic.androidapp", 0);
|
|
|
|
file = new File(Environment.getExternalStorageDirectory(), "ultrasonic-stacktrace.txt");
|
2013-04-02 03:16:45 +02:00
|
|
|
printWriter = new PrintWriter(file);
|
|
|
|
printWriter.println("Android API level: " + Build.VERSION.SDK_INT);
|
2013-04-24 18:31:42 +02:00
|
|
|
printWriter.println("UltraSonic version name: " + packageInfo.versionName);
|
|
|
|
printWriter.println("UltraSonic version code: " + packageInfo.versionCode);
|
2013-04-02 03:16:45 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-24 18:31:42 +02:00
|
|
|
@Override
|
|
|
|
public void onClick(View v) {
|
|
|
|
menuActiveViewId = v.getId();
|
2013-04-02 03:16:45 +02:00
|
|
|
|
2013-04-24 18:31:42 +02:00
|
|
|
Intent intent;
|
|
|
|
|
|
|
|
switch (menuActiveViewId) {
|
|
|
|
case R.id.menu_home:
|
|
|
|
intent = new Intent(SubsonicTabActivity.this, MainActivity.class);
|
|
|
|
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
|
|
|
Util.startActivityWithoutTransition(SubsonicTabActivity.this, intent);
|
|
|
|
break;
|
|
|
|
case R.id.menu_browse:
|
|
|
|
intent = new Intent(SubsonicTabActivity.this, SelectArtistActivity.class);
|
|
|
|
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
|
|
|
Util.startActivityWithoutTransition(SubsonicTabActivity.this, intent);
|
|
|
|
break;
|
|
|
|
case R.id.menu_search:
|
|
|
|
intent = new Intent(SubsonicTabActivity.this, SearchActivity.class);
|
|
|
|
intent.putExtra(Constants.INTENT_EXTRA_REQUEST_SEARCH, true);
|
|
|
|
Util.startActivityWithoutTransition(SubsonicTabActivity.this, intent);
|
|
|
|
break;
|
|
|
|
case R.id.menu_playlists:
|
|
|
|
intent = new Intent(SubsonicTabActivity.this, SelectPlaylistActivity.class);
|
|
|
|
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
|
|
|
Util.startActivityWithoutTransition(SubsonicTabActivity.this, intent);
|
|
|
|
break;
|
2013-05-22 07:47:40 +02:00
|
|
|
case R.id.menu_chat:
|
|
|
|
Util.startActivityWithoutTransition(SubsonicTabActivity.this, ChatActivity.class);
|
|
|
|
break;
|
2013-05-29 10:21:09 +02:00
|
|
|
case R.id.menu_bookmarks:
|
|
|
|
Util.startActivityWithoutTransition(this, BookmarkActivity.class);
|
|
|
|
break;
|
2013-04-24 18:31:42 +02:00
|
|
|
case R.id.menu_now_playing:
|
|
|
|
Util.startActivityWithoutTransition(SubsonicTabActivity.this, DownloadActivity.class);
|
|
|
|
break;
|
|
|
|
case R.id.menu_settings:
|
|
|
|
Util.startActivityWithoutTransition(SubsonicTabActivity.this, SettingsActivity.class);
|
|
|
|
break;
|
2013-04-27 11:52:25 +02:00
|
|
|
case R.id.menu_about:
|
2013-04-24 18:31:42 +02:00
|
|
|
Util.startActivityWithoutTransition(SubsonicTabActivity.this, HelpActivity.class);
|
|
|
|
break;
|
|
|
|
case R.id.menu_exit:
|
|
|
|
intent = new Intent(SubsonicTabActivity.this, MainActivity.class);
|
|
|
|
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
|
|
|
intent.putExtra(Constants.INTENT_EXTRA_NAME_EXIT, true);
|
|
|
|
Util.startActivityWithoutTransition(SubsonicTabActivity.this, intent);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
menuDrawer.closeMenu();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void onRestoreInstanceState(Bundle inState) {
|
|
|
|
super.onRestoreInstanceState(inState);
|
|
|
|
menuDrawer.restoreState(inState.getParcelable(STATE_MENUDRAWER));
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void onSaveInstanceState(Bundle outState) {
|
|
|
|
super.onSaveInstanceState(outState);
|
|
|
|
outState.putParcelable(STATE_MENUDRAWER, menuDrawer.saveState());
|
|
|
|
outState.putInt(STATE_ACTIVE_VIEW_ID, menuActiveViewId);
|
2013-04-27 11:52:25 +02:00
|
|
|
outState.putInt(STATE_ACTIVE_POSITION, activePosition);
|
2013-04-24 18:31:42 +02:00
|
|
|
}
|
2013-04-02 03:16:45 +02:00
|
|
|
|
2013-04-24 18:31:42 +02:00
|
|
|
@Override
|
|
|
|
public void onBackPressed() {
|
|
|
|
final int drawerState = menuDrawer.getDrawerState();
|
|
|
|
|
|
|
|
if (drawerState == MenuDrawer.STATE_OPEN || drawerState == MenuDrawer.STATE_OPENING) {
|
|
|
|
menuDrawer.closeMenu();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
super.onBackPressed();
|
|
|
|
}
|
2013-04-27 11:52:25 +02:00
|
|
|
|
|
|
|
static class SwipeDetector implements OnTouchListener {
|
|
|
|
|
|
|
|
public static enum Action {
|
2013-04-28 01:01:34 +02:00
|
|
|
LeftToRight,
|
|
|
|
RightToLeft,
|
|
|
|
TopToBottom,
|
|
|
|
BottomToTop,
|
|
|
|
None,
|
2013-04-27 11:52:25 +02:00
|
|
|
Click
|
|
|
|
}
|
|
|
|
|
|
|
|
public static SwipeDetector Create(SubsonicTabActivity activity, final DownloadService downloadService) {
|
|
|
|
SwipeDetector swipeDetector = new SwipeDetector();
|
|
|
|
swipeDetector.downloadService = downloadService;
|
|
|
|
swipeDetector.activity = activity;
|
|
|
|
return swipeDetector;
|
|
|
|
}
|
|
|
|
|
2013-04-28 01:01:34 +02:00
|
|
|
private static final int MIN_DISTANCE = 30;
|
2013-04-27 11:52:25 +02:00
|
|
|
private float downX, downY, upX, upY;
|
|
|
|
private DownloadService downloadService;
|
|
|
|
private SubsonicTabActivity activity;
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean onTouch(View v, MotionEvent event) {
|
|
|
|
switch (event.getAction()) {
|
|
|
|
case MotionEvent.ACTION_DOWN: {
|
|
|
|
downX = event.getX();
|
|
|
|
downY = event.getY();
|
2013-04-28 01:01:34 +02:00
|
|
|
return false;
|
2013-04-27 11:52:25 +02:00
|
|
|
} case MotionEvent.ACTION_UP: {
|
|
|
|
upX = event.getX();
|
|
|
|
upY = event.getY();
|
|
|
|
|
|
|
|
float deltaX = downX - upX;
|
|
|
|
float deltaY = downY - upY;
|
|
|
|
|
|
|
|
if (Math.abs(deltaX) > MIN_DISTANCE) {
|
|
|
|
// left or right
|
|
|
|
if (deltaX < 0) {
|
|
|
|
downloadService.previous();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (deltaX > 0) {
|
|
|
|
downloadService.next();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else if (Math.abs(deltaY) > MIN_DISTANCE) {
|
|
|
|
if (deltaY < 0) {
|
2013-05-20 08:33:40 +02:00
|
|
|
SubsonicTabActivity.nowPlayingHidden = true;
|
2013-04-27 11:52:25 +02:00
|
|
|
activity.hideNowPlaying();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (deltaY > 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Util.startActivityWithoutTransition(activity, DownloadActivity.class);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2013-05-16 09:59:55 +02:00
|
|
|
}
|