Add image concurrency settings, stop jukebox and ImageLoader threads on Exit, show settings screen after welcome dialog, only start jukebox thread when jukebox is enabled, version 1.3.0.4

This commit is contained in:
Joshua Bahnsen 2014-01-22 01:58:38 -07:00
parent 8efd70292d
commit d122105b92
16 changed files with 237 additions and 25 deletions

View File

@ -2,8 +2,8 @@
<manifest xmlns:a="http://schemas.android.com/apk/res/android"
package="com.thejoshwa.ultrasonic.androidapp"
a:installLocation="auto"
a:versionCode="43"
a:versionName="1.3.0.3">
a:versionCode="44"
a:versionName="1.3.0.4">
<uses-permission a:name="android.permission.INTERNET"/>
<uses-permission a:name="android.permission.READ_PHONE_STATE"/>

View File

@ -374,6 +374,19 @@
<string name="download.menu_show_artist">Afficher l\'artiste</string>
<string name="settings.scan_media">Scan Media After Download</string>
<string name="settings.scan_media_summary">Automatically scan media after download</string>
<string name="settings.image_loader_concurrency">Image Loader Concurrency</string>
<string name="settings.image_loader_concurrency_1">1</string>
<string name="settings.image_loader_concurrency_2">2</string>
<string name="settings.image_loader_concurrency_3">3</string>
<string name="settings.image_loader_concurrency_4">4</string>
<string name="settings.image_loader_concurrency_5">5</string>
<string name="settings.image_loader_concurrency_6">6</string>
<string name="settings.image_loader_concurrency_7">7</string>
<string name="settings.image_loader_concurrency_8">8</string>
<string name="settings.image_loader_concurrency_9">9</string>
<string name="settings.image_loader_concurrency_10">10</string>
<string name="settings.image_loader_concurrency_11">11</string>
<string name="settings.image_loader_concurrency_12">12</string>
<plurals name="select_album_n_songs">
<item quantity="zero">Aucun titre</item>

View File

@ -374,6 +374,19 @@
<string name="download.menu_show_artist">Ugrás az előadóhoz</string>
<string name="settings.scan_media">Scan Media After Download</string>
<string name="settings.scan_media_summary">Automatically scan media after download</string>
<string name="settings.image_loader_concurrency">Image Loader Concurrency</string>
<string name="settings.image_loader_concurrency_1">1</string>
<string name="settings.image_loader_concurrency_2">2</string>
<string name="settings.image_loader_concurrency_3">3</string>
<string name="settings.image_loader_concurrency_4">4</string>
<string name="settings.image_loader_concurrency_5">5</string>
<string name="settings.image_loader_concurrency_6">6</string>
<string name="settings.image_loader_concurrency_7">7</string>
<string name="settings.image_loader_concurrency_8">8</string>
<string name="settings.image_loader_concurrency_9">9</string>
<string name="settings.image_loader_concurrency_10">10</string>
<string name="settings.image_loader_concurrency_11">11</string>
<string name="settings.image_loader_concurrency_12">12</string>
<plurals name="select_album_n_songs">
<item quantity="zero">Nincsenek dalok</item>

View File

@ -263,5 +263,33 @@
<item>@string/settings.share_hours</item>
<item>@string/settings.share_days</item>
</string-array>
<string-array name="imageConcurrencyNames">
<item>@string/settings.image_loader_concurrency_1</item>
<item>@string/settings.image_loader_concurrency_2</item>
<item>@string/settings.image_loader_concurrency_3</item>
<item>@string/settings.image_loader_concurrency_4</item>
<item>@string/settings.image_loader_concurrency_5</item>
<item>@string/settings.image_loader_concurrency_6</item>
<item>@string/settings.image_loader_concurrency_7</item>
<item>@string/settings.image_loader_concurrency_8</item>
<item>@string/settings.image_loader_concurrency_9</item>
<item>@string/settings.image_loader_concurrency_10</item>
<item>@string/settings.image_loader_concurrency_11</item>
<item>@string/settings.image_loader_concurrency_12</item>
</string-array>
<string-array name="imageConcurrencyValues">
<item>1</item>
<item>2</item>
<item>3</item>
<item>4</item>
<item>5</item>
<item>6</item>
<item>7</item>
<item>8</item>
<item>9</item>
<item>10</item>
<item>11</item>
<item>12</item>
</string-array>
</resources>

View File

