diff options
Diffstat (limited to 'subsonic-android')
16 files changed, 1367 insertions, 419 deletions
diff --git a/subsonic-android/res/layout/home.xml b/subsonic-android/res/layout/home.xml new file mode 100644 index 00000000..56b013fe --- /dev/null +++ b/subsonic-android/res/layout/home.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+
+ <View
+ android:layout_width="fill_parent"
+ android:layout_height="1px"
+ android:background="@color/dividerColor"/>
+
+ <ListView
+ android:id="@+id/main_list"
+ android:layout_width="fill_parent"
+ android:layout_height="0px"
+ android:layout_weight="1"/>
+
+ <View android:id="@+id/main_dummy"
+ android:layout_width="0px"
+ android:layout_height="0px"/>
+
+ <include
+ layout="@layout/button_bar"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"/>
+
+</LinearLayout>
+
diff --git a/subsonic-android/res/layout/main.xml b/subsonic-android/res/layout/main.xml index 56b013fe..f5c95b3f 100644 --- a/subsonic-android/res/layout/main.xml +++ b/subsonic-android/res/layout/main.xml @@ -1,28 +1,25 @@ -<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent">
-
- <View
- android:layout_width="fill_parent"
- android:layout_height="1px"
- android:background="@color/dividerColor"/>
-
- <ListView
- android:id="@+id/main_list"
- android:layout_width="fill_parent"
- android:layout_height="0px"
- android:layout_weight="1"/>
-
- <View android:id="@+id/main_dummy"
- android:layout_width="0px"
- android:layout_height="0px"/>
-
- <include
- layout="@layout/button_bar"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"/>
-
-</LinearLayout>
-
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:gravity="center_horizontal" + android:orientation="vertical" > + + <android.support.v4.view.ViewPager + android:id="@+id/pager" + android:layout_width="match_parent" + android:layout_height="0px" + android:layout_weight="1" > + + <!-- Optional page title view --> + + <android.support.v4.view.PagerTitleStrip + android:id="@+id/pager_title_strip" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="top" + android:background="#33b5e5" + android:paddingBottom="4dp" + android:paddingTop="4dp" + android:textColor="#fff" /> + </android.support.v4.view.ViewPager> +</LinearLayout>
\ No newline at end of file diff --git a/subsonic-android/res/layout/select_artist.xml b/subsonic-android/res/layout/select_artist.xml index bebfdec4..4dd3471f 100644 --- a/subsonic-android/res/layout/select_artist.xml +++ b/subsonic-android/res/layout/select_artist.xml @@ -1,24 +1,24 @@ <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent">
+ android:id="@+id/select_artist_layout"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
- <View
- android:layout_width="fill_parent"
- android:layout_height="1px"
- android:background="@color/dividerColor"/>
+ <View
+ android:layout_width="fill_parent"
+ android:layout_height="1px"
+ android:background="@color/dividerColor"/>
- <include layout="@layout/tab_progress"/>
+ <include layout="@layout/tab_progress"/>
- <ListView android:id="@+id/select_artist_list"
- android:textFilterEnabled="true"
- android:fastScrollEnabled="true"
- android:layout_width="fill_parent"
- android:layout_height="0dip"
- android:layout_weight="1.0"/>
-
- <include layout="@layout/button_bar"/>
+ <ListView android:id="@+id/select_artist_list"
+ android:textFilterEnabled="true"
+ android:fastScrollEnabled="true"
+ android:layout_width="fill_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1.0"/>
+ <include layout="@layout/button_bar"/>
</LinearLayout>
diff --git a/subsonic-android/src/github/daneren2005/dsub/activity/MainActivity.java b/subsonic-android/src/github/daneren2005/dsub/activity/MainActivity.java index fd2f31d4..c012e4c8 100644 --- a/subsonic-android/src/github/daneren2005/dsub/activity/MainActivity.java +++ b/subsonic-android/src/github/daneren2005/dsub/activity/MainActivity.java @@ -1,356 +1,108 @@ -/* - This file is part of Subsonic. - - Subsonic is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Subsonic is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Subsonic. If not, see <http://www.gnu.org/licenses/>. - - Copyright 2009 (C) Sindre Mehus - */ - package github.daneren2005.dsub.activity; -import java.util.Arrays; - +import android.content.Intent; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentPagerAdapter; +import android.support.v4.view.ViewPager; import github.daneren2005.dsub.R; -import github.daneren2005.dsub.service.DownloadService; +import github.daneren2005.dsub.fragments.MainFragment; +import github.daneren2005.dsub.fragments.SelectArtistFragment; +import github.daneren2005.dsub.fragments.SelectPlaylistFragment; import github.daneren2005.dsub.service.DownloadServiceImpl; -import github.daneren2005.dsub.util.Constants; -import github.daneren2005.dsub.util.MergeAdapter; import github.daneren2005.dsub.util.Util; -import github.daneren2005.dsub.util.FileUtil; -import com.actionbarsherlock.view.Menu; -import com.actionbarsherlock.view.MenuItem; -import com.actionbarsherlock.view.MenuInflater; -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.net.Uri; -import android.os.Bundle; -import android.os.StatFs; -import android.preference.PreferenceManager; -import android.view.ContextMenu; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.AdapterView; -import android.widget.ImageButton; -import android.widget.ListView; -import android.widget.TextView; -import android.widget.PopupWindow; -import github.daneren2005.dsub.util.*; -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; -public class MainActivity extends SubsonicTabActivity { +public class MainActivity extends SubsonicActivity { + private static final String TAG = MainActivity.class.getSimpleName(); + private static boolean infoDialogDisplayed; + private MainActivityPagerAdapter pagerAdapter; + private ViewPager viewPager; - private static final int MENU_GROUP_SERVER = 10; - private static final int MENU_ITEM_SERVER_1 = 101; - private static final int MENU_ITEM_SERVER_2 = 102; - private static final int MENU_ITEM_SERVER_3 = 103; + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.main); - private static boolean infoDialogDisplayed; + pagerAdapter = new MainActivityPagerAdapter(getSupportFragmentManager()); + viewPager = (ViewPager) findViewById(R.id.pager); + viewPager.setAdapter(pagerAdapter); + } - /** - * Called when the activity is first created. - */ - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - if (getIntent().hasExtra(Constants.INTENT_EXTRA_NAME_EXIT)) { - exit(); - } - setContentView(R.layout.main); + @Override + protected void onPostCreate(Bundle bundle) { + super.onPostCreate(bundle); - loadSettings(); + getSupportActionBar().setDisplayHomeAsUpEnabled(false); + getSupportActionBar().setHomeButtonEnabled(false); - View buttons = LayoutInflater.from(this).inflate(R.layout.main_buttons, null); + showInfoDialog(); + } - final View serverButton = buttons.findViewById(R.id.main_select_server); - final TextView serverTextView = (TextView) serverButton.findViewById(R.id.main_select_server_2); - final TextView offlineButton = (TextView) buttons.findViewById(R.id.main_offline); - offlineButton.setText(Util.isOffline(this) ? R.string.main_online : R.string.main_offline); + @Override + public void onResume() { + super.onResume(); + } - final View albumsTitle = buttons.findViewById(R.id.main_albums); - final View albumsNewestButton = buttons.findViewById(R.id.main_albums_newest); - final View albumsRandomButton = buttons.findViewById(R.id.main_albums_random); - final View albumsHighestButton = buttons.findViewById(R.id.main_albums_highest); - final View albumsRecentButton = buttons.findViewById(R.id.main_albums_recent); - final View albumsFrequentButton = buttons.findViewById(R.id.main_albums_frequent); - final View albumsStarredButton = buttons.findViewById(R.id.main_albums_starred); + private void exit() { + stopService(new Intent(this, DownloadServiceImpl.class)); + finish(); + } - final View dummyView = findViewById(R.id.main_dummy); + private void showInfoDialog() { + if (!infoDialogDisplayed) { + infoDialogDisplayed = true; + if (Util.getRestUrl(this, null).contains("demo.subsonic.org")) { + Util.info(this, R.string.main_welcome_title, R.string.main_welcome_text); + } + } + } - int instance = Util.getActiveServer(this); - String name = Util.getServerName(this, instance); - serverTextView.setText(name); + public class MainActivityPagerAdapter extends FragmentPagerAdapter { - ListView list = (ListView) findViewById(R.id.main_list); + private final String [] titles = new String [] { + "Home", + "Library", + "Playlists" + }; - MergeAdapter adapter = new MergeAdapter(); - if (!Util.isOffline(this)) { - adapter.addViews(Arrays.asList(serverButton), true); + public MainActivityPagerAdapter(FragmentManager fm) { + super(fm); } - adapter.addView(offlineButton, true); - if (!Util.isOffline(this)) { - adapter.addView(albumsTitle, false); - adapter.addViews(Arrays.asList(albumsNewestButton, albumsRandomButton, albumsHighestButton, albumsStarredButton, albumsRecentButton, albumsFrequentButton), true); - } - list.setAdapter(adapter); - registerForContextMenu(dummyView); - list.setOnItemClickListener(new AdapterView.OnItemClickListener() { - @Override - public void onItemClick(AdapterView<?> parent, View view, int position, long id) { - if (view == serverButton) { - dummyView.showContextMenu(); - } else if (view == offlineButton) { - toggleOffline(); - } else if (view == albumsNewestButton) { - showAlbumList("newest"); - } else if (view == albumsRandomButton) { - showAlbumList("random"); - } else if (view == albumsHighestButton) { - showAlbumList("highest"); - } else if (view == albumsRecentButton) { - showAlbumList("recent"); - } else if (view == albumsFrequentButton) { - showAlbumList("frequent"); - } else if (view == albumsStarredButton) { - showAlbumList("starred"); + @Override + public Fragment getItem(int i) { + Fragment fragment; + Bundle args = new Bundle(); + switch(i) { + case 0: + fragment = new MainFragment(); + break; + case 1: + fragment = new SelectArtistFragment(); + break; + case 2: + fragment = new SelectPlaylistFragment(); + break; + default: + fragment = null; + } + + if (fragment != null) { + fragment.setArguments(args); + } + + return fragment; } - } - }); - - // Title: Subsonic - setTitle(R.string.common_appname); - showInfoDialog(); - - checkUpdates(); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - MenuInflater inflater = getSupportMenuInflater(); - inflater.inflate(R.menu.main, menu); - return true; - } - @Override - public boolean onOptionsItemSelected(MenuItem item) { - Intent intent; - switch (item.getItemId()) { - case R.id.menu_shuffle: - onShuffleRequested(); - return true; - case R.id.menu_search: - onSearchRequested(); - return true; - case R.id.menu_exit: - intent = new Intent(this, MainActivity.class); - intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - intent.putExtra(Constants.INTENT_EXTRA_NAME_EXIT, true); - Util.startActivityWithoutTransition(this, intent); - return true; - case R.id.menu_settings: - startActivity(new Intent(this, SettingsActivity.class)); - return true; - case R.id.menu_help: - startActivity(new Intent(this, HelpActivity.class)); - return true; - case R.id.menu_log: - getLogs(); - return true; - case R.id.menu_about: - showAboutDialog(); - return true; - } - - return false; - } - - private void loadSettings() { - PreferenceManager.setDefaultValues(this, R.xml.settings, false); - SharedPreferences prefs = Util.getPreferences(this); - if (!prefs.contains(Constants.PREFERENCES_KEY_CACHE_LOCATION)) { - SharedPreferences.Editor editor = prefs.edit(); - editor.putString(Constants.PREFERENCES_KEY_CACHE_LOCATION, FileUtil.getDefaultMusicDirectory().getPath()); - editor.commit(); - } - - if (!prefs.contains(Constants.PREFERENCES_KEY_OFFLINE)) { - SharedPreferences.Editor editor = prefs.edit(); - editor.putBoolean(Constants.PREFERENCES_KEY_OFFLINE, false); - editor.putInt(Constants.PREFERENCES_KEY_SERVER_INSTANCE, 1); - editor.commit(); - } - } - - @Override - protected void onResume() { - super.onResume(); - } - - @Override - public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) { - super.onCreateContextMenu(menu, view, menuInfo); - - android.view.MenuItem menuItem1 = menu.add(MENU_GROUP_SERVER, MENU_ITEM_SERVER_1, MENU_ITEM_SERVER_1, Util.getServerName(this, 1)); - android.view.MenuItem menuItem2 = menu.add(MENU_GROUP_SERVER, MENU_ITEM_SERVER_2, MENU_ITEM_SERVER_2, Util.getServerName(this, 2)); - android.view.MenuItem menuItem3 = menu.add(MENU_GROUP_SERVER, MENU_ITEM_SERVER_3, MENU_ITEM_SERVER_3, Util.getServerName(this, 3)); - menu.setGroupCheckable(MENU_GROUP_SERVER, true, true); - menu.setHeaderTitle(R.string.main_select_server); - - switch (Util.getActiveServer(this)) { - case 1: - menuItem1.setChecked(true); - break; - case 2: - menuItem2.setChecked(true); - break; - case 3: - menuItem3.setChecked(true); - break; - } - } - - @Override - public boolean onContextItemSelected(android.view.MenuItem menuItem) { - switch (menuItem.getItemId()) { - case MENU_ITEM_SERVER_1: - setActiveServer(1); - break; - case MENU_ITEM_SERVER_2: - setActiveServer(2); - break; - case MENU_ITEM_SERVER_3: - setActiveServer(3); - break; - default: - return super.onContextItemSelected(menuItem); - } - - // Restart activity - restart(); - return true; - } - - private void setActiveServer(int instance) { - if (Util.getActiveServer(this) != instance) { - DownloadService service = getDownloadService(); - if (service != null) { - service.clearIncomplete(); - } - Util.setActiveServer(this, instance); - } - } - - private void exit() { - stopService(new Intent(this, DownloadServiceImpl.class)); - finish(); - } - - private void showInfoDialog() { - if (!infoDialogDisplayed) { - infoDialogDisplayed = true; - if (Util.getRestUrl(this, null).contains("demo.subsonic.org")) { - Util.info(this, R.string.main_welcome_title, R.string.main_welcome_text); - } - } - } - - private void showAboutDialog() { - try { - File rootFolder = FileUtil.getMusicDirectory(MainActivity.this); - StatFs stat = new StatFs(rootFolder.getPath()); - long bytesTotalFs = (long) stat.getBlockCount() * (long) stat.getBlockSize(); - long bytesAvailableFs = (long) stat.getAvailableBlocks() * (long) stat.getBlockSize(); - - String msg = getResources().getString(R.string.main_about_text, - getPackageManager().getPackageInfo(getPackageName(), 0).versionName, - Util.formatBytes(FileUtil.getUsedSize(MainActivity.this, rootFolder)), - Util.formatBytes(Util.getCacheSizeMB(MainActivity.this) * 1024L * 1024L), - Util.formatBytes(bytesAvailableFs), - Util.formatBytes(bytesTotalFs)); - Util.info(this, R.string.main_about_title, msg); - } catch(Exception e) { - Util.toast(MainActivity.this, "Failed to open dialog"); + @Override + public int getCount() { + return 3; } - // Util.toast(MainActivity.this, "Size: " + Util.formatBytes(FileUtil.getUsedSize(MainActivity.this, FileUtil.getMusicDirectory(MainActivity.this)))); - } - - private void showAlbumList(String type) { - Intent intent = new Intent(this, SelectAlbumActivity.class); - intent.putExtra(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TYPE, type); - intent.putExtra(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, 20); - intent.putExtra(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_OFFSET, 0); - Util.startActivityWithoutTransition(this, intent); - } - - private void toggleOffline() { - Util.setOffline(this, !Util.isOffline(this)); - restart(); - } - - private void getLogs() { - try { - final String version = getPackageManager().getPackageInfo(getPackageName(), 0).versionName; - new ModalBackgroundTask<File>(this, false) { - @Override - protected File doInBackground() throws Throwable { - updateProgress("Gathering Logs"); - File logcat = new File(FileUtil.getSubsonicDirectory(), "logcat.txt"); - Process logcatProc = null; - try { - List<String> progs = new ArrayList<String>(); - progs.add("logcat"); - progs.add("-v"); - progs.add("time"); - progs.add("-d"); - progs.add("-f"); - progs.add(logcat.getPath()); - progs.add("*:I"); - - logcatProc = Runtime.getRuntime().exec(progs.toArray(new String[0])); - logcatProc.waitFor(); - } catch(Exception e) { - Util.toast(MainActivity.this, "Failed to gather logs"); - } finally { - if(logcatProc != null) { - logcatProc.destroy(); - } - } - - return logcat; - } - - @Override - protected void done(File logcat) { - Intent email = new Intent(android.content.Intent.ACTION_SEND); - email.setType("text/plain"); - email.putExtra(Intent.EXTRA_EMAIL, new String[] {"daneren2005@gmail.com"}); - email.putExtra(Intent.EXTRA_SUBJECT, "DSub " + version + " Error Logs"); - email.putExtra(Intent.EXTRA_TEXT, "Describe the problem here"); - Uri attachment = Uri.fromFile(logcat); - email.putExtra(Intent.EXTRA_STREAM, attachment); - startActivity(email); - } - }.execute(); - } catch(Exception e) {} + @Override + public CharSequence getPageTitle(int position) { + return titles[position]; + } } -}
\ No newline at end of file +} diff --git a/subsonic-android/src/github/daneren2005/dsub/activity/SubsonicActivity.java b/subsonic-android/src/github/daneren2005/dsub/activity/SubsonicActivity.java new file mode 100644 index 00000000..bada0da0 --- /dev/null +++ b/subsonic-android/src/github/daneren2005/dsub/activity/SubsonicActivity.java @@ -0,0 +1,194 @@ +package github.daneren2005.dsub.activity;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.media.AudioManager;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Environment;
+import android.util.Log;
+import android.view.KeyEvent;
+import com.actionbarsherlock.app.ActionBar;
+import com.actionbarsherlock.app.SherlockFragmentActivity;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuItem;
+import com.actionbarsherlock.view.Window;
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.service.DownloadService;
+import github.daneren2005.dsub.service.DownloadServiceImpl;
+import github.daneren2005.dsub.updates.Updater;
+import github.daneren2005.dsub.util.ImageLoader;
+import github.daneren2005.dsub.util.Util;
+import java.io.File;
+import java.io.PrintWriter;
+
+public class SubsonicActivity extends SherlockFragmentActivity {
+ private static final String TAG = SubsonicActivity.class.getSimpleName();
+ private static ImageLoader IMAGE_LOADER;
+ protected static String theme;
+ private boolean destroyed = false;
+
+ @Override
+ protected void onCreate(Bundle bundle) {
+ setUncaughtExceptionHandler();
+ applyTheme();
+ super.onCreate(bundle);
+ startService(new Intent(this, DownloadServiceImpl.class));
+ setVolumeControlStream(AudioManager.STREAM_MUSIC);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ Util.registerMediaButtonEventReceiver(this);
+
+ // Make sure to update theme
+ if (theme != null && !theme.equals(Util.getTheme(this))) {
+ restart();
+ }
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ destroyed = true;
+ getImageLoader().clear();
+ }
+
+ @Override
+ public void finish() {
+ super.finish();
+ Util.disablePendingTransition(this);
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ boolean isVolumeDown = keyCode == KeyEvent.KEYCODE_VOLUME_DOWN;
+ boolean isVolumeUp = keyCode == KeyEvent.KEYCODE_VOLUME_UP;
+ boolean isVolumeAdjust = isVolumeDown || isVolumeUp;
+ boolean isJukebox = getDownloadService() != null && getDownloadService().isJukeboxEnabled();
+
+ if (isVolumeAdjust && isJukebox) {
+ getDownloadService().adjustJukeboxVolume(isVolumeUp);
+ return true;
+ }
+ return super.onKeyDown(keyCode, event);
+ }
+
+ protected void restart() {
+ Intent intent = new Intent(this, this.getClass());
+ intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ intent.putExtras(getIntent());
+ Util.startActivityWithoutTransition(this, intent);
+ }
+
+ private void applyTheme() {
+ theme = Util.getTheme(this);
+ if ("dark".equals(theme)) {
+ setTheme(R.style.Theme_DSub_Dark);
+ } else if ("light".equals(theme)) {
+ setTheme(R.style.Theme_DSub_Light);
+ } else if ("dark_fullscreen".equals(theme)) {
+ setTheme(R.style.Theme_DSub_Dark_Fullscreen);
+ } else if ("light_fullscreen".equals(theme)) {
+ setTheme(R.style.Theme_DSub_Light_Fullscreen);
+ } else if("holo".equals(theme)) {
+ setTheme(R.style.Theme_DSub_Holo);
+ } else if("holo_fullscreen".equals(theme)) {
+ setTheme(R.style.Theme_DSub_Holo_Fullscreen);
+ }else {
+ setTheme(R.style.Theme_DSub_Holo);
+ }
+ }
+
+ public boolean isDestroyed() {
+ return destroyed;
+ }
+
+ public synchronized ImageLoader getImageLoader() {
+ if (IMAGE_LOADER == null) {
+ IMAGE_LOADER = new ImageLoader(this);
+ }
+ return IMAGE_LOADER;
+ }
+ public synchronized static ImageLoader getStaticImageLoader(Context context) {
+ if (IMAGE_LOADER == null) {
+ IMAGE_LOADER = new ImageLoader(context);
+ }
+ return IMAGE_LOADER;
+ }
+
+ public DownloadService getDownloadService() {
+ // If service is not available, request it to start and wait for it.
+ for (int i = 0; i < 5; i++) {
+ DownloadService downloadService = DownloadServiceImpl.getInstance();
+ if (downloadService != null) {
+ return downloadService;
+ }
+ Log.w(TAG, "DownloadService not running. Attempting to start it.");
+ startService(new Intent(this, DownloadServiceImpl.class));
+ Util.sleepQuietly(50L);
+ }
+ return DownloadServiceImpl.getInstance();
+ }
+
+ private void setUncaughtExceptionHandler() {
+ Thread.UncaughtExceptionHandler handler = Thread.getDefaultUncaughtExceptionHandler();
+ if (!(handler instanceof SubsonicActivity.SubsonicUncaughtExceptionHandler)) {
+ Thread.setDefaultUncaughtExceptionHandler(new SubsonicActivity.SubsonicUncaughtExceptionHandler(this));
+ }
+ }
+
+ /**
+ * Logs the stack trace of uncaught exceptions to a file on the SD card.
+ */
+ private static class SubsonicUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
+
+ private final Thread.UncaughtExceptionHandler defaultHandler;
+ private final Context context;
+
+ private SubsonicUncaughtExceptionHandler(Context context) {
+ this.context = context;
+ defaultHandler = Thread.getDefaultUncaughtExceptionHandler();
+ }
+
+ @Override
+ public void uncaughtException(Thread thread, Throwable throwable) {
+ File file = null;
+ PrintWriter printWriter = null;
+ try {
+
+ PackageInfo packageInfo = context.getPackageManager().getPackageInfo("github.daneren2005.dsub", 0);
+ file = new File(Environment.getExternalStorageDirectory(), "subsonic-stacktrace.txt");
+ printWriter = new PrintWriter(file);
+ printWriter.println("Android API level: " + Build.VERSION.SDK);
+ printWriter.println("Subsonic version name: " + packageInfo.versionName);
+ printWriter.println("Subsonic version code: " + packageInfo.versionCode);
+ printWriter.println();
+ throwable.printStackTrace(printWriter);
+ Log.i(TAG, "Stack trace written to " + file);
+ } catch (Throwable x) {
+ Log.e(TAG, "Failed to write stack trace to " + file, x);
+ } finally {
+ Util.close(printWriter);
+ if (defaultHandler != null) {
+ defaultHandler.uncaughtException(thread, throwable);
+ }
+
+ }
+ }
+ }
+
+ public void checkUpdates() {
+ try {
+ String version = getPackageManager().getPackageInfo(getPackageName(), 0).versionName;
+ int ver = Integer.parseInt(version.replace(".", ""));
+ Updater updater = new Updater(ver);
+ updater.checkUpdates(SubsonicActivity.this);
+ }
+ catch(Exception e) {
+
+ }
+ }
+}
diff --git a/subsonic-android/src/github/daneren2005/dsub/activity/SubsonicTabActivity.java b/subsonic-android/src/github/daneren2005/dsub/activity/SubsonicTabActivity.java index 938bf624..4df7fb96 100644 --- a/subsonic-android/src/github/daneren2005/dsub/activity/SubsonicTabActivity.java +++ b/subsonic-android/src/github/daneren2005/dsub/activity/SubsonicTabActivity.java @@ -124,9 +124,9 @@ public class SubsonicTabActivity extends SherlockActivity { }
});
- if (this instanceof MainActivity) {
+ /*if (this instanceof MainActivity) {
homeButton.setEnabled(false);
- } else if (this instanceof SelectAlbumActivity || this instanceof SelectArtistActivity) {
+ } else*/ if (this instanceof SelectAlbumActivity || this instanceof SelectArtistActivity) {
musicButton.setEnabled(false);
} else if (this instanceof SelectPlaylistActivity) {
playlistButton.setEnabled(false);
diff --git a/subsonic-android/src/github/daneren2005/dsub/fragments/MainFragment.java b/subsonic-android/src/github/daneren2005/dsub/fragments/MainFragment.java new file mode 100644 index 00000000..d179c150 --- /dev/null +++ b/subsonic-android/src/github/daneren2005/dsub/fragments/MainFragment.java @@ -0,0 +1,194 @@ +package github.daneren2005.dsub.fragments;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.preference.PreferenceManager;
+import android.view.ContextMenu;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ListView;
+import android.widget.TextView;
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.service.DownloadService;
+import github.daneren2005.dsub.util.Constants;
+import github.daneren2005.dsub.util.FileUtil;
+import github.daneren2005.dsub.util.MergeAdapter;
+import github.daneren2005.dsub.util.Util;
+import java.util.Arrays;
+
+public class MainFragment extends SubsonicTabFragment {
+ private LayoutInflater inflater;
+
+ private static final int MENU_GROUP_SERVER = 10;
+ private static final int MENU_ITEM_SERVER_1 = 101;
+ private static final int MENU_ITEM_SERVER_2 = 102;
+ private static final int MENU_ITEM_SERVER_3 = 103;
+
+ @Override
+ public void onCreate(Bundle bundle) {
+ super.onCreate(bundle);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
+ this.inflater = inflater;
+ rootView = inflater.inflate(R.layout.home, container, false);
+
+ loadSettings();
+ createLayout();
+
+ return rootView;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ }
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
+ super.onCreateContextMenu(menu, view, menuInfo);
+
+ android.view.MenuItem menuItem1 = menu.add(MENU_GROUP_SERVER, MENU_ITEM_SERVER_1, MENU_ITEM_SERVER_1, Util.getServerName(context, 1));
+ android.view.MenuItem menuItem2 = menu.add(MENU_GROUP_SERVER, MENU_ITEM_SERVER_2, MENU_ITEM_SERVER_2, Util.getServerName(context, 2));
+ android.view.MenuItem menuItem3 = menu.add(MENU_GROUP_SERVER, MENU_ITEM_SERVER_3, MENU_ITEM_SERVER_3, Util.getServerName(context, 3));
+ menu.setGroupCheckable(MENU_GROUP_SERVER, true, true);
+ menu.setHeaderTitle(R.string.main_select_server);
+
+ switch (Util.getActiveServer(context)) {
+ case 1:
+ menuItem1.setChecked(true);
+ break;
+ case 2:
+ menuItem2.setChecked(true);
+ break;
+ case 3:
+ menuItem3.setChecked(true);
+ break;
+ }
+ }
+
+ @Override
+ public boolean onContextItemSelected(android.view.MenuItem menuItem) {
+ switch (menuItem.getItemId()) {
+ case MENU_ITEM_SERVER_1:
+ setActiveServer(1);
+ break;
+ case MENU_ITEM_SERVER_2:
+ setActiveServer(2);
+ break;
+ case MENU_ITEM_SERVER_3:
+ setActiveServer(3);
+ break;
+ default:
+ return super.onContextItemSelected(menuItem);
+ }
+
+ return true;
+ }
+
+ @Override
+ protected void refresh() {
+ createLayout();
+ }
+
+ private void createLayout() {
+ View buttons = inflater.inflate(R.layout.main_buttons, null);
+
+ final View serverButton = buttons.findViewById(R.id.main_select_server);
+ final TextView serverTextView = (TextView) serverButton.findViewById(R.id.main_select_server_2);
+ final TextView offlineButton = (TextView) buttons.findViewById(R.id.main_offline);
+ offlineButton.setText(Util.isOffline(context) ? R.string.main_online : R.string.main_offline);
+
+ final View albumsTitle = buttons.findViewById(R.id.main_albums);
+ final View albumsNewestButton = buttons.findViewById(R.id.main_albums_newest);
+ final View albumsRandomButton = buttons.findViewById(R.id.main_albums_random);
+ final View albumsHighestButton = buttons.findViewById(R.id.main_albums_highest);
+ final View albumsRecentButton = buttons.findViewById(R.id.main_albums_recent);
+ final View albumsFrequentButton = buttons.findViewById(R.id.main_albums_frequent);
+ final View albumsStarredButton = buttons.findViewById(R.id.main_albums_starred);
+
+ final View dummyView = rootView.findViewById(R.id.main_dummy);
+
+ int instance = Util.getActiveServer(context);
+ String name = Util.getServerName(context, instance);
+ serverTextView.setText(name);
+
+ ListView list = (ListView) rootView.findViewById(R.id.main_list);
+
+ MergeAdapter adapter = new MergeAdapter();
+ if (!Util.isOffline(context)) {
+ adapter.addViews(Arrays.asList(serverButton), true);
+ }
+ adapter.addView(offlineButton, true);
+ if (!Util.isOffline(context)) {
+ adapter.addView(albumsTitle, false);
+ adapter.addViews(Arrays.asList(albumsNewestButton, albumsRandomButton, albumsHighestButton, albumsStarredButton, albumsRecentButton, albumsFrequentButton), true);
+ }
+ list.setAdapter(adapter);
+ registerForContextMenu(dummyView);
+
+ list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ if (view == serverButton) {
+ dummyView.showContextMenu();
+ } else if (view == offlineButton) {
+ toggleOffline();
+ } else if (view == albumsNewestButton) {
+ // showAlbumList("newest");
+ } else if (view == albumsRandomButton) {
+ // showAlbumList("random");
+ } else if (view == albumsHighestButton) {
+ // showAlbumList("highest");
+ } else if (view == albumsRecentButton) {
+ // showAlbumList("recent");
+ } else if (view == albumsFrequentButton) {
+ // showAlbumList("frequent");
+ } else if (view == albumsStarredButton) {
+ // showAlbumList("starred");
+ }
+ }
+ });
+ }
+
+ private void loadSettings() {
+ PreferenceManager.setDefaultValues(context, R.xml.settings, false);
+ SharedPreferences prefs = Util.getPreferences(context);
+ if (!prefs.contains(Constants.PREFERENCES_KEY_CACHE_LOCATION)) {
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putString(Constants.PREFERENCES_KEY_CACHE_LOCATION, FileUtil.getDefaultMusicDirectory().getPath());
+ editor.commit();
+ }
+
+ if (!prefs.contains(Constants.PREFERENCES_KEY_OFFLINE)) {
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putBoolean(Constants.PREFERENCES_KEY_OFFLINE, false);
+ editor.putInt(Constants.PREFERENCES_KEY_SERVER_INSTANCE, 1);
+ editor.commit();
+ }
+ }
+
+ private void setActiveServer(int instance) {
+ if (Util.getActiveServer(context) != instance) {
+ DownloadService service = getDownloadService();
+ if (service != null) {
+ service.clearIncomplete();
+ }
+ Util.setActiveServer(context, instance);
+ }
+ }
+
+ private void toggleOffline() {
+ Util.setOffline(context, !Util.isOffline(context));
+ refresh();
+ }
+}
diff --git a/subsonic-android/src/github/daneren2005/dsub/fragments/SelectArtistFragment.java b/subsonic-android/src/github/daneren2005/dsub/fragments/SelectArtistFragment.java new file mode 100644 index 00000000..12a58678 --- /dev/null +++ b/subsonic-android/src/github/daneren2005/dsub/fragments/SelectArtistFragment.java @@ -0,0 +1,205 @@ +package github.daneren2005.dsub.fragments;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v4.app.FragmentTransaction;
+import android.util.Log;
+import android.view.ContextMenu;
+import android.view.LayoutInflater;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ListView;
+import android.widget.TextView;
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.Artist;
+import github.daneren2005.dsub.domain.Indexes;
+import github.daneren2005.dsub.domain.MusicFolder;
+import github.daneren2005.dsub.service.MusicService;
+import github.daneren2005.dsub.service.MusicServiceFactory;
+import github.daneren2005.dsub.util.BackgroundTask;
+import github.daneren2005.dsub.util.Constants;
+import github.daneren2005.dsub.util.TabBackgroundTask;
+import github.daneren2005.dsub.util.Util;
+import github.daneren2005.dsub.view.ArtistAdapter;
+import java.util.ArrayList;
+import java.util.List;
+
+public class SelectArtistFragment extends SubsonicTabFragment implements AdapterView.OnItemClickListener {
+ private static final String TAG = SelectArtistFragment.class.getSimpleName();
+ private static final int MENU_GROUP_MUSIC_FOLDER = 10;
+
+ private ListView artistList;
+ private View folderButton;
+ private TextView folderName;
+ private List<MusicFolder> musicFolders = null;
+
+ @Override
+ public void onCreate(Bundle bundle) {
+ super.onCreate(bundle);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
+ rootView = inflater.inflate(R.layout.select_artist, container, false);
+
+ artistList = (ListView) rootView.findViewById(R.id.select_artist_list);
+ artistList.setOnItemClickListener(this);
+
+ folderButton = inflater.inflate(R.layout.select_artist_header, artistList, false);
+ folderName = (TextView) folderButton.findViewById(R.id.select_artist_folder_2);
+
+ if (!Util.isOffline(context)) {
+ artistList.addHeaderView(folderButton);
+ }
+
+ registerForContextMenu(artistList);
+ load(false);
+
+ return rootView;
+ }
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
+ super.onCreateContextMenu(menu, view, menuInfo);
+
+ AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
+
+ if (artistList.getItemAtPosition(info.position) instanceof Artist) {
+ MenuInflater inflater = context.getMenuInflater();
+ if(Util.isOffline(context))
+ inflater.inflate(R.menu.select_artist_context_offline, menu);
+ else
+ inflater.inflate(R.menu.select_artist_context, menu);
+ } else if (info.position == 0) {
+ String musicFolderId = Util.getSelectedMusicFolderId(context);
+ MenuItem menuItem = menu.add(MENU_GROUP_MUSIC_FOLDER, -1, 0, R.string.select_artist_all_folders);
+ if (musicFolderId == null) {
+ menuItem.setChecked(true);
+ }
+ if (musicFolders != null) {
+ for (int i = 0; i < musicFolders.size(); i++) {
+ MusicFolder musicFolder = musicFolders.get(i);
+ menuItem = menu.add(MENU_GROUP_MUSIC_FOLDER, i, i + 1, musicFolder.getName());
+ if (musicFolder.getId().equals(musicFolderId)) {
+ menuItem.setChecked(true);
+ }
+ }
+ }
+ menu.setGroupCheckable(MENU_GROUP_MUSIC_FOLDER, true, true);
+ }
+ }
+
+ @Override
+ public boolean onContextItemSelected(MenuItem menuItem) {
+ AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo();
+
+ Artist artist = (Artist) artistList.getItemAtPosition(info.position);
+
+ if (artist != null) {
+ switch (menuItem.getItemId()) {
+ case R.id.artist_menu_play_now:
+ // downloadRecursively(artist.getId(), false, false, true, false, false);
+ break;
+ case R.id.artist_menu_play_shuffled:
+ // downloadRecursively(artist.getId(), false, false, true, true, false);
+ break;
+ case R.id.artist_menu_play_last:
+ // downloadRecursively(artist.getId(), false, true, false, false, false);
+ break;
+ case R.id.artist_menu_download:
+ // downloadRecursively(artist.getId(), false, true, false, false, true);
+ break;
+ case R.id.artist_menu_pin:
+ // downloadRecursively(artist.getId(), true, true, false, false, true);
+ break;
+ case R.id.artist_menu_delete:
+ // deleteRecursively(artist);
+ break;
+ default:
+ // return super.onContextItemSelected(menuItem);
+ }
+ } else if (info.position == 0) {
+ MusicFolder selectedFolder = menuItem.getItemId() == -1 ? null : musicFolders.get(menuItem.getItemId());
+ String musicFolderId = selectedFolder == null ? null : selectedFolder.getId();
+ String musicFolderName = selectedFolder == null ? context.getString(R.string.select_artist_all_folders)
+ : selectedFolder.getName();
+ Util.setSelectedMusicFolderId(context, musicFolderId);
+ folderName.setText(musicFolderName);
+ refresh();
+ }
+
+ return true;
+ }
+
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ if (view == folderButton) {
+ selectFolder();
+ } else {
+ Artist artist = (Artist) parent.getItemAtPosition(position);
+ SubsonicTabFragment fragment = new SelectDirectoryFragment();
+ Bundle args = new Bundle();
+ args.putString(Constants.INTENT_EXTRA_NAME_ID, artist.getId());
+ args.putString(Constants.INTENT_EXTRA_NAME_NAME, artist.getName());
+ fragment.setArguments(args);
+
+ final FragmentTransaction trans = getFragmentManager().beginTransaction();
+ trans.replace(R.id.select_artist_layout, fragment);
+ trans.addToBackStack(null);
+ trans.commit();
+ }
+ }
+
+ @Override
+ protected void refresh() {
+ load(true);
+ }
+
+ private void load(final boolean refresh) {
+ artistList.setVisibility(View.INVISIBLE);
+
+ BackgroundTask<Indexes> task = new TabBackgroundTask<Indexes>(this) {
+ @Override
+ protected Indexes doInBackground() throws Throwable {
+ MusicService musicService = MusicServiceFactory.getMusicService(context);
+ if (!Util.isOffline(context)) {
+ musicFolders = musicService.getMusicFolders(refresh, context, this);
+ }
+ String musicFolderId = Util.getSelectedMusicFolderId(context);
+ return musicService.getIndexes(musicFolderId, refresh, context, this);
+ }
+
+ @Override
+ protected void done(Indexes result) {
+ List<Artist> artists = new ArrayList<Artist>(result.getShortcuts().size() + result.getArtists().size());
+ artists.addAll(result.getShortcuts());
+ artists.addAll(result.getArtists());
+ artistList.setAdapter(new ArtistAdapter(context, artists));
+
+ // Display selected music folder
+ if (musicFolders != null) {
+ String musicFolderId = Util.getSelectedMusicFolderId(context);
+ if (musicFolderId == null) {
+ folderName.setText(R.string.select_artist_all_folders);
+ } else {
+ for (MusicFolder musicFolder : musicFolders) {
+ if (musicFolder.getId().equals(musicFolderId)) {
+ folderName.setText(musicFolder.getName());
+ break;
+ }
+ }
+ }
+ artistList.setVisibility(View.VISIBLE);
+ }
+ }
+ };
+ task.execute();
+ }
+
+ private void selectFolder() {
+ folderButton.showContextMenu();
+ }
+}
diff --git a/subsonic-android/src/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java b/subsonic-android/src/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java new file mode 100644 index 00000000..b393521b --- /dev/null +++ b/subsonic-android/src/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java @@ -0,0 +1,282 @@ +package github.daneren2005.dsub.fragments;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.Button;
+import android.widget.ListView;
+import android.widget.TextView;
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.MusicDirectory;
+import github.daneren2005.dsub.view.EntryAdapter;
+import java.util.List;
+import com.mobeta.android.dslv.*;
+import github.daneren2005.dsub.service.MusicService;
+import github.daneren2005.dsub.service.MusicServiceFactory;
+import github.daneren2005.dsub.util.Constants;
+import github.daneren2005.dsub.util.Pair;
+import github.daneren2005.dsub.util.TabBackgroundTask;
+import java.util.HashSet;
+import java.util.Set;
+
+public class SelectDirectoryFragment extends SubsonicTabFragment implements AdapterView.OnItemClickListener {
+ private static final String TAG = SelectDirectoryFragment.class.getSimpleName();
+
+ private DragSortListView entryList;
+ private View footer;
+ private View emptyView;
+ private boolean hideButtons = false;
+ private Button moreButton;
+ private Boolean licenseValid;
+ private boolean showHeader = true;
+ private EntryAdapter entryAdapter;
+ private List<MusicDirectory.Entry> entries;
+
+ @Override
+ public void onCreate(Bundle bundle) {
+ super.onCreate(bundle);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
+ rootView = inflater.inflate(R.layout.select_album, container, false);
+
+ entryList = (DragSortListView) rootView.findViewById(R.id.select_album_entries);
+ footer = LayoutInflater.from(context).inflate(R.layout.select_album_footer, entryList, false);
+ entryList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
+ entryList.setOnItemClickListener(this);
+ entryList.setDropListener(new DragSortListView.DropListener() {
+ @Override
+ public void drop(int from, int to) {
+ int max = entries.size();
+ if(to >= max) {
+ to = max - 1;
+ }
+ else if(to < 0) {
+ to = 0;
+ }
+ entries.add(to, entries.remove(from));
+ entryAdapter.notifyDataSetChanged();
+ }
+ });
+
+ moreButton = (Button) footer.findViewById(R.id.select_album_more);
+ emptyView = rootView.findViewById(R.id.select_album_empty);
+
+ registerForContextMenu(entryList);
+ load(false);
+
+ return rootView;
+ }
+
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ if (position >= 0) {
+ MusicDirectory.Entry entry = (MusicDirectory.Entry) parent.getItemAtPosition(position);
+ /*if (entry.isDirectory()) {
+ Intent intent = new Intent(SelectAlbumActivity.this, SelectAlbumActivity.class);
+ intent.putExtra(Constants.INTENT_EXTRA_NAME_ID, entry.getId());
+ intent.putExtra(Constants.INTENT_EXTRA_NAME_NAME, entry.getTitle());
+ Util.startActivityWithoutTransition(SelectAlbumActivity.this, intent);
+ } else if (entry.isVideo()) {
+ if(entryExists(entry)) {
+ playExternalPlayer(entry);
+ } else {
+ streamExternalPlayer(entry);
+ }
+ }*/
+ }
+ }
+
+ @Override
+ protected void refresh() {
+ load(true);
+ }
+
+ private void load(boolean refresh) {
+ Bundle args = getArguments();
+ if(args != null) {
+ String id = args.getString(Constants.INTENT_EXTRA_NAME_ID);
+ String name = args.getString(Constants.INTENT_EXTRA_NAME_NAME);
+ String playlistId = args.getString(Constants.INTENT_EXTRA_NAME_PLAYLIST_ID);
+ String playlistName = args.getString(Constants.INTENT_EXTRA_NAME_PLAYLIST_NAME);
+ String albumListType = args.getString(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TYPE);
+ int albumListSize = args.getInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, 0);
+ int albumListOffset = args.getInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_OFFSET, 0);
+
+ if (playlistId != null) {
+ getPlaylist(playlistId, playlistName);
+ } else if (albumListType != null) {
+ getAlbumList(albumListType, albumListSize, albumListOffset);
+ } else {
+ getMusicDirectory(id, name, refresh);
+ }
+ }
+ }
+
+ private void getMusicDirectory(final String id, final String name, final boolean refresh) {
+ // setTitle(name);
+
+ new LoadTask() {
+ @Override
+ protected MusicDirectory load(MusicService service) throws Exception {
+ return service.getMusicDirectory(id, name, refresh, context, this);
+ }
+ }.execute();
+ }
+
+ private void getPlaylist(final String playlistId, final String playlistName) {
+ // setTitle(playlistName);
+
+ new LoadTask() {
+ @Override
+ protected MusicDirectory load(MusicService service) throws Exception {
+ return service.getPlaylist(playlistId, playlistName, context, this);
+ }
+ }.execute();
+ }
+
+ private void getAlbumList(final String albumListType, final int size, final int offset) {
+ showHeader = false;
+
+ /*if ("newest".equals(albumListType)) {
+ setTitle(R.string.main_albums_newest);
+ } else if ("random".equals(albumListType)) {
+ setTitle(R.string.main_albums_random);
+ } else if ("highest".equals(albumListType)) {
+ setTitle(R.string.main_albums_highest);
+ } else if ("recent".equals(albumListType)) {
+ setTitle(R.string.main_albums_recent);
+ } else if ("frequent".equals(albumListType)) {
+ setTitle(R.string.main_albums_frequent);
+ } else if ("starred".equals(albumListType)) {
+ setTitle(R.string.main_albums_starred);
+ }*/
+
+ new LoadTask() {
+ @Override
+ protected MusicDirectory load(MusicService service) throws Exception {
+ MusicDirectory result;
+ if ("starred".equals(albumListType)) {
+ result = service.getStarredList(context, this);
+ } else {
+ result = service.getAlbumList(albumListType, size, offset, context, this);
+ }
+ return result;
+ }
+
+ @Override
+ protected void done(Pair<MusicDirectory, Boolean> result) {
+ if (!result.getFirst().getChildren().isEmpty()) {
+ if (!("starred".equals(albumListType))) {
+ moreButton.setVisibility(View.VISIBLE);
+ entryList.addFooterView(footer);
+ }
+
+ moreButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ /*Intent intent = new Intent(SelectAlbumActivity.this, SelectAlbumActivity.class);
+ String type = getIntent().getStringExtra(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TYPE);
+ int size = getIntent().getIntExtra(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, 0);
+ int offset = getIntent().getIntExtra(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_OFFSET, 0) + size;
+
+ intent.putExtra(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TYPE, type);
+ intent.putExtra(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, size);
+ intent.putExtra(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_OFFSET, offset);
+ Util.startActivityWithoutTransition(SelectAlbumActivity.this, intent);*/
+ }
+ });
+ }
+ super.done(result);
+ }
+ }.execute();
+ }
+
+ private abstract class LoadTask extends TabBackgroundTask<Pair<MusicDirectory, Boolean>> {
+
+ public LoadTask() {
+ super(SelectDirectoryFragment.this);
+ }
+
+ protected abstract MusicDirectory load(MusicService service) throws Exception;
+
+ @Override
+ protected Pair<MusicDirectory, Boolean> doInBackground() throws Throwable {
+ MusicService musicService = MusicServiceFactory.getMusicService(context);
+ MusicDirectory dir = load(musicService);
+ boolean valid = musicService.isLicenseValid(context, this);
+ return new Pair<MusicDirectory, Boolean>(dir, valid);
+ }
+
+ @Override
+ protected void done(Pair<MusicDirectory, Boolean> result) {
+ entries = result.getFirst().getChildren();
+
+ int songCount = 0;
+ for (MusicDirectory.Entry entry : entries) {
+ if (!entry.isDirectory()) {
+ songCount++;
+ }
+ }
+
+ if (songCount > 0) {
+ if(showHeader) {
+ entryList.addHeaderView(createHeader(entries), null, false);
+ }
+ } else {
+ hideButtons = true;
+ }
+
+ emptyView.setVisibility(entries.isEmpty() ? View.VISIBLE : View.GONE);
+ entryList.setAdapter(entryAdapter = new EntryAdapter(context, getImageLoader(), entries, true));
+ licenseValid = result.getSecond();
+ // invalidateOptionsMenu();
+
+ /*boolean playAll = getIntent().getBooleanExtra(Constants.INTENT_EXTRA_NAME_AUTOPLAY, false);
+ if (playAll && songCount > 0) {
+ playAll(getIntent().getBooleanExtra(Constants.INTENT_EXTRA_NAME_SHUFFLE, false), false);
+ }*/
+ }
+ }
+
+ private View createHeader(List<MusicDirectory.Entry> entries) {
+ View header = LayoutInflater.from(context).inflate(R.layout.select_album_header, entryList, false);
+
+ View coverArtView = header.findViewById(R.id.select_album_art);
+ getImageLoader().loadImage(coverArtView, entries.get(0), true, true);
+
+ TextView titleView = (TextView) header.findViewById(R.id.select_album_title);
+ // titleView.setText(getTitle());
+
+ int songCount = 0;
+
+ Set<String> artists = new HashSet<String>();
+ for (MusicDirectory.Entry entry : entries) {
+ if (!entry.isDirectory()) {
+ songCount++;
+ if (entry.getArtist() != null) {
+ artists.add(entry.getArtist());
+ }
+ }
+ }
+
+ TextView artistView = (TextView) header.findViewById(R.id.select_album_artist);
+ if (artists.size() == 1) {
+ artistView.setText(artists.iterator().next());
+ artistView.setVisibility(View.VISIBLE);
+ } else {
+ artistView.setVisibility(View.GONE);
+ }
+
+ TextView songCountView = (TextView) header.findViewById(R.id.select_album_song_count);
+ String s = context.getResources().getQuantityString(R.plurals.select_album_n_songs, songCount, songCount);
+ songCountView.setText(s.toUpperCase());
+
+ return header;
+ }
+}
\ No newline at end of file diff --git a/subsonic-android/src/github/daneren2005/dsub/fragments/SelectPlaylistFragment.java b/subsonic-android/src/github/daneren2005/dsub/fragments/SelectPlaylistFragment.java new file mode 100644 index 00000000..eaba77e5 --- /dev/null +++ b/subsonic-android/src/github/daneren2005/dsub/fragments/SelectPlaylistFragment.java @@ -0,0 +1,141 @@ +package github.daneren2005.dsub.fragments;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.ContextMenu;
+import android.view.LayoutInflater;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ListView;
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.domain.Playlist;
+import github.daneren2005.dsub.service.MusicService;
+import github.daneren2005.dsub.service.MusicServiceFactory;
+import github.daneren2005.dsub.util.BackgroundTask;
+import github.daneren2005.dsub.util.CacheCleaner;
+import github.daneren2005.dsub.util.TabBackgroundTask;
+import github.daneren2005.dsub.util.Util;
+import github.daneren2005.dsub.view.PlaylistAdapter;
+import java.util.List;
+
+public class SelectPlaylistFragment extends SubsonicTabFragment implements AdapterView.OnItemClickListener {
+ private static final String TAG = SelectPlaylistFragment.class.getSimpleName();
+
+ private ListView list;
+ private View emptyTextView;
+ private PlaylistAdapter playlistAdapter;
+
+ @Override
+ public void onCreate(Bundle bundle) {
+ super.onCreate(bundle);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
+ rootView = inflater.inflate(R.layout.select_playlist, container, false);
+
+ list = (ListView) rootView.findViewById(R.id.select_playlist_list);
+ emptyTextView = rootView.findViewById(R.id.select_playlist_empty);
+ list.setOnItemClickListener(this);
+ registerForContextMenu(list);
+ load(false);
+
+ return rootView;
+ }
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
+ super.onCreateContextMenu(menu, view, menuInfo);
+
+ MenuInflater inflater = context.getMenuInflater();
+ if (Util.isOffline(context)) {
+ inflater.inflate(R.menu.select_playlist_context_offline, menu);
+ }
+ else {
+ inflater.inflate(R.menu.select_playlist_context, menu);
+ }
+ }
+
+ @Override
+ public boolean onContextItemSelected(MenuItem menuItem) {
+ AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo();
+ Playlist playlist = (Playlist) list.getItemAtPosition(info.position);
+
+ Intent intent;
+ /*switch (menuItem.getItemId()) {
+ case R.id.playlist_menu_download:
+ downloadPlaylist(playlist.getId(), playlist.getName(), false, true, false, false, true);
+ break;
+ case R.id.playlist_menu_pin:
+ downloadPlaylist(playlist.getId(), playlist.getName(), true, true, false, false, true);
+ break;
+ case R.id.playlist_menu_play_now:
+ intent = new Intent(SelectPlaylistActivity.this, SelectAlbumActivity.class);
+ intent.putExtra(Constants.INTENT_EXTRA_NAME_PLAYLIST_ID, playlist.getId());
+ intent.putExtra(Constants.INTENT_EXTRA_NAME_PLAYLIST_NAME, playlist.getName());
+ intent.putExtra(Constants.INTENT_EXTRA_NAME_AUTOPLAY, true);
+ Util.startActivityWithoutTransition(SelectPlaylistActivity.this, intent);
+ break;
+ case R.id.playlist_menu_play_shuffled:
+ intent = new Intent(SelectPlaylistActivity.this, SelectAlbumActivity.class);
+ intent.putExtra(Constants.INTENT_EXTRA_NAME_PLAYLIST_ID, playlist.getId());
+ intent.putExtra(Constants.INTENT_EXTRA_NAME_PLAYLIST_NAME, playlist.getName());
+ intent.putExtra(Constants.INTENT_EXTRA_NAME_AUTOPLAY, true);
+ intent.putExtra(Constants.INTENT_EXTRA_NAME_SHUFFLE, true);
+ Util.startActivityWithoutTransition(SelectPlaylistActivity.this, intent);
+ break;
+ case R.id.playlist_menu_delete:
+ deletePlaylist(playlist);
+ break;
+ case R.id.playlist_info:
+ displayPlaylistInfo(playlist);
+ break;
+ case R.id.playlist_update_info:
+ updatePlaylistInfo(playlist);
+ break;
+ default:
+ return super.onContextItemSelected(menuItem);
+ }*/
+ return true;
+ }
+
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ Playlist playlist = (Playlist) parent.getItemAtPosition(position);
+
+ /*Intent intent = new Intent(SelectPlaylistActivity.this, SelectAlbumActivity.class);
+ intent.putExtra(Constants.INTENT_EXTRA_NAME_PLAYLIST_ID, playlist.getId());
+ intent.putExtra(Constants.INTENT_EXTRA_NAME_PLAYLIST_NAME, playlist.getName());
+ Util.startActivityWithoutTransition(SelectPlaylistActivity.this, intent);*/
+ }
+
+ @Override
+ protected void refresh() {
+ load(true);
+ }
+
+ private void load(final boolean refresh) {
+ BackgroundTask<List<Playlist>> task = new TabBackgroundTask<List<Playlist>>(this) {
+ @Override
+ protected List<Playlist> doInBackground() throws Throwable {
+ MusicService musicService = MusicServiceFactory.getMusicService(context);
+ List<Playlist> playlists = musicService.getPlaylists(refresh, context, this);
+ if(!Util.isOffline(context) && refresh) {
+ new CacheCleaner(context, getDownloadService()).cleanPlaylists(playlists);
+ }
+ return playlists;
+ }
+
+ @Override
+ protected void done(List<Playlist> result) {
+ list.setAdapter(playlistAdapter = new PlaylistAdapter(context, result));
+ emptyTextView.setVisibility(result.isEmpty() ? View.VISIBLE : View.GONE);
+ }
+ };
+ task.execute();
+ }
+}
diff --git a/subsonic-android/src/github/daneren2005/dsub/fragments/SubsonicTabFragment.java b/subsonic-android/src/github/daneren2005/dsub/fragments/SubsonicTabFragment.java new file mode 100644 index 00000000..9b79fd19 --- /dev/null +++ b/subsonic-android/src/github/daneren2005/dsub/fragments/SubsonicTabFragment.java @@ -0,0 +1,88 @@ +/*
+ This file is part of Subsonic.
+
+ Subsonic is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Subsonic is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Subsonic. If not, see <http://www.gnu.org/licenses/>.
+
+ Copyright 2009 (C) Sindre Mehus
+ */
+package github.daneren2005.dsub.fragments;
+
+import android.app.Activity;
+import android.content.Context;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.widget.TextView;
+import com.actionbarsherlock.app.SherlockFragment;
+import github.daneren2005.dsub.R;
+import github.daneren2005.dsub.activity.SubsonicActivity;
+import github.daneren2005.dsub.activity.SubsonicTabActivity;
+import github.daneren2005.dsub.service.DownloadService;
+import github.daneren2005.dsub.util.ImageLoader;
+
+public class SubsonicTabFragment extends SherlockFragment {
+ private static final String TAG = SubsonicTabActivity.class.getSimpleName();
+ protected SubsonicActivity context;
+ protected View rootView;
+
+ @Override
+ public void onCreate(Bundle bundle) {
+ super.onCreate(bundle);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ context = (SubsonicActivity)activity;
+ }
+
+ public DownloadService getDownloadService() {
+ return context != null ? context.getDownloadService() : null;
+ }
+
+ protected void refresh() {
+
+ }
+
+ public void setProgressVisible(boolean visible) {
+ View view = rootView.findViewById(R.id.tab_progress);
+ if (view != null) {
+ view.setVisibility(visible ? View.VISIBLE : View.GONE);
+ }
+ }
+
+ public void updateProgress(String message) {
+ TextView view = (TextView) rootView.findViewById(R.id.tab_progress_message);
+ if (view != null) {
+ view.setText(message);
+ }
+ }
+
+ protected synchronized ImageLoader getImageLoader() {
+ return context.getImageLoader();
+ }
+ public synchronized static ImageLoader getStaticImageLoader(Context context) {
+ return SubsonicActivity.getStaticImageLoader(context);
+ }
+}
diff --git a/subsonic-android/src/github/daneren2005/dsub/util/TabBackgroundTask.java b/subsonic-android/src/github/daneren2005/dsub/util/TabBackgroundTask.java new file mode 100644 index 00000000..57a36c1f --- /dev/null +++ b/subsonic-android/src/github/daneren2005/dsub/util/TabBackgroundTask.java @@ -0,0 +1,68 @@ +package github.daneren2005.dsub.util;
+
+import github.daneren2005.dsub.activity.SubsonicTabActivity;
+import github.daneren2005.dsub.fragments.SubsonicTabFragment;
+
+/**
+ * @author Sindre Mehus
+ * @version $Id$
+ */
+public abstract class TabBackgroundTask<T> extends BackgroundTask<T> {
+
+ private final SubsonicTabFragment tabFragment;
+
+ public TabBackgroundTask(SubsonicTabFragment fragment) {
+ super(fragment.getActivity());
+ tabFragment = fragment;
+ }
+
+ @Override
+ public void execute() {
+ tabFragment.setProgressVisible(true);
+
+ new Thread() {
+ @Override
+ public void run() {
+ try {
+ final T result = doInBackground();
+ if (isCancelled()) {
+ return;
+ }
+
+ getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ tabFragment.setProgressVisible(false);
+ done(result);
+ }
+ });
+ } catch (final Throwable t) {
+ if (isCancelled()) {
+ return;
+ }
+ getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ tabFragment.setProgressVisible(false);
+ error(t);
+ }
+ });
+ }
+ }
+ }.start();
+ }
+
+ private boolean isCancelled() {
+ return !tabFragment.isAdded();
+ }
+
+ @Override
+ public void updateProgress(final String message) {
+ getHandler().post(new Runnable() {
+ @Override
+ public void run() {
+ tabFragment.updateProgress(message);
+ }
+ });
+ }
+}
diff --git a/subsonic-android/src/github/daneren2005/dsub/view/ArtistAdapter.java b/subsonic-android/src/github/daneren2005/dsub/view/ArtistAdapter.java index 0edb03c5..64e5f055 100644 --- a/subsonic-android/src/github/daneren2005/dsub/view/ArtistAdapter.java +++ b/subsonic-android/src/github/daneren2005/dsub/view/ArtistAdapter.java @@ -18,6 +18,7 @@ */ package github.daneren2005.dsub.view; +import android.content.Context; import github.daneren2005.dsub.R; import java.util.List; import android.view.View; @@ -35,13 +36,13 @@ import java.util.Set; */ public class ArtistAdapter extends ArrayAdapter<Artist> implements SectionIndexer { - private final SubsonicTabActivity activity; + private final Context activity; // Both arrays are indexed by section ID. private final Object[] sections; private final Integer[] positions; - public ArtistAdapter(SubsonicTabActivity activity, List<Artist> artists) { + public ArtistAdapter(Context activity, List<Artist> artists) { super(activity, R.layout.artist_list_item, artists); this.activity = activity; diff --git a/subsonic-android/src/github/daneren2005/dsub/view/EntryAdapter.java b/subsonic-android/src/github/daneren2005/dsub/view/EntryAdapter.java index 476d3478..0d67c4c4 100644 --- a/subsonic-android/src/github/daneren2005/dsub/view/EntryAdapter.java +++ b/subsonic-android/src/github/daneren2005/dsub/view/EntryAdapter.java @@ -18,14 +18,12 @@ */ package github.daneren2005.dsub.view; -import github.daneren2005.dsub.view.AlbumView; -import github.daneren2005.dsub.view.SongView; +import android.content.Context; import java.util.List; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; -import github.daneren2005.dsub.activity.SubsonicTabActivity; import github.daneren2005.dsub.domain.MusicDirectory; import github.daneren2005.dsub.util.ImageLoader; @@ -34,12 +32,12 @@ import github.daneren2005.dsub.util.ImageLoader; */ public class EntryAdapter extends ArrayAdapter<MusicDirectory.Entry> { - private final SubsonicTabActivity activity; + private final Context activity; private final ImageLoader imageLoader; private final boolean checkable; private List<MusicDirectory.Entry> entries; - public EntryAdapter(SubsonicTabActivity activity, ImageLoader imageLoader, List<MusicDirectory.Entry> entries, boolean checkable) { + public EntryAdapter(Context activity, ImageLoader imageLoader, List<MusicDirectory.Entry> entries, boolean checkable) { super(activity, android.R.layout.simple_list_item_1, entries); this.entries = entries; this.activity = activity; diff --git a/subsonic-android/src/github/daneren2005/dsub/view/PlaylistAdapter.java b/subsonic-android/src/github/daneren2005/dsub/view/PlaylistAdapter.java index c9377721..71727c04 100644 --- a/subsonic-android/src/github/daneren2005/dsub/view/PlaylistAdapter.java +++ b/subsonic-android/src/github/daneren2005/dsub/view/PlaylistAdapter.java @@ -18,12 +18,12 @@ */ package github.daneren2005.dsub.view; +import android.content.Context; import github.daneren2005.dsub.R; import java.util.List; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; -import github.daneren2005.dsub.activity.SubsonicTabActivity; import github.daneren2005.dsub.domain.Playlist; import java.util.Collections; import java.util.Comparator; @@ -33,16 +33,16 @@ import java.util.Comparator; */ public class PlaylistAdapter extends ArrayAdapter<Playlist> { - private final SubsonicTabActivity activity; + private final Context activity; - public PlaylistAdapter(SubsonicTabActivity activity, List<Playlist> Playlists) { - super(activity, R.layout.playlist_list_item, Playlists); - this.activity = activity; - } + public PlaylistAdapter(Context activity, List<Playlist> Playlists) { + super(activity, R.layout.playlist_list_item, Playlists); + this.activity = activity; + } - @Override - public View getView(int position, View convertView, ViewGroup parent) { - Playlist entry = getItem(position); + @Override + public View getView(int position, View convertView, ViewGroup parent) { + Playlist entry = getItem(position); PlaylistView view; if (convertView != null && convertView instanceof PlaylistView) { view = (PlaylistView) convertView; @@ -51,18 +51,18 @@ public class PlaylistAdapter extends ArrayAdapter<Playlist> { } view.setPlaylist(entry); return view; - } - + } + public static class PlaylistComparator implements Comparator<Playlist> { - @Override - public int compare(Playlist playlist1, Playlist playlist2) { - return playlist1.getName().compareToIgnoreCase(playlist2.getName()); - } + @Override + public int compare(Playlist playlist1, Playlist playlist2) { + return playlist1.getName().compareToIgnoreCase(playlist2.getName()); + } - public static List<Playlist> sort(List<Playlist> playlists) { - Collections.sort(playlists, new PlaylistComparator()); - return playlists; - } + public static List<Playlist> sort(List<Playlist> playlists) { + Collections.sort(playlists, new PlaylistComparator()); + return playlists; + } - } + } } diff --git a/subsonic-android/src/github/daneren2005/dsub/view/PlaylistView.java b/subsonic-android/src/github/daneren2005/dsub/view/PlaylistView.java index b09c91d4..effd5a98 100644 --- a/subsonic-android/src/github/daneren2005/dsub/view/PlaylistView.java +++ b/subsonic-android/src/github/daneren2005/dsub/view/PlaylistView.java @@ -36,32 +36,32 @@ import java.io.File; */
public class PlaylistView extends UpdateView {
private static final String TAG = PlaylistView.class.getSimpleName();
-
+
private Playlist playlist;
- private TextView titleView;
+ private TextView titleView;
private ImageView moreButton;
- public PlaylistView(Context context) {
- super(context);
- LayoutInflater.from(context).inflate(R.layout.playlist_list_item, this, true);
+ public PlaylistView(Context context) {
+ super(context);
+ LayoutInflater.from(context).inflate(R.layout.playlist_list_item, this, true);
- titleView = (TextView) findViewById(R.id.playlist_name);
+ titleView = (TextView) findViewById(R.id.playlist_name);
moreButton = (ImageView) findViewById(R.id.playlist_more);
moreButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
v.showContextMenu();
}
});
- }
+ }
+
+ public void setPlaylist(Playlist playlist) {
+ this.playlist = playlist;
- public void setPlaylist(Playlist playlist) {
- this.playlist = playlist;
-
- titleView.setText(playlist.getName());
+ titleView.setText(playlist.getName());
update();
- }
-
+ }
+
@Override
protected void update() {
File file = FileUtil.getPlaylistFile(playlist.getName());
@@ -70,5 +70,5 @@ public class PlaylistView extends UpdateView { } else {
moreButton.setImageResource(R.drawable.list_item_more);
}
- }
+ }
}
|