aboutsummaryrefslogtreecommitdiff
path: root/subsonic-android/src/net/sourceforge/subsonic/androidapp/util
diff options
context:
space:
mode:
authorScott Jackson <daneren2005@gmail.com>2012-07-07 08:29:52 -0700
committerScott Jackson <daneren2005@gmail.com>2012-07-07 08:29:52 -0700
commit6ebae86dfbb7fa79d81e6b2485f395eeab7267ef (patch)
treebc26b39df3c6a666bcac960042f2ac8cb06ad202 /subsonic-android/src/net/sourceforge/subsonic/androidapp/util
parent8a7bb33f73d4fab1e380adf972efc2f3a7ee8b3e (diff)
downloaddsub-6ebae86dfbb7fa79d81e6b2485f395eeab7267ef.tar.gz
dsub-6ebae86dfbb7fa79d81e6b2485f395eeab7267ef.tar.bz2
dsub-6ebae86dfbb7fa79d81e6b2485f395eeab7267ef.zip
Changed project package to github.daneren2005.subphonic
Diffstat (limited to 'subsonic-android/src/net/sourceforge/subsonic/androidapp/util')
-rw-r--r--subsonic-android/src/net/sourceforge/subsonic/androidapp/util/AlbumView.java55
-rw-r--r--subsonic-android/src/net/sourceforge/subsonic/androidapp/util/ArtistAdapter.java78
-rw-r--r--subsonic-android/src/net/sourceforge/subsonic/androidapp/util/BackgroundTask.java96
-rw-r--r--subsonic-android/src/net/sourceforge/subsonic/androidapp/util/CacheCleaner.java171
-rw-r--r--subsonic-android/src/net/sourceforge/subsonic/androidapp/util/CancellableTask.java87
-rw-r--r--subsonic-android/src/net/sourceforge/subsonic/androidapp/util/Constants.java91
-rw-r--r--subsonic-android/src/net/sourceforge/subsonic/androidapp/util/EntryAdapter.java71
-rw-r--r--subsonic-android/src/net/sourceforge/subsonic/androidapp/util/ErrorDialog.java61
-rw-r--r--subsonic-android/src/net/sourceforge/subsonic/androidapp/util/FileUtil.java311
-rw-r--r--subsonic-android/src/net/sourceforge/subsonic/androidapp/util/HorizontalSlider.java141
-rw-r--r--subsonic-android/src/net/sourceforge/subsonic/androidapp/util/ImageLoader.java252
-rw-r--r--subsonic-android/src/net/sourceforge/subsonic/androidapp/util/LRUCache.java102
-rw-r--r--subsonic-android/src/net/sourceforge/subsonic/androidapp/util/MergeAdapter.java290
-rw-r--r--subsonic-android/src/net/sourceforge/subsonic/androidapp/util/ModalBackgroundTask.java139
-rw-r--r--subsonic-android/src/net/sourceforge/subsonic/androidapp/util/MyViewFlipper.java53
-rw-r--r--subsonic-android/src/net/sourceforge/subsonic/androidapp/util/Pair.java54
-rw-r--r--subsonic-android/src/net/sourceforge/subsonic/androidapp/util/PlaylistAdapter.java99
-rw-r--r--subsonic-android/src/net/sourceforge/subsonic/androidapp/util/ProgressListener.java27
-rw-r--r--subsonic-android/src/net/sourceforge/subsonic/androidapp/util/SackOfViewsAdapter.java181
-rw-r--r--subsonic-android/src/net/sourceforge/subsonic/androidapp/util/ShufflePlayBuffer.java109
-rw-r--r--subsonic-android/src/net/sourceforge/subsonic/androidapp/util/SilentBackgroundTask.java67
-rw-r--r--subsonic-android/src/net/sourceforge/subsonic/androidapp/util/SimpleServiceBinder.java37
-rw-r--r--subsonic-android/src/net/sourceforge/subsonic/androidapp/util/SongView.java178
-rw-r--r--subsonic-android/src/net/sourceforge/subsonic/androidapp/util/TabActivityBackgroundTask.java67
-rw-r--r--subsonic-android/src/net/sourceforge/subsonic/androidapp/util/TimeLimitedCache.java55
-rw-r--r--subsonic-android/src/net/sourceforge/subsonic/androidapp/util/Util.java829
26 files changed, 0 insertions, 3701 deletions
diff --git a/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/AlbumView.java b/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/AlbumView.java
deleted file mode 100644
index a4dd3acd..00000000
--- a/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/AlbumView.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- 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 net.sourceforge.subsonic.androidapp.util;
-
-import android.content.Context;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-import net.sourceforge.subsonic.androidapp.R;
-import net.sourceforge.subsonic.androidapp.domain.MusicDirectory;
-
-/**
- * Used to display albums in a {@code ListView}.
- *
- * @author Sindre Mehus
- */
-public class AlbumView extends LinearLayout {
-
- private TextView titleView;
- private TextView artistView;
- private View coverArtView;
-
- public AlbumView(Context context) {
- super(context);
- LayoutInflater.from(context).inflate(R.layout.album_list_item, this, true);
-
- titleView = (TextView) findViewById(R.id.album_title);
- artistView = (TextView) findViewById(R.id.album_artist);
- coverArtView = findViewById(R.id.album_coverart);
- }
-
- public void setAlbum(MusicDirectory.Entry album, ImageLoader imageLoader) {
- titleView.setText(album.getTitle());
- artistView.setText(album.getArtist());
- artistView.setVisibility(album.getArtist() == null ? View.GONE : View.VISIBLE);
- imageLoader.loadImage(coverArtView, album, false, true);
- }
-}
diff --git a/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/ArtistAdapter.java b/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/ArtistAdapter.java
deleted file mode 100644
index 98ed3c9b..00000000
--- a/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/ArtistAdapter.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- 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 2010 (C) Sindre Mehus
- */
-package net.sourceforge.subsonic.androidapp.util;
-
-import net.sourceforge.subsonic.androidapp.domain.Artist;
-import net.sourceforge.subsonic.androidapp.R;
-import android.widget.ArrayAdapter;
-import android.widget.SectionIndexer;
-import android.content.Context;
-
-import java.util.List;
-import java.util.Set;
-import java.util.LinkedHashSet;
-import java.util.ArrayList;
-
-/**
- * @author Sindre Mehus
-*/
-public class ArtistAdapter extends ArrayAdapter<Artist> implements SectionIndexer {
-
- // Both arrays are indexed by section ID.
- private final Object[] sections;
- private final Integer[] positions;
-
- public ArtistAdapter(Context context, List<Artist> artists) {
- super(context, R.layout.artist_list_item, artists);
-
- Set<String> sectionSet = new LinkedHashSet<String>(30);
- List<Integer> positionList = new ArrayList<Integer>(30);
- for (int i = 0; i < artists.size(); i++) {
- Artist artist = artists.get(i);
- String index = artist.getIndex();
- if (!sectionSet.contains(index)) {
- sectionSet.add(index);
- positionList.add(i);
- }
- }
- sections = sectionSet.toArray(new Object[sectionSet.size()]);
- positions = positionList.toArray(new Integer[positionList.size()]);
- }
-
- @Override
- public Object[] getSections() {
- return sections;
- }
-
- @Override
- public int getPositionForSection(int section) {
- section = Math.min(section, positions.length - 1);
- return positions[section];
- }
-
- @Override
- public int getSectionForPosition(int pos) {
- for (int i = 0; i < sections.length - 1; i++) {
- if (pos < positions[i + 1]) {
- return i;
- }
- }
- return sections.length - 1;
- }
-}
diff --git a/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/BackgroundTask.java b/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/BackgroundTask.java
deleted file mode 100644
index 1db2fdc1..00000000
--- a/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/BackgroundTask.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- 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 net.sourceforge.subsonic.androidapp.util;
-
-import java.io.FileNotFoundException;
-import java.io.IOException;
-
-import org.xmlpull.v1.XmlPullParserException;
-
-import android.app.Activity;
-import android.os.Handler;
-import android.util.Log;
-import net.sourceforge.subsonic.androidapp.R;
-
-/**
- * @author Sindre Mehus
- */
-public abstract class BackgroundTask<T> implements ProgressListener {
-
- private static final String TAG = BackgroundTask.class.getSimpleName();
- private final Activity activity;
- private final Handler handler;
-
- public BackgroundTask(Activity activity) {
- this.activity = activity;
- handler = new Handler();
- }
-
- protected Activity getActivity() {
- return activity;
- }
-
- protected Handler getHandler() {
- return handler;
- }
-
- public abstract void execute();
-
- protected abstract T doInBackground() throws Throwable;
-
- protected abstract void done(T result);
-
- protected void error(Throwable error) {
- Log.w(TAG, "Got exception: " + error, error);
- new ErrorDialog(activity, getErrorMessage(error), true);
- }
-
- protected String getErrorMessage(Throwable error) {
-
- if (error instanceof IOException && !Util.isNetworkConnected(activity)) {
- return activity.getResources().getString(R.string.background_task_no_network);
- }
-
- if (error instanceof FileNotFoundException) {
- return activity.getResources().getString(R.string.background_task_not_found);
- }
-
- if (error instanceof IOException) {
- return activity.getResources().getString(R.string.background_task_network_error);
- }
-
- if (error instanceof XmlPullParserException) {
- return activity.getResources().getString(R.string.background_task_parse_error);
- }
-
- String message = error.getMessage();
- if (message != null) {
- return message;
- }
- return error.getClass().getSimpleName();
- }
-
- @Override
- public abstract void updateProgress(final String message);
-
- @Override
- public void updateProgress(int messageId) {
- updateProgress(activity.getResources().getString(messageId));
- }
-} \ No newline at end of file
diff --git a/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/CacheCleaner.java b/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/CacheCleaner.java
deleted file mode 100644
index 46459571..00000000
--- a/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/CacheCleaner.java
+++ /dev/null
@@ -1,171 +0,0 @@
-package net.sourceforge.subsonic.androidapp.util;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-import android.content.Context;
-import android.util.Log;
-import android.os.StatFs;
-import net.sourceforge.subsonic.androidapp.service.DownloadFile;
-import net.sourceforge.subsonic.androidapp.service.DownloadService;
-
-/**
- * @author Sindre Mehus
- * @version $Id$
- */
-public class CacheCleaner {
-
- private static final String TAG = CacheCleaner.class.getSimpleName();
- private static final double MAX_FILE_SYSTEM_USAGE = 0.95;
-
- private final Context context;
- private final DownloadService downloadService;
-
- public CacheCleaner(Context context, DownloadService downloadService) {
- this.context = context;
- this.downloadService = downloadService;
- }
-
- public void clean() {
-
- Log.i(TAG, "Starting cache cleaning.");
-
- if (downloadService == null) {
- Log.e(TAG, "DownloadService not set. Aborting cache cleaning.");
- return;
- }
-
- try {
-
- List<File> files = new ArrayList<File>();
- List<File> dirs = new ArrayList<File>();
-
- findCandidatesForDeletion(FileUtil.getMusicDirectory(context), files, dirs);
- sortByAscendingModificationTime(files);
-
- Set<File> undeletable = findUndeletableFiles();
-
- deleteFiles(files, undeletable);
- deleteEmptyDirs(dirs, undeletable);
- Log.i(TAG, "Completed cache cleaning.");
-
- } catch (RuntimeException x) {
- Log.e(TAG, "Error in cache cleaning.", x);
- }
- }
-
- private void deleteEmptyDirs(List<File> dirs, Set<File> undeletable) {
- for (File dir : dirs) {
- if (undeletable.contains(dir)) {
- continue;
- }
-
- File[] children = dir.listFiles();
-
- // Delete empty directory and associated album artwork.
- if (children.length == 0) {
- Util.delete(dir);
- Util.delete(FileUtil.getAlbumArtFile(dir));
- }
- }
- }
-
- private void deleteFiles(List<File> files, Set<File> undeletable) {
-
- if (files.isEmpty()) {
- return;
- }
-
- long cacheSizeBytes = Util.getCacheSizeMB(context) * 1024L * 1024L;
-
- long bytesUsedBySubsonic = 0L;
- for (File file : files) {
- bytesUsedBySubsonic += file.length();
- }
-
- // Ensure that file system is not more than 95% full.
- StatFs stat = new StatFs(files.get(0).getPath());
- long bytesTotalFs = (long) stat.getBlockCount() * (long) stat.getBlockSize();
- long bytesAvailableFs = (long) stat.getAvailableBlocks() * (long) stat.getBlockSize();
- long bytesUsedFs = bytesTotalFs - bytesAvailableFs;
- long minFsAvailability = Math.round(MAX_FILE_SYSTEM_USAGE * (double) bytesTotalFs);
-
- long bytesToDeleteCacheLimit = Math.max(bytesUsedBySubsonic - cacheSizeBytes, 0L);
- long bytesToDeleteFsLimit = Math.max(bytesUsedFs - minFsAvailability, 0L);
- long bytesToDelete = Math.max(bytesToDeleteCacheLimit, bytesToDeleteFsLimit);
-
- Log.i(TAG, "File system : " + Util.formatBytes(bytesAvailableFs) + " of " + Util.formatBytes(bytesTotalFs) + " available");
- Log.i(TAG, "Cache limit : " + Util.formatBytes(cacheSizeBytes));
- Log.i(TAG, "Cache size before : " + Util.formatBytes(bytesUsedBySubsonic));
- Log.i(TAG, "Minimum to delete : " + Util.formatBytes(bytesToDelete));
-
- long bytesDeleted = 0L;
- for (File file : files) {
-
- if (file.getName().equals(Constants.ALBUM_ART_FILE)) {
- // Move artwork to new folder.
- file.renameTo(FileUtil.getAlbumArtFile(file.getParentFile()));
-
- } else if (bytesToDelete > bytesDeleted || file.getName().endsWith(".partial") || file.getName().contains(".partial.")) {
- if (!undeletable.contains(file)) {
- long size = file.length();
- if (Util.delete(file)) {
- bytesDeleted += size;
- }
- }
- }
- }
-
- Log.i(TAG, "Deleted : " + Util.formatBytes(bytesDeleted));
- Log.i(TAG, "Cache size after : " + Util.formatBytes(bytesUsedBySubsonic - bytesDeleted));
- }
-
- private void findCandidatesForDeletion(File file, List<File> files, List<File> dirs) {
- if (file.isFile()) {
- String name = file.getName();
- boolean isCacheFile = name.endsWith(".partial") || name.contains(".partial.") || name.endsWith(".complete") || name.contains(".complete.");
- boolean isAlbumArtFile = name.equals(Constants.ALBUM_ART_FILE);
- if (isCacheFile || isAlbumArtFile) {
- files.add(file);
- }
- } else {
- // Depth-first
- for (File child : FileUtil.listFiles(file)) {
- findCandidatesForDeletion(child, files, dirs);
- }
- dirs.add(file);
- }
- }
-
- private void sortByAscendingModificationTime(List<File> files) {
- Collections.sort(files, new Comparator<File>() {
- @Override
- public int compare(File a, File b) {
- if (a.lastModified() < b.lastModified()) {
- return -1;
- }
- if (a.lastModified() > b.lastModified()) {
- return 1;
- }
- return 0;
- }
- });
- }
-
- private Set<File> findUndeletableFiles() {
- Set<File> undeletable = new HashSet<File>(5);
-
- for (DownloadFile downloadFile : downloadService.getDownloads()) {
- undeletable.add(downloadFile.getPartialFile());
- undeletable.add(downloadFile.getCompleteFile());
- }
-
- undeletable.add(FileUtil.getMusicDirectory(context));
- return undeletable;
- }
-}
diff --git a/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/CancellableTask.java b/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/CancellableTask.java
deleted file mode 100644
index 9c8b06e1..00000000
--- a/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/CancellableTask.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- 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 net.sourceforge.subsonic.androidapp.util;
-
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicReference;
-
-import android.util.Log;
-
-/**
- * @author Sindre Mehus
- * @version $Id$
- */
-public abstract class CancellableTask {
-
- private static final String TAG = CancellableTask.class.getSimpleName();
-
- private final AtomicBoolean running = new AtomicBoolean(false);
- private final AtomicBoolean cancelled = new AtomicBoolean(false);
- private final AtomicReference<Thread> thread = new AtomicReference<Thread>();
- private final AtomicReference<OnCancelListener> cancelListener = new AtomicReference<OnCancelListener>();
-
- public void cancel() {
- Log.d(TAG, "Cancelling " + CancellableTask.this);
- cancelled.set(true);
-
- OnCancelListener listener = cancelListener.get();
- if (listener != null) {
- try {
- listener.onCancel();
- } catch (Throwable x) {
- Log.w(TAG, "Error when invoking OnCancelListener.", x);
- }
- }
- }
-
- public boolean isCancelled() {
- return cancelled.get();
- }
-
- public void setOnCancelListener(OnCancelListener listener) {
- cancelListener.set(listener);
- }
-
- public boolean isRunning() {
- return running.get();
- }
-
- public abstract void execute();
-
- public void start() {
- thread.set(new Thread() {
- @Override
- public void run() {
- running.set(true);
- Log.d(TAG, "Starting thread for " + CancellableTask.this);
- try {
- execute();
- } finally {
- running.set(false);
- Log.d(TAG, "Stopping thread for " + CancellableTask.this);
- }
- }
- });
- thread.get().start();
- }
-
- public static interface OnCancelListener {
- void onCancel();
- }
-}
diff --git a/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/Constants.java b/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/Constants.java
deleted file mode 100644
index bebe49ce..00000000
--- a/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/Constants.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- 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 net.sourceforge.subsonic.androidapp.util;
-
-/**
- * @author Sindre Mehus
- * @version $Id$
- */
-public final class Constants {
-
- // Character encoding used throughout.
- public static final String UTF_8 = "UTF-8";
-
- // REST protocol version and client ID.
- // Note: Keep it as low as possible to maintain compatibility with older servers.
- public static final String REST_PROTOCOL_VERSION = "1.2.0";
- public static final String REST_CLIENT_ID = "android";
-
- // Names for intent extras.
- public static final String INTENT_EXTRA_NAME_ID = "subsonic.id";
- public static final String INTENT_EXTRA_NAME_NAME = "subsonic.name";
- public static final String INTENT_EXTRA_NAME_ARTIST = "subsonic.artist";
- public static final String INTENT_EXTRA_NAME_TITLE = "subsonic.title";
- public static final String INTENT_EXTRA_NAME_AUTOPLAY = "subsonic.playall";
- public static final String INTENT_EXTRA_NAME_ERROR = "subsonic.error";
- public static final String INTENT_EXTRA_NAME_QUERY = "subsonic.query";
- public static final String INTENT_EXTRA_NAME_PLAYLIST_ID = "subsonic.playlist.id";
- public static final String INTENT_EXTRA_NAME_PLAYLIST_NAME = "subsonic.playlist.name";
- public static final String INTENT_EXTRA_NAME_ALBUM_LIST_TYPE = "subsonic.albumlisttype";
- public static final String INTENT_EXTRA_NAME_ALBUM_LIST_SIZE = "subsonic.albumlistsize";
- public static final String INTENT_EXTRA_NAME_ALBUM_LIST_OFFSET = "subsonic.albumlistoffset";
- public static final String INTENT_EXTRA_NAME_SHUFFLE = "subsonic.shuffle";
- public static final String INTENT_EXTRA_NAME_REFRESH = "subsonic.refresh";
- public static final String INTENT_EXTRA_REQUEST_SEARCH = "subsonic.requestsearch";
- public static final String INTENT_EXTRA_NAME_EXIT = "subsonic.exit" ;
-
- // Notification IDs.
- public static final int NOTIFICATION_ID_PLAYING = 100;
- public static final int NOTIFICATION_ID_ERROR = 101;
-
- // Preferences keys.
- public static final String PREFERENCES_KEY_SERVER_INSTANCE = "serverInstanceId";
- public static final String PREFERENCES_KEY_SERVER_NAME = "serverName";
- public static final String PREFERENCES_KEY_SERVER_URL = "serverUrl";
- public static final String PREFERENCES_KEY_MUSIC_FOLDER_ID = "musicFolderId";
- public static final String PREFERENCES_KEY_USERNAME = "username";
- public static final String PREFERENCES_KEY_PASSWORD = "password";
- public static final String PREFERENCES_KEY_INSTALL_TIME = "installTime";
- public static final String PREFERENCES_KEY_THEME = "theme";
- public static final String PREFERENCES_KEY_MAX_BITRATE_WIFI = "maxBitrateWifi";
- public static final String PREFERENCES_KEY_MAX_BITRATE_MOBILE = "maxBitrateMobile";
- public static final String PREFERENCES_KEY_CACHE_SIZE = "cacheSize";
- public static final String PREFERENCES_KEY_CACHE_LOCATION = "cacheLocation";
- public static final String PREFERENCES_KEY_PRELOAD_COUNT = "preloadCount";
- public static final String PREFERENCES_KEY_HIDE_MEDIA = "hideMedia";
- public static final String PREFERENCES_KEY_MEDIA_BUTTONS = "mediaButtons";
- public static final String PREFERENCES_KEY_SCREEN_LIT_ON_DOWNLOAD = "screenLitOnDownload";
- public static final String PREFERENCES_KEY_SCROBBLE = "scrobble";
- public static final String PREFERENCES_KEY_REPEAT_MODE = "repeatMode";
- public static final String PREFERENCES_KEY_WIFI_REQUIRED_FOR_DOWNLOAD = "wifiRequiredForDownload";
-
- // Name of the preferences file.
- public static final String PREFERENCES_FILE_NAME = "net.sourceforge.subsonic.androidapp_preferences";
-
- // Number of free trial days for non-licensed servers.
- public static final int FREE_TRIAL_DAYS = 30;
-
- // URL for project donations.
- public static final String DONATION_URL = "http://subsonic.org/pages/android-donation.jsp";
-
- public static final String ALBUM_ART_FILE = "folder.jpeg";
-
- private Constants() {
- }
-}
diff --git a/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/EntryAdapter.java b/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/EntryAdapter.java
deleted file mode 100644
index 1b4d72cf..00000000
--- a/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/EntryAdapter.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- 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 2010 (C) Sindre Mehus
- */
-package net.sourceforge.subsonic.androidapp.util;
-
-import java.util.List;
-
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-import net.sourceforge.subsonic.androidapp.activity.SubsonicTabActivity;
-import net.sourceforge.subsonic.androidapp.domain.MusicDirectory;
-
-/**
- * @author Sindre Mehus
- */
-public class EntryAdapter extends ArrayAdapter<MusicDirectory.Entry> {
-
- private final SubsonicTabActivity activity;
- private final ImageLoader imageLoader;
- private final boolean checkable;
-
- public EntryAdapter(SubsonicTabActivity activity, ImageLoader imageLoader, List<MusicDirectory.Entry> entries, boolean checkable) {
- super(activity, android.R.layout.simple_list_item_1, entries);
- this.activity = activity;
- this.imageLoader = imageLoader;
- this.checkable = checkable;
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- MusicDirectory.Entry entry = getItem(position);
-
- if (entry.isDirectory()) {
- AlbumView view;
- // TODO: Reuse AlbumView objects once cover art loading is working.
-// if (convertView != null && convertView instanceof AlbumView) {
-// view = (AlbumView) convertView;
-// } else {
- view = new AlbumView(activity);
-// }
- view.setAlbum(entry, imageLoader);
- return view;
-
- } else {
- SongView view;
- if (convertView != null && convertView instanceof SongView) {
- view = (SongView) convertView;
- } else {
- view = new SongView(activity);
- }
- view.setSong(entry, checkable);
- return view;
- }
- }
-}
diff --git a/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/ErrorDialog.java b/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/ErrorDialog.java
deleted file mode 100644
index b1c51573..00000000
--- a/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/ErrorDialog.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- 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 net.sourceforge.subsonic.androidapp.util;
-
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.content.DialogInterface;
-import net.sourceforge.subsonic.androidapp.R;
-
-/**
- * @author Sindre Mehus
- */
-public class ErrorDialog {
-
- public ErrorDialog(Activity activity, int messageId, boolean finishActivityOnCancel) {
- this(activity, activity.getResources().getString(messageId), finishActivityOnCancel);
- }
-
- public ErrorDialog(final Activity activity, String message, final boolean finishActivityOnClose) {
-
- AlertDialog.Builder builder = new AlertDialog.Builder(activity);
- builder.setIcon(android.R.drawable.ic_dialog_alert);
- builder.setTitle(R.string.error_label);
- builder.setMessage(message);
- builder.setCancelable(true);
- builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
- @Override
- public void onCancel(DialogInterface dialogInterface) {
- if (finishActivityOnClose) {
- activity.finish();
- }
- }
- });
- builder.setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialogInterface, int i) {
- if (finishActivityOnClose) {
- activity.finish();
- }
- }
- });
-
- builder.create().show();
- }
-}
diff --git a/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/FileUtil.java b/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/FileUtil.java
deleted file mode 100644
index 88d10d3e..00000000
--- a/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/FileUtil.java
+++ /dev/null
@@ -1,311 +0,0 @@
-/*
- 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 net.sourceforge.subsonic.androidapp.util;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.io.Serializable;
-import java.util.Arrays;
-import java.util.SortedSet;
-import java.util.TreeSet;
-import java.util.Iterator;
-import java.util.List;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.os.Environment;
-import android.util.Log;
-import net.sourceforge.subsonic.androidapp.domain.MusicDirectory;
-
-/**
- * @author Sindre Mehus
- */
-public class FileUtil {
-
- private static final String TAG = FileUtil.class.getSimpleName();
- private static final String[] FILE_SYSTEM_UNSAFE = {"/", "\\", "..", ":", "\"", "?", "*", "<", ">"};
- private static final String[] FILE_SYSTEM_UNSAFE_DIR = {"\\", "..", ":", "\"", "?", "*", "<", ">"};
- private static final List<String> MUSIC_FILE_EXTENSIONS = Arrays.asList("mp3", "ogg", "aac", "flac", "m4a", "wav", "wma");
- private static final File DEFAULT_MUSIC_DIR = createDirectory("music");
-
- public static File getSongFile(Context context, MusicDirectory.Entry song) {
- File dir = getAlbumDirectory(context, song);
-
- StringBuilder fileName = new StringBuilder();
- Integer track = song.getTrack();
- if (track != null) {
- if (track < 10) {
- fileName.append("0");
- }
- fileName.append(track).append("-");
- }
-
- fileName.append(fileSystemSafe(song.getTitle())).append(".");
-
- if (song.getTranscodedSuffix() != null) {
- fileName.append(song.getTranscodedSuffix());
- } else {
- fileName.append(song.getSuffix());
- }
-
- return new File(dir, fileName.toString());
- }
-
- public static File getPlaylistFile(String id) {
- File playlistDir = getPlaylistDirectory();
- return new File(playlistDir, id);
- }
- public static File getPlaylistDirectory() {
- File playlistDir = new File(getSubsonicDirectory(), "playlists");
- ensureDirectoryExistsAndIsReadWritable(playlistDir);
- return playlistDir;
- }
-
- public static File getAlbumArtFile(Context context, MusicDirectory.Entry entry) {
- File albumDir = getAlbumDirectory(context, entry);
- return getAlbumArtFile(albumDir);
- }
-
- public static File getAlbumArtFile(File albumDir) {
- File albumArtDir = getAlbumArtDirectory();
- return new File(albumArtDir, Util.md5Hex(albumDir.getPath()) + ".jpeg");
- }
-
- public static Bitmap getAlbumArtBitmap(Context context, MusicDirectory.Entry entry, int size) {
- File albumArtFile = getAlbumArtFile(context, entry);
- if (albumArtFile.exists()) {
- Bitmap bitmap = BitmapFactory.decodeFile(albumArtFile.getPath());
- return bitmap == null ? null : Bitmap.createScaledBitmap(bitmap, size, size, true);
- }
- return null;
- }
-
- public static File getAlbumArtDirectory() {
- File albumArtDir = new File(getSubsonicDirectory(), "artwork");
- ensureDirectoryExistsAndIsReadWritable(albumArtDir);
- ensureDirectoryExistsAndIsReadWritable(new File(albumArtDir, ".nomedia"));
- return albumArtDir;
- }
-
- private static File getAlbumDirectory(Context context, MusicDirectory.Entry entry) {
- File dir;
- if (entry.getPath() != null) {
- File f = new File(fileSystemSafeDir(entry.getPath()));
- dir = new File(getMusicDirectory(context).getPath() + "/" + (entry.isDirectory() ? f.getPath() : f.getParent()));
- } else {
- String artist = fileSystemSafe(entry.getArtist());
- String album = fileSystemSafe(entry.getAlbum());
- dir = new File(getMusicDirectory(context).getPath() + "/" + artist + "/" + album);
- }
- return dir;
- }
-
- public static void createDirectoryForParent(File file) {
- File dir = file.getParentFile();
- if (!dir.exists()) {
- if (!dir.mkdirs()) {
- Log.e(TAG, "Failed to create directory " + dir);
- }
- }
- }
-
- private static File createDirectory(String name) {
- File dir = new File(getSubsonicDirectory(), name);
- if (!dir.exists() && !dir.mkdirs()) {
- Log.e(TAG, "Failed to create " + name);
- }
- return dir;
- }
-
- public static File getSubsonicDirectory() {
- return new File(Environment.getExternalStorageDirectory(), "subsonic");
- }
-
- public static File getDefaultMusicDirectory() {
- return DEFAULT_MUSIC_DIR;
- }
-
- public static File getMusicDirectory(Context context) {
- String path = Util.getPreferences(context).getString(Constants.PREFERENCES_KEY_CACHE_LOCATION, DEFAULT_MUSIC_DIR.getPath());
- File dir = new File(path);
- return ensureDirectoryExistsAndIsReadWritable(dir) ? dir : getDefaultMusicDirectory();
- }
-
- public static boolean ensureDirectoryExistsAndIsReadWritable(File dir) {
- if (dir == null) {
- return false;
- }
-
- if (dir.exists()) {
- if (!dir.isDirectory()) {
- Log.w(TAG, dir + " exists but is not a directory.");
- return false;
- }
- } else {
- if (dir.mkdirs()) {
- Log.i(TAG, "Created directory " + dir);
- } else {
- Log.w(TAG, "Failed to create directory " + dir);
- return false;
- }
- }
-
- if (!dir.canRead()) {
- Log.w(TAG, "No read permission for directory " + dir);
- return false;
- }
-
- if (!dir.canWrite()) {
- Log.w(TAG, "No write permission for directory " + dir);
- return false;
- }
- return true;
- }
-
- /**
- * Makes a given filename safe by replacing special characters like slashes ("/" and "\")
- * with dashes ("-").
- *
- * @param filename The filename in question.
- * @return The filename with special characters replaced by hyphens.
- */
- private static String fileSystemSafe(String filename) {
- if (filename == null || filename.trim().length() == 0) {
- return "unnamed";
- }
-
- for (String s : FILE_SYSTEM_UNSAFE) {
- filename = filename.replace(s, "-");
- }
- return filename;
- }
-
- /**
- * Makes a given filename safe by replacing special characters like colons (":")
- * with dashes ("-").
- *
- * @param path The path of the directory in question.
- * @return The the directory name with special characters replaced by hyphens.
- */
- private static String fileSystemSafeDir(String path) {
- if (path == null || path.trim().length() == 0) {
- return "";
- }
-
- for (String s : FILE_SYSTEM_UNSAFE_DIR) {
- path = path.replace(s, "-");
- }
- return path;
- }
-
- /**
- * Similar to {@link File#listFiles()}, but returns a sorted set.
- * Never returns {@code null}, instead a warning is logged, and an empty set is returned.
- */
- public static SortedSet<File> listFiles(File dir) {
- File[] files = dir.listFiles();
- if (files == null) {
- Log.w(TAG, "Failed to list children for " + dir.getPath());
- return new TreeSet<File>();
- }
-
- return new TreeSet<File>(Arrays.asList(files));
- }
-
- public static SortedSet<File> listMusicFiles(File dir) {
- SortedSet<File> files = listFiles(dir);
- Iterator<File> iterator = files.iterator();
- while (iterator.hasNext()) {
- File file = iterator.next();
- if (!file.isDirectory() && !isMusicFile(file)) {
- iterator.remove();
- }
- }
- return files;
- }
-
- private static boolean isMusicFile(File file) {
- String extension = getExtension(file.getName());
- return MUSIC_FILE_EXTENSIONS.contains(extension);
- }
-
- /**
- * Returns the extension (the substring after the last dot) of the given file. The dot
- * is not included in the returned extension.
- *
- * @param name The filename in question.
- * @return The extension, or an empty string if no extension is found.
- */
- public static String getExtension(String name) {
- int index = name.lastIndexOf('.');
- return index == -1 ? "" : name.substring(index + 1).toLowerCase();
- }
-
- /**
- * Returns the base name (the substring before the last dot) of the given file. The dot
- * is not included in the returned basename.
- *
- * @param name The filename in question.
- * @return The base name, or an empty string if no basename is found.
- */
- public static String getBaseName(String name) {
- int index = name.lastIndexOf('.');
- return index == -1 ? name : name.substring(0, index);
- }
-
- public static <T extends Serializable> boolean serialize(Context context, T obj, String fileName) {
- File file = new File(context.getCacheDir(), fileName);
- ObjectOutputStream out = null;
- try {
- out = new ObjectOutputStream(new FileOutputStream(file));
- out.writeObject(obj);
- Log.i(TAG, "Serialized object to " + file);
- return true;
- } catch (Throwable x) {
- Log.w(TAG, "Failed to serialize object to " + file);
- return false;
- } finally {
- Util.close(out);
- }
- }
-
- public static <T extends Serializable> T deserialize(Context context, String fileName) {
- File file = new File(context.getCacheDir(), fileName);
- if (!file.exists() || !file.isFile()) {
- return null;
- }
-
- ObjectInputStream in = null;
- try {
- in = new ObjectInputStream(new FileInputStream(file));
- T result = (T) in.readObject();
- Log.i(TAG, "Deserialized object from " + file);
- return result;
- } catch (Throwable x) {
- Log.w(TAG, "Failed to deserialize object from " + file, x);
- return null;
- } finally {
- Util.close(in);
- }
- }
-}
diff --git a/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/HorizontalSlider.java b/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/HorizontalSlider.java
deleted file mode 100644
index 6a79a0a0..00000000
--- a/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/HorizontalSlider.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- 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 net.sourceforge.subsonic.androidapp.util;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Canvas;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.view.View;
-import android.widget.ProgressBar;
-import net.sourceforge.subsonic.androidapp.R;
-
-/**
- * @author Sindre Mehus
- * @version $Id$
- */
-public class HorizontalSlider extends ProgressBar {
-
- private final Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.slider_knob);
- private boolean slidingEnabled;
- private OnSliderChangeListener listener;
- private static final int PADDING = 2;
- private boolean sliding;
- private int sliderPosition;
- private int startPosition;
-
- public interface OnSliderChangeListener {
- void onSliderChanged(View view, int position, boolean inProgress);
- }
-
- public HorizontalSlider(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
-
- public HorizontalSlider(Context context, AttributeSet attrs) {
- super(context, attrs, android.R.attr.progressBarStyleHorizontal);
- }
-
- public HorizontalSlider(Context context) {
- super(context);
- }
-
- public void setSlidingEnabled(boolean slidingEnabled) {
- if (this.slidingEnabled != slidingEnabled) {
- this.slidingEnabled = slidingEnabled;
- invalidate();
- }
- }
-
- public boolean isSlidingEnabled() {
- return slidingEnabled;
- }
-
- public void setOnSliderChangeListener(OnSliderChangeListener listener) {
- this.listener = listener;
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
-
- int max = getMax();
- if (!slidingEnabled || max == 0) {
- return;
- }
-
- int paddingLeft = getPaddingLeft();
- int paddingRight = getPaddingRight();
- int paddingTop = getPaddingTop();
- int paddingBottom = getPaddingBottom();
-
- int w = getWidth() - paddingLeft - paddingRight;
- int h = getHeight() - paddingTop - paddingBottom;
- int position = sliding ? sliderPosition : getProgress();
-
- int bitmapWidth = bitmap.getWidth();
- int bitmapHeight = bitmap.getWidth();
- float x = paddingLeft + w * ((float) position / max) - bitmapWidth / 2.0F;
- x = Math.max(x, paddingLeft);
- x = Math.min(x, paddingLeft + w - bitmapWidth);
- float y = paddingTop + h / 2.0F - bitmapHeight / 2.0F;
-
- canvas.drawBitmap(bitmap, x, y, null);
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- if (!slidingEnabled) {
- return false;
- }
-
- int action = event.getAction();
-
- if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_MOVE) {
-
- if (action == MotionEvent.ACTION_DOWN) {
- sliding = true;
- startPosition = getProgress();
- }
-
- float x = event.getX() - PADDING;
- float width = getWidth() - 2 * PADDING;
- sliderPosition = Math.round((float) getMax() * (x / width));
- sliderPosition = Math.max(sliderPosition, 0);
-
- setProgress(Math.min(startPosition, sliderPosition));
- setSecondaryProgress(Math.max(startPosition, sliderPosition));
- if (listener != null) {
- listener.onSliderChanged(this, sliderPosition, true);
- }
-
- } else if (action == MotionEvent.ACTION_UP) {
- sliding = false;
- setProgress(sliderPosition);
- setSecondaryProgress(0);
- if (listener != null) {
- listener.onSliderChanged(this, sliderPosition, false);
- }
- }
-
- return true;
- }
-} \ No newline at end of file
diff --git a/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/ImageLoader.java b/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/ImageLoader.java
deleted file mode 100644
index 5cbd8c9f..00000000
--- a/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/ImageLoader.java
+++ /dev/null
@@ -1,252 +0,0 @@
-/*
- 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 net.sourceforge.subsonic.androidapp.util;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.LinearGradient;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.Shader;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.TransitionDrawable;
-import android.os.Handler;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.TextView;
-import net.sourceforge.subsonic.androidapp.R;
-import net.sourceforge.subsonic.androidapp.domain.MusicDirectory;
-import net.sourceforge.subsonic.androidapp.service.MusicService;
-import net.sourceforge.subsonic.androidapp.service.MusicServiceFactory;
-
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.LinkedBlockingQueue;
-
-/**
- * Asynchronous loading of images, with caching.
- * <p/>
- * There should normally be only one instance of this class.
- *
- * @author Sindre Mehus
- */
-public class ImageLoader implements Runnable {
-
- private static final String TAG = ImageLoader.class.getSimpleName();
- private static final int CONCURRENCY = 5;
-
- private final LRUCache<String, Drawable> cache = new LRUCache<String, Drawable>(100);
- private final BlockingQueue<Task> queue;
- private final int imageSizeDefault;
- private final int imageSizeLarge;
- private Drawable largeUnknownImage;
-
- public ImageLoader(Context context) {
- queue = new LinkedBlockingQueue<Task>(500);
-
- // Determine the density-dependent image sizes.
- imageSizeDefault = context.getResources().getDrawable(R.drawable.unknown_album).getIntrinsicHeight();
- DisplayMetrics metrics = context.getResources().getDisplayMetrics();
- imageSizeLarge = (int) Math.round(Math.min(metrics.widthPixels, metrics.heightPixels) * 0.6);
-
- for (int i = 0; i < CONCURRENCY; i++) {
- new Thread(this, "ImageLoader").start();
- }
-
- createLargeUnknownImage(context);
- }
-
- private void createLargeUnknownImage(Context context) {
- BitmapDrawable drawable = (BitmapDrawable) context.getResources().getDrawable(R.drawable.unknown_album_large);
- Bitmap bitmap = Bitmap.createScaledBitmap(drawable.getBitmap(), imageSizeLarge, imageSizeLarge, true);
- bitmap = createReflection(bitmap);
- largeUnknownImage = Util.createDrawableFromBitmap(context, bitmap);
- }
-
- public void loadImage(View view, MusicDirectory.Entry entry, boolean large, boolean crossfade) {
- if (entry == null || entry.getCoverArt() == null) {
- setUnknownImage(view, large);
- return;
- }
-
- int size = large ? imageSizeLarge : imageSizeDefault;
- Drawable drawable = cache.get(getKey(entry.getCoverArt(), size));
- if (drawable != null) {
- setImage(view, drawable, large);
- return;
- }
-
- if (!large) {
- setUnknownImage(view, large);
- }
- queue.offer(new Task(view, entry, size, large, large, crossfade));
- }
-
- private String getKey(String coverArtId, int size) {
- return coverArtId + size;
- }
-
- private void setImage(View view, Drawable drawable, boolean crossfade) {
- if (view instanceof TextView) {
- // Cross-fading is not implemented for TextView since it's not in use. It would be easy to add it, though.
- TextView textView = (TextView) view;
- textView.setCompoundDrawablesWithIntrinsicBounds(drawable, null, null, null);
- } else if (view instanceof ImageView) {
- ImageView imageView = (ImageView) view;
- if (crossfade) {
-
- Drawable existingDrawable = imageView.getDrawable();
- if (existingDrawable == null) {
- Bitmap emptyImage = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
- existingDrawable = new BitmapDrawable(emptyImage);
- }
-
- Drawable[] layers = new Drawable[]{existingDrawable, drawable};
-
- TransitionDrawable transitionDrawable = new TransitionDrawable(layers);
- imageView.setImageDrawable(transitionDrawable);
- transitionDrawable.startTransition(250);
- } else {
- imageView.setImageDrawable(drawable);
- }
- }
- }
-
- private void setUnknownImage(View view, boolean large) {
- if (large) {
- setImage(view, largeUnknownImage, false);
- } else {
- if (view instanceof TextView) {
- ((TextView) view).setCompoundDrawablesWithIntrinsicBounds(R.drawable.unknown_album, 0, 0, 0);
- } else if (view instanceof ImageView) {
- ((ImageView) view).setImageResource(R.drawable.unknown_album);
- }
- }
- }
-
- public void clear() {
- queue.clear();
- }
-
- @Override
- public void run() {
- while (true) {
- try {
- Task task = queue.take();
- task.execute();
- } catch (Throwable x) {
- Log.e(TAG, "Unexpected exception in ImageLoader.", x);
- }
- }
- }
-
- private Bitmap createReflection(Bitmap originalImage) {
-
- int width = originalImage.getWidth();
- int height = originalImage.getHeight();
-
- // The gap we want between the reflection and the original image
- final int reflectionGap = 4;
-
- // This will not scale but will flip on the Y axis
- Matrix matrix = new Matrix();
- matrix.preScale(1, -1);
-
- // Create a Bitmap with the flip matix applied to it.
- // We only want the bottom half of the image
- Bitmap reflectionImage = Bitmap.createBitmap(originalImage, 0, height / 2, width, height / 2, matrix, false);
-
- // Create a new bitmap with same width but taller to fit reflection
- Bitmap bitmapWithReflection = Bitmap.createBitmap(width, (height + height / 2), Bitmap.Config.ARGB_8888);
-
- // Create a new Canvas with the bitmap that's big enough for
- // the image plus gap plus reflection
- Canvas canvas = new Canvas(bitmapWithReflection);
-
- // Draw in the original image
- canvas.drawBitmap(originalImage, 0, 0, null);
-
- // Draw in the gap
- Paint defaultPaint = new Paint();
- canvas.drawRect(0, height, width, height + reflectionGap, defaultPaint);
-
- // Draw in the reflection
- canvas.drawBitmap(reflectionImage, 0, height + reflectionGap, null);
-
- // Create a shader that is a linear gradient that covers the reflection
- Paint paint = new Paint();
- LinearGradient shader = new LinearGradient(0, originalImage.getHeight(), 0,
- bitmapWithReflection.getHeight() + reflectionGap, 0x70000000, 0xff000000,
- Shader.TileMode.CLAMP);
-
- // Set the paint to use this shader (linear gradient)
- paint.setShader(shader);
-
- // Draw a rectangle using the paint with our linear gradient
- canvas.drawRect(0, height, width, bitmapWithReflection.getHeight() + reflectionGap, paint);
-
- return bitmapWithReflection;
- }
-
- private class Task {
- private final View view;
- private final MusicDirectory.Entry entry;
- private final Handler handler;
- private final int size;
- private final boolean reflection;
- private final boolean saveToFile;
- private final boolean crossfade;
-
- public Task(View view, MusicDirectory.Entry entry, int size, boolean reflection, boolean saveToFile, boolean crossfade) {
- this.view = view;
- this.entry = entry;
- this.size = size;
- this.reflection = reflection;
- this.saveToFile = saveToFile;
- this.crossfade = crossfade;
- handler = new Handler();
- }
-
- public void execute() {
- try {
- MusicService musicService = MusicServiceFactory.getMusicService(view.getContext());
- Bitmap bitmap = musicService.getCoverArt(view.getContext(), entry, size, saveToFile, null);
-
- if (reflection) {
- bitmap = createReflection(bitmap);
- }
-
- final Drawable drawable = Util.createDrawableFromBitmap(view.getContext(), bitmap);
- cache.put(getKey(entry.getCoverArt(), size), drawable);
-
- handler.post(new Runnable() {
- @Override
- public void run() {
- setImage(view, drawable, crossfade);
- }
- });
- } catch (Throwable x) {
- Log.e(TAG, "Failed to download album art.", x);
- }
- }
- }
-}
diff --git a/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/LRUCache.java b/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/LRUCache.java
deleted file mode 100644
index f6145fb7..00000000
--- a/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/LRUCache.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- 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 net.sourceforge.subsonic.androidapp.util;
-
-import java.lang.ref.SoftReference;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * @author Sindre Mehus
- */
-public class LRUCache<K,V>{
-
- private final int capacity;
- private final Map<K, TimestampedValue> map;
-
- public LRUCache(int capacity) {
- map = new HashMap<K, TimestampedValue>(capacity);
- this.capacity = capacity;
- }
-
- public synchronized V get(K key) {
- TimestampedValue value = map.get(key);
-
- V result = null;
- if (value != null) {
- value.updateTimestamp();
- result = value.getValue();
- }
-
- return result;
- }
-
- public synchronized void put(K key, V value) {
- if (map.size() >= capacity) {
- removeOldest();
- }
- map.put(key, new TimestampedValue(value));
- }
-
- public void clear() {
- map.clear();
- }
-
- private void removeOldest() {
- K oldestKey = null;
- long oldestTimestamp = Long.MAX_VALUE;
-
- for (Map.Entry<K, TimestampedValue> entry : map.entrySet()) {
- K key = entry.getKey();
- TimestampedValue value = entry.getValue();
- if (value.getTimestamp() < oldestTimestamp) {
- oldestTimestamp = value.getTimestamp();
- oldestKey = key;
- }
- }
-
- if (oldestKey != null) {
- map.remove(oldestKey);
- }
- }
-
- private final class TimestampedValue {
-
- private final SoftReference<V> value;
- private long timestamp;
-
- public TimestampedValue(V value) {
- this.value = new SoftReference<V>(value);
- updateTimestamp();
- }
-
- public V getValue() {
- return value.get();
- }
-
- public long getTimestamp() {
- return timestamp;
- }
-
- public void updateTimestamp() {
- timestamp = System.currentTimeMillis();
- }
- }
-
-} \ No newline at end of file
diff --git a/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/MergeAdapter.java b/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/MergeAdapter.java
deleted file mode 100644
index 97dbc125..00000000
--- a/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/MergeAdapter.java
+++ /dev/null
@@ -1,290 +0,0 @@
-/***
- Copyright (c) 2008-2009 CommonsWare, LLC
- Portions (c) 2009 Google, Inc.
-
- Licensed under the Apache License, Version 2.0 (the "License"); you may
- not use this file except in compliance with the License. You may obtain
- a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
-
-package net.sourceforge.subsonic.androidapp.util;
-
-import android.database.DataSetObserver;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-import android.widget.ListAdapter;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Arrays;
-
-/**
- * Adapter that merges multiple child adapters and views
- * into a single contiguous whole.
- * <p/>
- * Adapters used as pieces within MergeAdapter must
- * have view type IDs monotonically increasing from 0. Ideally,
- * adapters also have distinct ranges for their row ids, as
- * returned by getItemId().
- */
-public class MergeAdapter extends BaseAdapter {
-
- private final CascadeDataSetObserver observer = new CascadeDataSetObserver();
- private final ArrayList<ListAdapter> pieces = new ArrayList<ListAdapter>();
-
- /**
- * Stock constructor, simply chaining to the superclass.
- */
- public MergeAdapter() {
- super();
- }
-
- /**
- * Adds a new adapter to the roster of things to appear
- * in the aggregate list.
- *
- * @param adapter Source for row views for this section
- */
- public void addAdapter(ListAdapter adapter) {
- pieces.add(adapter);
- adapter.registerDataSetObserver(observer);
- }
-
- public void removeAdapter(ListAdapter adapter) {
- adapter.unregisterDataSetObserver(observer);
- pieces.remove(adapter);
- }
-
- /**
- * Adds a new View to the roster of things to appear
- * in the aggregate list.
- *
- * @param view Single view to add
- */
- public ListAdapter addView(View view) {
- return addView(view, false);
- }
-
- /**
- * Adds a new View to the roster of things to appear
- * in the aggregate list.
- *
- * @param view Single view to add
- * @param enabled false if views are disabled, true if enabled
- */
- public ListAdapter addView(View view, boolean enabled) {
- return addViews(Arrays.asList(view), enabled);
- }
-
- /**
- * Adds a list of views to the roster of things to appear
- * in the aggregate list.
- *
- * @param views List of views to add
- */
- public ListAdapter addViews(List<View> views) {
- return addViews(views, false);
- }
-
- /**
- * Adds a list of views to the roster of things to appear
- * in the aggregate list.
- *
- * @param views List of views to add
- * @param enabled false if views are disabled, true if enabled
- */
- public ListAdapter addViews(List<View> views, boolean enabled) {
- ListAdapter adapter = enabled ? new EnabledSackAdapter(views) : new SackOfViewsAdapter(views);
- addAdapter(adapter);
- return adapter;
- }
-
- /**
- * Get the data item associated with the specified
- * position in the data set.
- *
- * @param position Position of the item whose data we want
- */
- @Override
- public Object getItem(int position) {
- for (ListAdapter piece : pieces) {
- int size = piece.getCount();
-
- if (position < size) {
- return (piece.getItem(position));
- }
-
- position -= size;
- }
-
- return (null);
- }
-
- /**
- * How many items are in the data set represented by this
- * Adapter.
- */
- @Override
- public int getCount() {
- int total = 0;
-
- for (ListAdapter piece : pieces) {
- total += piece.getCount();
- }
-
- return (total);
- }
-
- /**
- * Returns the number of types of Views that will be
- * created by getView().
- */
- @Override
- public int getViewTypeCount() {
- int total = 0;
-
- for (ListAdapter piece : pieces) {
- total += piece.getViewTypeCount();
- }
-
- return (Math.max(total, 1)); // needed for setListAdapter() before content add'
- }
-
- /**
- * Get the type of View that will be created by getView()
- * for the specified item.
- *
- * @param position Position of the item whose data we want
- */
- @Override
- public int getItemViewType(int position) {
- int typeOffset = 0;
- int result = -1;
-
- for (ListAdapter piece : pieces) {
- int size = piece.getCount();
-
- if (position < size) {
- result = typeOffset + piece.getItemViewType(position);
- break;
- }
-
- position -= size;
- typeOffset += piece.getViewTypeCount();
- }
-
- return (result);
- }
-
- /**
- * Are all items in this ListAdapter enabled? If yes it
- * means all items are selectable and clickable.
- */
- @Override
- public boolean areAllItemsEnabled() {
- return (false);
- }
-
- /**
- * Returns true if the item at the specified position is
- * not a separator.
- *
- * @param position Position of the item whose data we want
- */
- @Override
- public boolean isEnabled(int position) {
- for (ListAdapter piece : pieces) {
- int size = piece.getCount();
-
- if (position < size) {
- return (piece.isEnabled(position));
- }
-
- position -= size;
- }
-
- return (false);
- }
-
- /**
- * Get a View that displays the data at the specified
- * position in the data set.
- *
- * @param position Position of the item whose data we want
- * @param convertView View to recycle, if not null
- * @param parent ViewGroup containing the returned View
- */
- @Override
- public View getView(int position, View convertView,
- ViewGroup parent) {
- for (ListAdapter piece : pieces) {
- int size = piece.getCount();
-
- if (position < size) {
-
- return (piece.getView(position, convertView, parent));
- }
-
- position -= size;
- }
-
- return (null);
- }
-
- /**
- * Get the row id associated with the specified position
- * in the list.
- *
- * @param position Position of the item whose data we want
- */
- @Override
- public long getItemId(int position) {
- for (ListAdapter piece : pieces) {
- int size = piece.getCount();
-
- if (position < size) {
- return (piece.getItemId(position));
- }
-
- position -= size;
- }
-
- return (-1);
- }
-
- private static class EnabledSackAdapter extends SackOfViewsAdapter {
- public EnabledSackAdapter(List<View> views) {
- super(views);
- }
-
- @Override
- public boolean areAllItemsEnabled() {
- return (true);
- }
-
- @Override
- public boolean isEnabled(int position) {
- return (true);
- }
- }
-
- private class CascadeDataSetObserver extends DataSetObserver {
- @Override
- public void onChanged() {
- notifyDataSetChanged();
- }
-
- @Override
- public void onInvalidated() {
- notifyDataSetInvalidated();
- }
- }
-}
-
diff --git a/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/ModalBackgroundTask.java b/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/ModalBackgroundTask.java
deleted file mode 100644
index 15e2add2..00000000
--- a/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/ModalBackgroundTask.java
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- 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 net.sourceforge.subsonic.androidapp.util;
-
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.content.DialogInterface;
-import android.util.Log;
-import net.sourceforge.subsonic.androidapp.R;
-
-/**
- * @author Sindre Mehus
- */
-public abstract class ModalBackgroundTask<T> extends BackgroundTask<T> {
-
- private static final String TAG = ModalBackgroundTask.class.getSimpleName();
-
- private final AlertDialog progressDialog;
- private Thread thread;
- private final boolean finishActivityOnCancel;
- private boolean cancelled;
-
- public ModalBackgroundTask(Activity activity, boolean finishActivityOnCancel) {
- super(activity);
- this.finishActivityOnCancel = finishActivityOnCancel;
- progressDialog = createProgressDialog();
- }
-
- public ModalBackgroundTask(Activity activity) {
- this(activity, true);
- }
-
- private AlertDialog createProgressDialog() {
- AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
- builder.setIcon(android.R.drawable.ic_dialog_info);
- builder.setTitle(R.string.background_task_wait);
- builder.setMessage(R.string.background_task_loading);
- builder.setCancelable(true);
- builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
- @Override
- public void onCancel(DialogInterface dialogInterface) {
- cancel();
- }
- });
- builder.setPositiveButton(R.string.common_cancel, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialogInterface, int i) {
- cancel();
- }
- });
-
- return builder.create();
- }
-
- public void execute() {
- cancelled = false;
- progressDialog.show();
-
- thread = new Thread() {
- @Override
- public void run() {
- try {
- final T result = doInBackground();
- if (cancelled) {
- progressDialog.dismiss();
- return;
- }
-
- getHandler().post(new Runnable() {
- @Override
- public void run() {
- progressDialog.dismiss();
- done(result);
- }
- });
-
- } catch (final Throwable t) {
- if (cancelled) {
- return;
- }
- getHandler().post(new Runnable() {
- @Override
- public void run() {
- progressDialog.dismiss();
- error(t);
- }
- });
- }
- }
- };
- thread.start();
- }
-
- protected void cancel() {
- cancelled = true;
- if (thread != null) {
- thread.interrupt();
- }
-
- if (finishActivityOnCancel) {
- getActivity().finish();
- }
- }
-
- protected boolean isCancelled() {
- return cancelled;
- }
-
- protected void error(Throwable error) {
- Log.w(TAG, "Got exception: " + error, error);
- new ErrorDialog(getActivity(), getErrorMessage(error), finishActivityOnCancel);
- }
-
- @Override
- public void updateProgress(final String message) {
- getHandler().post(new Runnable() {
- @Override
- public void run() {
- progressDialog.setMessage(message);
- }
- });
- }
-}
diff --git a/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/MyViewFlipper.java b/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/MyViewFlipper.java
deleted file mode 100644
index 94f217ff..00000000
--- a/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/MyViewFlipper.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- 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 net.sourceforge.subsonic.androidapp.util;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.widget.ViewFlipper;
-
-/**
- * Work-around for Android Issue 6191 (http://code.google.com/p/android/issues/detail?id=6191)
- *
- * @author Sindre Mehus
- * @version $Id$
- */
-public class MyViewFlipper extends ViewFlipper {
-
- public MyViewFlipper(Context context) {
- super(context);
- }
-
- public MyViewFlipper(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
-
- @Override
- protected void onDetachedFromWindow() {
- try {
- super.onDetachedFromWindow();
- }
- catch (IllegalArgumentException e) {
- // Call stopFlipping() in order to kick off updateRunning()
- stopFlipping();
- }
- }
-}
-
diff --git a/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/Pair.java b/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/Pair.java
deleted file mode 100644
index 73dc3224..00000000
--- a/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/Pair.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- 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 net.sourceforge.subsonic.androidapp.util;
-
-import java.io.Serializable;
-
-/**
- * @author Sindre Mehus
- */
-public class Pair<S, T> implements Serializable {
-
- private S first;
- private T second;
-
- public Pair() {
- }
-
- public Pair(S first, T second) {
- this.first = first;
- this.second = second;
- }
-
- public S getFirst() {
- return first;
- }
-
- public void setFirst(S first) {
- this.first = first;
- }
-
- public T getSecond() {
- return second;
- }
-
- public void setSecond(T second) {
- this.second = second;
- }
-}
diff --git a/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/PlaylistAdapter.java b/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/PlaylistAdapter.java
deleted file mode 100644
index 16028c12..00000000
--- a/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/PlaylistAdapter.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- 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 net.sourceforge.subsonic.androidapp.util;
-
-import android.content.Context;
-import android.widget.ArrayAdapter;
-import android.widget.SectionIndexer;
-import net.sourceforge.subsonic.androidapp.R;
-import net.sourceforge.subsonic.androidapp.domain.Playlist;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Set;
-
-/**
-* @author Sindre Mehus
-* @version $Id$
-*/
-public class PlaylistAdapter extends ArrayAdapter<Playlist> implements SectionIndexer {
-
- // Both arrays are indexed by section ID.
- private final Object[] sections;
- private final Integer[] positions;
-
- /**
- * Note: playlists must be sorted alphabetically.
- */
- public PlaylistAdapter(Context context, List<Playlist> playlists) {
- super(context, R.layout.playlist_list_item, playlists);
-
- Set<String> sectionSet = new LinkedHashSet<String>(30);
- List<Integer> positionList = new ArrayList<Integer>(30);
- for (int i = 0; i < playlists.size(); i++) {
- Playlist playlist = playlists.get(i);
- if (playlist.getName().length() > 0) {
- String index = playlist.getName().substring(0, 1).toUpperCase();
- if (!sectionSet.contains(index)) {
- sectionSet.add(index);
- positionList.add(i);
- }
- }
- }
- sections = sectionSet.toArray(new Object[sectionSet.size()]);
- positions = positionList.toArray(new Integer[positionList.size()]);
- }
-
- @Override
- public Object[] getSections() {
- return sections;
- }
-
- @Override
- public int getPositionForSection(int section) {
- section = Math.min(section, positions.length - 1);
- return positions[section];
- }
-
- @Override
- public int getSectionForPosition(int pos) {
- for (int i = 0; i < sections.length - 1; i++) {
- if (pos < positions[i + 1]) {
- return i;
- }
- }
- return sections.length - 1;
- }
-
- public static class PlaylistComparator implements Comparator<Playlist> {
- @Override
- public int compare(Playlist playlist1, Playlist playlist2) {
- return playlist1.getName().compareToIgnoreCase(playlist2.getName());
- }
-
- public static List<Playlist> sort(List<Playlist> playlists) {
- Collections.sort(playlists, new PlaylistComparator());
- return playlists;
- }
-
- }
-}
diff --git a/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/ProgressListener.java b/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/ProgressListener.java
deleted file mode 100644
index 0d2924f7..00000000
--- a/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/ProgressListener.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- 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 net.sourceforge.subsonic.androidapp.util;
-
-/**
- * @author Sindre Mehus
- */
-public interface ProgressListener {
- void updateProgress(String message);
- void updateProgress(int messageId);
-}
diff --git a/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/SackOfViewsAdapter.java b/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/SackOfViewsAdapter.java
deleted file mode 100644
index ca825e55..00000000
--- a/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/SackOfViewsAdapter.java
+++ /dev/null
@@ -1,181 +0,0 @@
-/***
- Copyright (c) 2008-2009 CommonsWare, LLC
- Portions (c) 2009 Google, Inc.
-
- Licensed under the Apache License, Version 2.0 (the "License"); you may
- not use this file except in compliance with the License. You may obtain
- a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
-package net.sourceforge.subsonic.androidapp.util;
-
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-import android.widget.ListView;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Adapter that simply returns row views from a list.
- * <p/>
- * If you supply a size, you must implement newView(), to
- * create a required view. The adapter will then cache these
- * views.
- * <p/>
- * If you supply a list of views in the constructor, that
- * list will be used directly. If any elements in the list
- * are null, then newView() will be called just for those
- * slots.
- * <p/>
- * Subclasses may also wish to override areAllItemsEnabled()
- * (default: false) and isEnabled() (default: false), if some
- * of their rows should be selectable.
- * <p/>
- * It is assumed each view is unique, and therefore will not
- * get recycled.
- * <p/>
- * Note that this adapter is not designed for long lists. It
- * is more for screens that should behave like a list. This
- * is particularly useful if you combine this with other
- * adapters (e.g., SectionedAdapter) that might have an
- * arbitrary number of rows, so it all appears seamless.
- */
-public class SackOfViewsAdapter extends BaseAdapter {
- private List<View> views = null;
-
- /**
- * Constructor creating an empty list of views, but with
- * a specified count. Subclasses must override newView().
- */
- public SackOfViewsAdapter(int count) {
- super();
-
- views = new ArrayList<View>(count);
-
- for (int i = 0; i < count; i++) {
- views.add(null);
- }
- }
-
- /**
- * Constructor wrapping a supplied list of views.
- * Subclasses must override newView() if any of the elements
- * in the list are null.
- */
- public SackOfViewsAdapter(List<View> views) {
- for (View view : views) {
- view.setLayoutParams(new ListView.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
- }
- this.views = views;
- }
-
- /**
- * Get the data item associated with the specified
- * position in the data set.
- *
- * @param position Position of the item whose data we want
- */
- @Override
- public Object getItem(int position) {
- return (views.get(position));
- }
-
- /**
- * How many items are in the data set represented by this
- * Adapter.
- */
- @Override
- public int getCount() {
- return (views.size());
- }
-
- /**
- * Returns the number of types of Views that will be
- * created by getView().
- */
- @Override
- public int getViewTypeCount() {
- return (getCount());
- }
-
- /**
- * Get the type of View that will be created by getView()
- * for the specified item.
- *
- * @param position Position of the item whose data we want
- */
- @Override
- public int getItemViewType(int position) {
- return (position);
- }
-
- /**
- * Are all items in this ListAdapter enabled? If yes it
- * means all items are selectable and clickable.
- */
- @Override
- public boolean areAllItemsEnabled() {
- return (false);
- }
-
- /**
- * Returns true if the item at the specified position is
- * not a separator.
- *
- * @param position Position of the item whose data we want
- */
- @Override
- public boolean isEnabled(int position) {
- return (false);
- }
-
- /**
- * Get a View that displays the data at the specified
- * position in the data set.
- *
- * @param position Position of the item whose data we want
- * @param convertView View to recycle, if not null
- * @param parent ViewGroup containing the returned View
- */
- @Override
- public View getView(int position, View convertView,
- ViewGroup parent) {
- View result = views.get(position);
-
- if (result == null) {
- result = newView(position, parent);
- views.set(position, result);
- }
-
- return (result);
- }
-
- /**
- * Get the row id associated with the specified position
- * in the list.
- *
- * @param position Position of the item whose data we want
- */
- @Override
- public long getItemId(int position) {
- return (position);
- }
-
- /**
- * Create a new View to go into the list at the specified
- * position.
- *
- * @param position Position of the item whose data we want
- * @param parent ViewGroup containing the returned View
- */
- protected View newView(int position, ViewGroup parent) {
- throw new RuntimeException("You must override newView()!");
- }
-}
diff --git a/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/ShufflePlayBuffer.java b/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/ShufflePlayBuffer.java
deleted file mode 100644
index 825fcc44..00000000
--- a/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/ShufflePlayBuffer.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- 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 net.sourceforge.subsonic.androidapp.util;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
-
-import android.content.Context;
-import android.util.Log;
-import net.sourceforge.subsonic.androidapp.domain.MusicDirectory;
-import net.sourceforge.subsonic.androidapp.service.MusicService;
-import net.sourceforge.subsonic.androidapp.service.MusicServiceFactory;
-
-/**
- * @author Sindre Mehus
- * @version $Id$
- */
-public class ShufflePlayBuffer {
-
- private static final String TAG = ShufflePlayBuffer.class.getSimpleName();
- private static final int CAPACITY = 50;
- private static final int REFILL_THRESHOLD = 40;
-
- private final ScheduledExecutorService executorService;
- private final List<MusicDirectory.Entry> buffer = new ArrayList<MusicDirectory.Entry>();
- private Context context;
- private int currentServer;
-
- public ShufflePlayBuffer(Context context) {
- this.context = context;
- executorService = Executors.newSingleThreadScheduledExecutor();
- Runnable runnable = new Runnable() {
- @Override
- public void run() {
- refill();
- }
- };
- executorService.scheduleWithFixedDelay(runnable, 1, 10, TimeUnit.SECONDS);
- }
-
- public List<MusicDirectory.Entry> get(int size) {
- clearBufferIfnecessary();
-
- List<MusicDirectory.Entry> result = new ArrayList<MusicDirectory.Entry>(size);
- synchronized (buffer) {
- while (!buffer.isEmpty() && result.size() < size) {
- result.add(buffer.remove(buffer.size() - 1));
- }
- }
- Log.i(TAG, "Taking " + result.size() + " songs from shuffle play buffer. " + buffer.size() + " remaining.");
- return result;
- }
-
- public void shutdown() {
- executorService.shutdown();
- }
-
- private void refill() {
-
- // Check if active server has changed.
- clearBufferIfnecessary();
-
- if (buffer.size() > REFILL_THRESHOLD || (!Util.isNetworkConnected(context) && !Util.isOffline(context))) {
- return;
- }
-
- try {
- MusicService service = MusicServiceFactory.getMusicService(context);
- int n = CAPACITY - buffer.size();
- MusicDirectory songs = service.getRandomSongs(n, context, null);
-
- synchronized (buffer) {
- buffer.addAll(songs.getChildren());
- Log.i(TAG, "Refilled shuffle play buffer with " + songs.getChildren().size() + " songs.");
- }
- } catch (Exception x) {
- Log.w(TAG, "Failed to refill shuffle play buffer.", x);
- }
- }
-
- private void clearBufferIfnecessary() {
- synchronized (buffer) {
- if (currentServer != Util.getActiveServer(context)) {
- currentServer = Util.getActiveServer(context);
- buffer.clear();
- }
- }
- }
-
-}
diff --git a/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/SilentBackgroundTask.java b/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/SilentBackgroundTask.java
deleted file mode 100644
index 7aa85d7c..00000000
--- a/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/SilentBackgroundTask.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- 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 2010 (C) Sindre Mehus
- */
-package net.sourceforge.subsonic.androidapp.util;
-
-import android.app.Activity;
-
-/**
- * @author Sindre Mehus
- */
-public abstract class SilentBackgroundTask<T> extends BackgroundTask<T> {
-
- public SilentBackgroundTask(Activity activity) {
- super(activity);
- }
-
- @Override
- public void execute() {
- Thread thread = new Thread() {
- @Override
- public void run() {
- try {
- final T result = doInBackground();
-
- getHandler().post(new Runnable() {
- @Override
- public void run() {
- done(result);
- }
- });
-
- } catch (final Throwable t) {
- getHandler().post(new Runnable() {
- @Override
- public void run() {
- error(t);
- }
- });
- }
- }
- };
- thread.start();
- }
-
- @Override
- public void updateProgress(int messageId) {
- }
-
- @Override
- public void updateProgress(String message) {
- }
-}
diff --git a/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/SimpleServiceBinder.java b/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/SimpleServiceBinder.java
deleted file mode 100644
index 9ddf9903..00000000
--- a/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/SimpleServiceBinder.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- 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 net.sourceforge.subsonic.androidapp.util;
-
-import android.os.Binder;
-
-/**
- * @author Sindre Mehus
- */
-public class SimpleServiceBinder<S> extends Binder {
-
- private final S service;
-
- public SimpleServiceBinder(S service) {
- this.service = service;
- }
-
- public S getService() {
- return service;
- }
-}
diff --git a/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/SongView.java b/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/SongView.java
deleted file mode 100644
index 22902a11..00000000
--- a/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/SongView.java
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- 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 net.sourceforge.subsonic.androidapp.util;
-
-import android.content.Context;
-import android.os.Handler;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.Checkable;
-import android.widget.CheckedTextView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-import net.sourceforge.subsonic.androidapp.R;
-import net.sourceforge.subsonic.androidapp.domain.MusicDirectory;
-import net.sourceforge.subsonic.androidapp.service.DownloadService;
-import net.sourceforge.subsonic.androidapp.service.DownloadServiceImpl;
-import net.sourceforge.subsonic.androidapp.service.DownloadFile;
-
-import java.io.File;
-import java.util.WeakHashMap;
-
-/**
- * Used to display songs in a {@code ListView}.
- *
- * @author Sindre Mehus
- */
-public class SongView extends LinearLayout implements Checkable {
-
- private static final String TAG = SongView.class.getSimpleName();
- private static final WeakHashMap<SongView, ?> INSTANCES = new WeakHashMap<SongView, Object>();
- private static Handler handler;
-
- private CheckedTextView checkedTextView;
- private TextView titleTextView;
- private TextView artistTextView;
- private TextView durationTextView;
- private TextView statusTextView;
- private MusicDirectory.Entry song;
-
- public SongView(Context context) {
- super(context);
- LayoutInflater.from(context).inflate(R.layout.song_list_item, this, true);
-
- checkedTextView = (CheckedTextView) findViewById(R.id.song_check);
- titleTextView = (TextView) findViewById(R.id.song_title);
- artistTextView = (TextView) findViewById(R.id.song_artist);
- durationTextView = (TextView) findViewById(R.id.song_duration);
- statusTextView = (TextView) findViewById(R.id.song_status);
-
- INSTANCES.put(this, null);
- int instanceCount = INSTANCES.size();
- if (instanceCount > 50) {
- Log.w(TAG, instanceCount + " live SongView instances");
- }
- startUpdater();
- }
-
- public void setSong(MusicDirectory.Entry song, boolean checkable) {
- this.song = song;
- StringBuilder artist = new StringBuilder(40);
-
- String bitRate = null;
- if (song.getBitRate() != null) {
- bitRate = String.format(getContext().getString(R.string.song_details_kbps), song.getBitRate());
- }
-
- String fileFormat = null;
- if (song.getTranscodedSuffix() != null && !song.getTranscodedSuffix().equals(song.getSuffix())) {
- fileFormat = String.format("%s > %s", song.getSuffix(), song.getTranscodedSuffix());
- } else {
- fileFormat = song.getSuffix();
- }
-
- artist.append(song.getArtist()).append(" (")
- .append(String.format(getContext().getString(R.string.song_details_all), bitRate == null ? "" : bitRate, fileFormat))
- .append(")");
-
- titleTextView.setText(song.getTitle());
- artistTextView.setText(artist);
- durationTextView.setText(Util.formatDuration(song.getDuration()));
- checkedTextView.setVisibility(checkable && !song.isVideo() ? View.VISIBLE : View.GONE);
-
- update();
- }
-
- private void update() {
- DownloadService downloadService = DownloadServiceImpl.getInstance();
- if (downloadService == null) {
- return;
- }
-
- DownloadFile downloadFile = downloadService.forSong(song);
- File completeFile = downloadFile.getCompleteFile();
- File partialFile = downloadFile.getPartialFile();
-
- int leftImage = 0;
- int rightImage = 0;
-
- if (completeFile.exists()) {
- leftImage = downloadFile.isSaved() ? R.drawable.saved : R.drawable.downloaded;
- }
-
- if (downloadFile.isDownloading() && !downloadFile.isDownloadCancelled() && partialFile.exists()) {
- statusTextView.setText(Util.formatLocalizedBytes(partialFile.length(), getContext()));
- rightImage = R.drawable.downloading;
- } else {
- statusTextView.setText(null);
- }
- statusTextView.setCompoundDrawablesWithIntrinsicBounds(leftImage, 0, rightImage, 0);
-
- boolean playing = downloadService.getCurrentPlaying() == downloadFile;
- if (playing) {
- titleTextView.setCompoundDrawablesWithIntrinsicBounds(R.drawable.stat_notify_playing, 0, 0, 0);
- } else {
- titleTextView.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
- }
- }
-
- private static synchronized void startUpdater() {
- if (handler != null) {
- return;
- }
-
- handler = new Handler();
- Runnable runnable = new Runnable() {
- @Override
- public void run() {
- updateAll();
- handler.postDelayed(this, 1000L);
- }
- };
- handler.postDelayed(runnable, 1000L);
- }
-
- private static void updateAll() {
- try {
- for (SongView view : INSTANCES.keySet()) {
- if (view.isShown()) {
- view.update();
- }
- }
- } catch (Throwable x) {
- Log.w(TAG, "Error when updating song views.", x);
- }
- }
-
- @Override
- public void setChecked(boolean b) {
- checkedTextView.setChecked(b);
- }
-
- @Override
- public boolean isChecked() {
- return checkedTextView.isChecked();
- }
-
- @Override
- public void toggle() {
- checkedTextView.toggle();
- }
-}
diff --git a/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/TabActivityBackgroundTask.java b/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/TabActivityBackgroundTask.java
deleted file mode 100644
index 033a51ad..00000000
--- a/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/TabActivityBackgroundTask.java
+++ /dev/null
@@ -1,67 +0,0 @@
-package net.sourceforge.subsonic.androidapp.util;
-
-import net.sourceforge.subsonic.androidapp.activity.SubsonicTabActivity;
-
-/**
- * @author Sindre Mehus
- * @version $Id$
- */
-public abstract class TabActivityBackgroundTask<T> extends BackgroundTask<T> {
-
- private final SubsonicTabActivity tabActivity;
-
- public TabActivityBackgroundTask(SubsonicTabActivity activity) {
- super(activity);
- tabActivity = activity;
- }
-
- @Override
- public void execute() {
- tabActivity.setProgressVisible(true);
-
- new Thread() {
- @Override
- public void run() {
- try {
- final T result = doInBackground();
- if (isCancelled()) {
- return;
- }
-
- getHandler().post(new Runnable() {
- @Override
- public void run() {
- tabActivity.setProgressVisible(false);
- done(result);
- }
- });
- } catch (final Throwable t) {
- if (isCancelled()) {
- return;
- }
- getHandler().post(new Runnable() {
- @Override
- public void run() {
- tabActivity.setProgressVisible(false);
- error(t);
- }
- });
- }
- }
- }.start();
- }
-
- private boolean isCancelled() {
- return tabActivity.isDestroyed();
- }
-
- @Override
- public void updateProgress(final String message) {
- getHandler().post(new Runnable() {
- @Override
- public void run() {
- tabActivity.updateProgress(message);
- }
- });
- }
-}
diff --git a/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/TimeLimitedCache.java b/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/TimeLimitedCache.java
deleted file mode 100644
index 5df5901e..00000000
--- a/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/TimeLimitedCache.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- 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 net.sourceforge.subsonic.androidapp.util;
-
-import java.lang.ref.SoftReference;
-import java.util.concurrent.TimeUnit;
-
-/**
- * @author Sindre Mehus
- * @version $Id$
- */
-public class TimeLimitedCache<T> {
-
- private SoftReference<T> value;
- private final long ttlMillis;
- private long expires;
-
- public TimeLimitedCache(long ttl, TimeUnit timeUnit) {
- this.ttlMillis = TimeUnit.MILLISECONDS.convert(ttl, timeUnit);
- }
-
- public T get() {
- return System.currentTimeMillis() < expires ? value.get() : null;
- }
-
- public void set(T value) {
- set(value, ttlMillis, TimeUnit.MILLISECONDS);
- }
-
- public void set(T value, long ttl, TimeUnit timeUnit) {
- this.value = new SoftReference<T>(value);
- expires = System.currentTimeMillis() + timeUnit.toMillis(ttl);
- }
-
- public void clear() {
- expires = 0L;
- value = null;
- }
-}
diff --git a/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/Util.java b/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/Util.java
deleted file mode 100644
index 9a8c692d..00000000
--- a/subsonic-android/src/net/sourceforge/subsonic/androidapp/util/Util.java
+++ /dev/null
@@ -1,829 +0,0 @@
-/*
- 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 net.sourceforge.subsonic.androidapp.util;
-
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.app.Service;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.media.AudioManager;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
-import android.os.Environment;
-import android.os.Handler;
-import android.util.Log;
-import android.view.Gravity;
-import android.view.ViewGroup;
-import android.widget.LinearLayout;
-import android.widget.RemoteViews;
-import android.widget.TextView;
-import android.widget.Toast;
-import net.sourceforge.subsonic.androidapp.R;
-import net.sourceforge.subsonic.androidapp.activity.DownloadActivity;
-import net.sourceforge.subsonic.androidapp.domain.MusicDirectory;
-import net.sourceforge.subsonic.androidapp.domain.PlayerState;
-import net.sourceforge.subsonic.androidapp.domain.RepeatMode;
-import net.sourceforge.subsonic.androidapp.domain.Version;
-import net.sourceforge.subsonic.androidapp.provider.SubsonicAppWidgetProvider;
-import net.sourceforge.subsonic.androidapp.receiver.MediaButtonIntentReceiver;
-import net.sourceforge.subsonic.androidapp.service.DownloadServiceImpl;
-import org.apache.http.HttpEntity;
-
-import java.io.ByteArrayOutputStream;
-import java.io.Closeable;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.UnsupportedEncodingException;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Method;
-import java.security.MessageDigest;
-import java.text.DecimalFormat;
-import java.text.NumberFormat;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * @author Sindre Mehus
- * @version $Id$
- */
-public final class Util {
-
- private static final String TAG = Util.class.getSimpleName();
-
- private static final DecimalFormat GIGA_BYTE_FORMAT = new DecimalFormat("0.00 GB");
- private static final DecimalFormat MEGA_BYTE_FORMAT = new DecimalFormat("0.00 MB");
- private static final DecimalFormat KILO_BYTE_FORMAT = new DecimalFormat("0 KB");
-
- private static DecimalFormat GIGA_BYTE_LOCALIZED_FORMAT = null;
- private static DecimalFormat MEGA_BYTE_LOCALIZED_FORMAT = null;
- private static DecimalFormat KILO_BYTE_LOCALIZED_FORMAT = null;
- private static DecimalFormat BYTE_LOCALIZED_FORMAT = null;
-
- public static final String EVENT_META_CHANGED = "net.sourceforge.subsonic.androidapp.EVENT_META_CHANGED";
- public static final String EVENT_PLAYSTATE_CHANGED = "net.sourceforge.subsonic.androidapp.EVENT_PLAYSTATE_CHANGED";
-
- private static final Map<Integer, Version> SERVER_REST_VERSIONS = new ConcurrentHashMap<Integer, Version>();
-
- // Used by hexEncode()
- private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
-
- private final static Pair<Integer, Integer> NOTIFICATION_TEXT_COLORS = new Pair<Integer, Integer>();
- private static Toast toast;
-
- private Util() {
- }
-
- public static boolean isOffline(Context context) {
- return getActiveServer(context) == 0;
- }
-
- public static boolean isScreenLitOnDownload(Context context) {
- SharedPreferences prefs = getPreferences(context);
- return prefs.getBoolean(Constants.PREFERENCES_KEY_SCREEN_LIT_ON_DOWNLOAD, false);
- }
-
- public static RepeatMode getRepeatMode(Context context) {
- SharedPreferences prefs = getPreferences(context);
- return RepeatMode.valueOf(prefs.getString(Constants.PREFERENCES_KEY_REPEAT_MODE, RepeatMode.OFF.name()));
- }
-
- public static void setRepeatMode(Context context, RepeatMode repeatMode) {
- SharedPreferences prefs = getPreferences(context);
- SharedPreferences.Editor editor = prefs.edit();
- editor.putString(Constants.PREFERENCES_KEY_REPEAT_MODE, repeatMode.name());
- editor.commit();
- }
-
- public static boolean isScrobblingEnabled(Context context) {
- if (isOffline(context)) {
- return false;
- }
- SharedPreferences prefs = getPreferences(context);
- return prefs.getBoolean(Constants.PREFERENCES_KEY_SCROBBLE, false);
- }
-
- public static void setActiveServer(Context context, int instance) {
- SharedPreferences prefs = getPreferences(context);
- SharedPreferences.Editor editor = prefs.edit();
- editor.putInt(Constants.PREFERENCES_KEY_SERVER_INSTANCE, instance);
- editor.commit();
- }
-
- public static int getActiveServer(Context context) {
- SharedPreferences prefs = getPreferences(context);
- return prefs.getInt(Constants.PREFERENCES_KEY_SERVER_INSTANCE, 1);
- }
-
- public static String getServerName(Context context, int instance) {
- if (instance == 0) {
- return context.getResources().getString(R.string.main_offline);
- }
- SharedPreferences prefs = getPreferences(context);
- return prefs.getString(Constants.PREFERENCES_KEY_SERVER_NAME + instance, null);
- }
-
- public static void setServerRestVersion(Context context, Version version) {
- SERVER_REST_VERSIONS.put(getActiveServer(context), version);
- }
-
- public static Version getServerRestVersion(Context context) {
- return SERVER_REST_VERSIONS.get(getActiveServer(context));
- }
-
- public static void setSelectedMusicFolderId(Context context, String musicFolderId) {
- int instance = getActiveServer(context);
- SharedPreferences prefs = getPreferences(context);
- SharedPreferences.Editor editor = prefs.edit();
- editor.putString(Constants.PREFERENCES_KEY_MUSIC_FOLDER_ID + instance, musicFolderId);
- editor.commit();
- }
-
- public static String getSelectedMusicFolderId(Context context) {
- SharedPreferences prefs = getPreferences(context);
- int instance = getActiveServer(context);
- return prefs.getString(Constants.PREFERENCES_KEY_MUSIC_FOLDER_ID + instance, null);
- }
-
- public static String getTheme(Context context) {
- SharedPreferences prefs = getPreferences(context);
- return prefs.getString(Constants.PREFERENCES_KEY_THEME, null);
- }
-
- public static int getMaxBitrate(Context context) {
- ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
- NetworkInfo networkInfo = manager.getActiveNetworkInfo();
- if (networkInfo == null) {
- return 0;
- }
-
- boolean wifi = networkInfo.getType() == ConnectivityManager.TYPE_WIFI;
- SharedPreferences prefs = getPreferences(context);
- return Integer.parseInt(prefs.getString(wifi ? Constants.PREFERENCES_KEY_MAX_BITRATE_WIFI : Constants.PREFERENCES_KEY_MAX_BITRATE_MOBILE, "0"));
- }
-
- public static int getPreloadCount(Context context) {
- SharedPreferences prefs = getPreferences(context);
- int preloadCount = Integer.parseInt(prefs.getString(Constants.PREFERENCES_KEY_PRELOAD_COUNT, "-1"));
- return preloadCount == -1 ? Integer.MAX_VALUE : preloadCount;
- }
-
- public static int getCacheSizeMB(Context context) {
- SharedPreferences prefs = getPreferences(context);
- int cacheSize = Integer.parseInt(prefs.getString(Constants.PREFERENCES_KEY_CACHE_SIZE, "-1"));
- return cacheSize == -1 ? Integer.MAX_VALUE : cacheSize;
- }
-
- public static String getRestUrl(Context context, String method) {
- StringBuilder builder = new StringBuilder();
-
- SharedPreferences prefs = getPreferences(context);
-
- int instance = prefs.getInt(Constants.PREFERENCES_KEY_SERVER_INSTANCE, 1);
- String serverUrl = prefs.getString(Constants.PREFERENCES_KEY_SERVER_URL + instance, null);
- String username = prefs.getString(Constants.PREFERENCES_KEY_USERNAME + instance, null);
- String password = prefs.getString(Constants.PREFERENCES_KEY_PASSWORD + instance, null);
-
- // Slightly obfuscate password
- password = "enc:" + Util.utf8HexEncode(password);
-
- builder.append(serverUrl);
- if (builder.charAt(builder.length() - 1) != '/') {
- builder.append("/");
- }
- builder.append("rest/").append(method).append(".view");
- builder.append("?u=").append(username);
- builder.append("&p=").append(password);
- builder.append("&v=").append(Constants.REST_PROTOCOL_VERSION);
- builder.append("&c=").append(Constants.REST_CLIENT_ID);
-
- return builder.toString();
- }
-
- public static SharedPreferences getPreferences(Context context) {
- return context.getSharedPreferences(Constants.PREFERENCES_FILE_NAME, 0);
- }
-
- public static String getContentType(HttpEntity entity) {
- if (entity == null || entity.getContentType() == null) {
- return null;
- }
- return entity.getContentType().getValue();
- }
-
- public static int getRemainingTrialDays(Context context) {
- SharedPreferences prefs = getPreferences(context);
- long installTime = prefs.getLong(Constants.PREFERENCES_KEY_INSTALL_TIME, 0L);
-
- if (installTime == 0L) {
- installTime = System.currentTimeMillis();
- SharedPreferences.Editor editor = prefs.edit();
- editor.putLong(Constants.PREFERENCES_KEY_INSTALL_TIME, installTime);
- editor.commit();
- }
-
- long now = System.currentTimeMillis();
- long millisPerDay = 24L * 60L * 60L * 1000L;
- int daysSinceInstall = (int) ((now - installTime) / millisPerDay);
- return Math.max(0, Constants.FREE_TRIAL_DAYS - daysSinceInstall);
- }
-
- /**
- * Get the contents of an <code>InputStream</code> as a <code>byte[]</code>.
- * <p/>
- * This method buffers the input internally, so there is no need to use a
- * <code>BufferedInputStream</code>.
- *
- * @param input the <code>InputStream</code> to read from
- * @return the requested byte array
- * @throws NullPointerException if the input is null
- * @throws IOException if an I/O error occurs
- */
- public static byte[] toByteArray(InputStream input) throws IOException {
- ByteArrayOutputStream output = new ByteArrayOutputStream();
- copy(input, output);
- return output.toByteArray();
- }
-
- public static long copy(InputStream input, OutputStream output)
- throws IOException {
- byte[] buffer = new byte[1024 * 4];
- long count = 0;
- int n;
- while (-1 != (n = input.read(buffer))) {
- output.write(buffer, 0, n);
- count += n;
- }
- return count;
- }
-
- public static void atomicCopy(File from, File to) throws IOException {
- FileInputStream in = null;
- FileOutputStream out = null;
- File tmp = null;
- try {
- tmp = new File(to.getPath() + ".tmp");
- in = new FileInputStream(from);
- out = new FileOutputStream(tmp);
- in.getChannel().transferTo(0, from.length(), out.getChannel());
- out.close();
- if (!tmp.renameTo(to)) {
- throw new IOException("Failed to rename " + tmp + " to " + to);
- }
- Log.i(TAG, "Copied " + from + " to " + to);
- } catch (IOException x) {
- close(out);
- delete(to);
- throw x;
- } finally {
- close(in);
- close(out);
- delete(tmp);
- }
- }
-
- public static void close(Closeable closeable) {
- try {
- if (closeable != null) {
- closeable.close();
- }
- } catch (Throwable x) {
- // Ignored
- }
- }
-
- public static boolean delete(File file) {
- if (file != null && file.exists()) {
- if (!file.delete()) {
- Log.w(TAG, "Failed to delete file " + file);
- return false;
- }
- Log.i(TAG, "Deleted file " + file);
- }
- return true;
- }
-
- public static void toast(Context context, int messageId) {
- toast(context, messageId, true);
- }
-
- public static void toast(Context context, int messageId, boolean shortDuration) {
- toast(context, context.getString(messageId), shortDuration);
- }
-
- public static void toast(Context context, String message) {
- toast(context, message, true);
- }
-
- public static void toast(Context context, String message, boolean shortDuration) {
- if (toast == null) {
- toast = Toast.makeText(context, message, shortDuration ? Toast.LENGTH_SHORT : Toast.LENGTH_LONG);
- toast.setGravity(Gravity.CENTER, 0, 0);
- } else {
- toast.setText(message);
- toast.setDuration(shortDuration ? Toast.LENGTH_SHORT : Toast.LENGTH_LONG);
- }
- toast.show();
- }
-
- /**
- * Converts a byte-count to a formatted string suitable for display to the user.
- * For instance:
- * <ul>
- * <li><code>format(918)</code> returns <em>"918 B"</em>.</li>
- * <li><code>format(98765)</code> returns <em>"96 KB"</em>.</li>
- * <li><code>format(1238476)</code> returns <em>"1.2 MB"</em>.</li>
- * </ul>
- * This method assumes that 1 KB is 1024 bytes.
- * To get a localized string, please use formatLocalizedBytes instead.
- *
- * @param byteCount The number of bytes.
- * @return The formatted string.
- */
- public static synchronized String formatBytes(long byteCount) {
-
- // More than 1 GB?
- if (byteCount >= 1024 * 1024 * 1024) {
- NumberFormat gigaByteFormat = GIGA_BYTE_FORMAT;
- return gigaByteFormat.format((double) byteCount / (1024 * 1024 * 1024));
- }
-
- // More than 1 MB?
- if (byteCount >= 1024 * 1024) {
- NumberFormat megaByteFormat = MEGA_BYTE_FORMAT;
- return megaByteFormat.format((double) byteCount / (1024 * 1024));
- }
-
- // More than 1 KB?
- if (byteCount >= 1024) {
- NumberFormat kiloByteFormat = KILO_BYTE_FORMAT;
- return kiloByteFormat.format((double) byteCount / 1024);
- }
-
- return byteCount + " B";
- }
-
- /**
- * Converts a byte-count to a formatted string suitable for display to the user.
- * For instance:
- * <ul>
- * <li><code>format(918)</code> returns <em>"918 B"</em>.</li>
- * <li><code>format(98765)</code> returns <em>"96 KB"</em>.</li>
- * <li><code>format(1238476)</code> returns <em>"1.2 MB"</em>.</li>
- * </ul>
- * This method assumes that 1 KB is 1024 bytes.
- * This version of the method returns a localized string.
- *
- * @param byteCount The number of bytes.
- * @return The formatted string.
- */
- public static synchronized String formatLocalizedBytes(long byteCount, Context context) {
-
- // More than 1 GB?
- if (byteCount >= 1024 * 1024 * 1024) {
- if (GIGA_BYTE_LOCALIZED_FORMAT == null) {
- GIGA_BYTE_LOCALIZED_FORMAT = new DecimalFormat(context.getResources().getString(R.string.util_bytes_format_gigabyte));
- }
-
- return GIGA_BYTE_LOCALIZED_FORMAT.format((double) byteCount / (1024 * 1024 * 1024));
- }
-
- // More than 1 MB?
- if (byteCount >= 1024 * 1024) {
- if (MEGA_BYTE_LOCALIZED_FORMAT == null) {
- MEGA_BYTE_LOCALIZED_FORMAT = new DecimalFormat(context.getResources().getString(R.string.util_bytes_format_megabyte));
- }
-
- return MEGA_BYTE_LOCALIZED_FORMAT.format((double) byteCount / (1024 * 1024));
- }
-
- // More than 1 KB?
- if (byteCount >= 1024) {
- if (KILO_BYTE_LOCALIZED_FORMAT == null) {
- KILO_BYTE_LOCALIZED_FORMAT = new DecimalFormat(context.getResources().getString(R.string.util_bytes_format_kilobyte));
- }
-
- return KILO_BYTE_LOCALIZED_FORMAT.format((double) byteCount / 1024);
- }
-
- if (BYTE_LOCALIZED_FORMAT == null) {
- BYTE_LOCALIZED_FORMAT = new DecimalFormat(context.getResources().getString(R.string.util_bytes_format_byte));
- }
-
- return BYTE_LOCALIZED_FORMAT.format((double) byteCount);
- }
-
- public static String formatDuration(Integer seconds) {
- if (seconds == null) {
- return null;
- }
-
- int minutes = seconds / 60;
- int secs = seconds % 60;
-
- StringBuilder builder = new StringBuilder(6);
- builder.append(minutes).append(":");
- if (secs < 10) {
- builder.append("0");
- }
- builder.append(secs);
- return builder.toString();
- }
-
- public static boolean equals(Object object1, Object object2) {
- if (object1 == object2) {
- return true;
- }
- if (object1 == null || object2 == null) {
- return false;
- }
- return object1.equals(object2);
-
- }
-
- /**
- * Encodes the given string by using the hexadecimal representation of its UTF-8 bytes.
- *
- * @param s The string to encode.
- * @return The encoded string.
- */
- public static String utf8HexEncode(String s) {
- if (s == null) {
- return null;
- }
- byte[] utf8;
- try {
- utf8 = s.getBytes(Constants.UTF_8);
- } catch (UnsupportedEncodingException x) {
- throw new RuntimeException(x);
- }
- return hexEncode(utf8);
- }
-
- /**
- * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order.
- * The returned array will be double the length of the passed array, as it takes two characters to represent any
- * given byte.
- *
- * @param data Bytes to convert to hexadecimal characters.
- * @return A string containing hexadecimal characters.
- */
- public static String hexEncode(byte[] data) {
- int length = data.length;
- char[] out = new char[length << 1];
- // two characters form the hex value.
- for (int i = 0, j = 0; i < length; i++) {
- out[j++] = HEX_DIGITS[(0xF0 & data[i]) >>> 4];
- out[j++] = HEX_DIGITS[0x0F & data[i]];
- }
- return new String(out);
- }
-
- /**
- * Calculates the MD5 digest and returns the value as a 32 character hex string.
- *
- * @param s Data to digest.
- * @return MD5 digest as a hex string.
- */
- public static String md5Hex(String s) {
- if (s == null) {
- return null;
- }
-
- try {
- MessageDigest md5 = MessageDigest.getInstance("MD5");
- return hexEncode(md5.digest(s.getBytes(Constants.UTF_8)));
- } catch (Exception x) {
- throw new RuntimeException(x.getMessage(), x);
- }
- }
-
- public static boolean isNetworkConnected(Context context) {
- ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
- NetworkInfo networkInfo = manager.getActiveNetworkInfo();
- boolean connected = networkInfo != null && networkInfo.isConnected();
-
- boolean wifiConnected = connected && networkInfo.getType() == ConnectivityManager.TYPE_WIFI;
- boolean wifiRequired = isWifiRequiredForDownload(context);
-
- return connected && (!wifiRequired || wifiConnected);
- }
-
- public static boolean isExternalStoragePresent() {
- return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState());
- }
-
- private static boolean isWifiRequiredForDownload(Context context) {
- SharedPreferences prefs = getPreferences(context);
- return prefs.getBoolean(Constants.PREFERENCES_KEY_WIFI_REQUIRED_FOR_DOWNLOAD, false);
- }
-
- public static void info(Context context, int titleId, int messageId) {
- showDialog(context, android.R.drawable.ic_dialog_info, titleId, messageId);
- }
-
- 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() {
- @Override
- public void onClick(DialogInterface dialog, int i) {
- dialog.dismiss();
- }
- })
- .show();
- }
-
- public static void showPlayingNotification(final Context context, final DownloadServiceImpl downloadService, Handler handler, MusicDirectory.Entry song) {
-
- // Use the same text for the ticker and the expanded notification
- String title = song.getTitle();
- String text = song.getArtist();
-
- // Set the icon, scrolling text and timestamp
- final Notification notification = new Notification(R.drawable.stat_notify_playing, title, System.currentTimeMillis());
- notification.flags |= Notification.FLAG_NO_CLEAR | Notification.FLAG_ONGOING_EVENT;
-
- RemoteViews contentView = new RemoteViews(context.getPackageName(), R.layout.notification);
-
- // Set the album art.
- try {
- int size = context.getResources().getDrawable(R.drawable.unknown_album).getIntrinsicHeight();
- Bitmap bitmap = FileUtil.getAlbumArtBitmap(context, song, size);
- if (bitmap == null) {
- // set default album art
- contentView.setImageViewResource(R.id.notification_image, R.drawable.unknown_album);
- } else {
- contentView.setImageViewBitmap(R.id.notification_image, bitmap);
- }
- } catch (Exception x) {
- Log.w(TAG, "Failed to get notification cover art", x);
- contentView.setImageViewResource(R.id.notification_image, R.drawable.unknown_album);
- }
-
- // set the text for the notifications
- contentView.setTextViewText(R.id.notification_title, title);
- contentView.setTextViewText(R.id.notification_artist, text);
-
- Pair<Integer, Integer> colors = getNotificationTextColors(context);
- if (colors.getFirst() != null) {
- contentView.setTextColor(R.id.notification_title, colors.getFirst());
- }
- if (colors.getSecond() != null) {
- contentView.setTextColor(R.id.notification_artist, colors.getSecond());
- }
-
- notification.contentView = contentView;
-
- Intent notificationIntent = new Intent(context, DownloadActivity.class);
- notification.contentIntent = PendingIntent.getActivity(context, 0, notificationIntent, 0);
-
- // Send the notification and put the service in the foreground.
- handler.post(new Runnable() {
- @Override
- public void run() {
- startForeground(downloadService, Constants.NOTIFICATION_ID_PLAYING, notification);
- }
- });
-
- // Update widget
- SubsonicAppWidgetProvider.getInstance().notifyChange(context, downloadService, true);
- }
-
- public static void hidePlayingNotification(final Context context, final DownloadServiceImpl downloadService, Handler handler) {
-
- // Remove notification and remove the service from the foreground
- handler.post(new Runnable() {
- @Override
- public void run() {
- stopForeground(downloadService, true);
- }
- });
-
- // Update widget
- SubsonicAppWidgetProvider.getInstance().notifyChange(context, downloadService, false);
- }
-
- public static void sleepQuietly(long millis) {
- try {
- Thread.sleep(millis);
- } catch (InterruptedException x) {
- Log.w(TAG, "Interrupted from sleep.", x);
- }
- }
-
- public static void startActivityWithoutTransition(Activity currentActivity, Class<? extends Activity> newActivitiy) {
- startActivityWithoutTransition(currentActivity, new Intent(currentActivity, newActivitiy));
- }
-
- public static void startActivityWithoutTransition(Activity currentActivity, Intent intent) {
- currentActivity.startActivity(intent);
- disablePendingTransition(currentActivity);
- }
-
- public static void disablePendingTransition(Activity activity) {
-
- // Activity.overridePendingTransition() was introduced in Android 2.0. Use reflection to maintain
- // compatibility with 1.5.
- try {
- Method method = Activity.class.getMethod("overridePendingTransition", int.class, int.class);
- method.invoke(activity, 0, 0);
- } catch (Throwable x) {
- // Ignored
- }
- }
-
- public static Drawable createDrawableFromBitmap(Context context, Bitmap bitmap) {
- // BitmapDrawable(Resources, Bitmap) was introduced in Android 1.6. Use reflection to maintain
- // compatibility with 1.5.
- try {
- Constructor<BitmapDrawable> constructor = BitmapDrawable.class.getConstructor(Resources.class, Bitmap.class);
- return constructor.newInstance(context.getResources(), bitmap);
- } catch (Throwable x) {
- return new BitmapDrawable(bitmap);
- }
- }
-
- public static void registerMediaButtonEventReceiver(Context context) {
-
- // Only do it if enabled in the settings.
- SharedPreferences prefs = getPreferences(context);
- boolean enabled = prefs.getBoolean(Constants.PREFERENCES_KEY_MEDIA_BUTTONS, true);
-
- if (enabled) {
-
- // AudioManager.registerMediaButtonEventReceiver() was introduced in Android 2.2.
- // Use reflection to maintain compatibility with 1.5.
- try {
- AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
- ComponentName componentName = new ComponentName(context.getPackageName(), MediaButtonIntentReceiver.class.getName());
- Method method = AudioManager.class.getMethod("registerMediaButtonEventReceiver", ComponentName.class);
- method.invoke(audioManager, componentName);
- } catch (Throwable x) {
- // Ignored.
- }
- }
- }
-
- public static void unregisterMediaButtonEventReceiver(Context context) {
- // AudioManager.unregisterMediaButtonEventReceiver() was introduced in Android 2.2.
- // Use reflection to maintain compatibility with 1.5.
- try {
- AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
- ComponentName componentName = new ComponentName(context.getPackageName(), MediaButtonIntentReceiver.class.getName());
- Method method = AudioManager.class.getMethod("unregisterMediaButtonEventReceiver", ComponentName.class);
- method.invoke(audioManager, componentName);
- } catch (Throwable x) {
- // Ignored.
- }
- }
-
- private static void startForeground(Service service, int notificationId, Notification notification) {
- // Service.startForeground() was introduced in Android 2.0.
- // Use reflection to maintain compatibility with 1.5.
- try {
- Method method = Service.class.getMethod("startForeground", int.class, Notification.class);
- method.invoke(service, notificationId, notification);
- Log.i(TAG, "Successfully invoked Service.startForeground()");
- } catch (Throwable x) {
- NotificationManager notificationManager = (NotificationManager) service.getSystemService(Context.NOTIFICATION_SERVICE);
- notificationManager.notify(Constants.NOTIFICATION_ID_PLAYING, notification);
- Log.i(TAG, "Service.startForeground() not available. Using work-around.");
- }
- }
-
- private static void stopForeground(Service service, boolean removeNotification) {
- // Service.stopForeground() was introduced in Android 2.0.
- // Use reflection to maintain compatibility with 1.5.
- try {
- Method method = Service.class.getMethod("stopForeground", boolean.class);
- method.invoke(service, removeNotification);
- Log.i(TAG, "Successfully invoked Service.stopForeground()");
- } catch (Throwable x) {
- NotificationManager notificationManager = (NotificationManager) service.getSystemService(Context.NOTIFICATION_SERVICE);
- notificationManager.cancel(Constants.NOTIFICATION_ID_PLAYING);
- Log.i(TAG, "Service.stopForeground() not available. Using work-around.");
- }
- }
-
- /**
- * <p>Broadcasts the given song info as the new song being played.</p>
- */
- public static void broadcastNewTrackInfo(Context context, MusicDirectory.Entry song) {
- Intent intent = new Intent(EVENT_META_CHANGED);
-
- if (song != null) {
- intent.putExtra("title", song.getTitle());
- intent.putExtra("artist", song.getArtist());
- intent.putExtra("album", song.getAlbum());
-
- File albumArtFile = FileUtil.getAlbumArtFile(context, song);
- intent.putExtra("coverart", albumArtFile.getAbsolutePath());
- } else {
- intent.putExtra("title", "");
- intent.putExtra("artist", "");
- intent.putExtra("album", "");
- intent.putExtra("coverart", "");
- }
-
- context.sendBroadcast(intent);
- }
-
- /**
- * <p>Broadcasts the given player state as the one being set.</p>
- */
- public static void broadcastPlaybackStatusChange(Context context, PlayerState state) {
- Intent intent = new Intent(EVENT_PLAYSTATE_CHANGED);
-
- switch (state) {
- case STARTED:
- intent.putExtra("state", "play");
- break;
- case STOPPED:
- intent.putExtra("state", "stop");
- break;
- case PAUSED:
- intent.putExtra("state", "pause");
- break;
- case COMPLETED:
- intent.putExtra("state", "complete");
- break;
- default:
- return; // No need to broadcast.
- }
-
- context.sendBroadcast(intent);
- }
-
- /**
- * Resolves the default text color for notifications.
- *
- * Based on http://stackoverflow.com/questions/4867338/custom-notification-layouts-and-text-colors/7320604#7320604
- */
- private static Pair<Integer, Integer> getNotificationTextColors(Context context) {
- if (NOTIFICATION_TEXT_COLORS.getFirst() == null && NOTIFICATION_TEXT_COLORS.getSecond() == null) {
- try {
- Notification notification = new Notification();
- String title = "title";
- String content = "content";
- notification.setLatestEventInfo(context, title, content, null);
- LinearLayout group = new LinearLayout(context);
- ViewGroup event = (ViewGroup) notification.contentView.apply(context, group);
- findNotificationTextColors(event, title, content);
- group.removeAllViews();
- } catch (Exception x) {
- Log.w(TAG, "Failed to resolve notification text colors.", x);
- }
- }
- return NOTIFICATION_TEXT_COLORS;
- }
-
- private static void findNotificationTextColors(ViewGroup group, String title, String content) {
- for (int i = 0; i < group.getChildCount(); i++) {
- if (group.getChildAt(i) instanceof TextView) {
- TextView textView = (TextView) group.getChildAt(i);
- String text = textView.getText().toString();
- if (title.equals(text)) {
- NOTIFICATION_TEXT_COLORS.setFirst(textView.getTextColors().getDefaultColor());
- }
- else if (content.equals(text)) {
- NOTIFICATION_TEXT_COLORS.setSecond(textView.getTextColors().getDefaultColor());
- }
- }
- else if (group.getChildAt(i) instanceof ViewGroup)
- findNotificationTextColors((ViewGroup) group.getChildAt(i), title, content);
- }
- }
-}