@ -374,6 +374,19 @@
<string name="download.menu_show_artist">Show Artist</string>
<string name="settings.scan_media">Scan Media After Download</string>
<string name="settings.scan_media_summary">Automatically scan media after download</string>
<string name="settings.image_loader_concurrency">Image Loader Concurrency</string>
<string name="settings.image_loader_concurrency_1">1</string>
<string name="settings.image_loader_concurrency_2">2</string>
<string name="settings.image_loader_concurrency_3">3</string>
<string name="settings.image_loader_concurrency_4">4</string>
<string name="settings.image_loader_concurrency_5">5</string>
<string name="settings.image_loader_concurrency_6">6</string>
<string name="settings.image_loader_concurrency_7">7</string>
<string name="settings.image_loader_concurrency_8">8</string>
<string name="settings.image_loader_concurrency_9">9</string>
<string name="settings.image_loader_concurrency_10">10</string>
<string name="settings.image_loader_concurrency_11">11</string>
<string name="settings.image_loader_concurrency_12">12</string>
<plurals name="select_album_n_songs">
<item quantity="zero">No songs</item>

View File

@ -49,6 +49,12 @@
a:entryValues="@array/viewRefreshValues"
a:key="viewRefresh"
a:title="@string/settings.view_refresh"/>
<ListPreference
a:defaultValue="5"
a:entries="@array/imageConcurrencyNames"
a:entryValues="@array/imageConcurrencyValues"
a:key="imageLoaderConcurrency"
a:title="@string/settings.image_loader_concurrency"/>
</PreferenceCategory>
<PreferenceCategory
a:title="@string/settings.playback_control_title"

View File

@ -75,6 +75,13 @@ public class MainActivity extends SubsonicTabActivity
if (getIntent().hasExtra(Constants.INTENT_EXTRA_NAME_EXIT))
{
setResult(Constants.RESULT_CLOSE_ALL);
getDownloadService().stopJukeboxService();
if (getImageLoader() != null)
{
getImageLoader().stopImageLoader();
}
finish();
exit();
return;
@ -471,7 +478,7 @@ public class MainActivity extends SubsonicTabActivity
if (show || Util.getRestUrl(this, null).contains("yourhost"))
{
Util.info(this, R.string.main_welcome_title, R.string.main_welcome_text);
Util.showWelcomeDialog(this, this, R.string.main_welcome_title, R.string.main_welcome_text);
}
}
}

View File

@ -23,12 +23,12 @@ public class ResultActivity extends Activity
super.onActivityResult(requestCode, resultCode, data);
}
protected void startActivityForResultWithoutTransition(Activity currentActivity, Class<? extends Activity> newActivity)
public void startActivityForResultWithoutTransition(Activity currentActivity, Class<? extends Activity> newActivity)
{
startActivityForResultWithoutTransition(currentActivity, new Intent(currentActivity, newActivity));
}
protected void startActivityForResultWithoutTransition(Activity currentActivity, Intent intent)
public void startActivityForResultWithoutTransition(Activity currentActivity, Intent intent)
{
startActivityForResult(intent, 0);
Util.disablePendingTransition(currentActivity);

View File

@ -88,6 +88,7 @@ public class SettingsActivity extends PreferenceResultActivity implements Shared
private CheckBoxPreference sendBluetoothNotifications;
private CheckBoxPreference sendBluetoothAlbumArt;
private ListPreference viewRefresh;
private ListPreference imageLoaderConcurrency;
private EditTextPreference sharingDefaultDescription;
private EditTextPreference sharingDefaultGreeting;
private TimeSpanPreference sharingDefaultExpiration;
@ -181,6 +182,7 @@ public class SettingsActivity extends PreferenceResultActivity implements Shared
sendBluetoothAlbumArt = (CheckBoxPreference) findPreference(Constants.PREFERENCES_KEY_SEND_BLUETOOTH_ALBUM_ART);
sendBluetoothNotifications = (CheckBoxPreference) findPreference(Constants.PREFERENCES_KEY_SEND_BLUETOOTH_NOTIFICATIONS);
viewRefresh = (ListPreference) findPreference(Constants.PREFERENCES_KEY_VIEW_REFRESH);
imageLoaderConcurrency = (ListPreference) findPreference(Constants.PREFERENCES_KEY_IMAGE_LOADER_CONCURRENCY);
sharingDefaultDescription = (EditTextPreference) findPreference(Constants.PREFERENCES_KEY_DEFAULT_SHARE_DESCRIPTION);
sharingDefaultGreeting = (EditTextPreference) findPreference(Constants.PREFERENCES_KEY_DEFAULT_SHARE_GREETING);
sharingDefaultExpiration = (TimeSpanPreference) findPreference(Constants.PREFERENCES_KEY_DEFAULT_SHARE_EXPIRATION);
@ -542,6 +544,7 @@ public class SettingsActivity extends PreferenceResultActivity implements Shared
chatRefreshInterval.setSummary(chatRefreshInterval.getEntry());
directoryCacheTime.setSummary(directoryCacheTime.getEntry());
viewRefresh.setSummary(viewRefresh.getEntry());
imageLoaderConcurrency.setSummary(imageLoaderConcurrency.getEntry());
sharingDefaultExpiration.setSummary(sharingDefaultExpiration.getText());
sharingDefaultDescription.setSummary(sharingDefaultDescription.getText());
sharingDefaultGreeting.setSummary(sharingDefaultGreeting.getText());

View File

@ -94,7 +94,7 @@ public class SubsonicTabActivity extends ResultActivity implements OnClickListen
{
private static final String TAG = SubsonicTabActivity.class.getSimpleName();
private static final Pattern COMPILE = Pattern.compile(":");
private static ImageLoader IMAGE_LOADER;
protected static ImageLoader IMAGE_LOADER;
protected static String theme;
private static SubsonicTabActivity instance;
@ -228,7 +228,7 @@ public class SubsonicTabActivity extends ResultActivity implements OnClickListen
super.onDestroy();
destroyed = true;
nowPlayingView = null;
getImageLoader().clear();
clearImageLoader();
}
@Override
@ -957,12 +957,19 @@ public class SubsonicTabActivity extends ResultActivity implements OnClickListen
}
}
public synchronized void clearImageLoader()
{
if (IMAGE_LOADER != null && IMAGE_LOADER.isRunning()) IMAGE_LOADER.clear();
}
public synchronized ImageLoader getImageLoader()
{
if (IMAGE_LOADER == null)
if (IMAGE_LOADER == null || !IMAGE_LOADER.isRunning())
{
IMAGE_LOADER = new ImageLoader(this);
IMAGE_LOADER = new ImageLoader(this, Util.getImageLoaderConcurrency(this));
IMAGE_LOADER.startImageLoader();
}
return IMAGE_LOADER;
}

