From 6ebae86dfbb7fa79d81e6b2485f395eeab7267ef Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Sat, 7 Jul 2012 08:29:52 -0700 Subject: Changed project package to github.daneren2005.subphonic --- .../subsonic/androidapp/util/AlbumView.java | 55 -- .../subsonic/androidapp/util/ArtistAdapter.java | 78 -- .../subsonic/androidapp/util/BackgroundTask.java | 96 --- .../subsonic/androidapp/util/CacheCleaner.java | 171 ----- .../subsonic/androidapp/util/CancellableTask.java | 87 --- .../subsonic/androidapp/util/Constants.java | 91 --- .../subsonic/androidapp/util/EntryAdapter.java | 71 -- .../subsonic/androidapp/util/ErrorDialog.java | 61 -- .../subsonic/androidapp/util/FileUtil.java | 311 -------- .../subsonic/androidapp/util/HorizontalSlider.java | 141 ---- .../subsonic/androidapp/util/ImageLoader.java | 252 ------- .../subsonic/androidapp/util/LRUCache.java | 102 --- .../subsonic/androidapp/util/MergeAdapter.java | 290 ------- .../androidapp/util/ModalBackgroundTask.java | 139 ---- .../subsonic/androidapp/util/MyViewFlipper.java | 53 -- .../sourceforge/subsonic/androidapp/util/Pair.java | 54 -- .../subsonic/androidapp/util/PlaylistAdapter.java | 99 --- .../subsonic/androidapp/util/ProgressListener.java | 27 - .../androidapp/util/SackOfViewsAdapter.java | 181 ----- .../androidapp/util/ShufflePlayBuffer.java | 109 --- .../androidapp/util/SilentBackgroundTask.java | 67 -- .../androidapp/util/SimpleServiceBinder.java | 37 - .../subsonic/androidapp/util/SongView.java | 178 ----- .../androidapp/util/TabActivityBackgroundTask.java | 67 -- .../subsonic/androidapp/util/TimeLimitedCache.java | 55 -- .../sourceforge/subsonic/androidapp/util/Util.java | 829 --------------------- 26 files changed, 3701 deletions(-) delete mode 100644 subsonic-android/src/net/sourceforge/subsonic/androidapp/util/AlbumView.java delete mode 100644 subsonic-android/src/net/sourceforge/subsonic/androidapp/util/ArtistAdapter.java delete mode 100644 subsonic-android/src/net/sourceforge/subsonic/androidapp/util/BackgroundTask.java delete mode 100644 subsonic-android/src/net/sourceforge/subsonic/androidapp/util/CacheCleaner.java delete mode 100644 subsonic-android/src/net/sourceforge/subsonic/androidapp/util/CancellableTask.java delete mode 100644 subsonic-android/src/net/sourceforge/subsonic/androidapp/util/Constants.java delete mode 100644 subsonic-android/src/net/sourceforge/subsonic/androidapp/util/EntryAdapter.java delete mode 100644 subsonic-android/src/net/sourceforge/subsonic/androidapp/util/ErrorDialog.java delete mode 100644 subsonic-android/src/net/sourceforge/subsonic/androidapp/util/FileUtil.java delete mode 100644 subsonic-android/src/net/sourceforge/subsonic/androidapp/util/HorizontalSlider.java delete mode 100644 subsonic-android/src/net/sourceforge/subsonic/androidapp/util/ImageLoader.java delete mode 100644 subsonic-android/src/net/sourceforge/subsonic/androidapp/util/LRUCache.java delete mode 100644 subsonic-android/src/net/sourceforge/subsonic/androidapp/util/MergeAdapter.java delete mode 100644 subsonic-android/src/net/sourceforge/subsonic/androidapp/util/ModalBackgroundTask.java delete mode 100644 subsonic-android/src/net/sourceforge/subsonic/androidapp/util/MyViewFlipper.java delete mode 100644 subsonic-android/src/net/sourceforge/subsonic/androidapp/util/Pair.java delete mode 100644 subsonic-android/src/net/sourceforge/subsonic/androidapp/util/PlaylistAdapter.java delete mode 100644 subsonic-android/src/net/sourceforge/subsonic/androidapp/util/ProgressListener.java delete mode 100644 subsonic-android/src/net/sourceforge/subsonic/androidapp/util/SackOfViewsAdapter.java delete mode 100644 subsonic-android/src/net/sourceforge/subsonic/androidapp/util/ShufflePlayBuffer.java delete mode 100644 subsonic-android/src/net/sourceforge/subsonic/androidapp/util/SilentBackgroundTask.java delete mode 100644 subsonic-android/src/net/sourceforge/subsonic/androidapp/util/SimpleServiceBinder.java delete mode 100644 subsonic-android/src/net/sourceforge/subsonic/androidapp/util/SongView.java delete mode 100644 subsonic-android/src/net/sourceforge/subsonic/androidapp/util/TabActivityBackgroundTask.java delete mode 100644 subsonic-android/src/net/sourceforge/subsonic/androidapp/util/TimeLimitedCache.java delete mode 100644 subsonic-android/src/net/sourceforge/subsonic/androidapp/util/Util.java (limited to 'subsonic-android/src/net/sourceforge/subsonic/androidapp/util') 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 . - - 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 . - - 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 implements SectionIndexer { - - // Both arrays are indexed by section ID. - private final Object[] sections; - private final Integer[] positions; - - public ArtistAdapter(Context context, List artists) { - super(context, R.layout.artist_list_item, artists); - - Set sectionSet = new LinkedHashSet(30); - List positionList = new ArrayList(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 . - - 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 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 files = new ArrayList(); - List dirs = new ArrayList(); - - findCandidatesForDeletion(FileUtil.getMusicDirectory(context), files, dirs); - sortByAscendingModificationTime(files); - - Set 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 dirs, Set 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 files, Set 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 files, List 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 files) { - Collections.sort(files, new Comparator() { - @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 findUndeletableFiles() { - Set undeletable = new HashSet(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 . - - 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 = new AtomicReference(); - private final AtomicReference cancelListener = new AtomicReference(); - - 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 . - - 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 . - - 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 { - - private final SubsonicTabActivity activity; - private final ImageLoader imageLoader; - private final boolean checkable; - - public EntryAdapter(SubsonicTabActivity activity, ImageLoader imageLoader, List 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 . - - 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 . - - 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 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 listFiles(File dir) { - File[] files = dir.listFiles(); - if (files == null) { - Log.w(TAG, "Failed to list children for " + dir.getPath()); - return new TreeSet(); - } - - return new TreeSet(Arrays.asList(files)); - } - - public static SortedSet listMusicFiles(File dir) { - SortedSet files = listFiles(dir); - Iterator 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 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 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 . - - 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 . - - 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. - *

- * 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 cache = new LRUCache(100); - private final BlockingQueue queue; - private final int imageSizeDefault; - private final int imageSizeLarge; - private Drawable largeUnknownImage; - - public ImageLoader(Context context) { - queue = new LinkedBlockingQueue(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 . - - 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{ - - private final int capacity; - private final Map map; - - public LRUCache(int capacity) { - map = new HashMap(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 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 value; - private long timestamp; - - public TimestampedValue(V value) { - this.value = new SoftReference(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. - *

- * 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 pieces = new ArrayList(); - - /** - * 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 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 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 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 . - - 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 extends BackgroundTask { - - 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 . - - 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 . - - Copyright 2009 (C) Sindre Mehus - */ -package net.sourceforge.subsonic.androidapp.util; - -import java.io.Serializable; - -/** - * @author Sindre Mehus - */ -public class Pair 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 . - - 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 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 playlists) { - super(context, R.layout.playlist_list_item, playlists); - - Set sectionSet = new LinkedHashSet(30); - List positionList = new ArrayList(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 { - @Override - public int compare(Playlist playlist1, Playlist playlist2) { - return playlist1.getName().compareToIgnoreCase(playlist2.getName()); - } - - public static List sort(List 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 . - - 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. - *

- * If you supply a size, you must implement newView(), to - * create a required view. The adapter will then cache these - * views. - *

- * 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. - *

- * Subclasses may also wish to override areAllItemsEnabled() - * (default: false) and isEnabled() (default: false), if some - * of their rows should be selectable. - *

- * It is assumed each view is unique, and therefore will not - * get recycled. - *

- * 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 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(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 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 . - - 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 buffer = new ArrayList(); - 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 get(int size) { - clearBufferIfnecessary(); - - List result = new ArrayList(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 . - - Copyright 2010 (C) Sindre Mehus - */ -package net.sourceforge.subsonic.androidapp.util; - -import android.app.Activity; - -/** - * @author Sindre Mehus - */ -public abstract class SilentBackgroundTask extends BackgroundTask { - - 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 . - - Copyright 2009 (C) Sindre Mehus - */ -package net.sourceforge.subsonic.androidapp.util; - -import android.os.Binder; - -/** - * @author Sindre Mehus - */ -public class SimpleServiceBinder 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 . - - 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 INSTANCES = new WeakHashMap(); - 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 extends BackgroundTask { - - 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 . - - 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 { - - private SoftReference 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(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 . - - 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 SERVER_REST_VERSIONS = new ConcurrentHashMap(); - - // 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 NOTIFICATION_TEXT_COLORS = new Pair(); - 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 InputStream as a byte[]. - *

- * This method buffers the input internally, so there is no need to use a - * BufferedInputStream. - * - * @param input the InputStream 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: - *

    - *
  • format(918) returns "918 B".
  • - *
  • format(98765) returns "96 KB".
  • - *
  • format(1238476) returns "1.2 MB".
  • - *
- * 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: - *
    - *
  • format(918) returns "918 B".
  • - *
  • format(98765) returns "96 KB".
  • - *
  • format(1238476) returns "1.2 MB".
  • - *
- * 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 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 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 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."); - } - } - - /** - *

Broadcasts the given song info as the new song being played.

- */ - 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); - } - - /** - *

Broadcasts the given player state as the one being set.

- */ - 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 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); - } - } -} -- cgit v1.2.3