View File

@ -140,4 +140,8 @@ public interface DownloadService
void swap(boolean mainList, int from, int to);
void restore(List<Entry> songs, int currentPlayingIndex, int currentPlayingPosition, boolean autoPlay, boolean newPlaylist);
void stopJukeboxService();
void startJukeboxService();
}

View File

@ -466,6 +466,18 @@ public class DownloadServiceImpl extends Service implements DownloadService
}
}
@Override
public void stopJukeboxService()
{
jukeboxService.stopJukeboxService();
}
@Override
public void startJukeboxService()
{
jukeboxService.startJukeboxService();
}
@Override
public synchronized void setShufflePlayEnabled(boolean enabled)
{
@ -1414,8 +1426,11 @@ public class DownloadServiceImpl extends Service implements DownloadService
{
this.jukeboxEnabled = jukeboxEnabled;
jukeboxService.setEnabled(jukeboxEnabled);
if (jukeboxEnabled)
{
jukeboxService.startJukeboxService();
reset();
// Cancel current download, if necessary.
@ -1424,6 +1439,10 @@ public class DownloadServiceImpl extends Service implements DownloadService
currentDownloading.cancelDownload();
}
}
else
{
jukeboxService.stopJukeboxService();
}
}
@Override
@ -1505,6 +1524,10 @@ public class DownloadServiceImpl extends Service implements DownloadService
audioManager.unregisterRemoteControlClient(remoteControlClient);
audioManager.registerRemoteControlClient(remoteControlClient);
}
else
{
setUpRemoteControlClient();
}
Log.i(TAG, String.format("In updateRemoteControl, playerState: %s [%d]", playerState, getPlayerPosition()));

View File

@ -64,6 +64,8 @@ public class JukeboxService
private JukeboxStatus jukeboxStatus;
private float gain = 0.5f;
private VolumeToast volumeToast;
private boolean running = false;
private Thread serviceThread;
// TODO: Report warning if queue fills up.
// TODO: Create shutdown method?
@ -74,19 +76,42 @@ public class JukeboxService
public JukeboxService(DownloadServiceImpl downloadService)
{
this.downloadService = downloadService;
new Thread()
}
public void startJukeboxService()
{
if (running) return;
running = true;
startProcessTasks();
}
public void stopJukeboxService()
{
running = false;
Util.sleepQuietly(1000);
if (serviceThread != null) serviceThread.interrupt();
}
private void startProcessTasks()
{
serviceThread = new Thread()
{
@Override
public void run()
{
processTasks();
}
}.start();
};
serviceThread.start();
}
private synchronized void startStatusUpdate()
{
stopStatusUpdate();
Runnable updateTask = new Runnable()
{
@Override
@ -96,6 +121,7 @@ public class JukeboxService
tasks.add(new GetStatus());
}
};
statusUpdateFuture = executorService.scheduleWithFixedDelay(updateTask, STATUS_UPDATE_INTERVAL_SECONDS, STATUS_UPDATE_INTERVAL_SECONDS, TimeUnit.SECONDS);
}
@ -110,9 +136,10 @@ public class JukeboxService
private void processTasks()
{
while (true)
while (running)
{
JukeboxTask task = null;
try
{
if (!Util.isOffline(downloadService))
@ -121,11 +148,17 @@ public class JukeboxService
JukeboxStatus status = task.execute();
onStatusUpdate(status);
}
}
catch (InterruptedException ignored)
{
}
catch (Throwable x)
{
onError(task, x);
}
Util.sleepQuietly(1);
}
}
@ -136,6 +169,7 @@ public class JukeboxService
// Track change?
Integer index = jukeboxStatus.getCurrentPlayingIndex();
if (index != null && index != -1 && index != downloadService.getCurrentPlayingIndex())
{
downloadService.setCurrentPlaying(index);
@ -165,6 +199,7 @@ public class JukeboxService
private void disableJukeboxOnError(Throwable x, final int resourceId)
{
Log.w(TAG, x.toString());
handler.post(new Runnable()
{
@Override
@ -173,6 +208,7 @@ public class JukeboxService
Util.toast(downloadService, resourceId, false);
}
});
downloadService.setJukeboxEnabled(false);
}
@ -187,6 +223,7 @@ public class JukeboxService
{
ids.add(file.getSong().getId());
}
tasks.add(new SetPlaylist(ids));
}
@ -213,6 +250,7 @@ public class JukeboxService
tasks.remove(Start.class);
stopStatusUpdate();
tasks.add(new Stop());
}
@ -266,17 +304,19 @@ public class JukeboxService
public void setEnabled(boolean enabled)
{
tasks.clear();
if (enabled)
{
updatePlaylist();
}
stop();
downloadService.setPlayerState(PlayerState.IDLE);
}
private static class TaskQueue
{
private final LinkedBlockingQueue<JukeboxTask> queue = new LinkedBlockingQueue<JukeboxTask>();
void add(JukeboxTask jukeboxTask)
@ -289,15 +329,17 @@ public class JukeboxService
return queue.take();
}
void remove(Class<? extends JukeboxTask> clazz)
void remove(Class<? extends JukeboxTask> taskClass)
{
try
{
Iterator<JukeboxTask> iterator = queue.iterator();
while (iterator.hasNext())
{
JukeboxTask task = iterator.next();
if (clazz.equals(task.getClass()))
if (taskClass.equals(task.getClass()))
{
iterator.remove();
}
@ -317,7 +359,6 @@ public class JukeboxService
private abstract class JukeboxTask
{
abstract JukeboxStatus execute() throws Exception;
@Override
@ -338,7 +379,6 @@ public class JukeboxService
private class SetPlaylist extends JukeboxTask
{
private final List<String> ids;
SetPlaylist(List<String> ids)

View File

@ -128,6 +128,7 @@ public final class Constants
public static final String PREFERENCES_KEY_DEFAULT_SHARE_EXPIRATION = "sharingDefaultExpiration";
public static final String PREFERENCES_KEY_SHOW_ALL_SONGS_BY_ARTIST = "showAllSongsByArtist";
public static final String PREFERENCES_KEY_SCAN_MEDIA = "scanMedia";
public static final String PREFERENCES_KEY_IMAGE_LOADER_CONCURRENCY = "imageLoaderConcurrency";
// Name of the preferences file.
public static final String PREFERENCES_FILE_NAME = "com.thejoshwa.ultrasonic.androidapp_preferences";

View File

@ -36,6 +36,8 @@ import com.thejoshwa.ultrasonic.androidapp.domain.MusicDirectory;
import com.thejoshwa.ultrasonic.androidapp.service.MusicService;
import com.thejoshwa.ultrasonic.androidapp.service.MusicServiceFactory;
import java.util.ArrayList;
import java.util.Collection;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
@ -50,7 +52,6 @@ public class ImageLoader implements Runnable
{
private static final String TAG = ImageLoader.class.getSimpleName();
private static final int CONCURRENCY = 5;
private final LRUCache<String, Bitmap> cache = new LRUCache<String, Bitmap>(150);
private final BlockingQueue<Task> queue;
@ -58,10 +59,14 @@ public class ImageLoader implements Runnable
private final int imageSizeLarge;
private Bitmap largeUnknownImage;
private Context context;
private Collection<Thread> threads = new ArrayList<Thread>();
private boolean running = false;
private int concurrency;
public ImageLoader(Context context)
public ImageLoader(Context context, int concurrency)
{
this.context = context;
this.concurrency = concurrency;
queue = new LinkedBlockingQueue<Task>(1000);
Resources resources = context.getResources();
@ -76,12 +81,36 @@ public class ImageLoader implements Runnable
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
imageSizeLarge = Math.round(Math.min(metrics.widthPixels, metrics.heightPixels));
for (int i = 0; i < CONCURRENCY; i++)
createLargeUnknownImage(context);
}
public synchronized boolean isRunning()
{
return running && !threads.isEmpty();
}
public void startImageLoader()
{
running = true;
for (int i = 0; i < this.concurrency; i++)
{
new Thread(this, "ImageLoader").start();
Thread thread = new Thread(this, "ImageLoader");
threads.add(thread);
thread.start();
}
}
public synchronized void stopImageLoader()
{
clear();
for (Thread thread : threads)
{
thread.interrupt();
}
createLargeUnknownImage(context);
running = false;
}
private void createLargeUnknownImage(Context context)
@ -131,7 +160,7 @@ public class ImageLoader implements Runnable
private static String getKey(String coverArtId, int size)
{
return coverArtId + ":" + size;
return String.format("%s:%d", coverArtId, size);
}
public Bitmap getImageBitmap(MusicDirectory.Entry entry, boolean large, int size)
@ -226,12 +255,16 @@ public class ImageLoader implements Runnable
@Override
public void run()
{
while (true)
while (running)
{
try
{
Task task = queue.take();
task.execute();
}
catch (InterruptedException ignored)
{
}
catch (Throwable x)
{

View File

@ -52,6 +52,7 @@ import android.widget.Toast;
import com.thejoshwa.ultrasonic.androidapp.R;
import com.thejoshwa.ultrasonic.androidapp.activity.DownloadActivity;
import com.thejoshwa.ultrasonic.androidapp.activity.MainActivity;
import com.thejoshwa.ultrasonic.androidapp.activity.SettingsActivity;
import com.thejoshwa.ultrasonic.androidapp.domain.Bookmark;
import com.thejoshwa.ultrasonic.androidapp.domain.MusicDirectory;
import com.thejoshwa.ultrasonic.androidapp.domain.MusicDirectory.Entry;
@ -127,7 +128,7 @@ public class Util extends DownloadActivity
public static boolean isOffline(Context context)
{
return context != null && getActiveServer(context) == 0;
return context == null || getActiveServer(context) == 0;
}
public static boolean isScreenLitOnDownload(Context context)
@ -257,6 +258,7 @@ public class Util extends DownloadActivity
{
return false;
}
SharedPreferences preferences = getPreferences(context);
return preferences.getBoolean(Constants.PREFERENCES_KEY_JUKEBOX_BY_DEFAULT + instance, false);
}
@ -806,6 +808,19 @@ public class Util extends DownloadActivity
showDialog(context, android.R.drawable.ic_dialog_info, titleId, messageId);
}
public static void showWelcomeDialog(final Context context, final MainActivity activity, int titleId, int messageId)
{
new AlertDialog.Builder(context).setIcon(android.R.drawable.ic_dialog_info).setTitle(titleId).setMessage(messageId).setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int i)
{
dialog.dismiss();
activity.startActivityForResultWithoutTransition(activity, SettingsActivity.class);
}
}).show();
}
private static void showDialog(Context context, int icon, int titleId, int messageId)
{
new AlertDialog.Builder(context).setIcon(icon).setTitle(titleId).setMessage(messageId).setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener()
@ -1631,4 +1646,10 @@ public class Util extends DownloadActivity
Intent scanFileIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri);
context.sendBroadcast(scanFileIntent);
}
public static int getImageLoaderConcurrency(Context context)
{
SharedPreferences preferences = getPreferences(context);
return Integer.parseInt(preferences.getString(Constants.PREFERENCES_KEY_IMAGE_LOADER_CONCURRENCY, "5"));
}
}