Started refactoring to NavigationUI
Main menu items are refactored, except Now Playing
This commit is contained in:
parent
4fdab06271
commit
95773c7994
|
@ -18,6 +18,7 @@ buildscript {
|
|||
classpath gradlePlugins.ktlintGradle
|
||||
classpath gradlePlugins.detekt
|
||||
classpath gradlePlugins.jacoco
|
||||
classpath gradlePlugins.navigationSafeArgs
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,11 +4,14 @@ ext.versions = [
|
|||
compileSdk : 29,
|
||||
gradle : '6.5',
|
||||
|
||||
navigation : "2.3.2",
|
||||
androidTools : "4.0.0",
|
||||
ktlint : "0.37.1",
|
||||
ktlintGradle : "9.2.1",
|
||||
detekt : "1.0.0.RC6-4",
|
||||
jacoco : "0.8.5",
|
||||
navigationSafeArgs : "2.3.2",
|
||||
preferences : "1.1.1",
|
||||
|
||||
androidSupport : "28.0.0",
|
||||
androidLegacySupport : "1.0.0",
|
||||
|
@ -42,23 +45,30 @@ ext.versions = [
|
|||
]
|
||||
|
||||
ext.gradlePlugins = [
|
||||
androidTools : "com.android.tools.build:gradle:$versions.androidTools",
|
||||
kotlin : "org.jetbrains.kotlin:kotlin-gradle-plugin:$versions.kotlin",
|
||||
ktlintGradle : "org.jlleitschuh.gradle:ktlint-gradle:$versions.ktlintGradle",
|
||||
detekt : "gradle.plugin.io.gitlab.arturbosch.detekt:detekt-gradle-plugin:$versions.detekt",
|
||||
jacoco : "org.jacoco:org.jacoco.core:$versions.jacoco"
|
||||
androidTools : "com.android.tools.build:gradle:$versions.androidTools",
|
||||
kotlin : "org.jetbrains.kotlin:kotlin-gradle-plugin:$versions.kotlin",
|
||||
ktlintGradle : "org.jlleitschuh.gradle:ktlint-gradle:$versions.ktlintGradle",
|
||||
detekt : "gradle.plugin.io.gitlab.arturbosch.detekt:detekt-gradle-plugin:$versions.detekt",
|
||||
jacoco : "org.jacoco:org.jacoco.core:$versions.jacoco",
|
||||
navigationSafeArgs: "androidx.navigation:navigation-safe-args-gradle-plugin:$versions.navigationSafeArgs"
|
||||
]
|
||||
|
||||
ext.androidSupport = [
|
||||
support : "androidx.legacy:legacy-support-v4:$versions.androidLegacySupport",
|
||||
design : "com.google.android.material:material:$versions.androidSupportDesign",
|
||||
annotations : "com.android.support:support-annotations:$versions.androidSupport",
|
||||
multidex : "androidx.multidex:multidex:$versions.multidex",
|
||||
constraintLayout : "androidx.constraintlayout:constraintlayout:$versions.constraintLayout",
|
||||
room : "androidx.room:room-compiler:$versions.room",
|
||||
roomRuntime : "androidx.room:room-runtime:$versions.room",
|
||||
roomKtx : "androidx.room:room-ktx:$versions.room",
|
||||
viewModelKtx : "androidx.lifecycle:lifecycle-viewmodel-ktx:$versions.viewModelKtx"
|
||||
support : "androidx.legacy:legacy-support-v4:$versions.androidLegacySupport",
|
||||
design : "com.google.android.material:material:$versions.androidSupportDesign",
|
||||
annotations : "com.android.support:support-annotations:$versions.androidSupport",
|
||||
multidex : "androidx.multidex:multidex:$versions.multidex",
|
||||
constraintLayout : "androidx.constraintlayout:constraintlayout:$versions.constraintLayout",
|
||||
room : "androidx.room:room-compiler:$versions.room",
|
||||
roomRuntime : "androidx.room:room-runtime:$versions.room",
|
||||
roomKtx : "androidx.room:room-ktx:$versions.room",
|
||||
viewModelKtx : "androidx.lifecycle:lifecycle-viewmodel-ktx:$versions.viewModelKtx",
|
||||
navigationFragment : "androidx.navigation:navigation-fragment:$versions.navigation",
|
||||
navigationUi : "androidx.navigation:navigation-ui:$versions.navigation",
|
||||
navigationFragmentKtx : "androidx.navigation:navigation-fragment-ktx:$versions.navigation",
|
||||
navigationUiKtx : "androidx.navigation:navigation-ui-ktx:$versions.navigation",
|
||||
navigationFeature : "androidx.navigation:navigation-dynamic-features-fragment:$versions.navigation",
|
||||
preferences : "androidx.preference:preference:$versions.preferences",
|
||||
]
|
||||
|
||||
ext.other = [
|
||||
|
|
|
@ -2,6 +2,7 @@ apply plugin: 'com.android.application'
|
|||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-kapt'
|
||||
apply plugin: 'jacoco'
|
||||
apply plugin: 'androidx.navigation.safeargs'
|
||||
apply from: "../gradle_scripts/code_quality.gradle"
|
||||
|
||||
android {
|
||||
|
@ -50,6 +51,10 @@ android {
|
|||
warning 'MissingTranslation'
|
||||
abortOnError true
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType(Test) {
|
||||
|
@ -71,6 +76,13 @@ dependencies {
|
|||
implementation androidSupport.roomKtx
|
||||
implementation androidSupport.viewModelKtx
|
||||
implementation androidSupport.constraintLayout
|
||||
implementation androidSupport.preferences
|
||||
|
||||
implementation androidSupport.navigationFragment
|
||||
implementation androidSupport.navigationUi
|
||||
implementation androidSupport.navigationFragmentKtx
|
||||
implementation androidSupport.navigationUiKtx
|
||||
implementation androidSupport.navigationFeature
|
||||
|
||||
implementation other.kotlinStdlib
|
||||
implementation other.kotlinxCoroutines
|
||||
|
|
|
@ -23,24 +23,36 @@
|
|||
android:allowBackup="false"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:theme="@style/Theme.AppCompat"
|
||||
android:theme="@style/NoActionBar"
|
||||
android:name=".app.UApp"
|
||||
android:label="@string/common.appname"
|
||||
android:usesCleartextTraffic="true">
|
||||
<activity android:name=".activity.NavigationActivity"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:label="@string/common.appname"
|
||||
android:launchMode="standard">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<action android:name="android.intent.action.SEARCH"/>
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
<nav-graph android:value="@navigation/navigation_graph" />
|
||||
<meta-data
|
||||
android:name="android.app.searchable"
|
||||
android:resource="@xml/searchable"/>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".activity.MainActivity"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:label="@string/common.appname"
|
||||
android:launchMode="standard">
|
||||
<intent-filter>
|
||||
<!--<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
</intent-filter>-->
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".activity.SelectArtistActivity"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:launchMode="standard"/>
|
||||
<activity
|
||||
android:name=".activity.SelectAlbumActivity"
|
||||
android:configChanges="orientation|keyboardHidden"/>
|
||||
|
@ -49,38 +61,11 @@
|
|||
android:configChanges="orientation|keyboardHidden"
|
||||
android:label="@string/search.label"
|
||||
android:launchMode="singleTask"/>
|
||||
<activity
|
||||
android:name=".activity.SelectPlaylistActivity"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:label="@string/playlist.label"
|
||||
android:launchMode="standard"/>
|
||||
<activity
|
||||
android:name=".activity.PodcastsActivity"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:label="@string/podcasts.label"
|
||||
android:launchMode="standard"/>
|
||||
<activity
|
||||
android:name=".activity.BookmarkActivity"
|
||||
android:configChanges="orientation|keyboardHidden"/>
|
||||
<activity
|
||||
android:name=".activity.ShareActivity"
|
||||
android:configChanges="orientation|keyboardHidden"/>
|
||||
<activity
|
||||
android:name=".activity.ChatActivity"
|
||||
android:configChanges="orientation|keyboardHidden"/>
|
||||
<activity
|
||||
android:name=".activity.DownloadActivity"
|
||||
android:configChanges="keyboardHidden"
|
||||
android:launchMode="singleTask"
|
||||
android:exported="true" />
|
||||
<activity
|
||||
android:name=".activity.SettingsActivity"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:launchMode="singleTask"/>
|
||||
<activity
|
||||
android:name=".activity.HelpActivity"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:launchMode="singleTask"/>
|
||||
<activity
|
||||
android:name=".activity.LyricsActivity"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
|
@ -90,10 +75,6 @@
|
|||
android:configChanges="orientation|keyboardHidden"
|
||||
android:label="@string/equalizer.label"
|
||||
android:launchMode="singleTask"/>
|
||||
<activity
|
||||
android:name=".activity.SelectGenreActivity"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:launchMode="standard"/>
|
||||
<activity
|
||||
android:name=".activity.VoiceQueryReceiverActivity"
|
||||
android:launchMode="singleTask">
|
||||
|
@ -102,7 +83,7 @@
|
|||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
<!-- <activity
|
||||
android:name=".activity.QueryReceiverActivity"
|
||||
android:launchMode="singleTask">
|
||||
<intent-filter>
|
||||
|
@ -114,7 +95,7 @@
|
|||
android:name="android.app.searchable"
|
||||
android:resource="@xml/searchable"/>
|
||||
</activity>
|
||||
|
||||
-->
|
||||
<activity
|
||||
android:name=".activity.ServerSelectorActivity"
|
||||
android:label="@string/server_selector.label" />
|
||||
|
|
|
@ -1,485 +0,0 @@
|
|||
/*
|
||||
This file is part of Subsonic.
|
||||
|
||||
Subsonic is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Subsonic is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Subsonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.moire.ultrasonic.activity;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ListView;
|
||||
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
|
||||
import org.moire.ultrasonic.R;
|
||||
import org.moire.ultrasonic.data.ActiveServerProvider;
|
||||
import org.moire.ultrasonic.domain.MusicDirectory;
|
||||
import org.moire.ultrasonic.domain.MusicDirectory.Entry;
|
||||
import org.moire.ultrasonic.service.DownloadFile;
|
||||
import org.moire.ultrasonic.service.MediaPlayerController;
|
||||
import org.moire.ultrasonic.service.MusicService;
|
||||
import org.moire.ultrasonic.service.MusicServiceFactory;
|
||||
import org.moire.ultrasonic.util.Constants;
|
||||
import org.moire.ultrasonic.util.Pair;
|
||||
import org.moire.ultrasonic.util.TabActivityBackgroundTask;
|
||||
import org.moire.ultrasonic.util.Util;
|
||||
import org.moire.ultrasonic.view.EntryAdapter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class BookmarkActivity extends SubsonicTabActivity
|
||||
{
|
||||
|
||||
private SwipeRefreshLayout refreshAlbumListView;
|
||||
private ListView albumListView;
|
||||
private View albumButtons;
|
||||
private View emptyView;
|
||||
private ImageView playNowButton;
|
||||
private ImageView pinButton;
|
||||
private ImageView unpinButton;
|
||||
private ImageView downloadButton;
|
||||
private ImageView deleteButton;
|
||||
|
||||
/**
|
||||
* Called when the activity is first created.
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.select_album);
|
||||
|
||||
albumButtons = findViewById(R.id.menu_album);
|
||||
|
||||
refreshAlbumListView = findViewById(R.id.select_album_entries_refresh);
|
||||
albumListView = findViewById(R.id.select_album_entries_list);
|
||||
|
||||
refreshAlbumListView.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener()
|
||||
{
|
||||
@Override
|
||||
public void onRefresh()
|
||||
{
|
||||
new GetDataTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
}
|
||||
});
|
||||
|
||||
albumListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
|
||||
|
||||
albumListView.setOnItemClickListener(new AdapterView.OnItemClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id)
|
||||
{
|
||||
if (position >= 0)
|
||||
{
|
||||
Entry entry = (Entry) parent.getItemAtPosition(position);
|
||||
|
||||
if (entry != null)
|
||||
{
|
||||
if (entry.isVideo())
|
||||
{
|
||||
playVideo(entry);
|
||||
}
|
||||
else
|
||||
{
|
||||
enableButtons();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
ImageView selectButton = (ImageView) findViewById(R.id.select_album_select);
|
||||
playNowButton = (ImageView) findViewById(R.id.select_album_play_now);
|
||||
ImageView playNextButton = (ImageView) findViewById(R.id.select_album_play_next);
|
||||
ImageView playLastButton = (ImageView) findViewById(R.id.select_album_play_last);
|
||||
pinButton = (ImageView) findViewById(R.id.select_album_pin);
|
||||
unpinButton = (ImageView) findViewById(R.id.select_album_unpin);
|
||||
downloadButton = (ImageView) findViewById(R.id.select_album_download);
|
||||
deleteButton = (ImageView) findViewById(R.id.select_album_delete);
|
||||
ImageView oreButton = (ImageView) findViewById(R.id.select_album_more);
|
||||
emptyView = findViewById(R.id.select_album_empty);
|
||||
|
||||
selectButton.setVisibility(View.GONE);
|
||||
playNextButton.setVisibility(View.GONE);
|
||||
playLastButton.setVisibility(View.GONE);
|
||||
oreButton.setVisibility(View.GONE);
|
||||
|
||||
playNowButton.setOnClickListener(new View.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(View view)
|
||||
{
|
||||
playNow(getSelectedSongs(albumListView));
|
||||
}
|
||||
});
|
||||
|
||||
selectButton.setOnClickListener(new View.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(View view)
|
||||
{
|
||||
selectAllOrNone();
|
||||
}
|
||||
});
|
||||
pinButton.setOnClickListener(new View.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(View view)
|
||||
{
|
||||
downloadBackground(true);
|
||||
selectAll(false, false);
|
||||
}
|
||||
});
|
||||
unpinButton.setOnClickListener(new View.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(View view)
|
||||
{
|
||||
unpin();
|
||||
selectAll(false, false);
|
||||
}
|
||||
});
|
||||
downloadButton.setOnClickListener(new View.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(View view)
|
||||
{
|
||||
downloadBackground(false);
|
||||
selectAll(false, false);
|
||||
}
|
||||
});
|
||||
deleteButton.setOnClickListener(new View.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(View view)
|
||||
{
|
||||
delete();
|
||||
selectAll(false, false);
|
||||
}
|
||||
});
|
||||
|
||||
registerForContextMenu(albumListView);
|
||||
|
||||
enableButtons();
|
||||
|
||||
View browseMenuItem = findViewById(R.id.menu_bookmarks);
|
||||
menuDrawer.setActiveView(browseMenuItem);
|
||||
|
||||
getBookmarks();
|
||||
}
|
||||
|
||||
private void getBookmarks()
|
||||
{
|
||||
setActionBarSubtitle(R.string.button_bar_bookmarks);
|
||||
|
||||
new LoadTask()
|
||||
{
|
||||
@Override
|
||||
protected MusicDirectory load(MusicService service) throws Exception
|
||||
{
|
||||
return Util.getSongsFromBookmarks(service.getBookmarks(BookmarkActivity.this, this));
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
private void playNow(List<Entry> songs)
|
||||
{
|
||||
if (!getSelectedSongs(albumListView).isEmpty())
|
||||
{
|
||||
int position = songs.get(0).getBookmarkPosition();
|
||||
if (getMediaPlayerController() == null) return;
|
||||
getMediaPlayerController().restore(songs, 0, position, true, true);
|
||||
selectAll(false, false);
|
||||
}
|
||||
}
|
||||
|
||||
private static List<MusicDirectory.Entry> getSelectedSongs(ListView albumListView)
|
||||
{
|
||||
List<MusicDirectory.Entry> songs = new ArrayList<MusicDirectory.Entry>(10);
|
||||
|
||||
if (albumListView != null)
|
||||
{
|
||||
int count = albumListView.getCount();
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
if (albumListView.isItemChecked(i))
|
||||
{
|
||||
songs.add((MusicDirectory.Entry) albumListView.getItemAtPosition(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return songs;
|
||||
}
|
||||
|
||||
private void refresh()
|
||||
{
|
||||
finish();
|
||||
Intent intent = getIntent();
|
||||
intent.putExtra(Constants.INTENT_EXTRA_NAME_REFRESH, true);
|
||||
startActivityForResultWithoutTransition(this, intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item)
|
||||
{
|
||||
switch (item.getItemId())
|
||||
{
|
||||
case android.R.id.home:
|
||||
menuDrawer.toggleMenu();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void selectAllOrNone()
|
||||
{
|
||||
boolean someUnselected = false;
|
||||
int count = albumListView.getCount();
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
if (!albumListView.isItemChecked(i) && albumListView.getItemAtPosition(i) instanceof MusicDirectory.Entry)
|
||||
{
|
||||
someUnselected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
selectAll(someUnselected, true);
|
||||
}
|
||||
|
||||
private void selectAll(boolean selected, boolean toast)
|
||||
{
|
||||
int count = albumListView.getCount();
|
||||
int selectedCount = 0;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
MusicDirectory.Entry entry = (MusicDirectory.Entry) albumListView.getItemAtPosition(i);
|
||||
if (entry != null && !entry.isDirectory() && !entry.isVideo())
|
||||
{
|
||||
albumListView.setItemChecked(i, selected);
|
||||
selectedCount++;
|
||||
}
|
||||
}
|
||||
|
||||
// Display toast: N tracks selected / N tracks unselected
|
||||
if (toast)
|
||||
{
|
||||
int toastResId = selected ? R.string.select_album_n_selected : R.string.select_album_n_unselected;
|
||||
Util.toast(this, getString(toastResId, selectedCount));
|
||||
}
|
||||
|
||||
enableButtons();
|
||||
}
|
||||
|
||||
private void enableButtons()
|
||||
{
|
||||
MediaPlayerController mediaPlayerController = getMediaPlayerController();
|
||||
if (mediaPlayerController == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
List<MusicDirectory.Entry> selection = getSelectedSongs(albumListView);
|
||||
boolean enabled = !selection.isEmpty();
|
||||
boolean unpinEnabled = false;
|
||||
boolean deleteEnabled = false;
|
||||
|
||||
int pinnedCount = 0;
|
||||
|
||||
for (MusicDirectory.Entry song : selection)
|
||||
{
|
||||
DownloadFile downloadFile = mediaPlayerController.getDownloadFileForSong(song);
|
||||
if (downloadFile.isWorkDone())
|
||||
{
|
||||
deleteEnabled = true;
|
||||
}
|
||||
|
||||
if (downloadFile.isSaved())
|
||||
{
|
||||
pinnedCount++;
|
||||
unpinEnabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
playNowButton.setVisibility(enabled && deleteEnabled ? View.VISIBLE : View.GONE);
|
||||
pinButton.setVisibility((enabled && !ActiveServerProvider.Companion.isOffline(this) && selection.size() > pinnedCount) ? View.VISIBLE : View.GONE);
|
||||
unpinButton.setVisibility(enabled && unpinEnabled ? View.VISIBLE : View.GONE);
|
||||
downloadButton.setVisibility(enabled && !deleteEnabled && !ActiveServerProvider.Companion.isOffline(this) ? View.VISIBLE : View.GONE);
|
||||
deleteButton.setVisibility(enabled && deleteEnabled ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
||||
private void downloadBackground(final boolean save)
|
||||
{
|
||||
List<MusicDirectory.Entry> songs = getSelectedSongs(albumListView);
|
||||
|
||||
if (songs.isEmpty())
|
||||
{
|
||||
selectAll(true, false);
|
||||
songs = getSelectedSongs(albumListView);
|
||||
}
|
||||
|
||||
downloadBackground(save, songs);
|
||||
}
|
||||
|
||||
private void downloadBackground(final boolean save, final List<MusicDirectory.Entry> songs)
|
||||
{
|
||||
if (getMediaPlayerController() == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Runnable onValid = new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
warnIfNetworkOrStorageUnavailable();
|
||||
getMediaPlayerController().downloadBackground(songs, save);
|
||||
|
||||
if (save)
|
||||
{
|
||||
Util.toast(BookmarkActivity.this, getResources().getQuantityString(R.plurals.select_album_n_songs_pinned, songs.size(), songs.size()));
|
||||
}
|
||||
else
|
||||
{
|
||||
Util.toast(BookmarkActivity.this, getResources().getQuantityString(R.plurals.select_album_n_songs_downloaded, songs.size(), songs.size()));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
checkLicenseAndTrialPeriod(onValid);
|
||||
}
|
||||
|
||||
private void delete()
|
||||
{
|
||||
List<MusicDirectory.Entry> songs = getSelectedSongs(albumListView);
|
||||
|
||||
if (songs.isEmpty())
|
||||
{
|
||||
selectAll(true, false);
|
||||
songs = getSelectedSongs(albumListView);
|
||||
}
|
||||
|
||||
if (getMediaPlayerController() != null)
|
||||
{
|
||||
getMediaPlayerController().delete(songs);
|
||||
}
|
||||
}
|
||||
|
||||
private void unpin()
|
||||
{
|
||||
if (getMediaPlayerController() != null)
|
||||
{
|
||||
List<MusicDirectory.Entry> songs = getSelectedSongs(albumListView);
|
||||
Util.toast(BookmarkActivity.this, getResources().getQuantityString(R.plurals.select_album_n_songs_unpinned, songs.size(), songs.size()));
|
||||
getMediaPlayerController().unpin(songs);
|
||||
}
|
||||
}
|
||||
|
||||
private abstract class LoadTask extends TabActivityBackgroundTask<Pair<MusicDirectory, Boolean>>
|
||||
{
|
||||
|
||||
public LoadTask()
|
||||
{
|
||||
super(BookmarkActivity.this, true);
|
||||
}
|
||||
|
||||
protected abstract MusicDirectory load(MusicService service) throws Exception;
|
||||
|
||||
@Override
|
||||
protected Pair<MusicDirectory, Boolean> doInBackground() throws Throwable
|
||||
{
|
||||
MusicService musicService = MusicServiceFactory.getMusicService(BookmarkActivity.this);
|
||||
MusicDirectory dir = load(musicService);
|
||||
boolean valid = musicService.isLicenseValid(BookmarkActivity.this, this);
|
||||
return new Pair<MusicDirectory, Boolean>(dir, valid);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done(Pair<MusicDirectory, Boolean> result)
|
||||
{
|
||||
MusicDirectory musicDirectory = result.getFirst();
|
||||
List<MusicDirectory.Entry> entries = musicDirectory.getChildren();
|
||||
|
||||
int songCount = 0;
|
||||
for (MusicDirectory.Entry entry : entries)
|
||||
{
|
||||
if (!entry.isDirectory())
|
||||
{
|
||||
songCount++;
|
||||
}
|
||||
}
|
||||
|
||||
final int listSize = getIntent().getIntExtra(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, 0);
|
||||
|
||||
if (songCount > 0)
|
||||
{
|
||||
pinButton.setVisibility(View.VISIBLE);
|
||||
unpinButton.setVisibility(View.VISIBLE);
|
||||
downloadButton.setVisibility(View.VISIBLE);
|
||||
deleteButton.setVisibility(View.VISIBLE);
|
||||
playNowButton.setVisibility(View.VISIBLE);
|
||||
}
|
||||
else
|
||||
{
|
||||
pinButton.setVisibility(View.GONE);
|
||||
unpinButton.setVisibility(View.GONE);
|
||||
downloadButton.setVisibility(View.GONE);
|
||||
deleteButton.setVisibility(View.GONE);
|
||||
playNowButton.setVisibility(View.GONE);
|
||||
|
||||
if (listSize == 0 || result.getFirst().getChildren().size() < listSize)
|
||||
{
|
||||
albumButtons.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
enableButtons();
|
||||
|
||||
emptyView.setVisibility(entries.isEmpty() ? View.VISIBLE : View.GONE);
|
||||
|
||||
albumListView.setAdapter(new EntryAdapter(BookmarkActivity.this, getImageLoader(), entries, true));
|
||||
licenseValid = result.getSecond();
|
||||
}
|
||||
}
|
||||
|
||||
private class GetDataTask extends AsyncTask<Void, Void, String[]>
|
||||
{
|
||||
@Override
|
||||
protected void onPostExecute(String[] result)
|
||||
{
|
||||
super.onPostExecute(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String[] doInBackground(Void... params)
|
||||
{
|
||||
refresh();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,310 +0,0 @@
|
|||
package org.moire.ultrasonic.activity;
|
||||
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ListAdapter;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
|
||||
import org.moire.ultrasonic.R;
|
||||
import org.moire.ultrasonic.data.ActiveServerProvider;
|
||||
import org.moire.ultrasonic.domain.ChatMessage;
|
||||
import org.moire.ultrasonic.service.JukeboxMediaPlayer;
|
||||
import org.moire.ultrasonic.service.MusicService;
|
||||
import org.moire.ultrasonic.service.MusicServiceFactory;
|
||||
import org.moire.ultrasonic.util.BackgroundTask;
|
||||
import org.moire.ultrasonic.util.TabActivityBackgroundTask;
|
||||
import org.moire.ultrasonic.util.Util;
|
||||
import org.moire.ultrasonic.view.ChatAdapter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
import kotlin.Lazy;
|
||||
|
||||
import static org.koin.java.KoinJavaComponent.inject;
|
||||
|
||||
/**
|
||||
* @author Joshua Bahnsen
|
||||
*/
|
||||
public final class ChatActivity extends SubsonicTabActivity
|
||||
{
|
||||
private ListView chatListView;
|
||||
private EditText messageEditText;
|
||||
private ImageButton sendButton;
|
||||
private Timer timer;
|
||||
private volatile static Long lastChatMessageTime = (long) 0;
|
||||
private volatile static ArrayList<ChatMessage> messageList = new ArrayList<ChatMessage>();
|
||||
private Lazy<ActiveServerProvider> activeServerProvider = inject(ActiveServerProvider.class);
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle bundle)
|
||||
{
|
||||
super.onCreate(bundle);
|
||||
setContentView(R.layout.chat);
|
||||
|
||||
messageEditText = (EditText) findViewById(R.id.chat_edittext);
|
||||
sendButton = (ImageButton) findViewById(R.id.chat_send);
|
||||
|
||||
sendButton.setOnClickListener(new View.OnClickListener()
|
||||
{
|
||||
|
||||
@Override
|
||||
public void onClick(View view)
|
||||
{
|
||||
sendMessage();
|
||||
}
|
||||
});
|
||||
|
||||
chatListView = findViewById(R.id.chat_entries_list);
|
||||
chatListView.setTranscriptMode(ListView.TRANSCRIPT_MODE_ALWAYS_SCROLL);
|
||||
chatListView.setStackFromBottom(true);
|
||||
|
||||
String serverName = activeServerProvider.getValue().getActiveServer().getName();
|
||||
String userName = activeServerProvider.getValue().getActiveServer().getUserName();
|
||||
String title = String.format("%s [%s@%s]", getResources().getString(R.string.button_bar_chat), userName, serverName);
|
||||
|
||||
setActionBarSubtitle(title);
|
||||
|
||||
messageEditText.setImeActionLabel("Send", KeyEvent.KEYCODE_ENTER);
|
||||
|
||||
messageEditText.addTextChangedListener(new TextWatcher()
|
||||
{
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2)
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2)
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable editable)
|
||||
{
|
||||
sendButton.setEnabled(!Util.isNullOrWhiteSpace(editable.toString()));
|
||||
}
|
||||
});
|
||||
|
||||
messageEditText.setOnEditorActionListener(new TextView.OnEditorActionListener()
|
||||
{
|
||||
|
||||
@Override
|
||||
public boolean onEditorAction(TextView v, int actionId, KeyEvent event)
|
||||
{
|
||||
if (actionId == EditorInfo.IME_ACTION_DONE || (actionId == EditorInfo.IME_NULL && event.getAction() == KeyEvent.ACTION_DOWN))
|
||||
{
|
||||
sendMessage();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
View chatMenuItem = findViewById(R.id.menu_chat);
|
||||
menuDrawer.setActiveView(chatMenuItem);
|
||||
|
||||
load();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostCreate(Bundle bundle)
|
||||
{
|
||||
super.onPostCreate(bundle);
|
||||
|
||||
timerMethod();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu)
|
||||
{
|
||||
MenuInflater inflater = getMenuInflater();
|
||||
inflater.inflate(R.menu.chat, menu);
|
||||
super.onCreateOptionsMenu(menu);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Listen for option item selections so that we receive a notification
|
||||
* when the user requests a refresh by selecting the refresh action bar item.
|
||||
*/
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
// Check if user triggered a refresh:
|
||||
case R.id.menu_refresh:
|
||||
// Start the refresh background task.
|
||||
new GetDataTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
return true;
|
||||
}
|
||||
// User didn't trigger a refresh, let the superclass handle this action
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume()
|
||||
{
|
||||
super.onResume();
|
||||
|
||||
if (!messageList.isEmpty())
|
||||
{
|
||||
ListAdapter chatAdapter = new ChatAdapter(ChatActivity.this, messageList);
|
||||
chatListView.setAdapter(chatAdapter);
|
||||
}
|
||||
|
||||
if (timer == null)
|
||||
{
|
||||
timerMethod();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause()
|
||||
{
|
||||
super.onPause();
|
||||
|
||||
if (timer != null)
|
||||
{
|
||||
timer.cancel();
|
||||
timer = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void timerMethod()
|
||||
{
|
||||
int refreshInterval = Util.getChatRefreshInterval(this);
|
||||
|
||||
if (refreshInterval > 0)
|
||||
{
|
||||
timer = new Timer();
|
||||
|
||||
timer.schedule(new TimerTask()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
ChatActivity.this.runOnUiThread(new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
load();
|
||||
}
|
||||
});
|
||||
}
|
||||
}, refreshInterval, refreshInterval);
|
||||
}
|
||||
}
|
||||
|
||||
private void sendMessage()
|
||||
{
|
||||
if (messageEditText != null)
|
||||
{
|
||||
final String message;
|
||||
Editable text = messageEditText.getText();
|
||||
|
||||
if (text == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
message = text.toString();
|
||||
|
||||
if (!Util.isNullOrWhiteSpace(message))
|
||||
{
|
||||
messageEditText.setText("");
|
||||
|
||||
BackgroundTask<Void> task = new TabActivityBackgroundTask<Void>(ChatActivity.this, false)
|
||||
{
|
||||
@Override
|
||||
protected Void doInBackground() throws Throwable
|
||||
{
|
||||
MusicService musicService = MusicServiceFactory.getMusicService(ChatActivity.this);
|
||||
musicService.addChatMessage(message, ChatActivity.this, this);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done(Void result)
|
||||
{
|
||||
load();
|
||||
}
|
||||
};
|
||||
|
||||
task.execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void load()
|
||||
{
|
||||
BackgroundTask<List<ChatMessage>> task = new TabActivityBackgroundTask<List<ChatMessage>>(this, false)
|
||||
{
|
||||
@Override
|
||||
protected List<ChatMessage> doInBackground() throws Throwable
|
||||
{
|
||||
MusicService musicService = MusicServiceFactory.getMusicService(ChatActivity.this);
|
||||
return musicService.getChatMessages(lastChatMessageTime, ChatActivity.this, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done(List<ChatMessage> result)
|
||||
{
|
||||
if (result != null && !result.isEmpty())
|
||||
{
|
||||
// Reset lastChatMessageTime if we have a newer message
|
||||
for (ChatMessage message : result)
|
||||
{
|
||||
if (message.getTime() > lastChatMessageTime)
|
||||
{
|
||||
lastChatMessageTime = message.getTime();
|
||||
}
|
||||
}
|
||||
|
||||
// Reverse results to show them on the bottom
|
||||
Collections.reverse(result);
|
||||
messageList.addAll(result);
|
||||
|
||||
ListAdapter chatAdapter = new ChatAdapter(ChatActivity.this, messageList);
|
||||
chatListView.setAdapter(chatAdapter);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
task.execute();
|
||||
}
|
||||
|
||||
private class GetDataTask extends AsyncTask<Void, Void, String[]>
|
||||
{
|
||||
@Override
|
||||
protected void onPostExecute(String[] result)
|
||||
{
|
||||
load();
|
||||
super.onPostExecute(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String[] doInBackground(Void... params)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1404,7 +1404,7 @@ public class DownloadActivity extends SubsonicTabActivity implements OnGestureLi
|
|||
artistTextView.setText(currentSong.getArtist());
|
||||
downloadTrackTextView.setText(trackFormat);
|
||||
downloadTotalDurationTextView.setText(duration);
|
||||
getImageLoader().loadImage(albumArtImageView, currentSong, true, 0, false, true);
|
||||
imageLoader.getValue().getImageLoader().loadImage(albumArtImageView, currentSong, true, 0, false, true);
|
||||
|
||||
displaySongRating();
|
||||
}
|
||||
|
@ -1416,7 +1416,7 @@ public class DownloadActivity extends SubsonicTabActivity implements OnGestureLi
|
|||
artistTextView.setText(null);
|
||||
downloadTrackTextView.setText(null);
|
||||
downloadTotalDurationTextView.setText(null);
|
||||
getImageLoader().loadImage(albumArtImageView, null, true, 0, false, true);
|
||||
imageLoader.getValue().getImageLoader().loadImage(albumArtImageView, null, true, 0, false, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,325 +0,0 @@
|
|||
/*
|
||||
This file is part of Subsonic.
|
||||
|
||||
Subsonic is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Subsonic is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Subsonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
|
||||
package org.moire.ultrasonic.activity;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.Window;
|
||||
import android.webkit.WebView;
|
||||
import android.webkit.WebViewClient;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import net.simonvt.menudrawer.MenuDrawer;
|
||||
import net.simonvt.menudrawer.Position;
|
||||
|
||||
import org.moire.ultrasonic.R;
|
||||
import org.moire.ultrasonic.data.ActiveServerProvider;
|
||||
import org.moire.ultrasonic.util.Constants;
|
||||
import org.moire.ultrasonic.util.Util;
|
||||
|
||||
/**
|
||||
* An HTML-based help screen with Back and Done buttons at the bottom.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public final class HelpActivity extends ResultActivity implements OnClickListener
|
||||
{
|
||||
private WebView webView;
|
||||
private ImageView backButton;
|
||||
private ImageView forwardButton;
|
||||
|
||||
private static final String STATE_MENUDRAWER = "org.moire.ultrasonic.menuDrawer";
|
||||
private static final String STATE_ACTIVE_VIEW_ID = "org.moire.ultrasonic.activeViewId";
|
||||
private static final String STATE_ACTIVE_POSITION = "org.moire.ultrasonic.activePosition";
|
||||
|
||||
public MenuDrawer menuDrawer;
|
||||
private int activePosition = 1;
|
||||
private int menuActiveViewId;
|
||||
View chatMenuItem;
|
||||
View bookmarksMenuItem;
|
||||
View sharesMenuItem;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle bundle)
|
||||
{
|
||||
Util.applyTheme(this);
|
||||
getWindow().requestFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
|
||||
super.onCreate(bundle);
|
||||
|
||||
setContentView(R.layout.help);
|
||||
|
||||
if (bundle != null)
|
||||
{
|
||||
activePosition = bundle.getInt(STATE_ACTIVE_POSITION);
|
||||
menuActiveViewId = bundle.getInt(STATE_ACTIVE_VIEW_ID);
|
||||
}
|
||||
|
||||
menuDrawer = MenuDrawer.attach(this, MenuDrawer.Type.BEHIND, Position.LEFT, MenuDrawer.MENU_DRAG_WINDOW);
|
||||
menuDrawer.setMenuView(R.layout.menu_main);
|
||||
|
||||
chatMenuItem = findViewById(R.id.menu_chat);
|
||||
bookmarksMenuItem = findViewById(R.id.menu_bookmarks);
|
||||
sharesMenuItem = findViewById(R.id.menu_shares);
|
||||
View aboutMenuItem = findViewById(R.id.menu_about);
|
||||
|
||||
findViewById(R.id.menu_home).setOnClickListener(this);
|
||||
findViewById(R.id.menu_browse).setOnClickListener(this);
|
||||
findViewById(R.id.menu_search).setOnClickListener(this);
|
||||
findViewById(R.id.menu_playlists).setOnClickListener(this);
|
||||
sharesMenuItem.setOnClickListener(this);
|
||||
chatMenuItem.setOnClickListener(this);
|
||||
bookmarksMenuItem.setOnClickListener(this);
|
||||
findViewById(R.id.menu_now_playing).setOnClickListener(this);
|
||||
findViewById(R.id.menu_settings).setOnClickListener(this);
|
||||
aboutMenuItem.setOnClickListener(this);
|
||||
findViewById(R.id.menu_exit).setOnClickListener(this);
|
||||
|
||||
ActionBar actionBar = getSupportActionBar();
|
||||
|
||||
if (actionBar != null)
|
||||
{
|
||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||
}
|
||||
|
||||
menuDrawer.setActiveView(aboutMenuItem);
|
||||
|
||||
webView = (WebView) findViewById(R.id.help_contents);
|
||||
webView.getSettings().setJavaScriptEnabled(true);
|
||||
webView.setWebViewClient(new HelpClient());
|
||||
|
||||
if (bundle != null)
|
||||
{
|
||||
webView.restoreState(bundle);
|
||||
}
|
||||
else
|
||||
{
|
||||
webView.loadUrl(getResources().getString(R.string.help_url));
|
||||
}
|
||||
|
||||
backButton = (ImageView) findViewById(R.id.help_back);
|
||||
backButton.setOnClickListener(new Button.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(View view)
|
||||
{
|
||||
webView.goBack();
|
||||
}
|
||||
});
|
||||
|
||||
ImageView stopButton = (ImageView) findViewById(R.id.help_stop);
|
||||
stopButton.setOnClickListener(new Button.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(View view)
|
||||
{
|
||||
webView.stopLoading();
|
||||
setProgressBarIndeterminateVisibility(false);
|
||||
}
|
||||
});
|
||||
|
||||
forwardButton = (ImageView) findViewById(R.id.help_forward);
|
||||
forwardButton.setOnClickListener(new Button.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(View view)
|
||||
{
|
||||
webView.goForward();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostCreate(Bundle bundle)
|
||||
{
|
||||
super.onPostCreate(bundle);
|
||||
|
||||
int visibility = ActiveServerProvider.Companion.isOffline(this) ? View.GONE : View.VISIBLE;
|
||||
chatMenuItem.setVisibility(visibility);
|
||||
bookmarksMenuItem.setVisibility(visibility);
|
||||
sharesMenuItem.setVisibility(visibility);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume()
|
||||
{
|
||||
super.onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSaveInstanceState(Bundle state)
|
||||
{
|
||||
webView.saveState(state);
|
||||
super.onSaveInstanceState(state);
|
||||
state.putParcelable(STATE_MENUDRAWER, menuDrawer.saveState());
|
||||
state.putInt(STATE_ACTIVE_VIEW_ID, menuActiveViewId);
|
||||
state.putInt(STATE_ACTIVE_POSITION, activePosition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event)
|
||||
{
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK)
|
||||
{
|
||||
if (webView.canGoBack())
|
||||
{
|
||||
webView.goBack();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return super.onKeyDown(keyCode, event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item)
|
||||
{
|
||||
switch (item.getItemId())
|
||||
{
|
||||
case android.R.id.home:
|
||||
menuDrawer.toggleMenu();
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRestoreInstanceState(Bundle state)
|
||||
{
|
||||
super.onRestoreInstanceState(state);
|
||||
menuDrawer.restoreState(state.getParcelable(STATE_MENUDRAWER));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed()
|
||||
{
|
||||
final int drawerState = menuDrawer.getDrawerState();
|
||||
|
||||
if (drawerState == MenuDrawer.STATE_OPEN || drawerState == MenuDrawer.STATE_OPENING)
|
||||
{
|
||||
menuDrawer.closeMenu(true);
|
||||
return;
|
||||
}
|
||||
|
||||
finish();
|
||||
|
||||
super.onBackPressed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v)
|
||||
{
|
||||
menuActiveViewId = v.getId();
|
||||
menuDrawer.setActiveView(v);
|
||||
|
||||
Intent intent;
|
||||
|
||||
switch (menuActiveViewId)
|
||||
{
|
||||
case R.id.menu_home:
|
||||
|
||||
intent = new Intent(this, MainActivity.class);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
startActivityForResultWithoutTransition(this, intent);
|
||||
break;
|
||||
case R.id.menu_browse:
|
||||
intent = new Intent(this, SelectArtistActivity.class);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
startActivityForResultWithoutTransition(this, intent);
|
||||
break;
|
||||
case R.id.menu_search:
|
||||
intent = new Intent(this, SearchActivity.class);
|
||||
intent.putExtra(Constants.INTENT_EXTRA_REQUEST_SEARCH, true);
|
||||
startActivityForResultWithoutTransition(this, intent);
|
||||
break;
|
||||
case R.id.menu_playlists:
|
||||
intent = new Intent(this, SelectPlaylistActivity.class);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
startActivityForResultWithoutTransition(this, intent);
|
||||
break;
|
||||
case R.id.menu_shares:
|
||||
intent = new Intent(this, ShareActivity.class);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
startActivityForResultWithoutTransition(this, intent);
|
||||
break;
|
||||
case R.id.menu_chat:
|
||||
startActivityForResultWithoutTransition(this, ChatActivity.class);
|
||||
break;
|
||||
case R.id.menu_bookmarks:
|
||||
startActivityForResultWithoutTransition(this, BookmarkActivity.class);
|
||||
break;
|
||||
case R.id.menu_now_playing:
|
||||
startActivityForResultWithoutTransition(this, DownloadActivity.class);
|
||||
break;
|
||||
case R.id.menu_settings:
|
||||
startActivityForResultWithoutTransition(this, SettingsActivity.class);
|
||||
break;
|
||||
case R.id.menu_about:
|
||||
startActivityForResultWithoutTransition(this, HelpActivity.class);
|
||||
break;
|
||||
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);
|
||||
startActivityForResultWithoutTransition(this, intent);
|
||||
break;
|
||||
}
|
||||
|
||||
menuDrawer.closeMenu(true);
|
||||
}
|
||||
|
||||
private final class HelpClient extends WebViewClient
|
||||
{
|
||||
@Override
|
||||
public void onLoadResource(WebView webView, String url)
|
||||
{
|
||||
setProgressBarIndeterminateVisibility(true);
|
||||
super.onLoadResource(webView, url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageFinished(WebView view, String url)
|
||||
{
|
||||
setProgressBarIndeterminateVisibility(false);
|
||||
String versionName = Util.getVersionName(HelpActivity.this);
|
||||
String title = String.format("%s (%s)", view.getTitle(), versionName);
|
||||
ActionBar actionBar = getSupportActionBar();
|
||||
|
||||
if (actionBar != null)
|
||||
{
|
||||
actionBar.setSubtitle(title);
|
||||
}
|
||||
|
||||
backButton.setEnabled(view.canGoBack());
|
||||
forwardButton.setEnabled(view.canGoForward());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl)
|
||||
{
|
||||
Util.toast(HelpActivity.this, description);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -67,161 +67,8 @@ public class MainActivity extends SubsonicTabActivity
|
|||
{
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// Determine first run and migrate server settings to DB as early as possible
|
||||
boolean showWelcomeScreen = Util.isFirstRun(this);
|
||||
boolean areServersMigrated = serverSettingsModel.getValue().migrateFromPreferences();
|
||||
|
||||
// If there are any servers in the DB, do not show the welcome screen
|
||||
showWelcomeScreen &= !areServersMigrated;
|
||||
|
||||
if (getIntent().hasExtra(Constants.INTENT_EXTRA_NAME_EXIT))
|
||||
{
|
||||
setResult(Constants.RESULT_CLOSE_ALL);
|
||||
|
||||
if (getMediaPlayerController() != null)
|
||||
{
|
||||
getMediaPlayerController().stopJukeboxService();
|
||||
}
|
||||
|
||||
if (getImageLoader() != null)
|
||||
{
|
||||
getImageLoader().stopImageLoader();
|
||||
}
|
||||
|
||||
finish();
|
||||
exit();
|
||||
return;
|
||||
}
|
||||
|
||||
setContentView(R.layout.main);
|
||||
|
||||
loadSettings();
|
||||
|
||||
final View buttons = LayoutInflater.from(this).inflate(R.layout.main_buttons, null);
|
||||
final View serverButton = buttons.findViewById(R.id.main_select_server);
|
||||
final TextView serverTextView = serverButton.findViewById(R.id.main_select_server_2);
|
||||
final View musicTitle = buttons.findViewById(R.id.main_music);
|
||||
final View artistsButton = buttons.findViewById(R.id.main_artists_button);
|
||||
final View albumsButton = buttons.findViewById(R.id.main_albums_button);
|
||||
final View genresButton = buttons.findViewById(R.id.main_genres_button);
|
||||
final View videosTitle = buttons.findViewById(R.id.main_videos_title);
|
||||
final View songsTitle = buttons.findViewById(R.id.main_songs);
|
||||
final View randomSongsButton = buttons.findViewById(R.id.main_songs_button);
|
||||
final View songsStarredButton = buttons.findViewById(R.id.main_songs_starred);
|
||||
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 albumsStarredButton = buttons.findViewById(R.id.main_albums_starred);
|
||||
final View albumsRecentButton = buttons.findViewById(R.id.main_albums_recent);
|
||||
final View albumsFrequentButton = buttons.findViewById(R.id.main_albums_frequent);
|
||||
final View albumsAlphaByNameButton = buttons.findViewById(R.id.main_albums_alphaByName);
|
||||
final View albumsAlphaByArtistButton = buttons.findViewById(R.id.main_albums_alphaByArtist);
|
||||
final View videosButton = buttons.findViewById(R.id.main_videos);
|
||||
|
||||
lastActiveServerProperties = getActiveServerProperties();
|
||||
String name = activeServerProvider.getValue().getActiveServer().getName();
|
||||
|
||||
serverTextView.setText(name);
|
||||
|
||||
final ListView list = findViewById(R.id.main_list);
|
||||
|
||||
final MergeAdapter adapter = new MergeAdapter();
|
||||
adapter.addViews(Collections.singletonList(serverButton), true);
|
||||
|
||||
if (!ActiveServerProvider.Companion.isOffline(this))
|
||||
{
|
||||
adapter.addView(musicTitle, false);
|
||||
adapter.addViews(asList(artistsButton, albumsButton, genresButton), true);
|
||||
adapter.addView(songsTitle, false);
|
||||
adapter.addViews(asList(randomSongsButton, songsStarredButton), true);
|
||||
adapter.addView(albumsTitle, false);
|
||||
|
||||
if (Util.getShouldUseId3Tags(MainActivity.this))
|
||||
{
|
||||
shouldUseId3 = true;
|
||||
adapter.addViews(asList(albumsNewestButton, albumsRecentButton, albumsFrequentButton, albumsRandomButton, albumsStarredButton, albumsAlphaByNameButton, albumsAlphaByArtistButton), true);
|
||||
}
|
||||
else
|
||||
{
|
||||
shouldUseId3 = false;
|
||||
adapter.addViews(asList(albumsNewestButton, albumsRecentButton, albumsFrequentButton, albumsHighestButton, albumsRandomButton, albumsStarredButton, albumsAlphaByNameButton, albumsAlphaByArtistButton), true);
|
||||
}
|
||||
|
||||
adapter.addView(videosTitle, false);
|
||||
adapter.addViews(Collections.singletonList(videosButton), true);
|
||||
}
|
||||
|
||||
list.setAdapter(adapter);
|
||||
|
||||
list.setOnItemClickListener(new AdapterView.OnItemClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id)
|
||||
{
|
||||
if (view == serverButton)
|
||||
{
|
||||
showServers();
|
||||
}
|
||||
else if (view == albumsNewestButton)
|
||||
{
|
||||
showAlbumList("newest", R.string.main_albums_newest);
|
||||
}
|
||||
else if (view == albumsRandomButton)
|
||||
{
|
||||
showAlbumList("random", R.string.main_albums_random);
|
||||
}
|
||||
else if (view == albumsHighestButton)
|
||||
{
|
||||
showAlbumList("highest", R.string.main_albums_highest);
|
||||
}
|
||||
else if (view == albumsRecentButton)
|
||||
{
|
||||
showAlbumList("recent", R.string.main_albums_recent);
|
||||
}
|
||||
else if (view == albumsFrequentButton)
|
||||
{
|
||||
showAlbumList("frequent", R.string.main_albums_frequent);
|
||||
}
|
||||
else if (view == albumsStarredButton)
|
||||
{
|
||||
showAlbumList(Constants.STARRED, R.string.main_albums_starred);
|
||||
}
|
||||
else if (view == albumsAlphaByNameButton)
|
||||
{
|
||||
showAlbumList(Constants.ALPHABETICAL_BY_NAME, R.string.main_albums_alphaByName);
|
||||
}
|
||||
else if (view == albumsAlphaByArtistButton)
|
||||
{
|
||||
showAlbumList("alphabeticalByArtist", R.string.main_albums_alphaByArtist);
|
||||
}
|
||||
else if (view == songsStarredButton)
|
||||
{
|
||||
showStarredSongs();
|
||||
}
|
||||
else if (view == artistsButton)
|
||||
{
|
||||
showArtists();
|
||||
}
|
||||
else if (view == albumsButton)
|
||||
{
|
||||
showAlbumList(Constants.ALPHABETICAL_BY_NAME, R.string.main_albums_title);
|
||||
}
|
||||
else if (view == randomSongsButton)
|
||||
{
|
||||
showRandomSongs();
|
||||
}
|
||||
else if (view == genresButton)
|
||||
{
|
||||
showGenres();
|
||||
}
|
||||
else if (view == videosButton)
|
||||
{
|
||||
showVideos();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
final View homeMenuItem = findViewById(R.id.menu_home);
|
||||
menuDrawer.setActiveView(homeMenuItem);
|
||||
|
||||
|
@ -230,46 +77,8 @@ public class MainActivity extends SubsonicTabActivity
|
|||
|
||||
// Remember the current theme.
|
||||
theme = Util.getTheme(this);
|
||||
|
||||
showInfoDialog(showWelcomeScreen);
|
||||
}
|
||||
|
||||
private void loadSettings()
|
||||
{
|
||||
PreferenceManager.setDefaultValues(this, R.xml.settings, false);
|
||||
final SharedPreferences preferences = Util.getPreferences(this);
|
||||
|
||||
if (!preferences.contains(Constants.PREFERENCES_KEY_CACHE_LOCATION))
|
||||
{
|
||||
final SharedPreferences.Editor editor = preferences.edit();
|
||||
editor.putString(Constants.PREFERENCES_KEY_CACHE_LOCATION, FileUtil.getDefaultMusicDirectory(this).getPath());
|
||||
editor.apply();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume()
|
||||
{
|
||||
super.onResume();
|
||||
boolean shouldRestart = false;
|
||||
|
||||
boolean id3 = Util.getShouldUseId3Tags(MainActivity.this);
|
||||
String currentActiveServerProperties = getActiveServerProperties();
|
||||
|
||||
if (id3 != shouldUseId3)
|
||||
{
|
||||
shouldUseId3 = id3;
|
||||
shouldRestart = true;
|
||||
}
|
||||
|
||||
if (!currentActiveServerProperties.equals(lastActiveServerProperties))
|
||||
{
|
||||
lastActiveServerProperties = currentActiveServerProperties;
|
||||
shouldRestart = true;
|
||||
}
|
||||
|
||||
if (shouldRestart) restart();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(final Menu menu)
|
||||
|
@ -298,85 +107,4 @@ public class MainActivity extends SubsonicTabActivity
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void exit()
|
||||
{
|
||||
lifecycleSupport.getValue().onDestroy();
|
||||
Util.unregisterMediaButtonEventReceiver(this, false);
|
||||
finish();
|
||||
}
|
||||
|
||||
private void showInfoDialog(final boolean show)
|
||||
{
|
||||
if (!infoDialogDisplayed)
|
||||
{
|
||||
infoDialogDisplayed = true;
|
||||
|
||||
if (show)
|
||||
{
|
||||
Util.showWelcomeDialog(this, this, R.string.main_welcome_title, R.string.main_welcome_text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void showAlbumList(final String type, final int title)
|
||||
{
|
||||
final 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_TITLE, title);
|
||||
intent.putExtra(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, Util.getMaxAlbums(this));
|
||||
intent.putExtra(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_OFFSET, 0);
|
||||
startActivityForResultWithoutTransition(this, intent);
|
||||
}
|
||||
|
||||
private void showStarredSongs()
|
||||
{
|
||||
final Intent intent = new Intent(this, SelectAlbumActivity.class);
|
||||
intent.putExtra(Constants.INTENT_EXTRA_NAME_STARRED, 1);
|
||||
startActivityForResultWithoutTransition(this, intent);
|
||||
}
|
||||
|
||||
private void showRandomSongs()
|
||||
{
|
||||
final Intent intent = new Intent(this, SelectAlbumActivity.class);
|
||||
intent.putExtra(Constants.INTENT_EXTRA_NAME_RANDOM, 1);
|
||||
intent.putExtra(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, Util.getMaxSongs(this));
|
||||
startActivityForResultWithoutTransition(this, intent);
|
||||
}
|
||||
|
||||
private void showArtists()
|
||||
{
|
||||
final Intent intent = new Intent(this, SelectArtistActivity.class);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
intent.putExtra(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TITLE, getResources().getString(R.string.main_artists_title));
|
||||
startActivityForResultWithoutTransition(this, intent);
|
||||
}
|
||||
|
||||
private void showGenres()
|
||||
{
|
||||
final Intent intent = new Intent(this, SelectGenreActivity.class);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
startActivityForResultWithoutTransition(this, intent);
|
||||
}
|
||||
|
||||
private void showVideos()
|
||||
{
|
||||
final Intent intent = new Intent(this, SelectAlbumActivity.class);
|
||||
intent.putExtra(Constants.INTENT_EXTRA_NAME_VIDEOS, 1);
|
||||
startActivityForResultWithoutTransition(this, intent);
|
||||
}
|
||||
|
||||
private void showServers()
|
||||
{
|
||||
final Intent intent = new Intent(this, ServerSelectorActivity.class);
|
||||
startActivityForResult(intent, 0);
|
||||
}
|
||||
|
||||
private String getActiveServerProperties()
|
||||
{
|
||||
ServerSetting currentSetting = activeServerProvider.getValue().getActiveServer();
|
||||
return String.format("%s;%s;%s;%s;%s;%s", currentSetting.getUrl(), currentSetting.getUserName(),
|
||||
currentSetting.getPassword(), currentSetting.getAllowSelfSignedCertificate(),
|
||||
currentSetting.getLdapSupport(), currentSetting.getMinimumApiVersion());
|
||||
}
|
||||
}
|
|
@ -1,106 +0,0 @@
|
|||
/*
|
||||
This file is part of Subsonic.
|
||||
|
||||
Subsonic is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Subsonic is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Subsonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
|
||||
package org.moire.ultrasonic.activity;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ListView;
|
||||
|
||||
import org.moire.ultrasonic.R;
|
||||
import org.moire.ultrasonic.domain.PodcastsChannel;
|
||||
import org.moire.ultrasonic.service.MusicService;
|
||||
import org.moire.ultrasonic.service.MusicServiceFactory;
|
||||
import org.moire.ultrasonic.util.BackgroundTask;
|
||||
import org.moire.ultrasonic.util.Constants;
|
||||
import org.moire.ultrasonic.util.TabActivityBackgroundTask;
|
||||
import org.moire.ultrasonic.view.PodcastsChannelsAdapter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class PodcastsActivity extends SubsonicTabActivity {
|
||||
|
||||
private View emptyTextView;
|
||||
SubsonicTabActivity currentActivity = null;
|
||||
ListView channelItemsListView = null;
|
||||
|
||||
Context currentContext = (Context)this;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
this.currentActivity = this;
|
||||
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.podcasts);
|
||||
|
||||
|
||||
emptyTextView = findViewById(R.id.select_podcasts_empty);
|
||||
channelItemsListView = (ListView)findViewById(R.id.podcasts_channels_items_list);
|
||||
channelItemsListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
PodcastsChannel pc = (PodcastsChannel) parent.getItemAtPosition(position);
|
||||
if (pc == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Intent intent = new Intent(currentContext, SelectAlbumActivity.class);
|
||||
intent.putExtra(Constants.INTENT_EXTRA_NAME_PODCAST_CHANNEL_ID, pc.getId());
|
||||
startActivityForResultWithoutTransition(PodcastsActivity.this, intent);
|
||||
}
|
||||
});
|
||||
|
||||
load();
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void load()
|
||||
{
|
||||
BackgroundTask<List<PodcastsChannel>> task = new TabActivityBackgroundTask<List<PodcastsChannel>>(this, true)
|
||||
{
|
||||
@Override
|
||||
protected List<PodcastsChannel> doInBackground() throws Throwable
|
||||
{
|
||||
MusicService musicService = MusicServiceFactory.getMusicService(PodcastsActivity.this);
|
||||
List<PodcastsChannel> channels = musicService.getPodcastsChannels(false,PodcastsActivity.this, this);
|
||||
|
||||
/* TODO c'est quoi ce nettoyage de cache ?
|
||||
if (!Util.isOffline(PodcastsActivity.this))
|
||||
new CacheCleaner(PodcastsActivity.this, getDownloadService()).cleanPlaylists(playlists);
|
||||
*/
|
||||
return channels;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done(List<PodcastsChannel> result)
|
||||
{
|
||||
channelItemsListView.setAdapter(new PodcastsChannelsAdapter(currentActivity, result));
|
||||
emptyTextView.setVisibility(result.isEmpty() ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
};
|
||||
task.execute();
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -41,6 +41,7 @@ import org.moire.ultrasonic.domain.SearchResult;
|
|||
import org.moire.ultrasonic.service.MediaPlayerController;
|
||||
import org.moire.ultrasonic.service.MusicService;
|
||||
import org.moire.ultrasonic.service.MusicServiceFactory;
|
||||
import org.moire.ultrasonic.subsonic.VideoPlayer;
|
||||
import org.moire.ultrasonic.util.BackgroundTask;
|
||||
import org.moire.ultrasonic.util.Constants;
|
||||
import org.moire.ultrasonic.util.MergeAdapter;
|
||||
|
@ -53,6 +54,10 @@ import java.util.ArrayList;
|
|||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import kotlin.Lazy;
|
||||
|
||||
import static org.koin.java.KoinJavaComponent.inject;
|
||||
|
||||
/**
|
||||
* Performs searches and displays the matching artists, albums and songs.
|
||||
*
|
||||
|
@ -83,6 +88,8 @@ public class SearchActivity extends SubsonicTabActivity
|
|||
private ListAdapter moreSongsAdapter;
|
||||
private EntryAdapter songAdapter;
|
||||
|
||||
private final Lazy<VideoPlayer> videoPlayer = inject(VideoPlayer.class);
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
|
@ -419,7 +426,7 @@ public class SearchActivity extends SubsonicTabActivity
|
|||
{
|
||||
mergeAdapter.addView(albumsHeading);
|
||||
List<MusicDirectory.Entry> displayedAlbums = new ArrayList<MusicDirectory.Entry>(albums.subList(0, Math.min(DEFAULT_ALBUMS, albums.size())));
|
||||
albumAdapter = new EntryAdapter(this, getImageLoader(), displayedAlbums, false);
|
||||
albumAdapter = new EntryAdapter(this, imageLoader.getValue().getImageLoader(), displayedAlbums, false);
|
||||
mergeAdapter.addAdapter(albumAdapter);
|
||||
if (albums.size() > DEFAULT_ALBUMS)
|
||||
{
|
||||
|
@ -432,7 +439,7 @@ public class SearchActivity extends SubsonicTabActivity
|
|||
{
|
||||
mergeAdapter.addView(songsHeading);
|
||||
List<MusicDirectory.Entry> displayedSongs = new ArrayList<MusicDirectory.Entry>(songs.subList(0, Math.min(DEFAULT_SONGS, songs.size())));
|
||||
songAdapter = new EntryAdapter(this, getImageLoader(), displayedSongs, false);
|
||||
songAdapter = new EntryAdapter(this, imageLoader.getValue().getImageLoader(), displayedSongs, false);
|
||||
mergeAdapter.addAdapter(songAdapter);
|
||||
if (songs.size() > DEFAULT_SONGS)
|
||||
{
|
||||
|
@ -530,7 +537,7 @@ public class SearchActivity extends SubsonicTabActivity
|
|||
|
||||
private void onVideoSelected(MusicDirectory.Entry entry)
|
||||
{
|
||||
playVideo(entry);
|
||||
videoPlayer.getValue().playVideo(entry);
|
||||
}
|
||||
|
||||
private void autoplay()
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,191 +0,0 @@
|
|||
/*
|
||||
This file is part of Subsonic.
|
||||
|
||||
Subsonic is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Subsonic is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Subsonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
|
||||
package org.moire.ultrasonic.activity;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import timber.log.Timber;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ListView;
|
||||
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
|
||||
import org.moire.ultrasonic.R;
|
||||
import org.moire.ultrasonic.domain.Genre;
|
||||
import org.moire.ultrasonic.service.MusicService;
|
||||
import org.moire.ultrasonic.service.MusicServiceFactory;
|
||||
import org.moire.ultrasonic.util.BackgroundTask;
|
||||
import org.moire.ultrasonic.util.Constants;
|
||||
import org.moire.ultrasonic.util.TabActivityBackgroundTask;
|
||||
import org.moire.ultrasonic.util.Util;
|
||||
import org.moire.ultrasonic.view.GenreAdapter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class SelectGenreActivity extends SubsonicTabActivity implements AdapterView.OnItemClickListener
|
||||
{
|
||||
private SwipeRefreshLayout refreshGenreListView;
|
||||
private ListView genreListView;
|
||||
private View emptyView;
|
||||
|
||||
/**
|
||||
* Called when the activity is first created.
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.select_genre);
|
||||
|
||||
refreshGenreListView = findViewById(R.id.select_genre_refresh);
|
||||
genreListView = findViewById(R.id.select_genre_list);
|
||||
|
||||
refreshGenreListView.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener()
|
||||
{
|
||||
@Override
|
||||
public void onRefresh()
|
||||
{
|
||||
new GetDataTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
}
|
||||
});
|
||||
|
||||
genreListView.setOnItemClickListener(this);
|
||||
|
||||
emptyView = findViewById(R.id.select_genre_empty);
|
||||
|
||||
registerForContextMenu(genreListView);
|
||||
|
||||
View browseMenuItem = findViewById(R.id.menu_browse);
|
||||
menuDrawer.setActiveView(browseMenuItem);
|
||||
|
||||
setActionBarSubtitle(R.string.main_genres_title);
|
||||
|
||||
load();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu)
|
||||
{
|
||||
super.onCreateOptionsMenu(menu);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void refresh()
|
||||
{
|
||||
finish();
|
||||
Intent intent = getIntent();
|
||||
intent.putExtra(Constants.INTENT_EXTRA_NAME_REFRESH, true);
|
||||
startActivityForResultWithoutTransition(this, intent);
|
||||
}
|
||||
|
||||
private void load()
|
||||
{
|
||||
BackgroundTask<List<Genre>> task = new TabActivityBackgroundTask<List<Genre>>(this, true)
|
||||
{
|
||||
@Override
|
||||
protected List<Genre> doInBackground() throws Throwable
|
||||
{
|
||||
boolean refresh = getIntent().getBooleanExtra(Constants.INTENT_EXTRA_NAME_REFRESH, false);
|
||||
MusicService musicService = MusicServiceFactory.getMusicService(SelectGenreActivity.this);
|
||||
|
||||
List<Genre> genres = new ArrayList<Genre>();
|
||||
|
||||
try
|
||||
{
|
||||
genres = musicService.getGenres(refresh, SelectGenreActivity.this, this);
|
||||
}
|
||||
catch (Exception x)
|
||||
{
|
||||
Timber.e(x, "Failed to load genres");
|
||||
}
|
||||
|
||||
return genres;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done(List<Genre> result)
|
||||
{
|
||||
emptyView.setVisibility(result == null || result.isEmpty() ? View.VISIBLE : View.GONE);
|
||||
|
||||
if (result != null)
|
||||
{
|
||||
genreListView.setAdapter(new GenreAdapter(SelectGenreActivity.this, result));
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
task.execute();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id)
|
||||
{
|
||||
Genre genre = (Genre) parent.getItemAtPosition(position);
|
||||
|
||||
if (genre != null)
|
||||
{
|
||||
Intent intent = new Intent(this, SelectAlbumActivity.class);
|
||||
intent.putExtra(Constants.INTENT_EXTRA_NAME_GENRE_NAME, genre.getName());
|
||||
intent.putExtra(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, Util.getMaxSongs(this));
|
||||
intent.putExtra(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_OFFSET, 0);
|
||||
startActivityForResultWithoutTransition(this, intent);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item)
|
||||
{
|
||||
switch (item.getItemId())
|
||||
{
|
||||
case android.R.id.home:
|
||||
menuDrawer.toggleMenu();
|
||||
return true;
|
||||
case R.id.main_shuffle:
|
||||
Intent intent = new Intent(this, DownloadActivity.class);
|
||||
intent.putExtra(Constants.INTENT_EXTRA_NAME_SHUFFLE, true);
|
||||
startActivityForResultWithoutTransition(this, intent);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private class GetDataTask extends AsyncTask<Void, Void, String[]>
|
||||
{
|
||||
@Override
|
||||
protected void onPostExecute(String[] result)
|
||||
{
|
||||
super.onPostExecute(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String[] doInBackground(Void... params)
|
||||
{
|
||||
refresh();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,391 +0,0 @@
|
|||
/*
|
||||
This file is part of Subsonic.
|
||||
|
||||
Subsonic is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Subsonic is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Subsonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
|
||||
package org.moire.ultrasonic.activity;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableString;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.text.util.Linkify;
|
||||
import android.view.ContextMenu;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
|
||||
import org.moire.ultrasonic.R;
|
||||
import org.moire.ultrasonic.api.subsonic.ApiNotSupportedException;
|
||||
import org.moire.ultrasonic.data.ActiveServerProvider;
|
||||
import org.moire.ultrasonic.domain.Playlist;
|
||||
import org.moire.ultrasonic.service.MusicService;
|
||||
import org.moire.ultrasonic.service.MusicServiceFactory;
|
||||
import org.moire.ultrasonic.service.OfflineException;
|
||||
import org.moire.ultrasonic.util.BackgroundTask;
|
||||
import org.moire.ultrasonic.util.CacheCleaner;
|
||||
import org.moire.ultrasonic.util.Constants;
|
||||
import org.moire.ultrasonic.util.LoadingTask;
|
||||
import org.moire.ultrasonic.util.TabActivityBackgroundTask;
|
||||
import org.moire.ultrasonic.util.Util;
|
||||
import org.moire.ultrasonic.view.PlaylistAdapter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class SelectPlaylistActivity extends SubsonicTabActivity implements AdapterView.OnItemClickListener
|
||||
{
|
||||
|
||||
private SwipeRefreshLayout refreshPlaylistsListView;
|
||||
private ListView playlistsListView;
|
||||
private View emptyTextView;
|
||||
private PlaylistAdapter playlistAdapter;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.select_playlist);
|
||||
|
||||
refreshPlaylistsListView = findViewById(R.id.select_playlist_refresh);
|
||||
playlistsListView = findViewById(R.id.select_playlist_list);
|
||||
|
||||
refreshPlaylistsListView.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener()
|
||||
{
|
||||
@Override
|
||||
public void onRefresh()
|
||||
{
|
||||
new GetDataTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
}
|
||||
});
|
||||
|
||||
emptyTextView = findViewById(R.id.select_playlist_empty);
|
||||
playlistsListView.setOnItemClickListener(this);
|
||||
registerForContextMenu(playlistsListView);
|
||||
|
||||
View playlistsMenuItem = findViewById(R.id.menu_playlists);
|
||||
menuDrawer.setActiveView(playlistsMenuItem);
|
||||
|
||||
setActionBarTitle(R.string.common_appname);
|
||||
setActionBarSubtitle(R.string.playlist_label);
|
||||
|
||||
load();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu)
|
||||
{
|
||||
super.onCreateOptionsMenu(menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void refresh()
|
||||
{
|
||||
finish();
|
||||
Intent intent = new Intent(this, SelectPlaylistActivity.class);
|
||||
intent.putExtra(Constants.INTENT_EXTRA_NAME_REFRESH, true);
|
||||
startActivityForResultWithoutTransition(this, intent);
|
||||
}
|
||||
|
||||
private void load()
|
||||
{
|
||||
BackgroundTask<List<Playlist>> task = new TabActivityBackgroundTask<List<Playlist>>(this, true)
|
||||
{
|
||||
@Override
|
||||
protected List<Playlist> doInBackground() throws Throwable
|
||||
{
|
||||
MusicService musicService = MusicServiceFactory.getMusicService(SelectPlaylistActivity.this);
|
||||
boolean refresh = getIntent().getBooleanExtra(Constants.INTENT_EXTRA_NAME_REFRESH, false);
|
||||
List<Playlist> playlists = musicService.getPlaylists(refresh, SelectPlaylistActivity.this, this);
|
||||
|
||||
if (!ActiveServerProvider.Companion.isOffline(SelectPlaylistActivity.this))
|
||||
new CacheCleaner(SelectPlaylistActivity.this).cleanPlaylists(playlists);
|
||||
return playlists;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done(List<Playlist> result)
|
||||
{
|
||||
playlistsListView.setAdapter(playlistAdapter = new PlaylistAdapter(SelectPlaylistActivity.this, result));
|
||||
emptyTextView.setVisibility(result.isEmpty() ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
};
|
||||
task.execute();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo)
|
||||
{
|
||||
super.onCreateContextMenu(menu, view, menuInfo);
|
||||
|
||||
MenuInflater inflater = getMenuInflater();
|
||||
if (ActiveServerProvider.Companion.isOffline(this)) inflater.inflate(R.menu.select_playlist_context_offline, menu);
|
||||
else inflater.inflate(R.menu.select_playlist_context, menu);
|
||||
|
||||
MenuItem downloadMenuItem = menu.findItem(R.id.album_menu_download);
|
||||
|
||||
if (downloadMenuItem != null)
|
||||
{
|
||||
downloadMenuItem.setVisible(!ActiveServerProvider.Companion.isOffline(this));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onContextItemSelected(MenuItem menuItem)
|
||||
{
|
||||
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo();
|
||||
if (info == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Playlist playlist = (Playlist) playlistsListView.getItemAtPosition(info.position);
|
||||
if (playlist == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Intent intent;
|
||||
switch (menuItem.getItemId())
|
||||
{
|
||||
case R.id.playlist_menu_pin:
|
||||
downloadPlaylist(playlist.getId(), playlist.getName(), true, true, false, false, true, false, false);
|
||||
break;
|
||||
case R.id.playlist_menu_unpin:
|
||||
downloadPlaylist(playlist.getId(), playlist.getName(), false, false, false, false, true, false, true);
|
||||
break;
|
||||
case R.id.playlist_menu_download:
|
||||
downloadPlaylist(playlist.getId(), playlist.getName(), false, false, false, false, true, false, false);
|
||||
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);
|
||||
startActivityForResultWithoutTransition(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);
|
||||
startActivityForResultWithoutTransition(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 boolean onOptionsItemSelected(MenuItem item)
|
||||
{
|
||||
switch (item.getItemId())
|
||||
{
|
||||
case android.R.id.home:
|
||||
menuDrawer.toggleMenu();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id)
|
||||
{
|
||||
Playlist playlist = (Playlist) parent.getItemAtPosition(position);
|
||||
|
||||
if (playlist == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Intent intent = new Intent(SelectPlaylistActivity.this, SelectAlbumActivity.class);
|
||||
intent.putExtra(Constants.INTENT_EXTRA_NAME_ID, playlist.getId());
|
||||
intent.putExtra(Constants.INTENT_EXTRA_NAME_PLAYLIST_ID, playlist.getId());
|
||||
intent.putExtra(Constants.INTENT_EXTRA_NAME_PLAYLIST_NAME, playlist.getName());
|
||||
startActivityForResultWithoutTransition(SelectPlaylistActivity.this, intent);
|
||||
}
|
||||
|
||||
private void deletePlaylist(final Playlist playlist)
|
||||
{
|
||||
new AlertDialog.Builder(this).setIcon(android.R.drawable.ic_dialog_alert).setTitle(R.string.common_confirm).setMessage(getResources().getString(R.string.delete_playlist, playlist.getName())).setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which)
|
||||
{
|
||||
new LoadingTask<Void>(SelectPlaylistActivity.this, false)
|
||||
{
|
||||
@Override
|
||||
protected Void doInBackground() throws Throwable
|
||||
{
|
||||
MusicService musicService = MusicServiceFactory.getMusicService(SelectPlaylistActivity.this);
|
||||
musicService.deletePlaylist(playlist.getId(), SelectPlaylistActivity.this, null);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done(Void result)
|
||||
{
|
||||
playlistAdapter.remove(playlist);
|
||||
playlistAdapter.notifyDataSetChanged();
|
||||
Util.toast(SelectPlaylistActivity.this, getResources().getString(R.string.menu_deleted_playlist, playlist.getName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void error(Throwable error)
|
||||
{
|
||||
String msg;
|
||||
msg = error instanceof OfflineException || error instanceof ApiNotSupportedException ? getErrorMessage(error) : String.format("%s %s", getResources().getString(R.string.menu_deleted_playlist_error, playlist.getName()), getErrorMessage(error));
|
||||
|
||||
Util.toast(SelectPlaylistActivity.this, msg, false);
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
}).setNegativeButton(R.string.common_cancel, null).show();
|
||||
}
|
||||
|
||||
private void displayPlaylistInfo(final Playlist playlist)
|
||||
{
|
||||
final TextView textView = new TextView(this);
|
||||
textView.setPadding(5, 5, 5, 5);
|
||||
|
||||
final Spannable message = new SpannableString("Owner: " + playlist.getOwner() + "\nComments: " +
|
||||
((playlist.getComment() == null) ? "" : playlist.getComment()) +
|
||||
"\nSong Count: " + playlist.getSongCount() +
|
||||
((playlist.getPublic() == null) ? "" : ("\nPublic: " + playlist.getPublic()) + ((playlist.getCreated() == null) ? "" : ("\nCreation Date: " + playlist.getCreated().replace('T', ' ')))));
|
||||
|
||||
Linkify.addLinks(message, Linkify.WEB_URLS);
|
||||
textView.setText(message);
|
||||
textView.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
|
||||
new AlertDialog.Builder(this).setTitle(playlist.getName()).setCancelable(true).setIcon(android.R.drawable.ic_dialog_info).setView(textView).show();
|
||||
}
|
||||
|
||||
private void updatePlaylistInfo(final Playlist playlist)
|
||||
{
|
||||
View dialogView = getLayoutInflater().inflate(R.layout.update_playlist, null);
|
||||
|
||||
if (dialogView == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
final EditText nameBox = (EditText) dialogView.findViewById(R.id.get_playlist_name);
|
||||
final EditText commentBox = (EditText) dialogView.findViewById(R.id.get_playlist_comment);
|
||||
final CheckBox publicBox = (CheckBox) dialogView.findViewById(R.id.get_playlist_public);
|
||||
|
||||
nameBox.setText(playlist.getName());
|
||||
commentBox.setText(playlist.getComment());
|
||||
Boolean pub = playlist.getPublic();
|
||||
|
||||
if (pub == null)
|
||||
{
|
||||
publicBox.setEnabled(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
publicBox.setChecked(pub);
|
||||
}
|
||||
|
||||
AlertDialog.Builder alertDialog = new AlertDialog.Builder(this);
|
||||
|
||||
alertDialog.setIcon(android.R.drawable.ic_dialog_alert);
|
||||
alertDialog.setTitle(R.string.playlist_update_info);
|
||||
alertDialog.setView(dialogView);
|
||||
alertDialog.setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which)
|
||||
{
|
||||
new LoadingTask<Void>(SelectPlaylistActivity.this, false)
|
||||
{
|
||||
@Override
|
||||
protected Void doInBackground() throws Throwable
|
||||
{
|
||||
Editable nameBoxText = nameBox.getText();
|
||||
Editable commentBoxText = commentBox.getText();
|
||||
String name = nameBoxText != null ? nameBoxText.toString() : null;
|
||||
String comment = commentBoxText != null ? commentBoxText.toString() : null;
|
||||
|
||||
MusicService musicService = MusicServiceFactory.getMusicService(SelectPlaylistActivity.this);
|
||||
musicService.updatePlaylist(playlist.getId(), name, comment, publicBox.isChecked(), SelectPlaylistActivity.this, null);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done(Void result)
|
||||
{
|
||||
refresh();
|
||||
Util.toast(SelectPlaylistActivity.this, getResources().getString(R.string.playlist_updated_info, playlist.getName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void error(Throwable error)
|
||||
{
|
||||
String msg;
|
||||
msg = error instanceof OfflineException || error instanceof ApiNotSupportedException ? getErrorMessage(error) : String.format("%s %s", getResources().getString(R.string.playlist_updated_info_error, playlist.getName()), getErrorMessage(error));
|
||||
|
||||
Util.toast(SelectPlaylistActivity.this, msg, false);
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
});
|
||||
alertDialog.setNegativeButton(R.string.common_cancel, null);
|
||||
alertDialog.show();
|
||||
}
|
||||
|
||||
private class GetDataTask extends AsyncTask<Void, Void, String[]>
|
||||
{
|
||||
@Override
|
||||
protected void onPostExecute(String[] result)
|
||||
{
|
||||
super.onPostExecute(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String[] doInBackground(Void... params)
|
||||
{
|
||||
refresh();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
/*
|
||||
This file is part of Subsonic.
|
||||
|
||||
Subsonic is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Subsonic is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Subsonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.moire.ultrasonic.activity;
|
||||
|
||||
import android.os.Bundle;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
|
||||
import org.moire.ultrasonic.R;
|
||||
import org.moire.ultrasonic.fragment.SettingsFragment;
|
||||
|
||||
public class SettingsActivity extends SubsonicTabActivity {
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
super.onCreate(savedInstanceState);
|
||||
getFragmentManager().beginTransaction()
|
||||
.replace(android.R.id.content, new SettingsFragment())
|
||||
.commit();
|
||||
|
||||
|
||||
final ActionBar actionBar = getSupportActionBar();
|
||||
if (actionBar != null)
|
||||
{
|
||||
actionBar.setSubtitle(R.string.menu_settings);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,385 +0,0 @@
|
|||
/*
|
||||
This file is part of Subsonic.
|
||||
|
||||
Subsonic is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Subsonic is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Subsonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
|
||||
package org.moire.ultrasonic.activity;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableString;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.text.util.Linkify;
|
||||
import android.view.ContextMenu;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
|
||||
import org.moire.ultrasonic.R;
|
||||
import org.moire.ultrasonic.api.subsonic.ApiNotSupportedException;
|
||||
import org.moire.ultrasonic.domain.Share;
|
||||
import org.moire.ultrasonic.service.MusicService;
|
||||
import org.moire.ultrasonic.service.MusicServiceFactory;
|
||||
import org.moire.ultrasonic.service.OfflineException;
|
||||
import org.moire.ultrasonic.util.BackgroundTask;
|
||||
import org.moire.ultrasonic.util.Constants;
|
||||
import org.moire.ultrasonic.util.LoadingTask;
|
||||
import org.moire.ultrasonic.util.TabActivityBackgroundTask;
|
||||
import org.moire.ultrasonic.util.TimeSpan;
|
||||
import org.moire.ultrasonic.util.TimeSpanPicker;
|
||||
import org.moire.ultrasonic.util.Util;
|
||||
import org.moire.ultrasonic.view.ShareAdapter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ShareActivity extends SubsonicTabActivity implements AdapterView.OnItemClickListener
|
||||
{
|
||||
|
||||
private SwipeRefreshLayout refreshSharesListView;
|
||||
private ListView sharesListView;
|
||||
private View emptyTextView;
|
||||
private ShareAdapter shareAdapter;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.select_share);
|
||||
|
||||
refreshSharesListView = findViewById(R.id.select_share_refresh);
|
||||
sharesListView = findViewById(R.id.select_share_list);
|
||||
|
||||
refreshSharesListView.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener()
|
||||
{
|
||||
@Override
|
||||
public void onRefresh()
|
||||
{
|
||||
new GetDataTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
}
|
||||
});
|
||||
|
||||
emptyTextView = findViewById(R.id.select_share_empty);
|
||||
sharesListView.setOnItemClickListener(this);
|
||||
registerForContextMenu(sharesListView);
|
||||
|
||||
View sharesMenuItem = findViewById(R.id.menu_shares);
|
||||
menuDrawer.setActiveView(sharesMenuItem);
|
||||
|
||||
setActionBarTitle(R.string.common_appname);
|
||||
setActionBarSubtitle(R.string.button_bar_shares);
|
||||
|
||||
load();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu)
|
||||
{
|
||||
super.onCreateOptionsMenu(menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void refresh()
|
||||
{
|
||||
finish();
|
||||
Intent intent = new Intent(this, ShareActivity.class);
|
||||
intent.putExtra(Constants.INTENT_EXTRA_NAME_REFRESH, true);
|
||||
startActivityForResultWithoutTransition(this, intent);
|
||||
}
|
||||
|
||||
private void load()
|
||||
{
|
||||
BackgroundTask<List<Share>> task = new TabActivityBackgroundTask<List<Share>>(this, true)
|
||||
{
|
||||
@Override
|
||||
protected List<Share> doInBackground() throws Throwable
|
||||
{
|
||||
MusicService musicService = MusicServiceFactory.getMusicService(ShareActivity.this);
|
||||
boolean refresh = getIntent().getBooleanExtra(Constants.INTENT_EXTRA_NAME_REFRESH, false);
|
||||
return musicService.getShares(refresh, ShareActivity.this, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done(List<Share> result)
|
||||
{
|
||||
sharesListView.setAdapter(shareAdapter = new ShareAdapter(ShareActivity.this, result));
|
||||
emptyTextView.setVisibility(result.isEmpty() ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
};
|
||||
task.execute();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo)
|
||||
{
|
||||
super.onCreateContextMenu(menu, view, menuInfo);
|
||||
|
||||
MenuInflater inflater = getMenuInflater();
|
||||
inflater.inflate(R.menu.select_share_context, menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onContextItemSelected(MenuItem menuItem)
|
||||
{
|
||||
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo();
|
||||
if (info == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Share share = (Share) sharesListView.getItemAtPosition(info.position);
|
||||
if (share == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (menuItem.getItemId())
|
||||
{
|
||||
case R.id.share_menu_pin:
|
||||
downloadShare(share.getId(), share.getName(), true, true, false, false, true, false, false);
|
||||
break;
|
||||
case R.id.share_menu_unpin:
|
||||
downloadShare(share.getId(), share.getName(), false, false, false, false, true, false, true);
|
||||
break;
|
||||
case R.id.share_menu_download:
|
||||
downloadShare(share.getId(), share.getName(), false, false, false, false, true, false, false);
|
||||
break;
|
||||
case R.id.share_menu_play_now:
|
||||
downloadShare(share.getId(), share.getName(), false, false, true, false, false, false, false);
|
||||
break;
|
||||
case R.id.share_menu_play_shuffled:
|
||||
downloadShare(share.getId(), share.getName(), false, false, true, true, false, false, false);
|
||||
break;
|
||||
case R.id.share_menu_delete:
|
||||
deleteShare(share);
|
||||
break;
|
||||
case R.id.share_info:
|
||||
displayShareInfo(share);
|
||||
break;
|
||||
case R.id.share_update_info:
|
||||
updateShareInfo(share);
|
||||
break;
|
||||
default:
|
||||
return super.onContextItemSelected(menuItem);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item)
|
||||
{
|
||||
switch (item.getItemId())
|
||||
{
|
||||
case android.R.id.home:
|
||||
menuDrawer.toggleMenu();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id)
|
||||
{
|
||||
Share share = (Share) parent.getItemAtPosition(position);
|
||||
|
||||
if (share == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Intent intent = new Intent(ShareActivity.this, SelectAlbumActivity.class);
|
||||
intent.putExtra(Constants.INTENT_EXTRA_NAME_SHARE_ID, share.getId());
|
||||
intent.putExtra(Constants.INTENT_EXTRA_NAME_SHARE_NAME, share.getName());
|
||||
startActivityForResultWithoutTransition(ShareActivity.this, intent);
|
||||
}
|
||||
|
||||
private void deleteShare(final Share share)
|
||||
{
|
||||
new AlertDialog.Builder(this).setIcon(android.R.drawable.ic_dialog_alert).setTitle(R.string.common_confirm).setMessage(getResources().getString(R.string.delete_playlist, share.getName())).setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which)
|
||||
{
|
||||
new LoadingTask<Void>(ShareActivity.this, false)
|
||||
{
|
||||
@Override
|
||||
protected Void doInBackground() throws Throwable
|
||||
{
|
||||
MusicService musicService = MusicServiceFactory.getMusicService(ShareActivity.this);
|
||||
musicService.deleteShare(share.getId(), ShareActivity.this, null);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done(Void result)
|
||||
{
|
||||
shareAdapter.remove(share);
|
||||
shareAdapter.notifyDataSetChanged();
|
||||
Util.toast(ShareActivity.this, getResources().getString(R.string.menu_deleted_share, share.getName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void error(Throwable error)
|
||||
{
|
||||
String msg;
|
||||
msg = error instanceof OfflineException || error instanceof ApiNotSupportedException ? getErrorMessage(error) : String.format("%s %s", getResources().getString(R.string.menu_deleted_share_error, share.getName()), getErrorMessage(error));
|
||||
|
||||
Util.toast(ShareActivity.this, msg, false);
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
}).setNegativeButton(R.string.common_cancel, null).show();
|
||||
}
|
||||
|
||||
private void displayShareInfo(final Share share)
|
||||
{
|
||||
final TextView textView = new TextView(this);
|
||||
textView.setPadding(5, 5, 5, 5);
|
||||
|
||||
final Spannable message = new SpannableString("Owner: " + share.getUsername() +
|
||||
"\nComments: " + ((share.getDescription() == null) ? "" : share.getDescription()) +
|
||||
"\nURL: " + share.getUrl() +
|
||||
"\nEntry Count: " + share.getEntries().size() +
|
||||
"\nVisit Count: " + share.getVisitCount() +
|
||||
((share.getCreated() == null) ? "" : ("\nCreation Date: " + share.getCreated().replace('T', ' '))) +
|
||||
((share.getLastVisited() == null) ? "" : ("\nLast Visited Date: " + share.getLastVisited().replace('T', ' '))) +
|
||||
((share.getExpires() == null) ? "" : ("\nExpiration Date: " + share.getExpires().replace('T', ' '))));
|
||||
|
||||
Linkify.addLinks(message, Linkify.WEB_URLS);
|
||||
textView.setText(message);
|
||||
textView.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
|
||||
new AlertDialog.Builder(this).setTitle("Share Details").setCancelable(true).setIcon(android.R.drawable.ic_dialog_info).setView(textView).show();
|
||||
}
|
||||
|
||||
private void updateShareInfo(final Share share)
|
||||
{
|
||||
View dialogView = getLayoutInflater().inflate(R.layout.share_details, null);
|
||||
if (dialogView == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
final EditText shareDescription = (EditText) dialogView.findViewById(R.id.share_description);
|
||||
final TimeSpanPicker timeSpanPicker = (TimeSpanPicker) dialogView.findViewById(R.id.date_picker);
|
||||
|
||||
shareDescription.setText(share.getDescription());
|
||||
|
||||
CheckBox hideDialogCheckBox = (CheckBox) dialogView.findViewById(R.id.hide_dialog);
|
||||
CheckBox saveAsDefaultsCheckBox = (CheckBox) dialogView.findViewById(R.id.save_as_defaults);
|
||||
CheckBox noExpirationCheckBox = (CheckBox) dialogView.findViewById(R.id.timeSpanDisableCheckBox);
|
||||
|
||||
noExpirationCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener()
|
||||
{
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton compoundButton, boolean b)
|
||||
{
|
||||
timeSpanPicker.setEnabled(!b);
|
||||
}
|
||||
});
|
||||
|
||||
noExpirationCheckBox.setChecked(true);
|
||||
|
||||
timeSpanPicker.setTimeSpanDisableText(getResources().getText(R.string.no_expiration));
|
||||
|
||||
hideDialogCheckBox.setVisibility(View.GONE);
|
||||
saveAsDefaultsCheckBox.setVisibility(View.GONE);
|
||||
|
||||
AlertDialog.Builder alertDialog = new AlertDialog.Builder(this);
|
||||
|
||||
alertDialog.setIcon(android.R.drawable.ic_dialog_alert);
|
||||
alertDialog.setTitle(R.string.playlist_update_info);
|
||||
alertDialog.setView(dialogView);
|
||||
alertDialog.setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which)
|
||||
{
|
||||
new LoadingTask<Void>(ShareActivity.this, false)
|
||||
{
|
||||
@Override
|
||||
protected Void doInBackground() throws Throwable
|
||||
{
|
||||
long millis = timeSpanPicker.getTimeSpan().getTotalMilliseconds();
|
||||
|
||||
if (millis > 0)
|
||||
{
|
||||
millis = TimeSpan.getCurrentTime().add(millis).getTotalMilliseconds();
|
||||
}
|
||||
|
||||
Editable shareDescriptionText = shareDescription.getText();
|
||||
String description = shareDescriptionText != null ? shareDescriptionText.toString() : null;
|
||||
|
||||
MusicService musicService = MusicServiceFactory.getMusicService(ShareActivity.this);
|
||||
musicService.updateShare(share.getId(), description, millis, ShareActivity.this, null);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done(Void result)
|
||||
{
|
||||
refresh();
|
||||
Util.toast(ShareActivity.this, getResources().getString(R.string.playlist_updated_info, share.getName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void error(Throwable error)
|
||||
{
|
||||
String msg;
|
||||
msg = error instanceof OfflineException || error instanceof ApiNotSupportedException ? getErrorMessage(error) : String.format("%s %s", getResources().getString(R.string.playlist_updated_info_error, share.getName()), getErrorMessage(error));
|
||||
|
||||
Util.toast(ShareActivity.this, msg, false);
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
});
|
||||
|
||||
alertDialog.setNegativeButton(R.string.common_cancel, null);
|
||||
alertDialog.show();
|
||||
}
|
||||
|
||||
private class GetDataTask extends AsyncTask<Void, Void, String[]>
|
||||
{
|
||||
@Override
|
||||
protected void onPostExecute(String[] result)
|
||||
{
|
||||
super.onPostExecute(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String[] doInBackground(Void... params)
|
||||
{
|
||||
refresh();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -49,6 +49,7 @@ import org.moire.ultrasonic.domain.Share;
|
|||
import org.moire.ultrasonic.featureflags.Feature;
|
||||
import org.moire.ultrasonic.featureflags.FeatureStorage;
|
||||
import org.moire.ultrasonic.service.*;
|
||||
import org.moire.ultrasonic.subsonic.ImageLoaderProvider;
|
||||
import org.moire.ultrasonic.subsonic.SubsonicImageLoaderProxy;
|
||||
import org.moire.ultrasonic.subsonic.loader.image.SubsonicImageLoader;
|
||||
import org.moire.ultrasonic.util.*;
|
||||
|
@ -63,10 +64,9 @@ import kotlin.Lazy;
|
|||
/**
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class SubsonicTabActivity extends ResultActivity implements OnClickListener
|
||||
public class SubsonicTabActivity extends ResultActivity
|
||||
{
|
||||
private static final Pattern COMPILE = Pattern.compile(":");
|
||||
protected static ImageLoader IMAGE_LOADER;
|
||||
protected static String theme;
|
||||
private static SubsonicTabActivity instance;
|
||||
|
||||
|
@ -79,6 +79,7 @@ public class SubsonicTabActivity extends ResultActivity implements OnClickListen
|
|||
|
||||
private Lazy<MediaPlayerController> mediaPlayerControllerLazy = inject(MediaPlayerController.class);
|
||||
private Lazy<MediaPlayerLifecycleSupport> lifecycleSupport = inject(MediaPlayerLifecycleSupport.class);
|
||||
protected Lazy<ImageLoaderProvider> imageLoader = inject(ImageLoaderProvider.class);
|
||||
|
||||
public MenuDrawer menuDrawer;
|
||||
private int activePosition = 1;
|
||||
|
@ -118,18 +119,6 @@ public class SubsonicTabActivity extends ResultActivity implements OnClickListen
|
|||
bookmarksMenuItem = findViewById(R.id.menu_bookmarks);
|
||||
sharesMenuItem = findViewById(R.id.menu_shares);
|
||||
|
||||
findViewById(R.id.menu_home).setOnClickListener(this);
|
||||
findViewById(R.id.menu_browse).setOnClickListener(this);
|
||||
findViewById(R.id.menu_search).setOnClickListener(this);
|
||||
findViewById(R.id.menu_playlists).setOnClickListener(this);
|
||||
findViewById(R.id.menu_podcasts).setOnClickListener(this);
|
||||
sharesMenuItem.setOnClickListener(this);
|
||||
chatMenuItem.setOnClickListener(this);
|
||||
bookmarksMenuItem.setOnClickListener(this);
|
||||
findViewById(R.id.menu_now_playing).setOnClickListener(this);
|
||||
findViewById(R.id.menu_settings).setOnClickListener(this);
|
||||
findViewById(R.id.menu_about).setOnClickListener(this);
|
||||
findViewById(R.id.menu_exit).setOnClickListener(this);
|
||||
setActionBarDisplayHomeAsUp(true);
|
||||
|
||||
TextView activeView = (TextView) findViewById(menuActiveViewId);
|
||||
|
@ -203,7 +192,7 @@ public class SubsonicTabActivity extends ResultActivity implements OnClickListen
|
|||
super.onDestroy();
|
||||
destroyed = true;
|
||||
nowPlayingView = null;
|
||||
clearImageLoader();
|
||||
imageLoader.getValue().clearImageLoader();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -351,7 +340,7 @@ public class SubsonicTabActivity extends ResultActivity implements OnClickListen
|
|||
@Override
|
||||
public void run()
|
||||
{
|
||||
getImageLoader().loadImage(nowPlayingAlbumArtImage, song, false, Util.getNotificationImageSize(context), false, true);
|
||||
imageLoader.getValue().getImageLoader().loadImage(nowPlayingAlbumArtImage, song, false, Util.getNotificationImageSize(context), false, true);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -771,40 +760,6 @@ public class SubsonicTabActivity extends ResultActivity implements OnClickListen
|
|||
}
|
||||
}
|
||||
|
||||
public synchronized void clearImageLoader() {
|
||||
if (IMAGE_LOADER != null &&
|
||||
IMAGE_LOADER.isRunning()) {
|
||||
IMAGE_LOADER.clear();
|
||||
}
|
||||
|
||||
IMAGE_LOADER = null;
|
||||
}
|
||||
|
||||
public synchronized ImageLoader getImageLoader() {
|
||||
if (IMAGE_LOADER == null ||
|
||||
!IMAGE_LOADER.isRunning()) {
|
||||
LegacyImageLoader legacyImageLoader = new LegacyImageLoader(
|
||||
this,
|
||||
Util.getImageLoaderConcurrency(this)
|
||||
);
|
||||
|
||||
boolean isNewImageLoaderEnabled = KoinJavaComponent.get(FeatureStorage.class)
|
||||
.isFeatureEnabled(Feature.NEW_IMAGE_DOWNLOADER);
|
||||
if (isNewImageLoaderEnabled) {
|
||||
IMAGE_LOADER = new SubsonicImageLoaderProxy(
|
||||
legacyImageLoader,
|
||||
KoinJavaComponent.get(SubsonicImageLoader.class)
|
||||
);
|
||||
} else {
|
||||
IMAGE_LOADER = legacyImageLoader;
|
||||
}
|
||||
|
||||
IMAGE_LOADER.startImageLoader();
|
||||
}
|
||||
|
||||
return IMAGE_LOADER;
|
||||
}
|
||||
|
||||
void download(final boolean append, final boolean save, final boolean autoPlay, final boolean playNext, final boolean shuffle, final List<Entry> songs)
|
||||
{
|
||||
if (getMediaPlayerController() == null)
|
||||
|
@ -1024,26 +979,6 @@ public class SubsonicTabActivity extends ResultActivity implements OnClickListen
|
|||
task.execute();
|
||||
}
|
||||
|
||||
protected void playVideo(Entry entry)
|
||||
{
|
||||
if (!Util.isNetworkConnected(this))
|
||||
{
|
||||
Util.toast(this, R.string.select_album_no_network);
|
||||
return;
|
||||
}
|
||||
|
||||
VideoPlayerType player = Util.getVideoPlayerType(this);
|
||||
|
||||
try
|
||||
{
|
||||
player.playVideo(this, entry);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Util.toast(this, e.getMessage(), false);
|
||||
}
|
||||
}
|
||||
|
||||
protected void checkLicenseAndTrialPeriod(Runnable onValid)
|
||||
{
|
||||
if (licenseValid)
|
||||
|
@ -1247,72 +1182,6 @@ public class SubsonicTabActivity extends ResultActivity implements OnClickListen
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v)
|
||||
{
|
||||
menuActiveViewId = v.getId();
|
||||
menuDrawer.setActiveView(v);
|
||||
|
||||
Intent intent;
|
||||
|
||||
switch (menuActiveViewId)
|
||||
{
|
||||
case R.id.menu_home:
|
||||
intent = new Intent(SubsonicTabActivity.this, MainActivity.class);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
startActivityForResultWithoutTransition(SubsonicTabActivity.this, intent);
|
||||
break;
|
||||
case R.id.menu_browse:
|
||||
intent = new Intent(SubsonicTabActivity.this, SelectArtistActivity.class);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
startActivityForResultWithoutTransition(SubsonicTabActivity.this, intent);
|
||||
break;
|
||||
case R.id.menu_search:
|
||||
intent = new Intent(SubsonicTabActivity.this, SearchActivity.class);
|
||||
intent.putExtra(Constants.INTENT_EXTRA_REQUEST_SEARCH, true);
|
||||
startActivityForResultWithoutTransition(SubsonicTabActivity.this, intent);
|
||||
break;
|
||||
case R.id.menu_playlists:
|
||||
intent = new Intent(SubsonicTabActivity.this, SelectPlaylistActivity.class);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
startActivityForResultWithoutTransition(SubsonicTabActivity.this, intent);
|
||||
break;
|
||||
case R.id.menu_podcasts:
|
||||
intent = new Intent(SubsonicTabActivity.this, PodcastsActivity.class);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
startActivityForResultWithoutTransition(SubsonicTabActivity.this, intent);
|
||||
break;
|
||||
case R.id.menu_shares:
|
||||
intent = new Intent(SubsonicTabActivity.this, ShareActivity.class);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
startActivityForResultWithoutTransition(SubsonicTabActivity.this, intent);
|
||||
break;
|
||||
case R.id.menu_chat:
|
||||
startActivityForResultWithoutTransition(SubsonicTabActivity.this, ChatActivity.class);
|
||||
break;
|
||||
case R.id.menu_bookmarks:
|
||||
startActivityForResultWithoutTransition(this, BookmarkActivity.class);
|
||||
break;
|
||||
case R.id.menu_now_playing:
|
||||
startActivityForResultWithoutTransition(SubsonicTabActivity.this, DownloadActivity.class);
|
||||
break;
|
||||
case R.id.menu_settings:
|
||||
startActivityForResultWithoutTransition(SubsonicTabActivity.this, SettingsActivity.class);
|
||||
break;
|
||||
case R.id.menu_about:
|
||||
startActivityForResultWithoutTransition(SubsonicTabActivity.this, HelpActivity.class);
|
||||
break;
|
||||
case R.id.menu_exit:
|
||||
intent = new Intent(SubsonicTabActivity.this, MainActivity.class);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
intent.putExtra(Constants.INTENT_EXTRA_NAME_EXIT, true);
|
||||
startActivityForResultWithoutTransition(SubsonicTabActivity.this, intent);
|
||||
break;
|
||||
}
|
||||
|
||||
menuDrawer.closeMenu(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRestoreInstanceState(Bundle inState)
|
||||
{
|
||||
|
|
|
@ -0,0 +1,147 @@
|
|||
package org.moire.ultrasonic.fragment;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.os.Bundle;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.webkit.WebView;
|
||||
import android.webkit.WebViewClient;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
|
||||
import org.moire.ultrasonic.R;
|
||||
import org.moire.ultrasonic.util.Util;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
public class AboutFragment extends Fragment {
|
||||
|
||||
private WebView webView;
|
||||
private ImageView backButton;
|
||||
private ImageView forwardButton;
|
||||
private SwipeRefreshLayout swipeRefresh;
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
Util.applyTheme(this.getContext());
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.help, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
|
||||
swipeRefresh = view.findViewById(R.id.help_refresh);
|
||||
swipeRefresh.setEnabled(false);
|
||||
|
||||
webView = view.findViewById(R.id.help_contents);
|
||||
webView.getSettings().setJavaScriptEnabled(true);
|
||||
webView.setWebViewClient(new HelpClient());
|
||||
|
||||
if (savedInstanceState != null)
|
||||
{
|
||||
webView.restoreState(savedInstanceState);
|
||||
}
|
||||
else
|
||||
{
|
||||
webView.loadUrl(getResources().getString(R.string.help_url));
|
||||
}
|
||||
|
||||
backButton = view.findViewById(R.id.help_back);
|
||||
backButton.setOnClickListener(new Button.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(View view)
|
||||
{
|
||||
webView.goBack();
|
||||
}
|
||||
});
|
||||
|
||||
ImageView stopButton = view.findViewById(R.id.help_stop);
|
||||
stopButton.setOnClickListener(new Button.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(View view)
|
||||
{
|
||||
webView.stopLoading();
|
||||
swipeRefresh.setRefreshing(false);
|
||||
}
|
||||
});
|
||||
|
||||
forwardButton = view.findViewById(R.id.help_forward);
|
||||
forwardButton.setOnClickListener(new Button.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(View view)
|
||||
{
|
||||
webView.goForward();
|
||||
}
|
||||
});
|
||||
|
||||
// TODO: Nicer Back key handling?
|
||||
webView.setFocusableInTouchMode(true);
|
||||
webView.requestFocus();
|
||||
webView.setOnKeyListener( new View.OnKeyListener()
|
||||
{
|
||||
@Override
|
||||
public boolean onKey( View v, int keyCode, KeyEvent event )
|
||||
{
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK)
|
||||
{
|
||||
if (webView.canGoBack())
|
||||
{
|
||||
webView.goBack();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle state)
|
||||
{
|
||||
webView.saveState(state);
|
||||
super.onSaveInstanceState(state);
|
||||
}
|
||||
|
||||
private final class HelpClient extends WebViewClient
|
||||
{
|
||||
@Override
|
||||
public void onPageStarted(WebView view, String url, Bitmap favicon) {
|
||||
swipeRefresh.setRefreshing(true);
|
||||
super.onPageStarted(view, url, favicon);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageFinished(WebView view, String url)
|
||||
{
|
||||
swipeRefresh.setRefreshing(false);
|
||||
String versionName = Util.getVersionName(getContext());
|
||||
String title = String.format("%s (%s)", view.getTitle(), versionName);
|
||||
|
||||
FragmentTitle.Companion.setTitle(AboutFragment.this, title);
|
||||
|
||||
backButton.setEnabled(view.canGoBack());
|
||||
forwardButton.setEnabled(view.canGoForward());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl)
|
||||
{
|
||||
Util.toast(getContext(), description);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,475 @@
|
|||
package org.moire.ultrasonic.fragment;
|
||||
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ListView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
|
||||
import org.moire.ultrasonic.R;
|
||||
import org.moire.ultrasonic.data.ActiveServerProvider;
|
||||
import org.moire.ultrasonic.domain.MusicDirectory;
|
||||
import org.moire.ultrasonic.service.DownloadFile;
|
||||
import org.moire.ultrasonic.service.MediaPlayerController;
|
||||
import org.moire.ultrasonic.service.MusicService;
|
||||
import org.moire.ultrasonic.service.MusicServiceFactory;
|
||||
import org.moire.ultrasonic.subsonic.ImageLoaderProvider;
|
||||
import org.moire.ultrasonic.subsonic.NetworkAndStorageChecker;
|
||||
import org.moire.ultrasonic.subsonic.VideoPlayer;
|
||||
import org.moire.ultrasonic.util.CancellationToken;
|
||||
import org.moire.ultrasonic.util.Constants;
|
||||
import org.moire.ultrasonic.util.Pair;
|
||||
import org.moire.ultrasonic.util.TabActivityBackgroundTask;
|
||||
import org.moire.ultrasonic.util.Util;
|
||||
import org.moire.ultrasonic.view.EntryAdapter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import kotlin.Lazy;
|
||||
import timber.log.Timber;
|
||||
|
||||
import static org.koin.java.KoinJavaComponent.inject;
|
||||
|
||||
public class BookmarksFragment extends Fragment {
|
||||
|
||||
private SwipeRefreshLayout refreshAlbumListView;
|
||||
private ListView albumListView;
|
||||
private View albumButtons;
|
||||
private View emptyView;
|
||||
private ImageView playNowButton;
|
||||
private ImageView pinButton;
|
||||
private ImageView unpinButton;
|
||||
private ImageView downloadButton;
|
||||
private ImageView deleteButton;
|
||||
|
||||
private final Lazy<VideoPlayer> videoPlayer = inject(VideoPlayer.class);
|
||||
private final Lazy<MediaPlayerController> mediaPlayerController = inject(MediaPlayerController.class);
|
||||
private final Lazy<ImageLoaderProvider> imageLoader = inject(ImageLoaderProvider.class);
|
||||
private final Lazy<NetworkAndStorageChecker> networkAndStorageChecker = inject(NetworkAndStorageChecker.class);
|
||||
private CancellationToken cancellationToken;
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
Util.applyTheme(this.getContext());
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.select_album, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
cancellationToken = new CancellationToken();
|
||||
albumButtons = view.findViewById(R.id.menu_album);
|
||||
|
||||
refreshAlbumListView = view.findViewById(R.id.select_album_entries_refresh);
|
||||
albumListView = view.findViewById(R.id.select_album_entries_list);
|
||||
|
||||
refreshAlbumListView.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener()
|
||||
{
|
||||
@Override
|
||||
public void onRefresh()
|
||||
{
|
||||
new GetDataTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
}
|
||||
});
|
||||
|
||||
albumListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
|
||||
|
||||
albumListView.setOnItemClickListener(new AdapterView.OnItemClickListener()
|
||||
{
|
||||
@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 != null)
|
||||
{
|
||||
if (entry.isVideo())
|
||||
{
|
||||
videoPlayer.getValue().playVideo(entry);
|
||||
}
|
||||
else
|
||||
{
|
||||
enableButtons();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
ImageView selectButton = view.findViewById(R.id.select_album_select);
|
||||
playNowButton = view.findViewById(R.id.select_album_play_now);
|
||||
ImageView playNextButton = view.findViewById(R.id.select_album_play_next);
|
||||
ImageView playLastButton = view.findViewById(R.id.select_album_play_last);
|
||||
pinButton = view.findViewById(R.id.select_album_pin);
|
||||
unpinButton = view.findViewById(R.id.select_album_unpin);
|
||||
downloadButton = view.findViewById(R.id.select_album_download);
|
||||
deleteButton = view.findViewById(R.id.select_album_delete);
|
||||
ImageView oreButton = view.findViewById(R.id.select_album_more);
|
||||
emptyView = view.findViewById(R.id.select_album_empty);
|
||||
|
||||
selectButton.setVisibility(View.GONE);
|
||||
playNextButton.setVisibility(View.GONE);
|
||||
playLastButton.setVisibility(View.GONE);
|
||||
oreButton.setVisibility(View.GONE);
|
||||
|
||||
playNowButton.setOnClickListener(new View.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(View view)
|
||||
{
|
||||
playNow(getSelectedSongs(albumListView));
|
||||
}
|
||||
});
|
||||
|
||||
selectButton.setOnClickListener(new View.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(View view)
|
||||
{
|
||||
selectAllOrNone();
|
||||
}
|
||||
});
|
||||
pinButton.setOnClickListener(new View.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(View view)
|
||||
{
|
||||
downloadBackground(true);
|
||||
selectAll(false, false);
|
||||
}
|
||||
});
|
||||
unpinButton.setOnClickListener(new View.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(View view)
|
||||
{
|
||||
unpin();
|
||||
selectAll(false, false);
|
||||
}
|
||||
});
|
||||
downloadButton.setOnClickListener(new View.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(View view)
|
||||
{
|
||||
downloadBackground(false);
|
||||
selectAll(false, false);
|
||||
}
|
||||
});
|
||||
deleteButton.setOnClickListener(new View.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(View view)
|
||||
{
|
||||
delete();
|
||||
selectAll(false, false);
|
||||
}
|
||||
});
|
||||
|
||||
registerForContextMenu(albumListView);
|
||||
FragmentTitle.Companion.setTitle(this, R.string.button_bar_bookmarks);
|
||||
|
||||
enableButtons();
|
||||
|
||||
getBookmarks();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
cancellationToken.cancel();
|
||||
super.onDestroyView();
|
||||
}
|
||||
|
||||
private void getBookmarks()
|
||||
{
|
||||
new LoadTask()
|
||||
{
|
||||
@Override
|
||||
protected MusicDirectory load(MusicService service) throws Exception
|
||||
{
|
||||
return Util.getSongsFromBookmarks(service.getBookmarks(getContext(), this));
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
private void playNow(List<MusicDirectory.Entry> songs)
|
||||
{
|
||||
if (!getSelectedSongs(albumListView).isEmpty())
|
||||
{
|
||||
int position = songs.get(0).getBookmarkPosition();
|
||||
mediaPlayerController.getValue().restore(songs, 0, position, true, true);
|
||||
selectAll(false, false);
|
||||
}
|
||||
}
|
||||
|
||||
private static List<MusicDirectory.Entry> getSelectedSongs(ListView albumListView)
|
||||
{
|
||||
List<MusicDirectory.Entry> songs = new ArrayList<MusicDirectory.Entry>(10);
|
||||
|
||||
if (albumListView != null)
|
||||
{
|
||||
int count = albumListView.getCount();
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
if (albumListView.isItemChecked(i))
|
||||
{
|
||||
songs.add((MusicDirectory.Entry) albumListView.getItemAtPosition(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return songs;
|
||||
}
|
||||
|
||||
private void refresh()
|
||||
{
|
||||
// TODO: create better restart
|
||||
getView().post(new Runnable() {
|
||||
public void run() {
|
||||
Timber.d("Refresh called...");
|
||||
if (getArguments() == null) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putBoolean(Constants.INTENT_EXTRA_NAME_REFRESH, true);
|
||||
setArguments(bundle);
|
||||
} else {
|
||||
getArguments().putBoolean(Constants.INTENT_EXTRA_NAME_REFRESH, true);
|
||||
}
|
||||
onViewCreated(getView(), null);
|
||||
}
|
||||
});
|
||||
|
||||
/* finish();
|
||||
Intent intent = getIntent();
|
||||
intent.putExtra(Constants.INTENT_EXTRA_NAME_REFRESH, true);
|
||||
startActivityForResultWithoutTransition(this, intent);
|
||||
|
||||
*/
|
||||
}
|
||||
|
||||
private void selectAllOrNone()
|
||||
{
|
||||
boolean someUnselected = false;
|
||||
int count = albumListView.getCount();
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
if (!albumListView.isItemChecked(i) && albumListView.getItemAtPosition(i) instanceof MusicDirectory.Entry)
|
||||
{
|
||||
someUnselected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
selectAll(someUnselected, true);
|
||||
}
|
||||
|
||||
private void selectAll(boolean selected, boolean toast)
|
||||
{
|
||||
int count = albumListView.getCount();
|
||||
int selectedCount = 0;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
MusicDirectory.Entry entry = (MusicDirectory.Entry) albumListView.getItemAtPosition(i);
|
||||
if (entry != null && !entry.isDirectory() && !entry.isVideo())
|
||||
{
|
||||
albumListView.setItemChecked(i, selected);
|
||||
selectedCount++;
|
||||
}
|
||||
}
|
||||
|
||||
// Display toast: N tracks selected / N tracks unselected
|
||||
if (toast)
|
||||
{
|
||||
int toastResId = selected ? R.string.select_album_n_selected : R.string.select_album_n_unselected;
|
||||
Util.toast(getContext(), getString(toastResId, selectedCount));
|
||||
}
|
||||
|
||||
enableButtons();
|
||||
}
|
||||
|
||||
private void enableButtons()
|
||||
{
|
||||
List<MusicDirectory.Entry> selection = getSelectedSongs(albumListView);
|
||||
boolean enabled = !selection.isEmpty();
|
||||
boolean unpinEnabled = false;
|
||||
boolean deleteEnabled = false;
|
||||
|
||||
int pinnedCount = 0;
|
||||
|
||||
for (MusicDirectory.Entry song : selection)
|
||||
{
|
||||
DownloadFile downloadFile = mediaPlayerController.getValue().getDownloadFileForSong(song);
|
||||
if (downloadFile.isWorkDone())
|
||||
{
|
||||
deleteEnabled = true;
|
||||
}
|
||||
|
||||
if (downloadFile.isSaved())
|
||||
{
|
||||
pinnedCount++;
|
||||
unpinEnabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
playNowButton.setVisibility(enabled && deleteEnabled ? View.VISIBLE : View.GONE);
|
||||
pinButton.setVisibility((enabled && !ActiveServerProvider.Companion.isOffline(getContext()) && selection.size() > pinnedCount) ? View.VISIBLE : View.GONE);
|
||||
unpinButton.setVisibility(enabled && unpinEnabled ? View.VISIBLE : View.GONE);
|
||||
downloadButton.setVisibility(enabled && !deleteEnabled && !ActiveServerProvider.Companion.isOffline(getContext()) ? View.VISIBLE : View.GONE);
|
||||
deleteButton.setVisibility(enabled && deleteEnabled ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
||||
private void downloadBackground(final boolean save)
|
||||
{
|
||||
List<MusicDirectory.Entry> songs = getSelectedSongs(albumListView);
|
||||
|
||||
if (songs.isEmpty())
|
||||
{
|
||||
selectAll(true, false);
|
||||
songs = getSelectedSongs(albumListView);
|
||||
}
|
||||
|
||||
downloadBackground(save, songs);
|
||||
}
|
||||
|
||||
private void downloadBackground(final boolean save, final List<MusicDirectory.Entry> songs)
|
||||
{
|
||||
Runnable onValid = new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
networkAndStorageChecker.getValue().warnIfNetworkOrStorageUnavailable();
|
||||
mediaPlayerController.getValue().downloadBackground(songs, save);
|
||||
|
||||
if (save)
|
||||
{
|
||||
Util.toast(getContext(), getResources().getQuantityString(R.plurals.select_album_n_songs_pinned, songs.size(), songs.size()));
|
||||
}
|
||||
else
|
||||
{
|
||||
Util.toast(getContext(), getResources().getQuantityString(R.plurals.select_album_n_songs_downloaded, songs.size(), songs.size()));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
onValid.run();
|
||||
}
|
||||
|
||||
private void delete()
|
||||
{
|
||||
List<MusicDirectory.Entry> songs = getSelectedSongs(albumListView);
|
||||
|
||||
if (songs.isEmpty())
|
||||
{
|
||||
selectAll(true, false);
|
||||
songs = getSelectedSongs(albumListView);
|
||||
}
|
||||
|
||||
mediaPlayerController.getValue().delete(songs);
|
||||
}
|
||||
|
||||
private void unpin()
|
||||
{
|
||||
List<MusicDirectory.Entry> songs = getSelectedSongs(albumListView);
|
||||
Util.toast(getContext(), getResources().getQuantityString(R.plurals.select_album_n_songs_unpinned, songs.size(), songs.size()));
|
||||
mediaPlayerController.getValue().unpin(songs);
|
||||
}
|
||||
|
||||
private abstract class LoadTask extends TabActivityBackgroundTask<Pair<MusicDirectory, Boolean>>
|
||||
{
|
||||
public LoadTask()
|
||||
{
|
||||
super(BookmarksFragment.this.getActivity(), true, refreshAlbumListView, cancellationToken);
|
||||
}
|
||||
|
||||
protected abstract MusicDirectory load(MusicService service) throws Exception;
|
||||
|
||||
@Override
|
||||
protected Pair<MusicDirectory, Boolean> doInBackground() throws Throwable
|
||||
{
|
||||
MusicService musicService = MusicServiceFactory.getMusicService(getContext());
|
||||
MusicDirectory dir = load(musicService);
|
||||
boolean valid = musicService.isLicenseValid(getContext(), this);
|
||||
return new Pair<>(dir, valid);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done(Pair<MusicDirectory, Boolean> result)
|
||||
{
|
||||
MusicDirectory musicDirectory = result.getFirst();
|
||||
List<MusicDirectory.Entry> entries = musicDirectory.getChildren();
|
||||
|
||||
int songCount = 0;
|
||||
for (MusicDirectory.Entry entry : entries)
|
||||
{
|
||||
if (!entry.isDirectory())
|
||||
{
|
||||
songCount++;
|
||||
}
|
||||
}
|
||||
|
||||
final int listSize = getArguments() == null? 0 : getArguments().getInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, 0);
|
||||
|
||||
if (songCount > 0)
|
||||
{
|
||||
pinButton.setVisibility(View.VISIBLE);
|
||||
unpinButton.setVisibility(View.VISIBLE);
|
||||
downloadButton.setVisibility(View.VISIBLE);
|
||||
deleteButton.setVisibility(View.VISIBLE);
|
||||
playNowButton.setVisibility(View.VISIBLE);
|
||||
}
|
||||
else
|
||||
{
|
||||
pinButton.setVisibility(View.GONE);
|
||||
unpinButton.setVisibility(View.GONE);
|
||||
downloadButton.setVisibility(View.GONE);
|
||||
deleteButton.setVisibility(View.GONE);
|
||||
playNowButton.setVisibility(View.GONE);
|
||||
|
||||
if (listSize == 0 || result.getFirst().getChildren().size() < listSize)
|
||||
{
|
||||
albumButtons.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
enableButtons();
|
||||
|
||||
emptyView.setVisibility(entries.isEmpty() ? View.VISIBLE : View.GONE);
|
||||
|
||||
albumListView.setAdapter(new EntryAdapter(getContext(), imageLoader.getValue().getImageLoader(), entries, true));
|
||||
}
|
||||
}
|
||||
|
||||
private class GetDataTask extends AsyncTask<Void, Void, String[]>
|
||||
{
|
||||
@Override
|
||||
protected void onPostExecute(String[] result)
|
||||
{
|
||||
super.onPostExecute(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String[] doInBackground(Void... params)
|
||||
{
|
||||
refresh();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,317 @@
|
|||
package org.moire.ultrasonic.fragment;
|
||||
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ListAdapter;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import org.moire.ultrasonic.R;
|
||||
import org.moire.ultrasonic.data.ActiveServerProvider;
|
||||
import org.moire.ultrasonic.domain.ChatMessage;
|
||||
import org.moire.ultrasonic.service.MusicService;
|
||||
import org.moire.ultrasonic.service.MusicServiceFactory;
|
||||
import org.moire.ultrasonic.util.BackgroundTask;
|
||||
import org.moire.ultrasonic.util.TabActivityBackgroundTask;
|
||||
import org.moire.ultrasonic.util.Util;
|
||||
import org.moire.ultrasonic.view.ChatAdapter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
import kotlin.Lazy;
|
||||
|
||||
import static org.koin.java.KoinJavaComponent.inject;
|
||||
|
||||
public class ChatFragment extends Fragment {
|
||||
|
||||
private ListView chatListView;
|
||||
private EditText messageEditText;
|
||||
private ImageButton sendButton;
|
||||
private Timer timer;
|
||||
private volatile static Long lastChatMessageTime = (long) 0;
|
||||
private static final ArrayList<ChatMessage> messageList = new ArrayList<ChatMessage>();
|
||||
|
||||
private final Lazy<ActiveServerProvider> activeServerProvider = inject(ActiveServerProvider.class);
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
Util.applyTheme(this.getContext());
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.chat, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
messageEditText = view.findViewById(R.id.chat_edittext);
|
||||
sendButton = view.findViewById(R.id.chat_send);
|
||||
|
||||
sendButton.setOnClickListener(new View.OnClickListener()
|
||||
{
|
||||
|
||||
@Override
|
||||
public void onClick(View view)
|
||||
{
|
||||
sendMessage();
|
||||
}
|
||||
});
|
||||
|
||||
chatListView = view.findViewById(R.id.chat_entries_list);
|
||||
chatListView.setTranscriptMode(ListView.TRANSCRIPT_MODE_ALWAYS_SCROLL);
|
||||
chatListView.setStackFromBottom(true);
|
||||
|
||||
String serverName = activeServerProvider.getValue().getActiveServer().getName();
|
||||
String userName = activeServerProvider.getValue().getActiveServer().getUserName();
|
||||
String title = String.format("%s [%s@%s]", getResources().getString(R.string.button_bar_chat), userName, serverName);
|
||||
|
||||
FragmentTitle.Companion.setTitle(this, title);
|
||||
setHasOptionsMenu(true);
|
||||
|
||||
messageEditText.setImeActionLabel("Send", KeyEvent.KEYCODE_ENTER);
|
||||
|
||||
messageEditText.addTextChangedListener(new TextWatcher()
|
||||
{
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2)
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2)
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable editable)
|
||||
{
|
||||
sendButton.setEnabled(!Util.isNullOrWhiteSpace(editable.toString()));
|
||||
}
|
||||
});
|
||||
|
||||
messageEditText.setOnEditorActionListener(new TextView.OnEditorActionListener()
|
||||
{
|
||||
|
||||
@Override
|
||||
public boolean onEditorAction(TextView v, int actionId, KeyEvent event)
|
||||
{
|
||||
if (actionId == EditorInfo.IME_ACTION_DONE || (actionId == EditorInfo.IME_NULL && event.getAction() == KeyEvent.ACTION_DOWN))
|
||||
{
|
||||
sendMessage();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
load();
|
||||
timerMethod();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
|
||||
inflater.inflate(R.menu.chat, menu);
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
}
|
||||
|
||||
/*
|
||||
* Listen for option item selections so that we receive a notification
|
||||
* when the user requests a refresh by selecting the refresh action bar item.
|
||||
*/
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
// Check if user triggered a refresh:
|
||||
case R.id.menu_refresh:
|
||||
// Start the refresh background task.
|
||||
new GetDataTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
return true;
|
||||
}
|
||||
// User didn't trigger a refresh, let the superclass handle this action
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume()
|
||||
{
|
||||
super.onResume();
|
||||
|
||||
if (!messageList.isEmpty())
|
||||
{
|
||||
ListAdapter chatAdapter = new ChatAdapter(getContext(), messageList);
|
||||
chatListView.setAdapter(chatAdapter);
|
||||
}
|
||||
|
||||
if (timer == null)
|
||||
{
|
||||
timerMethod();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause()
|
||||
{
|
||||
super.onPause();
|
||||
|
||||
if (timer != null)
|
||||
{
|
||||
timer.cancel();
|
||||
timer = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void timerMethod()
|
||||
{
|
||||
int refreshInterval = Util.getChatRefreshInterval(getContext());
|
||||
|
||||
if (refreshInterval > 0)
|
||||
{
|
||||
timer = new Timer();
|
||||
|
||||
timer.schedule(new TimerTask()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
getActivity().runOnUiThread(new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
load();
|
||||
}
|
||||
});
|
||||
}
|
||||
}, refreshInterval, refreshInterval);
|
||||
}
|
||||
}
|
||||
|
||||
private void sendMessage()
|
||||
{
|
||||
if (messageEditText != null)
|
||||
{
|
||||
final String message;
|
||||
Editable text = messageEditText.getText();
|
||||
|
||||
if (text == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
message = text.toString();
|
||||
|
||||
if (!Util.isNullOrWhiteSpace(message))
|
||||
{
|
||||
messageEditText.setText("");
|
||||
|
||||
BackgroundTask<Void> task = new TabActivityBackgroundTask<Void>(getActivity(), false)
|
||||
{
|
||||
@Override
|
||||
protected Void doInBackground() throws Throwable
|
||||
{
|
||||
MusicService musicService = MusicServiceFactory.getMusicService(getContext());
|
||||
musicService.addChatMessage(message, getContext(), this);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done(Void result)
|
||||
{
|
||||
load();
|
||||
}
|
||||
};
|
||||
|
||||
task.execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void load()
|
||||
{
|
||||
BackgroundTask<List<ChatMessage>> task = new TabActivityBackgroundTask<List<ChatMessage>>(getActivity(), false)
|
||||
{
|
||||
@Override
|
||||
protected List<ChatMessage> doInBackground() throws Throwable
|
||||
{
|
||||
MusicService musicService = MusicServiceFactory.getMusicService(getContext());
|
||||
return musicService.getChatMessages(lastChatMessageTime, getContext(), this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done(List<ChatMessage> result)
|
||||
{
|
||||
if (result != null && !result.isEmpty())
|
||||
{
|
||||
// Reset lastChatMessageTime if we have a newer message
|
||||
for (ChatMessage message : result)
|
||||
{
|
||||
if (message.getTime() > lastChatMessageTime)
|
||||
{
|
||||
lastChatMessageTime = message.getTime();
|
||||
}
|
||||
}
|
||||
|
||||
// Reverse results to show them on the bottom
|
||||
Collections.reverse(result);
|
||||
messageList.addAll(result);
|
||||
|
||||
ListAdapter chatAdapter = new ChatAdapter(getContext(), messageList);
|
||||
chatListView.setAdapter(chatAdapter);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void error(Throwable error) {
|
||||
// Stop the timer in case of an error, otherwise it may repeat the error message forever
|
||||
if (timer != null)
|
||||
{
|
||||
timer.cancel();
|
||||
timer = null;
|
||||
}
|
||||
super.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
task.execute();
|
||||
}
|
||||
|
||||
private class GetDataTask extends AsyncTask<Void, Void, String[]>
|
||||
{
|
||||
@Override
|
||||
protected void onPostExecute(String[] result)
|
||||
{
|
||||
load();
|
||||
super.onPostExecute(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String[] doInBackground(Void... params)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,278 @@
|
|||
package org.moire.ultrasonic.fragment;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
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 androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.navigation.Navigation;
|
||||
|
||||
import org.moire.ultrasonic.R;
|
||||
import org.moire.ultrasonic.activity.ServerSelectorActivity;
|
||||
import org.moire.ultrasonic.data.ActiveServerProvider;
|
||||
import org.moire.ultrasonic.data.ServerSetting;
|
||||
import org.moire.ultrasonic.util.Constants;
|
||||
import org.moire.ultrasonic.util.MergeAdapter;
|
||||
import org.moire.ultrasonic.util.Util;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import kotlin.Lazy;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.koin.java.KoinJavaComponent.inject;
|
||||
|
||||
public class MainFragment extends Fragment {
|
||||
|
||||
private static boolean shouldUseId3;
|
||||
private static String lastActiveServerProperties;
|
||||
|
||||
private ListView list;
|
||||
|
||||
private final Lazy<ActiveServerProvider> activeServerProvider = inject(ActiveServerProvider.class);
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
Util.applyTheme(this.getContext());
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.main, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
|
||||
list = view.findViewById(R.id.main_list);
|
||||
setupMenuList(list);
|
||||
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
boolean shouldRestart = false;
|
||||
|
||||
boolean id3 = Util.getShouldUseId3Tags(MainFragment.this.getContext());
|
||||
String currentActiveServerProperties = getActiveServerProperties();
|
||||
|
||||
if (id3 != shouldUseId3)
|
||||
{
|
||||
shouldUseId3 = id3;
|
||||
shouldRestart = true;
|
||||
}
|
||||
|
||||
if (!currentActiveServerProperties.equals(lastActiveServerProperties))
|
||||
{
|
||||
lastActiveServerProperties = currentActiveServerProperties;
|
||||
shouldRestart = true;
|
||||
}
|
||||
|
||||
if (shouldRestart) {
|
||||
setupMenuList(list);
|
||||
}
|
||||
}
|
||||
|
||||
private void setupMenuList(ListView list)
|
||||
{
|
||||
final View buttons = getLayoutInflater().inflate(R.layout.main_buttons, null);
|
||||
final View serverButton = buttons.findViewById(R.id.main_select_server);
|
||||
final TextView serverTextView = serverButton.findViewById(R.id.main_select_server_2);
|
||||
|
||||
lastActiveServerProperties = getActiveServerProperties();
|
||||
String name = activeServerProvider.getValue().getActiveServer().getName();
|
||||
|
||||
serverTextView.setText(name);
|
||||
|
||||
final View musicTitle = buttons.findViewById(R.id.main_music);
|
||||
final View artistsButton = buttons.findViewById(R.id.main_artists_button);
|
||||
final View albumsButton = buttons.findViewById(R.id.main_albums_button);
|
||||
final View genresButton = buttons.findViewById(R.id.main_genres_button);
|
||||
final View videosTitle = buttons.findViewById(R.id.main_videos_title);
|
||||
final View songsTitle = buttons.findViewById(R.id.main_songs);
|
||||
final View randomSongsButton = buttons.findViewById(R.id.main_songs_button);
|
||||
final View songsStarredButton = buttons.findViewById(R.id.main_songs_starred);
|
||||
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 albumsStarredButton = buttons.findViewById(R.id.main_albums_starred);
|
||||
final View albumsRecentButton = buttons.findViewById(R.id.main_albums_recent);
|
||||
final View albumsFrequentButton = buttons.findViewById(R.id.main_albums_frequent);
|
||||
final View albumsAlphaByNameButton = buttons.findViewById(R.id.main_albums_alphaByName);
|
||||
final View albumsAlphaByArtistButton = buttons.findViewById(R.id.main_albums_alphaByArtist);
|
||||
final View videosButton = buttons.findViewById(R.id.main_videos);
|
||||
|
||||
final MergeAdapter adapter = new MergeAdapter();
|
||||
adapter.addViews(Collections.singletonList(serverButton), true);
|
||||
|
||||
if (!ActiveServerProvider.Companion.isOffline(this.getContext()))
|
||||
{
|
||||
adapter.addView(musicTitle, false);
|
||||
adapter.addViews(asList(artistsButton, albumsButton, genresButton), true);
|
||||
adapter.addView(songsTitle, false);
|
||||
adapter.addViews(asList(randomSongsButton, songsStarredButton), true);
|
||||
adapter.addView(albumsTitle, false);
|
||||
|
||||
if (Util.getShouldUseId3Tags(MainFragment.this.getContext()))
|
||||
{
|
||||
shouldUseId3 = true;
|
||||
adapter.addViews(asList(albumsNewestButton, albumsRecentButton, albumsFrequentButton, albumsRandomButton, albumsStarredButton, albumsAlphaByNameButton, albumsAlphaByArtistButton), true);
|
||||
}
|
||||
else
|
||||
{
|
||||
shouldUseId3 = false;
|
||||
adapter.addViews(asList(albumsNewestButton, albumsRecentButton, albumsFrequentButton, albumsHighestButton, albumsRandomButton, albumsStarredButton, albumsAlphaByNameButton, albumsAlphaByArtistButton), true);
|
||||
}
|
||||
|
||||
adapter.addView(videosTitle, false);
|
||||
adapter.addViews(Collections.singletonList(videosButton), true);
|
||||
}
|
||||
|
||||
list.setAdapter(adapter);
|
||||
list.setOnItemClickListener(new AdapterView.OnItemClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id)
|
||||
{
|
||||
if (view == serverButton)
|
||||
{
|
||||
showServers();
|
||||
}
|
||||
else if (view == albumsNewestButton)
|
||||
{
|
||||
showAlbumList("newest", R.string.main_albums_newest);
|
||||
}
|
||||
else if (view == albumsRandomButton)
|
||||
{
|
||||
showAlbumList("random", R.string.main_albums_random);
|
||||
}
|
||||
else if (view == albumsHighestButton)
|
||||
{
|
||||
showAlbumList("highest", R.string.main_albums_highest);
|
||||
}
|
||||
else if (view == albumsRecentButton)
|
||||
{
|
||||
showAlbumList("recent", R.string.main_albums_recent);
|
||||
}
|
||||
else if (view == albumsFrequentButton)
|
||||
{
|
||||
showAlbumList("frequent", R.string.main_albums_frequent);
|
||||
}
|
||||
else if (view == albumsStarredButton)
|
||||
{
|
||||
showAlbumList(Constants.STARRED, R.string.main_albums_starred);
|
||||
}
|
||||
else if (view == albumsAlphaByNameButton)
|
||||
{
|
||||
showAlbumList(Constants.ALPHABETICAL_BY_NAME, R.string.main_albums_alphaByName);
|
||||
}
|
||||
else if (view == albumsAlphaByArtistButton)
|
||||
{
|
||||
showAlbumList("alphabeticalByArtist", R.string.main_albums_alphaByArtist);
|
||||
}
|
||||
else if (view == songsStarredButton)
|
||||
{
|
||||
showStarredSongs();
|
||||
}
|
||||
else if (view == artistsButton)
|
||||
{
|
||||
showArtists();
|
||||
}
|
||||
else if (view == albumsButton)
|
||||
{
|
||||
showAlbumList(Constants.ALPHABETICAL_BY_NAME, R.string.main_albums_title);
|
||||
}
|
||||
else if (view == randomSongsButton)
|
||||
{
|
||||
showRandomSongs();
|
||||
}
|
||||
else if (view == genresButton)
|
||||
{
|
||||
showGenres();
|
||||
}
|
||||
else if (view == videosButton)
|
||||
{
|
||||
showVideos();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private String getActiveServerProperties()
|
||||
{
|
||||
ServerSetting currentSetting = activeServerProvider.getValue().getActiveServer();
|
||||
return String.format("%s;%s;%s;%s;%s;%s", currentSetting.getUrl(), currentSetting.getUserName(),
|
||||
currentSetting.getPassword(), currentSetting.getAllowSelfSignedCertificate(),
|
||||
currentSetting.getLdapSupport(), currentSetting.getMinimumApiVersion());
|
||||
}
|
||||
|
||||
private void showAlbumList(final String type, final int title)
|
||||
{
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TYPE, type);
|
||||
bundle.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TITLE, title);
|
||||
bundle.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, Util.getMaxAlbums(getContext()));
|
||||
bundle.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_OFFSET, 0);
|
||||
Navigation.findNavController(getView()).navigate(R.id.mainToSelectAlbum, bundle);
|
||||
}
|
||||
|
||||
private void showStarredSongs()
|
||||
{
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putInt(Constants.INTENT_EXTRA_NAME_STARRED, 1);
|
||||
Navigation.findNavController(getView()).navigate(R.id.mainToSelectAlbum, bundle);
|
||||
}
|
||||
|
||||
private void showRandomSongs()
|
||||
{
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putInt(Constants.INTENT_EXTRA_NAME_RANDOM, 1);
|
||||
bundle.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, Util.getMaxSongs(getContext()));
|
||||
Navigation.findNavController(getView()).navigate(R.id.mainToSelectAlbum, bundle);
|
||||
}
|
||||
|
||||
private void showArtists()
|
||||
{
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TITLE, getContext().getResources().getString(R.string.main_artists_title));
|
||||
Navigation.findNavController(getView()).navigate(R.id.selectArtistFragment, bundle);
|
||||
}
|
||||
|
||||
private void showGenres()
|
||||
{
|
||||
Navigation.findNavController(getView()).navigate(R.id.mainToSelectGenre);
|
||||
}
|
||||
|
||||
private void showVideos()
|
||||
{
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putInt(Constants.INTENT_EXTRA_NAME_VIDEOS, 1);
|
||||
Navigation.findNavController(getView()).navigate(R.id.mainToSelectAlbum, bundle);
|
||||
}
|
||||
|
||||
private void showServers()
|
||||
{
|
||||
final Intent intent = new Intent(getContext(), ServerSelectorActivity.class);
|
||||
startActivityForResult(intent, 0);
|
||||
}
|
||||
|
||||
public void startActivityForResultWithoutTransition(Activity currentActivity, Intent intent)
|
||||
{
|
||||
startActivityForResult(intent, 0);
|
||||
Util.disablePendingTransition(currentActivity);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,393 @@
|
|||
package org.moire.ultrasonic.fragment;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableString;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.text.util.Linkify;
|
||||
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.CheckBox;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.navigation.Navigation;
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.moire.ultrasonic.R;
|
||||
import org.moire.ultrasonic.api.subsonic.ApiNotSupportedException;
|
||||
import org.moire.ultrasonic.data.ActiveServerProvider;
|
||||
import org.moire.ultrasonic.domain.Playlist;
|
||||
import org.moire.ultrasonic.service.MusicService;
|
||||
import org.moire.ultrasonic.service.MusicServiceFactory;
|
||||
import org.moire.ultrasonic.service.OfflineException;
|
||||
import org.moire.ultrasonic.subsonic.DownloadHandler;
|
||||
import org.moire.ultrasonic.util.BackgroundTask;
|
||||
import org.moire.ultrasonic.util.CacheCleaner;
|
||||
import org.moire.ultrasonic.util.CancellationToken;
|
||||
import org.moire.ultrasonic.util.Constants;
|
||||
import org.moire.ultrasonic.util.LoadingTask;
|
||||
import org.moire.ultrasonic.util.TabActivityBackgroundTask;
|
||||
import org.moire.ultrasonic.util.Util;
|
||||
import org.moire.ultrasonic.view.PlaylistAdapter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import kotlin.Lazy;
|
||||
import timber.log.Timber;
|
||||
|
||||
import static org.koin.java.KoinJavaComponent.inject;
|
||||
|
||||
public class PlaylistsFragment extends Fragment {
|
||||
|
||||
private SwipeRefreshLayout refreshPlaylistsListView;
|
||||
private ListView playlistsListView;
|
||||
private View emptyTextView;
|
||||
private PlaylistAdapter playlistAdapter;
|
||||
|
||||
private final Lazy<DownloadHandler> downloadHandler = inject(DownloadHandler.class);
|
||||
private CancellationToken cancellationToken;
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
Util.applyTheme(this.getContext());
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.select_playlist, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
cancellationToken = new CancellationToken();
|
||||
|
||||
refreshPlaylistsListView = view.findViewById(R.id.select_playlist_refresh);
|
||||
playlistsListView = view.findViewById(R.id.select_playlist_list);
|
||||
|
||||
refreshPlaylistsListView.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener()
|
||||
{
|
||||
@Override
|
||||
public void onRefresh()
|
||||
{
|
||||
new GetDataTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
}
|
||||
});
|
||||
|
||||
emptyTextView = view.findViewById(R.id.select_playlist_empty);
|
||||
playlistsListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
Playlist playlist = (Playlist) parent.getItemAtPosition(position);
|
||||
|
||||
if (playlist == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString(Constants.INTENT_EXTRA_NAME_ID, playlist.getId());
|
||||
bundle.putString(Constants.INTENT_EXTRA_NAME_PLAYLIST_ID, playlist.getId());
|
||||
bundle.putString(Constants.INTENT_EXTRA_NAME_PLAYLIST_NAME, playlist.getName());
|
||||
Navigation.findNavController(getView()).navigate(R.id.selectAlbumFragment, bundle);
|
||||
}
|
||||
});
|
||||
registerForContextMenu(playlistsListView);
|
||||
|
||||
FragmentTitle.Companion.setTitle(this, R.string.playlist_label);
|
||||
|
||||
load();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
cancellationToken.cancel();
|
||||
super.onDestroyView();
|
||||
}
|
||||
|
||||
private void refresh()
|
||||
{
|
||||
// TODO: create better restart
|
||||
getView().post(new Runnable() {
|
||||
public void run() {
|
||||
Timber.d("Refresh called...");
|
||||
if (getArguments() == null) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putBoolean(Constants.INTENT_EXTRA_NAME_REFRESH, true);
|
||||
setArguments(bundle);
|
||||
} else {
|
||||
getArguments().putBoolean(Constants.INTENT_EXTRA_NAME_REFRESH, true);
|
||||
}
|
||||
onViewCreated(getView(), null);
|
||||
}
|
||||
});
|
||||
|
||||
/* finish();
|
||||
Intent intent = new Intent(this, SelectPlaylistActivity.class);
|
||||
intent.putExtra(Constants.INTENT_EXTRA_NAME_REFRESH, true);
|
||||
startActivityForResultWithoutTransition(this, intent);
|
||||
*/
|
||||
}
|
||||
|
||||
private void load()
|
||||
{
|
||||
BackgroundTask<List<Playlist>> task = new TabActivityBackgroundTask<List<Playlist>>(getActivity(), true, refreshPlaylistsListView, cancellationToken)
|
||||
{
|
||||
@Override
|
||||
protected List<Playlist> doInBackground() throws Throwable
|
||||
{
|
||||
MusicService musicService = MusicServiceFactory.getMusicService(getContext());
|
||||
boolean refresh = getArguments() != null && getArguments().getBoolean(Constants.INTENT_EXTRA_NAME_REFRESH, false);
|
||||
List<Playlist> playlists = musicService.getPlaylists(refresh, getContext(), this);
|
||||
|
||||
if (!ActiveServerProvider.Companion.isOffline(getContext()))
|
||||
new CacheCleaner(getContext()).cleanPlaylists(playlists);
|
||||
return playlists;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done(List<Playlist> result)
|
||||
{
|
||||
playlistsListView.setAdapter(playlistAdapter = new PlaylistAdapter(getContext(), result));
|
||||
emptyTextView.setVisibility(result.isEmpty() ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
};
|
||||
task.execute();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateContextMenu(@NotNull ContextMenu menu, @NotNull View view, ContextMenu.ContextMenuInfo menuInfo)
|
||||
{
|
||||
super.onCreateContextMenu(menu, view, menuInfo);
|
||||
|
||||
MenuInflater inflater = getActivity().getMenuInflater();
|
||||
if (ActiveServerProvider.Companion.isOffline(getContext())) inflater.inflate(R.menu.select_playlist_context_offline, menu);
|
||||
else inflater.inflate(R.menu.select_playlist_context, menu);
|
||||
|
||||
MenuItem downloadMenuItem = menu.findItem(R.id.album_menu_download);
|
||||
|
||||
if (downloadMenuItem != null)
|
||||
{
|
||||
downloadMenuItem.setVisible(!ActiveServerProvider.Companion.isOffline(getContext()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onContextItemSelected(MenuItem menuItem)
|
||||
{
|
||||
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo();
|
||||
if (info == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Playlist playlist = (Playlist) playlistsListView.getItemAtPosition(info.position);
|
||||
if (playlist == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Bundle bundle;
|
||||
switch (menuItem.getItemId())
|
||||
{
|
||||
case R.id.playlist_menu_pin:
|
||||
downloadHandler.getValue().downloadPlaylist(this, playlist.getId(), playlist.getName(), true, true, false, false, true, false, false);
|
||||
break;
|
||||
case R.id.playlist_menu_unpin:
|
||||
downloadHandler.getValue().downloadPlaylist(this, playlist.getId(), playlist.getName(), false, false, false, false, true, false, true);
|
||||
break;
|
||||
case R.id.playlist_menu_download:
|
||||
downloadHandler.getValue().downloadPlaylist(this, playlist.getId(), playlist.getName(), false, false, false, false, true, false, false);
|
||||
break;
|
||||
case R.id.playlist_menu_play_now:
|
||||
bundle = new Bundle();
|
||||
bundle.putString(Constants.INTENT_EXTRA_NAME_PLAYLIST_ID, playlist.getId());
|
||||
bundle.putString(Constants.INTENT_EXTRA_NAME_PLAYLIST_NAME, playlist.getName());
|
||||
bundle.putBoolean(Constants.INTENT_EXTRA_NAME_AUTOPLAY, true);
|
||||
Navigation.findNavController(getView()).navigate(R.id.selectAlbumFragment, bundle);
|
||||
break;
|
||||
case R.id.playlist_menu_play_shuffled:
|
||||
bundle = new Bundle();
|
||||
bundle.putString(Constants.INTENT_EXTRA_NAME_PLAYLIST_ID, playlist.getId());
|
||||
bundle.putString(Constants.INTENT_EXTRA_NAME_PLAYLIST_NAME, playlist.getName());
|
||||
bundle.putBoolean(Constants.INTENT_EXTRA_NAME_AUTOPLAY, true);
|
||||
bundle.putBoolean(Constants.INTENT_EXTRA_NAME_SHUFFLE, true);
|
||||
Navigation.findNavController(getView()).navigate(R.id.selectAlbumFragment, bundle);
|
||||
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;
|
||||
}
|
||||
|
||||
private void deletePlaylist(final Playlist playlist)
|
||||
{
|
||||
new AlertDialog.Builder(getContext()).setIcon(android.R.drawable.ic_dialog_alert).setTitle(R.string.common_confirm).setMessage(getResources().getString(R.string.delete_playlist, playlist.getName())).setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which)
|
||||
{
|
||||
new LoadingTask<Void>(getActivity(), false, refreshPlaylistsListView)
|
||||
{
|
||||
@Override
|
||||
protected Void doInBackground() throws Throwable
|
||||
{
|
||||
MusicService musicService = MusicServiceFactory.getMusicService(getContext());
|
||||
musicService.deletePlaylist(playlist.getId(), getContext(), null);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done(Void result)
|
||||
{
|
||||
playlistAdapter.remove(playlist);
|
||||
playlistAdapter.notifyDataSetChanged();
|
||||
Util.toast(getContext(), getResources().getString(R.string.menu_deleted_playlist, playlist.getName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void error(Throwable error)
|
||||
{
|
||||
String msg;
|
||||
msg = error instanceof OfflineException || error instanceof ApiNotSupportedException ? getErrorMessage(error) : String.format("%s %s", getResources().getString(R.string.menu_deleted_playlist_error, playlist.getName()), getErrorMessage(error));
|
||||
|
||||
Util.toast(getContext(), msg, false);
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
}).setNegativeButton(R.string.common_cancel, null).show();
|
||||
}
|
||||
|
||||
private void displayPlaylistInfo(final Playlist playlist)
|
||||
{
|
||||
final TextView textView = new TextView(getContext());
|
||||
textView.setPadding(5, 5, 5, 5);
|
||||
|
||||
final Spannable message = new SpannableString("Owner: " + playlist.getOwner() + "\nComments: " +
|
||||
((playlist.getComment() == null) ? "" : playlist.getComment()) +
|
||||
"\nSong Count: " + playlist.getSongCount() +
|
||||
((playlist.getPublic() == null) ? "" : ("\nPublic: " + playlist.getPublic()) + ((playlist.getCreated() == null) ? "" : ("\nCreation Date: " + playlist.getCreated().replace('T', ' ')))));
|
||||
|
||||
Linkify.addLinks(message, Linkify.WEB_URLS);
|
||||
textView.setText(message);
|
||||
textView.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
|
||||
new AlertDialog.Builder(getContext()).setTitle(playlist.getName()).setCancelable(true).setIcon(android.R.drawable.ic_dialog_info).setView(textView).show();
|
||||
}
|
||||
|
||||
private void updatePlaylistInfo(final Playlist playlist)
|
||||
{
|
||||
View dialogView = getLayoutInflater().inflate(R.layout.update_playlist, null);
|
||||
|
||||
if (dialogView == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
final EditText nameBox = dialogView.findViewById(R.id.get_playlist_name);
|
||||
final EditText commentBox = dialogView.findViewById(R.id.get_playlist_comment);
|
||||
final CheckBox publicBox = dialogView.findViewById(R.id.get_playlist_public);
|
||||
|
||||
nameBox.setText(playlist.getName());
|
||||
commentBox.setText(playlist.getComment());
|
||||
Boolean pub = playlist.getPublic();
|
||||
|
||||
if (pub == null)
|
||||
{
|
||||
publicBox.setEnabled(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
publicBox.setChecked(pub);
|
||||
}
|
||||
|
||||
AlertDialog.Builder alertDialog = new AlertDialog.Builder(getContext());
|
||||
|
||||
alertDialog.setIcon(android.R.drawable.ic_dialog_alert);
|
||||
alertDialog.setTitle(R.string.playlist_update_info);
|
||||
alertDialog.setView(dialogView);
|
||||
alertDialog.setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which)
|
||||
{
|
||||
new LoadingTask<Void>(getActivity(), false, refreshPlaylistsListView)
|
||||
{
|
||||
@Override
|
||||
protected Void doInBackground() throws Throwable
|
||||
{
|
||||
Editable nameBoxText = nameBox.getText();
|
||||
Editable commentBoxText = commentBox.getText();
|
||||
String name = nameBoxText != null ? nameBoxText.toString() : null;
|
||||
String comment = commentBoxText != null ? commentBoxText.toString() : null;
|
||||
|
||||
MusicService musicService = MusicServiceFactory.getMusicService(getContext());
|
||||
musicService.updatePlaylist(playlist.getId(), name, comment, publicBox.isChecked(), getContext(), null);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done(Void result)
|
||||
{
|
||||
refresh();
|
||||
Util.toast(getContext(), getResources().getString(R.string.playlist_updated_info, playlist.getName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void error(Throwable error)
|
||||
{
|
||||
String msg;
|
||||
msg = error instanceof OfflineException || error instanceof ApiNotSupportedException ? getErrorMessage(error) : String.format("%s %s", getResources().getString(R.string.playlist_updated_info_error, playlist.getName()), getErrorMessage(error));
|
||||
|
||||
Util.toast(getContext(), msg, false);
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
});
|
||||
alertDialog.setNegativeButton(R.string.common_cancel, null);
|
||||
alertDialog.show();
|
||||
}
|
||||
|
||||
private class GetDataTask extends AsyncTask<Void, Void, String[]>
|
||||
{
|
||||
@Override
|
||||
protected void onPostExecute(String[] result)
|
||||
{
|
||||
super.onPostExecute(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String[] doInBackground(Void... params)
|
||||
{
|
||||
refresh();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
package org.moire.ultrasonic.fragment;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ListView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.navigation.Navigation;
|
||||
|
||||
import org.moire.ultrasonic.R;
|
||||
import org.moire.ultrasonic.domain.PodcastsChannel;
|
||||
import org.moire.ultrasonic.service.MusicService;
|
||||
import org.moire.ultrasonic.service.MusicServiceFactory;
|
||||
import org.moire.ultrasonic.util.BackgroundTask;
|
||||
import org.moire.ultrasonic.util.Constants;
|
||||
import org.moire.ultrasonic.util.TabActivityBackgroundTask;
|
||||
import org.moire.ultrasonic.util.Util;
|
||||
import org.moire.ultrasonic.view.PodcastsChannelsAdapter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class PodcastFragment extends Fragment {
|
||||
|
||||
private View emptyTextView;
|
||||
ListView channelItemsListView = null;
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
Util.applyTheme(this.getContext());
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.podcasts, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
FragmentTitle.Companion.setTitle(this, R.string.podcasts_label);
|
||||
|
||||
emptyTextView = view.findViewById(R.id.select_podcasts_empty);
|
||||
channelItemsListView = view.findViewById(R.id.podcasts_channels_items_list);
|
||||
channelItemsListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
PodcastsChannel pc = (PodcastsChannel) parent.getItemAtPosition(position);
|
||||
if (pc == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString(Constants.INTENT_EXTRA_NAME_PODCAST_CHANNEL_ID, pc.getId());
|
||||
Navigation.findNavController(getView()).navigate(R.id.selectAlbumFragment, bundle);
|
||||
}
|
||||
});
|
||||
|
||||
// TODO: Probably a swipeRefresh should be added here in the long run
|
||||
load();
|
||||
}
|
||||
|
||||
private void load()
|
||||
{
|
||||
BackgroundTask<List<PodcastsChannel>> task = new TabActivityBackgroundTask<List<PodcastsChannel>>(getActivity(), true)
|
||||
{
|
||||
@Override
|
||||
protected List<PodcastsChannel> doInBackground() throws Throwable
|
||||
{
|
||||
MusicService musicService = MusicServiceFactory.getMusicService(getContext());
|
||||
return musicService.getPodcastsChannels(false,getContext(), this);
|
||||
|
||||
/* TODO Why is here a cache cleaning? (original TODO text: c'est quoi ce nettoyage de cache ?)
|
||||
if (!Util.isOffline(PodcastsActivity.this))
|
||||
new CacheCleaner(PodcastsActivity.this, getDownloadService()).cleanPlaylists(playlists);
|
||||
*/
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done(List<PodcastsChannel> result)
|
||||
{
|
||||
channelItemsListView.setAdapter(new PodcastsChannelsAdapter(getContext(), result));
|
||||
emptyTextView.setVisibility(result.isEmpty() ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
};
|
||||
task.execute();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,586 @@
|
|||
package org.moire.ultrasonic.fragment;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.SearchManager;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.os.Bundle;
|
||||
import android.view.ContextMenu;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ListAdapter;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.widget.SearchView;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.navigation.Navigation;
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
|
||||
import org.moire.ultrasonic.R;
|
||||
import org.moire.ultrasonic.data.ActiveServerProvider;
|
||||
import org.moire.ultrasonic.domain.Artist;
|
||||
import org.moire.ultrasonic.domain.MusicDirectory;
|
||||
import org.moire.ultrasonic.domain.SearchCriteria;
|
||||
import org.moire.ultrasonic.domain.SearchResult;
|
||||
import org.moire.ultrasonic.service.MediaPlayerController;
|
||||
import org.moire.ultrasonic.service.MusicService;
|
||||
import org.moire.ultrasonic.service.MusicServiceFactory;
|
||||
import org.moire.ultrasonic.subsonic.DownloadHandler;
|
||||
import org.moire.ultrasonic.subsonic.ImageLoaderProvider;
|
||||
import org.moire.ultrasonic.subsonic.NetworkAndStorageChecker;
|
||||
import org.moire.ultrasonic.subsonic.ShareHandler;
|
||||
import org.moire.ultrasonic.subsonic.VideoPlayer;
|
||||
import org.moire.ultrasonic.util.BackgroundTask;
|
||||
import org.moire.ultrasonic.util.CancellationToken;
|
||||
import org.moire.ultrasonic.util.Constants;
|
||||
import org.moire.ultrasonic.util.MergeAdapter;
|
||||
import org.moire.ultrasonic.util.TabActivityBackgroundTask;
|
||||
import org.moire.ultrasonic.util.Util;
|
||||
import org.moire.ultrasonic.view.ArtistAdapter;
|
||||
import org.moire.ultrasonic.view.EntryAdapter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import kotlin.Lazy;
|
||||
import timber.log.Timber;
|
||||
|
||||
import static org.koin.java.KoinJavaComponent.inject;
|
||||
|
||||
public class SearchFragment extends Fragment {
|
||||
|
||||
private static int DEFAULT_ARTISTS;
|
||||
private static int DEFAULT_ALBUMS;
|
||||
private static int DEFAULT_SONGS;
|
||||
|
||||
private ListView list;
|
||||
|
||||
private View artistsHeading;
|
||||
private View albumsHeading;
|
||||
private View songsHeading;
|
||||
private TextView searchButton;
|
||||
private View moreArtistsButton;
|
||||
private View moreAlbumsButton;
|
||||
private View moreSongsButton;
|
||||
private SearchResult searchResult;
|
||||
private MergeAdapter mergeAdapter;
|
||||
private ArtistAdapter artistAdapter;
|
||||
private ListAdapter moreArtistsAdapter;
|
||||
private EntryAdapter albumAdapter;
|
||||
private ListAdapter moreAlbumsAdapter;
|
||||
private ListAdapter moreSongsAdapter;
|
||||
private EntryAdapter songAdapter;
|
||||
private SwipeRefreshLayout searchRefresh;
|
||||
|
||||
private final Lazy<VideoPlayer> videoPlayer = inject(VideoPlayer.class);
|
||||
private final Lazy<MediaPlayerController> mediaPlayerControllerLazy = inject(MediaPlayerController.class);
|
||||
private final Lazy<ImageLoaderProvider> imageLoaderProvider = inject(ImageLoaderProvider.class);
|
||||
private final Lazy<DownloadHandler> downloadHandler = inject(DownloadHandler.class);
|
||||
private final Lazy<ShareHandler> shareHandler = inject(ShareHandler.class);
|
||||
private final Lazy<NetworkAndStorageChecker> networkAndStorageChecker = inject(NetworkAndStorageChecker.class);
|
||||
private CancellationToken cancellationToken;
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
Util.applyTheme(this.getContext());
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.search, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
cancellationToken = new CancellationToken();
|
||||
|
||||
FragmentTitle.Companion.setTitle(this, R.string.search_title);
|
||||
setHasOptionsMenu(true);
|
||||
|
||||
DEFAULT_ARTISTS = Util.getDefaultArtists(getContext());
|
||||
DEFAULT_ALBUMS = Util.getDefaultAlbums(getContext());
|
||||
DEFAULT_SONGS = Util.getDefaultSongs(getContext());
|
||||
|
||||
View buttons = LayoutInflater.from(getContext()).inflate(R.layout.search_buttons, null);
|
||||
|
||||
if (buttons != null)
|
||||
{
|
||||
artistsHeading = buttons.findViewById(R.id.search_artists);
|
||||
albumsHeading = buttons.findViewById(R.id.search_albums);
|
||||
songsHeading = buttons.findViewById(R.id.search_songs);
|
||||
searchButton = buttons.findViewById(R.id.search_search);
|
||||
moreArtistsButton = buttons.findViewById(R.id.search_more_artists);
|
||||
moreAlbumsButton = buttons.findViewById(R.id.search_more_albums);
|
||||
moreSongsButton = buttons.findViewById(R.id.search_more_songs);
|
||||
}
|
||||
|
||||
list = view.findViewById(R.id.search_list);
|
||||
searchRefresh = view.findViewById(R.id.search_entries_refresh);
|
||||
searchRefresh.setEnabled(false); // TODO: Should this be enabled?
|
||||
|
||||
list.setOnItemClickListener(new AdapterView.OnItemClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id)
|
||||
{
|
||||
if (view == moreArtistsButton)
|
||||
{
|
||||
expandArtists();
|
||||
}
|
||||
else if (view == moreAlbumsButton)
|
||||
{
|
||||
expandAlbums();
|
||||
}
|
||||
else if (view == moreSongsButton)
|
||||
{
|
||||
expandSongs();
|
||||
}
|
||||
else
|
||||
{
|
||||
Object item = parent.getItemAtPosition(position);
|
||||
if (item instanceof Artist)
|
||||
{
|
||||
onArtistSelected((Artist) item);
|
||||
}
|
||||
else if (item instanceof MusicDirectory.Entry)
|
||||
{
|
||||
MusicDirectory.Entry entry = (MusicDirectory.Entry) item;
|
||||
if (entry.isDirectory())
|
||||
{
|
||||
onAlbumSelected(entry, false);
|
||||
}
|
||||
else if (entry.isVideo())
|
||||
{
|
||||
onVideoSelected(entry);
|
||||
}
|
||||
else
|
||||
{
|
||||
onSongSelected(entry, false, true, true, false);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
registerForContextMenu(list);
|
||||
|
||||
populateList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
|
||||
Activity activity = getActivity();
|
||||
if (activity == null) return;
|
||||
SearchManager searchManager = (SearchManager) activity.getSystemService(Context.SEARCH_SERVICE);
|
||||
Bundle arguments = getArguments();
|
||||
final boolean autoPlay = arguments != null && arguments.getBoolean(Constants.INTENT_EXTRA_NAME_AUTOPLAY, false);
|
||||
|
||||
inflater.inflate(R.menu.search, menu);
|
||||
MenuItem searchItem = menu.findItem(R.id.search_item);
|
||||
final SearchView searchView = (SearchView) searchItem.getActionView();
|
||||
searchView.setSearchableInfo(searchManager.getSearchableInfo(getActivity().getComponentName()));
|
||||
|
||||
searchView.setOnSuggestionListener(new SearchView.OnSuggestionListener() {
|
||||
@Override
|
||||
public boolean onSuggestionSelect(int position) { return true; }
|
||||
|
||||
@Override
|
||||
public boolean onSuggestionClick(int position) {
|
||||
Timber.d("onSuggestionClick: %d", position);
|
||||
Cursor cursor= searchView.getSuggestionsAdapter().getCursor();
|
||||
cursor.moveToPosition(position);
|
||||
String suggestion = cursor.getString(2); // TODO: Try to do something with this magic const -- 2 is the index of col containing suggestion name.
|
||||
searchView.setQuery(suggestion,true);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
|
||||
@Override
|
||||
public boolean onQueryTextSubmit(String query) {
|
||||
Timber.d("onQueryTextSubmit: %s", query);
|
||||
mergeAdapter = new MergeAdapter();
|
||||
list.setAdapter(mergeAdapter);
|
||||
search(query, autoPlay);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onQueryTextChange(String newText) { return true; }
|
||||
});
|
||||
searchView.setIconifiedByDefault(false);
|
||||
searchItem.expandActionView();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo)
|
||||
{
|
||||
super.onCreateContextMenu(menu, view, menuInfo);
|
||||
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
|
||||
Object selectedItem = list.getItemAtPosition(info.position);
|
||||
|
||||
boolean isArtist = selectedItem instanceof Artist;
|
||||
boolean isAlbum = selectedItem instanceof MusicDirectory.Entry && ((MusicDirectory.Entry) selectedItem).isDirectory();
|
||||
|
||||
if (!isArtist && !isAlbum)
|
||||
{
|
||||
MenuInflater inflater = getActivity().getMenuInflater();
|
||||
inflater.inflate(R.menu.select_song_context, menu);
|
||||
}
|
||||
else
|
||||
{
|
||||
MenuInflater inflater = getActivity().getMenuInflater();
|
||||
inflater.inflate(R.menu.select_album_context, menu);
|
||||
}
|
||||
|
||||
MenuItem shareButton = menu.findItem(R.id.menu_item_share);
|
||||
MenuItem downloadMenuItem = menu.findItem(R.id.album_menu_download);
|
||||
|
||||
if (downloadMenuItem != null)
|
||||
{
|
||||
downloadMenuItem.setVisible(!ActiveServerProvider.Companion.isOffline(getContext()));
|
||||
}
|
||||
|
||||
if (ActiveServerProvider.Companion.isOffline(getContext()) || isArtist)
|
||||
{
|
||||
if (shareButton != null)
|
||||
{
|
||||
shareButton.setVisible(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onContextItemSelected(MenuItem menuItem)
|
||||
{
|
||||
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo();
|
||||
|
||||
if (info == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
Object selectedItem = list.getItemAtPosition(info.position);
|
||||
|
||||
Artist artist = selectedItem instanceof Artist ? (Artist) selectedItem : null;
|
||||
MusicDirectory.Entry entry = selectedItem instanceof MusicDirectory.Entry ? (MusicDirectory.Entry) selectedItem : null;
|
||||
|
||||
String entryId = null;
|
||||
|
||||
if (entry != null)
|
||||
{
|
||||
entryId = entry.getId();
|
||||
}
|
||||
|
||||
String id = artist != null ? artist.getId() : entryId;
|
||||
|
||||
if (id == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
List<MusicDirectory.Entry> songs = new ArrayList<>(1);
|
||||
|
||||
switch (menuItem.getItemId())
|
||||
{
|
||||
case R.id.album_menu_play_now:
|
||||
downloadHandler.getValue().downloadRecursively(this, id, false, false, true, false, false, false, false, false);
|
||||
break;
|
||||
case R.id.album_menu_play_next:
|
||||
downloadHandler.getValue().downloadRecursively(this, id, false, true, false, true, false, true, false, false);
|
||||
break;
|
||||
case R.id.album_menu_play_last:
|
||||
downloadHandler.getValue().downloadRecursively(this, id, false, true, false, false, false, false, false, false);
|
||||
break;
|
||||
case R.id.album_menu_pin:
|
||||
downloadHandler.getValue().downloadRecursively(this, id, true, true, false, false, false, false, false, false);
|
||||
break;
|
||||
case R.id.album_menu_unpin:
|
||||
downloadHandler.getValue().downloadRecursively(this, id, false, false, false, false, false, false, true, false);
|
||||
break;
|
||||
case R.id.album_menu_download:
|
||||
downloadHandler.getValue().downloadRecursively(this, id, false, false, false, false, true, false, false, false);
|
||||
break;
|
||||
case R.id.song_menu_play_now:
|
||||
if (entry != null)
|
||||
{
|
||||
songs = new ArrayList<>(1);
|
||||
songs.add(entry);
|
||||
downloadHandler.getValue().download(this, false, false, true, false, false, songs);
|
||||
}
|
||||
break;
|
||||
case R.id.song_menu_play_next:
|
||||
if (entry != null)
|
||||
{
|
||||
songs = new ArrayList<>(1);
|
||||
songs.add(entry);
|
||||
downloadHandler.getValue().download(this, true, false, false, true, false, songs);
|
||||
}
|
||||
break;
|
||||
case R.id.song_menu_play_last:
|
||||
if (entry != null)
|
||||
{
|
||||
songs = new ArrayList<>(1);
|
||||
songs.add(entry);
|
||||
downloadHandler.getValue().download(this, true, false, false, false, false, songs);
|
||||
}
|
||||
break;
|
||||
case R.id.song_menu_pin:
|
||||
if (entry != null)
|
||||
{
|
||||
songs.add(entry);
|
||||
Util.toast(getContext(), getResources().getQuantityString(R.plurals.select_album_n_songs_pinned, songs.size(), songs.size()));
|
||||
downloadBackground(true, songs);
|
||||
}
|
||||
break;
|
||||
case R.id.song_menu_download:
|
||||
if (entry != null)
|
||||
{
|
||||
songs.add(entry);
|
||||
Util.toast(getContext(), getResources().getQuantityString(R.plurals.select_album_n_songs_downloaded, songs.size(), songs.size()));
|
||||
downloadBackground(false, songs);
|
||||
}
|
||||
break;
|
||||
case R.id.song_menu_unpin:
|
||||
if (entry != null)
|
||||
{
|
||||
songs.add(entry);
|
||||
Util.toast(getContext(), getResources().getQuantityString(R.plurals.select_album_n_songs_unpinned, songs.size(), songs.size()));
|
||||
mediaPlayerControllerLazy.getValue().unpin(songs);
|
||||
}
|
||||
break;
|
||||
case R.id.menu_item_share:
|
||||
if (entry != null)
|
||||
{
|
||||
songs = new ArrayList<>(1);
|
||||
songs.add(entry);
|
||||
// TODO: Add SwipeRefresh spinner
|
||||
shareHandler.getValue().createShare(this, songs, null, cancellationToken);
|
||||
}
|
||||
default:
|
||||
return super.onContextItemSelected(menuItem);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
cancellationToken.cancel();
|
||||
super.onDestroyView();
|
||||
}
|
||||
|
||||
private void downloadBackground(final boolean save, final List<MusicDirectory.Entry> songs)
|
||||
{
|
||||
Runnable onValid = new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
networkAndStorageChecker.getValue().warnIfNetworkOrStorageUnavailable();
|
||||
mediaPlayerControllerLazy.getValue().downloadBackground(songs, save);
|
||||
}
|
||||
};
|
||||
|
||||
onValid.run();
|
||||
}
|
||||
|
||||
private void search(final String query, final boolean autoplay)
|
||||
{
|
||||
final int maxArtists = Util.getMaxArtists(getContext());
|
||||
final int maxAlbums = Util.getMaxAlbums(getContext());
|
||||
final int maxSongs = Util.getMaxSongs(getContext());
|
||||
|
||||
BackgroundTask<SearchResult> task = new TabActivityBackgroundTask<SearchResult>(getActivity(), true, searchRefresh, cancellationToken)
|
||||
{
|
||||
@Override
|
||||
protected SearchResult doInBackground() throws Throwable
|
||||
{
|
||||
SearchCriteria criteria = new SearchCriteria(query, maxArtists, maxAlbums, maxSongs);
|
||||
MusicService service = MusicServiceFactory.getMusicService(getContext());
|
||||
return service.search(criteria, getContext(), this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done(SearchResult result)
|
||||
{
|
||||
searchResult = result;
|
||||
|
||||
populateList();
|
||||
|
||||
if (autoplay)
|
||||
{
|
||||
autoplay();
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
task.execute();
|
||||
}
|
||||
|
||||
private void populateList()
|
||||
{
|
||||
mergeAdapter = new MergeAdapter();
|
||||
|
||||
// TODO: Remove this if the search widget can do the same
|
||||
//mergeAdapter.addView(searchButton, true);
|
||||
|
||||
if (searchResult != null)
|
||||
{
|
||||
List<Artist> artists = searchResult.getArtists();
|
||||
if (!artists.isEmpty())
|
||||
{
|
||||
mergeAdapter.addView(artistsHeading);
|
||||
List<Artist> displayedArtists = new ArrayList<Artist>(artists.subList(0, Math.min(DEFAULT_ARTISTS, artists.size())));
|
||||
artistAdapter = new ArtistAdapter(getContext(), displayedArtists);
|
||||
mergeAdapter.addAdapter(artistAdapter);
|
||||
if (artists.size() > DEFAULT_ARTISTS)
|
||||
{
|
||||
moreArtistsAdapter = mergeAdapter.addView(moreArtistsButton, true);
|
||||
}
|
||||
}
|
||||
|
||||
List<MusicDirectory.Entry> albums = searchResult.getAlbums();
|
||||
if (!albums.isEmpty())
|
||||
{
|
||||
mergeAdapter.addView(albumsHeading);
|
||||
List<MusicDirectory.Entry> displayedAlbums = new ArrayList<MusicDirectory.Entry>(albums.subList(0, Math.min(DEFAULT_ALBUMS, albums.size())));
|
||||
albumAdapter = new EntryAdapter(getContext(), imageLoaderProvider.getValue().getImageLoader(), displayedAlbums, false);
|
||||
mergeAdapter.addAdapter(albumAdapter);
|
||||
if (albums.size() > DEFAULT_ALBUMS)
|
||||
{
|
||||
moreAlbumsAdapter = mergeAdapter.addView(moreAlbumsButton, true);
|
||||
}
|
||||
}
|
||||
|
||||
List<MusicDirectory.Entry> songs = searchResult.getSongs();
|
||||
if (!songs.isEmpty())
|
||||
{
|
||||
mergeAdapter.addView(songsHeading);
|
||||
List<MusicDirectory.Entry> displayedSongs = new ArrayList<MusicDirectory.Entry>(songs.subList(0, Math.min(DEFAULT_SONGS, songs.size())));
|
||||
songAdapter = new EntryAdapter(getContext(), imageLoaderProvider.getValue().getImageLoader(), displayedSongs, false);
|
||||
mergeAdapter.addAdapter(songAdapter);
|
||||
if (songs.size() > DEFAULT_SONGS)
|
||||
{
|
||||
moreSongsAdapter = mergeAdapter.addView(moreSongsButton, true);
|
||||
}
|
||||
}
|
||||
|
||||
boolean empty = searchResult.getArtists().isEmpty() && searchResult.getAlbums().isEmpty() && searchResult.getSongs().isEmpty();
|
||||
searchButton.setText(empty ? R.string.search_no_match : R.string.search_search);
|
||||
}
|
||||
|
||||
list.setAdapter(mergeAdapter);
|
||||
}
|
||||
|
||||
private void expandArtists()
|
||||
{
|
||||
artistAdapter.clear();
|
||||
|
||||
for (Artist artist : searchResult.getArtists())
|
||||
{
|
||||
artistAdapter.add(artist);
|
||||
}
|
||||
|
||||
artistAdapter.notifyDataSetChanged();
|
||||
mergeAdapter.removeAdapter(moreArtistsAdapter);
|
||||
mergeAdapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
private void expandAlbums()
|
||||
{
|
||||
albumAdapter.clear();
|
||||
|
||||
for (MusicDirectory.Entry album : searchResult.getAlbums())
|
||||
{
|
||||
albumAdapter.add(album);
|
||||
}
|
||||
|
||||
albumAdapter.notifyDataSetChanged();
|
||||
mergeAdapter.removeAdapter(moreAlbumsAdapter);
|
||||
mergeAdapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
private void expandSongs()
|
||||
{
|
||||
songAdapter.clear();
|
||||
|
||||
for (MusicDirectory.Entry song : searchResult.getSongs())
|
||||
{
|
||||
songAdapter.add(song);
|
||||
}
|
||||
|
||||
songAdapter.notifyDataSetChanged();
|
||||
mergeAdapter.removeAdapter(moreSongsAdapter);
|
||||
mergeAdapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
private void onArtistSelected(Artist artist)
|
||||
{
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString(Constants.INTENT_EXTRA_NAME_ID, artist.getId());
|
||||
bundle.putString(Constants.INTENT_EXTRA_NAME_NAME, artist.getId());
|
||||
Navigation.findNavController(getView()).navigate(R.id.searchToSelectAlbum, bundle);
|
||||
}
|
||||
|
||||
private void onAlbumSelected(MusicDirectory.Entry album, boolean autoplay)
|
||||
{
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString(Constants.INTENT_EXTRA_NAME_ID, album.getId());
|
||||
bundle.putString(Constants.INTENT_EXTRA_NAME_NAME, album.getTitle());
|
||||
bundle.putBoolean(Constants.INTENT_EXTRA_NAME_IS_ALBUM, album.isDirectory());
|
||||
bundle.putBoolean(Constants.INTENT_EXTRA_NAME_AUTOPLAY, autoplay);
|
||||
Navigation.findNavController(getView()).navigate(R.id.searchToSelectAlbum, bundle);
|
||||
}
|
||||
|
||||
private void onSongSelected(MusicDirectory.Entry song, boolean save, boolean append, boolean autoplay, boolean playNext)
|
||||
{
|
||||
MediaPlayerController mediaPlayerController = mediaPlayerControllerLazy.getValue();
|
||||
if (mediaPlayerController != null)
|
||||
{
|
||||
if (!append && !playNext)
|
||||
{
|
||||
mediaPlayerController.clear();
|
||||
}
|
||||
|
||||
mediaPlayerController.download(Collections.singletonList(song), save, false, playNext, false, false);
|
||||
|
||||
if (autoplay)
|
||||
{
|
||||
mediaPlayerController.play(mediaPlayerController.getPlaylistSize() - 1);
|
||||
}
|
||||
|
||||
Util.toast(getContext(), getResources().getQuantityString(R.plurals.select_album_n_songs_added, 1, 1));
|
||||
}
|
||||
}
|
||||
|
||||
private void onVideoSelected(MusicDirectory.Entry entry)
|
||||
{
|
||||
videoPlayer.getValue().playVideo(entry);
|
||||
}
|
||||
|
||||
private void autoplay()
|
||||
{
|
||||
if (!searchResult.getSongs().isEmpty())
|
||||
{
|
||||
onSongSelected(searchResult.getSongs().get(0), false, false, true, false);
|
||||
}
|
||||
else if (!searchResult.getAlbums().isEmpty())
|
||||
{
|
||||
onAlbumSelected(searchResult.getAlbums().get(0), true);
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,174 @@
|
|||
package org.moire.ultrasonic.fragment;
|
||||
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ListView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.navigation.Navigation;
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
|
||||
import org.moire.ultrasonic.R;
|
||||
import org.moire.ultrasonic.domain.Genre;
|
||||
import org.moire.ultrasonic.service.MusicService;
|
||||
import org.moire.ultrasonic.service.MusicServiceFactory;
|
||||
import org.moire.ultrasonic.util.BackgroundTask;
|
||||
import org.moire.ultrasonic.util.CancellationToken;
|
||||
import org.moire.ultrasonic.util.Constants;
|
||||
import org.moire.ultrasonic.util.TabActivityBackgroundTask;
|
||||
import org.moire.ultrasonic.util.Util;
|
||||
import org.moire.ultrasonic.view.GenreAdapter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
public class SelectGenreFragment extends Fragment {
|
||||
|
||||
private SwipeRefreshLayout refreshGenreListView;
|
||||
private ListView genreListView;
|
||||
private View emptyView;
|
||||
private CancellationToken cancellationToken;
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
Util.applyTheme(this.getContext());
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.select_genre, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
cancellationToken = new CancellationToken();
|
||||
refreshGenreListView = view.findViewById(R.id.select_genre_refresh);
|
||||
genreListView = view.findViewById(R.id.select_genre_list);
|
||||
|
||||
refreshGenreListView.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener()
|
||||
{
|
||||
@Override
|
||||
public void onRefresh()
|
||||
{
|
||||
new GetDataTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
}
|
||||
});
|
||||
|
||||
genreListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
Genre genre = (Genre) parent.getItemAtPosition(position);
|
||||
|
||||
if (genre != null)
|
||||
{
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString(Constants.INTENT_EXTRA_NAME_GENRE_NAME, genre.getName());
|
||||
bundle.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_SIZE, Util.getMaxSongs(getContext()));
|
||||
bundle.putInt(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_OFFSET, 0);
|
||||
Navigation.findNavController(getView()).navigate(R.id.selectAlbumFragment, bundle);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
emptyView = view.findViewById(R.id.select_genre_empty);
|
||||
registerForContextMenu(genreListView);
|
||||
|
||||
FragmentTitle.Companion.setTitle(this, R.string.main_genres_title);
|
||||
|
||||
load();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
cancellationToken.cancel();
|
||||
super.onDestroyView();
|
||||
}
|
||||
|
||||
private void refresh()
|
||||
{
|
||||
// TODO: create better restart
|
||||
getView().post(new Runnable() {
|
||||
public void run() {
|
||||
Timber.d("Refresh called...");
|
||||
if (getArguments() == null) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putBoolean(Constants.INTENT_EXTRA_NAME_REFRESH, true);
|
||||
setArguments(bundle);
|
||||
} else {
|
||||
getArguments().putBoolean(Constants.INTENT_EXTRA_NAME_REFRESH, true);
|
||||
}
|
||||
onViewCreated(getView(), null);
|
||||
}
|
||||
});
|
||||
/* finish();
|
||||
Intent intent = getIntent();
|
||||
intent.putExtra(Constants.INTENT_EXTRA_NAME_REFRESH, true);
|
||||
startActivityForResultWithoutTransition(this, intent);
|
||||
|
||||
*/
|
||||
}
|
||||
|
||||
private void load()
|
||||
{
|
||||
BackgroundTask<List<Genre>> task = new TabActivityBackgroundTask<List<Genre>>(getActivity(), true, refreshGenreListView, cancellationToken)
|
||||
{
|
||||
@Override
|
||||
protected List<Genre> doInBackground() throws Throwable
|
||||
{
|
||||
boolean refresh = getArguments() != null && getArguments().getBoolean(Constants.INTENT_EXTRA_NAME_REFRESH, false);
|
||||
MusicService musicService = MusicServiceFactory.getMusicService(getContext());
|
||||
|
||||
List<Genre> genres = new ArrayList<Genre>();
|
||||
|
||||
try
|
||||
{
|
||||
genres = musicService.getGenres(refresh, getContext(), this);
|
||||
}
|
||||
catch (Exception x)
|
||||
{
|
||||
Timber.e(x, "Failed to load genres");
|
||||
}
|
||||
|
||||
return genres;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done(List<Genre> result)
|
||||
{
|
||||
emptyView.setVisibility(result == null || result.isEmpty() ? View.VISIBLE : View.GONE);
|
||||
|
||||
if (result != null)
|
||||
{
|
||||
genreListView.setAdapter(new GenreAdapter(getContext(), result));
|
||||
}
|
||||
}
|
||||
};
|
||||
task.execute();
|
||||
}
|
||||
|
||||
private class GetDataTask extends AsyncTask<Void, Void, String[]>
|
||||
{
|
||||
@Override
|
||||
protected void onPostExecute(String[] result)
|
||||
{
|
||||
super.onPostExecute(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String[] doInBackground(Void... params)
|
||||
{
|
||||
refresh();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,10 +6,18 @@ import android.content.Intent;
|
|||
import android.content.SharedPreferences;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.preference.*;
|
||||
import android.provider.SearchRecentSuggestions;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.preference.CheckBoxPreference;
|
||||
import androidx.preference.EditTextPreference;
|
||||
import androidx.preference.ListPreference;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceCategory;
|
||||
import androidx.preference.PreferenceFragmentCompat;
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
import timber.log.Timber;
|
||||
import android.view.View;
|
||||
|
@ -26,6 +34,7 @@ import org.moire.ultrasonic.log.FileLoggerTree;
|
|||
import org.moire.ultrasonic.provider.SearchSuggestionProvider;
|
||||
import org.moire.ultrasonic.service.Consumer;
|
||||
import org.moire.ultrasonic.service.MediaPlayerController;
|
||||
import org.moire.ultrasonic.subsonic.ImageLoaderProvider;
|
||||
import org.moire.ultrasonic.util.*;
|
||||
|
||||
import java.io.File;
|
||||
|
@ -38,9 +47,10 @@ import static org.moire.ultrasonic.activity.ServerSelectorActivity.SERVER_SELECT
|
|||
/**
|
||||
* Shows main app settings.
|
||||
*/
|
||||
public class SettingsFragment extends PreferenceFragment
|
||||
public class SettingsFragment extends PreferenceFragmentCompat
|
||||
implements SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
|
||||
private Preference addServerPreference;
|
||||
private ListPreference theme;
|
||||
private ListPreference videoPlayer;
|
||||
private ListPreference maxBitrateWifi;
|
||||
|
@ -77,53 +87,60 @@ public class SettingsFragment extends PreferenceFragment
|
|||
private SharedPreferences settings;
|
||||
|
||||
private Lazy<MediaPlayerController> mediaPlayerControllerLazy = inject(MediaPlayerController.class);
|
||||
private Lazy<ImageLoaderProvider> imageLoader = inject(ImageLoaderProvider.class);
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
addPreferencesFromResource(R.xml.settings);
|
||||
|
||||
settings = PreferenceManager.getDefaultSharedPreferences(getActivity());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||
setPreferencesFromResource(R.xml.settings, rootKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
FragmentTitle.Companion.setTitle(this, R.string.menu_settings);
|
||||
|
||||
theme = (ListPreference) findPreference(Constants.PREFERENCES_KEY_THEME);
|
||||
videoPlayer = (ListPreference) findPreference(Constants.PREFERENCES_KEY_VIDEO_PLAYER);
|
||||
maxBitrateWifi = (ListPreference) findPreference(Constants.PREFERENCES_KEY_MAX_BITRATE_WIFI);
|
||||
maxBitrateMobile = (ListPreference) findPreference(Constants.PREFERENCES_KEY_MAX_BITRATE_MOBILE);
|
||||
cacheSize = (ListPreference) findPreference(Constants.PREFERENCES_KEY_CACHE_SIZE);
|
||||
addServerPreference = findPreference(Constants.PREFERENCES_KEY_SERVERS_EDIT);
|
||||
theme = findPreference(Constants.PREFERENCES_KEY_THEME);
|
||||
videoPlayer = findPreference(Constants.PREFERENCES_KEY_VIDEO_PLAYER);
|
||||
maxBitrateWifi = findPreference(Constants.PREFERENCES_KEY_MAX_BITRATE_WIFI);
|
||||
maxBitrateMobile = findPreference(Constants.PREFERENCES_KEY_MAX_BITRATE_MOBILE);
|
||||
cacheSize = findPreference(Constants.PREFERENCES_KEY_CACHE_SIZE);
|
||||
cacheLocation = findPreference(Constants.PREFERENCES_KEY_CACHE_LOCATION);
|
||||
preloadCount = (ListPreference) findPreference(Constants.PREFERENCES_KEY_PRELOAD_COUNT);
|
||||
bufferLength = (ListPreference) findPreference(Constants.PREFERENCES_KEY_BUFFER_LENGTH);
|
||||
incrementTime = (ListPreference) findPreference(Constants.PREFERENCES_KEY_INCREMENT_TIME);
|
||||
networkTimeout = (ListPreference) findPreference(Constants.PREFERENCES_KEY_NETWORK_TIMEOUT);
|
||||
maxAlbums = (ListPreference) findPreference(Constants.PREFERENCES_KEY_MAX_ALBUMS);
|
||||
maxSongs = (ListPreference) findPreference(Constants.PREFERENCES_KEY_MAX_SONGS);
|
||||
maxArtists = (ListPreference) findPreference(Constants.PREFERENCES_KEY_MAX_ARTISTS);
|
||||
defaultArtists = (ListPreference) findPreference(Constants.PREFERENCES_KEY_DEFAULT_ARTISTS);
|
||||
defaultSongs = (ListPreference) findPreference(Constants.PREFERENCES_KEY_DEFAULT_SONGS);
|
||||
defaultAlbums = (ListPreference) findPreference(Constants.PREFERENCES_KEY_DEFAULT_ALBUMS);
|
||||
chatRefreshInterval = (ListPreference) findPreference(Constants.PREFERENCES_KEY_CHAT_REFRESH_INTERVAL);
|
||||
directoryCacheTime = (ListPreference) findPreference(Constants.PREFERENCES_KEY_DIRECTORY_CACHE_TIME);
|
||||
mediaButtonsEnabled = (CheckBoxPreference) findPreference(Constants.PREFERENCES_KEY_MEDIA_BUTTONS);
|
||||
lockScreenEnabled = (CheckBoxPreference) findPreference(Constants.PREFERENCES_KEY_SHOW_LOCK_SCREEN_CONTROLS);
|
||||
sendBluetoothAlbumArt = (CheckBoxPreference) findPreference(Constants.PREFERENCES_KEY_SEND_BLUETOOTH_ALBUM_ART);
|
||||
sendBluetoothNotifications = (CheckBoxPreference) findPreference(Constants.PREFERENCES_KEY_SEND_BLUETOOTH_NOTIFICATIONS);
|
||||
viewRefresh = (ListPreference) findPreference(Constants.PREFERENCES_KEY_VIEW_REFRESH);
|
||||
imageLoaderConcurrency = (ListPreference) findPreference(Constants.PREFERENCES_KEY_IMAGE_LOADER_CONCURRENCY);
|
||||
sharingDefaultDescription = (EditTextPreference) findPreference(Constants.PREFERENCES_KEY_DEFAULT_SHARE_DESCRIPTION);
|
||||
sharingDefaultGreeting = (EditTextPreference) findPreference(Constants.PREFERENCES_KEY_DEFAULT_SHARE_GREETING);
|
||||
sharingDefaultExpiration = (TimeSpanPreference) findPreference(Constants.PREFERENCES_KEY_DEFAULT_SHARE_EXPIRATION);
|
||||
serversCategory = (PreferenceCategory) findPreference(Constants.PREFERENCES_KEY_SERVERS_KEY);
|
||||
preloadCount = findPreference(Constants.PREFERENCES_KEY_PRELOAD_COUNT);
|
||||
bufferLength = findPreference(Constants.PREFERENCES_KEY_BUFFER_LENGTH);
|
||||
incrementTime = findPreference(Constants.PREFERENCES_KEY_INCREMENT_TIME);
|
||||
networkTimeout = findPreference(Constants.PREFERENCES_KEY_NETWORK_TIMEOUT);
|
||||
maxAlbums = findPreference(Constants.PREFERENCES_KEY_MAX_ALBUMS);
|
||||
maxSongs = findPreference(Constants.PREFERENCES_KEY_MAX_SONGS);
|
||||
maxArtists = findPreference(Constants.PREFERENCES_KEY_MAX_ARTISTS);
|
||||
defaultArtists = findPreference(Constants.PREFERENCES_KEY_DEFAULT_ARTISTS);
|
||||
defaultSongs = findPreference(Constants.PREFERENCES_KEY_DEFAULT_SONGS);
|
||||
defaultAlbums = findPreference(Constants.PREFERENCES_KEY_DEFAULT_ALBUMS);
|
||||
chatRefreshInterval = findPreference(Constants.PREFERENCES_KEY_CHAT_REFRESH_INTERVAL);
|
||||
directoryCacheTime = findPreference(Constants.PREFERENCES_KEY_DIRECTORY_CACHE_TIME);
|
||||
mediaButtonsEnabled = findPreference(Constants.PREFERENCES_KEY_MEDIA_BUTTONS);
|
||||
lockScreenEnabled = findPreference(Constants.PREFERENCES_KEY_SHOW_LOCK_SCREEN_CONTROLS);
|
||||
sendBluetoothAlbumArt = findPreference(Constants.PREFERENCES_KEY_SEND_BLUETOOTH_ALBUM_ART);
|
||||
sendBluetoothNotifications = findPreference(Constants.PREFERENCES_KEY_SEND_BLUETOOTH_NOTIFICATIONS);
|
||||
viewRefresh = findPreference(Constants.PREFERENCES_KEY_VIEW_REFRESH);
|
||||
imageLoaderConcurrency = findPreference(Constants.PREFERENCES_KEY_IMAGE_LOADER_CONCURRENCY);
|
||||
sharingDefaultDescription = findPreference(Constants.PREFERENCES_KEY_DEFAULT_SHARE_DESCRIPTION);
|
||||
sharingDefaultGreeting = findPreference(Constants.PREFERENCES_KEY_DEFAULT_SHARE_GREETING);
|
||||
sharingDefaultExpiration = findPreference(Constants.PREFERENCES_KEY_DEFAULT_SHARE_EXPIRATION);
|
||||
serversCategory = findPreference(Constants.PREFERENCES_KEY_SERVERS_KEY);
|
||||
resumeOnBluetoothDevice = findPreference(Constants.PREFERENCES_KEY_RESUME_ON_BLUETOOTH_DEVICE);
|
||||
pauseOnBluetoothDevice = findPreference(Constants.PREFERENCES_KEY_PAUSE_ON_BLUETOOTH_DEVICE);
|
||||
debugLogToFile = (CheckBoxPreference) findPreference(Constants.PREFERENCES_KEY_DEBUG_LOG_TO_FILE);
|
||||
showArtistPicture = (CheckBoxPreference) findPreference(Constants.PREFERENCES_KEY_SHOW_ARTIST_PICTURE);
|
||||
debugLogToFile = findPreference(Constants.PREFERENCES_KEY_DEBUG_LOG_TO_FILE);
|
||||
showArtistPicture = findPreference(Constants.PREFERENCES_KEY_SHOW_ARTIST_PICTURE);
|
||||
|
||||
setupServersCategory();
|
||||
sharingDefaultGreeting.setText(Util.getShareGreeting(getActivity()));
|
||||
setupClearSearchPreference();
|
||||
setupGaplessControlSettingsV14();
|
||||
|
@ -133,7 +150,7 @@ public class SettingsFragment extends PreferenceFragment
|
|||
|
||||
// After API26 foreground services must be used for music playback, and they must have a notification
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
PreferenceCategory notificationsCategory = (PreferenceCategory) findPreference(Constants.PREFERENCES_KEY_CATEGORY_NOTIFICATIONS);
|
||||
PreferenceCategory notificationsCategory = findPreference(Constants.PREFERENCES_KEY_CATEGORY_NOTIFICATIONS);
|
||||
notificationsCategory.removePreference(findPreference(Constants.PREFERENCES_KEY_SHOW_NOTIFICATION));
|
||||
notificationsCategory.removePreference(findPreference(Constants.PREFERENCES_KEY_ALWAYS_SHOW_NOTIFICATION));
|
||||
}
|
||||
|
@ -149,8 +166,6 @@ public class SettingsFragment extends PreferenceFragment
|
|||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
setupServersCategory();
|
||||
SharedPreferences preferences = Util.getPreferences(getActivity());
|
||||
preferences.registerOnSharedPreferenceChangeListener(this);
|
||||
}
|
||||
|
@ -158,7 +173,6 @@ public class SettingsFragment extends PreferenceFragment
|
|||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
|
||||
SharedPreferences prefs = Util.getPreferences(getActivity());
|
||||
prefs.unregisterOnSharedPreferenceChangeListener(this);
|
||||
}
|
||||
|
@ -184,6 +198,29 @@ public class SettingsFragment extends PreferenceFragment
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisplayPreferenceDialog(Preference preference)
|
||||
{
|
||||
DialogFragment dialogFragment = null;
|
||||
if (preference instanceof TimeSpanPreference)
|
||||
{
|
||||
dialogFragment = new TimeSpanPreferenceDialogFragmentCompat();
|
||||
Bundle bundle = new Bundle(1);
|
||||
bundle.putString("key", preference.getKey());
|
||||
dialogFragment.setArguments(bundle);
|
||||
}
|
||||
|
||||
if (dialogFragment != null)
|
||||
{
|
||||
dialogFragment.setTargetFragment(this, 0);
|
||||
dialogFragment.show(this.getFragmentManager(), "android.support.v7.preference.PreferenceFragment.DIALOG");
|
||||
}
|
||||
else
|
||||
{
|
||||
super.onDisplayPreferenceDialog(preference);
|
||||
}
|
||||
}
|
||||
|
||||
private void setupCacheLocationPreference() {
|
||||
cacheLocation.setSummary(settings.getString(Constants.PREFERENCES_KEY_CACHE_LOCATION,
|
||||
FileUtil.getDefaultMusicDirectory(getActivity()).getPath()));
|
||||
|
@ -202,9 +239,9 @@ public class SettingsFragment extends PreferenceFragment
|
|||
filePickerDialog.setOnFileSelectedListener(new OnFileSelectedListener() {
|
||||
@Override
|
||||
public void onFileSelected(File file, String path) {
|
||||
SharedPreferences.Editor editor = cacheLocation.getEditor();
|
||||
SharedPreferences.Editor editor = cacheLocation.getSharedPreferences().edit();
|
||||
editor.putString(Constants.PREFERENCES_KEY_CACHE_LOCATION, path);
|
||||
editor.commit();
|
||||
editor.apply();
|
||||
|
||||
setCacheLocation(path);
|
||||
}
|
||||
|
@ -234,9 +271,9 @@ public class SettingsFragment extends PreferenceFragment
|
|||
new Consumer<Integer>() {
|
||||
@Override
|
||||
public void accept(Integer choice) {
|
||||
SharedPreferences.Editor editor = resumeOnBluetoothDevice.getEditor();
|
||||
SharedPreferences.Editor editor = resumeOnBluetoothDevice.getSharedPreferences().edit();
|
||||
editor.putInt(Constants.PREFERENCES_KEY_RESUME_ON_BLUETOOTH_DEVICE, choice);
|
||||
editor.commit();
|
||||
editor.apply();
|
||||
resumeOnBluetoothDevice.setSummary(bluetoothDevicePreferenceToString(choice));
|
||||
}
|
||||
});
|
||||
|
@ -253,9 +290,9 @@ public class SettingsFragment extends PreferenceFragment
|
|||
new Consumer<Integer>() {
|
||||
@Override
|
||||
public void accept(Integer choice) {
|
||||
SharedPreferences.Editor editor = pauseOnBluetoothDevice.getEditor();
|
||||
SharedPreferences.Editor editor = pauseOnBluetoothDevice.getSharedPreferences().edit();
|
||||
editor.putInt(Constants.PREFERENCES_KEY_PAUSE_ON_BLUETOOTH_DEVICE, choice);
|
||||
editor.commit();
|
||||
editor.apply();
|
||||
pauseOnBluetoothDevice.setSummary(bluetoothDevicePreferenceToString(choice));
|
||||
}
|
||||
});
|
||||
|
@ -330,7 +367,7 @@ public class SettingsFragment extends PreferenceFragment
|
|||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object o) {
|
||||
featureStorage.changeFeatureFlag(Feature.NEW_IMAGE_DOWNLOADER, (Boolean) o);
|
||||
((SubsonicTabActivity) getActivity()).clearImageLoader();
|
||||
imageLoader.getValue().clearImageLoader();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
@ -355,9 +392,9 @@ public class SettingsFragment extends PreferenceFragment
|
|||
private void setupGaplessControlSettingsV14() {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
|
||||
PreferenceCategory playbackControlSettings =
|
||||
(PreferenceCategory) findPreference(Constants.PREFERENCES_KEY_PLAYBACK_CONTROL_SETTINGS);
|
||||
findPreference(Constants.PREFERENCES_KEY_PLAYBACK_CONTROL_SETTINGS);
|
||||
CheckBoxPreference gaplessPlaybackEnabled =
|
||||
(CheckBoxPreference) findPreference(Constants.PREFERENCES_KEY_GAPLESS_PLAYBACK);
|
||||
findPreference(Constants.PREFERENCES_KEY_GAPLESS_PLAYBACK);
|
||||
|
||||
if (gaplessPlaybackEnabled != null) {
|
||||
gaplessPlaybackEnabled.setChecked(false);
|
||||
|
@ -371,15 +408,10 @@ public class SettingsFragment extends PreferenceFragment
|
|||
}
|
||||
|
||||
private void setupServersCategory() {
|
||||
final Preference addServerPreference = new Preference(getActivity());
|
||||
addServerPreference.setPersistent(false);
|
||||
addServerPreference.setTitle(getResources().getString(R.string.settings_server_manage_servers));
|
||||
addServerPreference.setEnabled(true);
|
||||
|
||||
// TODO new server management here
|
||||
serversCategory.removeAll();
|
||||
serversCategory.addPreference(addServerPreference);
|
||||
|
||||
addServerPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
|
@ -389,7 +421,6 @@ public class SettingsFragment extends PreferenceFragment
|
|||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private void update() {
|
||||
|
@ -437,15 +468,15 @@ public class SettingsFragment extends PreferenceFragment
|
|||
else showArtistPicture.setEnabled(false);
|
||||
}
|
||||
|
||||
private static void setImageLoaderConcurrency(int concurrency) {
|
||||
private void setImageLoaderConcurrency(int concurrency) {
|
||||
SubsonicTabActivity instance = SubsonicTabActivity.getInstance();
|
||||
|
||||
if (instance != null) {
|
||||
ImageLoader imageLoader = instance.getImageLoader();
|
||||
ImageLoader imageLoaderInstance = imageLoader.getValue().getImageLoader();
|
||||
|
||||
if (imageLoader != null) {
|
||||
imageLoader.stopImageLoader();
|
||||
imageLoader.setConcurrency(concurrency);
|
||||
if (imageLoaderInstance != null) {
|
||||
imageLoaderInstance.stopImageLoader();
|
||||
imageLoaderInstance.setConcurrency(concurrency);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,388 @@
|
|||
package org.moire.ultrasonic.fragment;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableString;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.text.util.Linkify;
|
||||
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.CheckBox;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.navigation.Navigation;
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
|
||||
import org.moire.ultrasonic.R;
|
||||
import org.moire.ultrasonic.api.subsonic.ApiNotSupportedException;
|
||||
import org.moire.ultrasonic.domain.Share;
|
||||
import org.moire.ultrasonic.service.MusicService;
|
||||
import org.moire.ultrasonic.service.MusicServiceFactory;
|
||||
import org.moire.ultrasonic.service.OfflineException;
|
||||
import org.moire.ultrasonic.subsonic.DownloadHandler;
|
||||
import org.moire.ultrasonic.util.BackgroundTask;
|
||||
import org.moire.ultrasonic.util.CancellationToken;
|
||||
import org.moire.ultrasonic.util.Constants;
|
||||
import org.moire.ultrasonic.util.LoadingTask;
|
||||
import org.moire.ultrasonic.util.TabActivityBackgroundTask;
|
||||
import org.moire.ultrasonic.util.TimeSpan;
|
||||
import org.moire.ultrasonic.util.TimeSpanPicker;
|
||||
import org.moire.ultrasonic.util.Util;
|
||||
import org.moire.ultrasonic.view.ShareAdapter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import kotlin.Lazy;
|
||||
import timber.log.Timber;
|
||||
|
||||
import static org.koin.java.KoinJavaComponent.inject;
|
||||
|
||||
public class SharesFragment extends Fragment {
|
||||
|
||||
private SwipeRefreshLayout refreshSharesListView;
|
||||
private ListView sharesListView;
|
||||
private View emptyTextView;
|
||||
private ShareAdapter shareAdapter;
|
||||
|
||||
private final Lazy<DownloadHandler> downloadHandler = inject(DownloadHandler.class);
|
||||
private CancellationToken cancellationToken;
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
Util.applyTheme(this.getContext());
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.select_share, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
cancellationToken = new CancellationToken();
|
||||
|
||||
refreshSharesListView = view.findViewById(R.id.select_share_refresh);
|
||||
sharesListView = view.findViewById(R.id.select_share_list);
|
||||
|
||||
refreshSharesListView.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener()
|
||||
{
|
||||
@Override
|
||||
public void onRefresh()
|
||||
{
|
||||
new GetDataTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
}
|
||||
});
|
||||
|
||||
emptyTextView = view.findViewById(R.id.select_share_empty);
|
||||
sharesListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
Share share = (Share) parent.getItemAtPosition(position);
|
||||
|
||||
if (share == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString(Constants.INTENT_EXTRA_NAME_SHARE_ID, share.getId());
|
||||
bundle.putString(Constants.INTENT_EXTRA_NAME_SHARE_NAME, share.getName());
|
||||
Navigation.findNavController(getView()).navigate(R.id.selectAlbumFragment, bundle);
|
||||
}
|
||||
});
|
||||
registerForContextMenu(sharesListView);
|
||||
|
||||
FragmentTitle.Companion.setTitle(this, R.string.button_bar_shares);
|
||||
|
||||
load();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
cancellationToken.cancel();
|
||||
super.onDestroyView();
|
||||
}
|
||||
|
||||
private void refresh()
|
||||
{
|
||||
// TODO: create better restart
|
||||
getView().post(new Runnable() {
|
||||
public void run() {
|
||||
Timber.d("Refresh called...");
|
||||
if (getArguments() == null) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putBoolean(Constants.INTENT_EXTRA_NAME_REFRESH, true);
|
||||
setArguments(bundle);
|
||||
} else {
|
||||
getArguments().putBoolean(Constants.INTENT_EXTRA_NAME_REFRESH, true);
|
||||
}
|
||||
onViewCreated(getView(), null);
|
||||
}
|
||||
});
|
||||
|
||||
/* finish();
|
||||
Intent intent = new Intent(this, ShareActivity.class);
|
||||
intent.putExtra(Constants.INTENT_EXTRA_NAME_REFRESH, true);
|
||||
startActivityForResultWithoutTransition(this, intent);
|
||||
|
||||
*/
|
||||
}
|
||||
|
||||
private void load()
|
||||
{
|
||||
BackgroundTask<List<Share>> task = new TabActivityBackgroundTask<List<Share>>(getActivity(), true, refreshSharesListView, cancellationToken)
|
||||
{
|
||||
@Override
|
||||
protected List<Share> doInBackground() throws Throwable
|
||||
{
|
||||
MusicService musicService = MusicServiceFactory.getMusicService(getContext());
|
||||
boolean refresh = getArguments() != null && getArguments().getBoolean(Constants.INTENT_EXTRA_NAME_REFRESH, false);
|
||||
return musicService.getShares(refresh, getContext(), this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done(List<Share> result)
|
||||
{
|
||||
sharesListView.setAdapter(shareAdapter = new ShareAdapter(getContext(), result));
|
||||
emptyTextView.setVisibility(result.isEmpty() ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
};
|
||||
task.execute();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo)
|
||||
{
|
||||
super.onCreateContextMenu(menu, view, menuInfo);
|
||||
|
||||
MenuInflater inflater = getActivity().getMenuInflater();
|
||||
inflater.inflate(R.menu.select_share_context, menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onContextItemSelected(MenuItem menuItem)
|
||||
{
|
||||
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo();
|
||||
if (info == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Share share = (Share) sharesListView.getItemAtPosition(info.position);
|
||||
if (share == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (menuItem.getItemId())
|
||||
{
|
||||
case R.id.share_menu_pin:
|
||||
downloadHandler.getValue().downloadShare(this, share.getId(), share.getName(), true, true, false, false, true, false, false);
|
||||
break;
|
||||
case R.id.share_menu_unpin:
|
||||
downloadHandler.getValue().downloadShare(this, share.getId(), share.getName(), false, false, false, false, true, false, true);
|
||||
break;
|
||||
case R.id.share_menu_download:
|
||||
downloadHandler.getValue().downloadShare(this, share.getId(), share.getName(), false, false, false, false, true, false, false);
|
||||
break;
|
||||
case R.id.share_menu_play_now:
|
||||
downloadHandler.getValue().downloadShare(this, share.getId(), share.getName(), false, false, true, false, false, false, false);
|
||||
break;
|
||||
case R.id.share_menu_play_shuffled:
|
||||
downloadHandler.getValue().downloadShare(this, share.getId(), share.getName(), false, false, true, true, false, false, false);
|
||||
break;
|
||||
case R.id.share_menu_delete:
|
||||
deleteShare(share);
|
||||
break;
|
||||
case R.id.share_info:
|
||||
displayShareInfo(share);
|
||||
break;
|
||||
case R.id.share_update_info:
|
||||
updateShareInfo(share);
|
||||
break;
|
||||
default:
|
||||
return super.onContextItemSelected(menuItem);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void deleteShare(final Share share)
|
||||
{
|
||||
new AlertDialog.Builder(getContext()).setIcon(android.R.drawable.ic_dialog_alert).setTitle(R.string.common_confirm).setMessage(getResources().getString(R.string.delete_playlist, share.getName())).setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which)
|
||||
{
|
||||
new LoadingTask<Void>(getActivity(), false, refreshSharesListView)
|
||||
{
|
||||
@Override
|
||||
protected Void doInBackground() throws Throwable
|
||||
{
|
||||
MusicService musicService = MusicServiceFactory.getMusicService(getContext());
|
||||
musicService.deleteShare(share.getId(), getContext(), null);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done(Void result)
|
||||
{
|
||||
shareAdapter.remove(share);
|
||||
shareAdapter.notifyDataSetChanged();
|
||||
Util.toast(getContext(), getResources().getString(R.string.menu_deleted_share, share.getName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void error(Throwable error)
|
||||
{
|
||||
String msg;
|
||||
msg = error instanceof OfflineException || error instanceof ApiNotSupportedException ? getErrorMessage(error) : String.format("%s %s", getResources().getString(R.string.menu_deleted_share_error, share.getName()), getErrorMessage(error));
|
||||
|
||||
Util.toast(getContext(), msg, false);
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
}).setNegativeButton(R.string.common_cancel, null).show();
|
||||
}
|
||||
|
||||
private void displayShareInfo(final Share share)
|
||||
{
|
||||
final TextView textView = new TextView(getContext());
|
||||
textView.setPadding(5, 5, 5, 5);
|
||||
|
||||
final Spannable message = new SpannableString("Owner: " + share.getUsername() +
|
||||
"\nComments: " + ((share.getDescription() == null) ? "" : share.getDescription()) +
|
||||
"\nURL: " + share.getUrl() +
|
||||
"\nEntry Count: " + share.getEntries().size() +
|
||||
"\nVisit Count: " + share.getVisitCount() +
|
||||
((share.getCreated() == null) ? "" : ("\nCreation Date: " + share.getCreated().replace('T', ' '))) +
|
||||
((share.getLastVisited() == null) ? "" : ("\nLast Visited Date: " + share.getLastVisited().replace('T', ' '))) +
|
||||
((share.getExpires() == null) ? "" : ("\nExpiration Date: " + share.getExpires().replace('T', ' '))));
|
||||
|
||||
Linkify.addLinks(message, Linkify.WEB_URLS);
|
||||
textView.setText(message);
|
||||
textView.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
|
||||
new AlertDialog.Builder(getContext()).setTitle("Share Details").setCancelable(true).setIcon(android.R.drawable.ic_dialog_info).setView(textView).show();
|
||||
}
|
||||
|
||||
private void updateShareInfo(final Share share)
|
||||
{
|
||||
View dialogView = getLayoutInflater().inflate(R.layout.share_details, null);
|
||||
if (dialogView == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
final EditText shareDescription = dialogView.findViewById(R.id.share_description);
|
||||
final TimeSpanPicker timeSpanPicker = dialogView.findViewById(R.id.date_picker);
|
||||
|
||||
shareDescription.setText(share.getDescription());
|
||||
|
||||
CheckBox hideDialogCheckBox = dialogView.findViewById(R.id.hide_dialog);
|
||||
CheckBox saveAsDefaultsCheckBox = dialogView.findViewById(R.id.save_as_defaults);
|
||||
CheckBox noExpirationCheckBox = dialogView.findViewById(R.id.timeSpanDisableCheckBox);
|
||||
|
||||
noExpirationCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener()
|
||||
{
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton compoundButton, boolean b)
|
||||
{
|
||||
timeSpanPicker.setEnabled(!b);
|
||||
}
|
||||
});
|
||||
|
||||
noExpirationCheckBox.setChecked(true);
|
||||
|
||||
timeSpanPicker.setTimeSpanDisableText(getResources().getText(R.string.no_expiration));
|
||||
|
||||
hideDialogCheckBox.setVisibility(View.GONE);
|
||||
saveAsDefaultsCheckBox.setVisibility(View.GONE);
|
||||
|
||||
AlertDialog.Builder alertDialog = new AlertDialog.Builder(getContext());
|
||||
|
||||
alertDialog.setIcon(android.R.drawable.ic_dialog_alert);
|
||||
alertDialog.setTitle(R.string.playlist_update_info);
|
||||
alertDialog.setView(dialogView);
|
||||
alertDialog.setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which)
|
||||
{
|
||||
new LoadingTask<Void>(getActivity(), false, refreshSharesListView)
|
||||
{
|
||||
@Override
|
||||
protected Void doInBackground() throws Throwable
|
||||
{
|
||||
long millis = timeSpanPicker.getTimeSpan().getTotalMilliseconds();
|
||||
|
||||
if (millis > 0)
|
||||
{
|
||||
millis = TimeSpan.getCurrentTime().add(millis).getTotalMilliseconds();
|
||||
}
|
||||
|
||||
Editable shareDescriptionText = shareDescription.getText();
|
||||
String description = shareDescriptionText != null ? shareDescriptionText.toString() : null;
|
||||
|
||||
MusicService musicService = MusicServiceFactory.getMusicService(getContext());
|
||||
musicService.updateShare(share.getId(), description, millis, getContext(), null);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done(Void result)
|
||||
{
|
||||
refresh();
|
||||
Util.toast(getContext(), getResources().getString(R.string.playlist_updated_info, share.getName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void error(Throwable error)
|
||||
{
|
||||
String msg;
|
||||
msg = error instanceof OfflineException || error instanceof ApiNotSupportedException ? getErrorMessage(error) : String.format("%s %s", getResources().getString(R.string.playlist_updated_info_error, share.getName()), getErrorMessage(error));
|
||||
|
||||
Util.toast(getContext(), msg, false);
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
});
|
||||
|
||||
alertDialog.setNegativeButton(R.string.common_cancel, null);
|
||||
alertDialog.show();
|
||||
}
|
||||
|
||||
private class GetDataTask extends AsyncTask<Void, Void, String[]>
|
||||
{
|
||||
@Override
|
||||
protected void onPostExecute(String[] result)
|
||||
{
|
||||
super.onPostExecute(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String[] doInBackground(Void... params)
|
||||
{
|
||||
refresh();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -76,6 +76,7 @@ public final class Constants
|
|||
// Preferences keys.
|
||||
public static final String PREFERENCES_KEY_SERVER_INSTANCE = "serverInstanceId";
|
||||
public static final String PREFERENCES_KEY_SERVERS_KEY = "serversKey";
|
||||
public static final String PREFERENCES_KEY_SERVERS_EDIT = "editServers";
|
||||
public static final String PREFERENCES_KEY_INSTALL_TIME = "installTime";
|
||||
public static final String PREFERENCES_KEY_THEME = "theme";
|
||||
public static final String PREFERENCES_KEY_THEME_LIGHT = "light";
|
||||
|
|
|
@ -24,10 +24,13 @@ import android.graphics.BitmapFactory;
|
|||
import android.os.Build;
|
||||
import android.os.Environment;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import kotlin.Lazy;
|
||||
import timber.log.Timber;
|
||||
|
||||
import org.moire.ultrasonic.activity.SubsonicTabActivity;
|
||||
import org.moire.ultrasonic.domain.MusicDirectory;
|
||||
import org.moire.ultrasonic.subsonic.ImageLoaderProvider;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
|
@ -43,6 +46,8 @@ import java.util.SortedSet;
|
|||
import java.util.TreeSet;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static org.koin.java.KoinJavaComponent.inject;
|
||||
|
||||
/**
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
|
@ -55,6 +60,8 @@ public class FileUtil
|
|||
private static final List<String> PLAYLIST_FILE_EXTENSIONS = Collections.singletonList("m3u");
|
||||
private static final Pattern TITLE_WITH_TRACK = Pattern.compile("^\\d\\d-.*");
|
||||
|
||||
private static Lazy<ImageLoaderProvider> imageLoaderProvider = inject(ImageLoaderProvider.class);
|
||||
|
||||
public static File getSongFile(Context context, MusicDirectory.Entry song)
|
||||
{
|
||||
File dir = getAlbumDirectory(context, song);
|
||||
|
@ -154,7 +161,7 @@ public class FileUtil
|
|||
|
||||
if (subsonicTabActivity != null)
|
||||
{
|
||||
imageLoader = subsonicTabActivity.getImageLoader();
|
||||
imageLoader = imageLoaderProvider.getValue().getImageLoader();
|
||||
|
||||
if (imageLoader != null)
|
||||
{
|
||||
|
@ -224,7 +231,7 @@ public class FileUtil
|
|||
|
||||
if (subsonicTabActivity != null)
|
||||
{
|
||||
imageLoader = subsonicTabActivity.getImageLoader();
|
||||
imageLoader = imageLoaderProvider.getValue().getImageLoader();
|
||||
|
||||
if (imageLoader != null)
|
||||
{
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
package org.moire.ultrasonic.util;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Build;
|
||||
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
|
||||
import org.moire.ultrasonic.activity.SubsonicTabActivity;
|
||||
|
||||
/**
|
||||
|
@ -13,29 +16,23 @@ import org.moire.ultrasonic.activity.SubsonicTabActivity;
|
|||
*/
|
||||
public abstract class LoadingTask<T> extends BackgroundTask<T>
|
||||
{
|
||||
private final SubsonicTabActivity tabActivity;
|
||||
private final Activity tabActivity;
|
||||
private final boolean cancellable;
|
||||
private boolean cancelled;
|
||||
private SwipeRefreshLayout swipe;
|
||||
|
||||
public LoadingTask(SubsonicTabActivity activity, final boolean cancellable)
|
||||
public LoadingTask(Activity activity, final boolean cancellable, SwipeRefreshLayout swipe)
|
||||
{
|
||||
super(activity);
|
||||
tabActivity = activity;
|
||||
this.cancellable = cancellable;
|
||||
this.swipe = swipe;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute()
|
||||
{
|
||||
final ProgressDialog loading = ProgressDialog.show(tabActivity, "", "Loading. Please Wait...", true, cancellable, new DialogInterface.OnCancelListener()
|
||||
{
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialog)
|
||||
{
|
||||
cancelled = true;
|
||||
}
|
||||
|
||||
});
|
||||
swipe.setRefreshing(true);
|
||||
|
||||
new Thread()
|
||||
{
|
||||
|
@ -55,7 +52,7 @@ public abstract class LoadingTask<T> extends BackgroundTask<T>
|
|||
@Override
|
||||
public void run()
|
||||
{
|
||||
loading.cancel();
|
||||
swipe.setRefreshing(false);
|
||||
done(result);
|
||||
}
|
||||
});
|
||||
|
@ -72,7 +69,7 @@ public abstract class LoadingTask<T> extends BackgroundTask<T>
|
|||
@Override
|
||||
public void run()
|
||||
{
|
||||
loading.cancel();
|
||||
swipe.setRefreshing(false);
|
||||
error(t);
|
||||
}
|
||||
});
|
||||
|
@ -84,7 +81,9 @@ public abstract class LoadingTask<T> extends BackgroundTask<T>
|
|||
@SuppressLint("NewApi")
|
||||
private boolean isCancelled()
|
||||
{
|
||||
return Build.VERSION.SDK_INT >= 17 ? tabActivity.isDestroyed() || cancelled : cancelled;
|
||||
// TODO: Implement cancelled
|
||||
//return Build.VERSION.SDK_INT >= 17 ? tabActivity.isDestroyed() || cancelled : cancelled;
|
||||
return cancelled;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -95,7 +94,7 @@ public abstract class LoadingTask<T> extends BackgroundTask<T>
|
|||
@Override
|
||||
public void run()
|
||||
{
|
||||
|
||||
// TODO: This seems to be NOOP, can it be removed?
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package org.moire.ultrasonic.util;
|
||||
|
||||
import org.moire.ultrasonic.activity.SubsonicTabActivity;
|
||||
import android.app.Activity;
|
||||
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
|
||||
/**
|
||||
* @author Sindre Mehus
|
||||
|
@ -9,14 +11,25 @@ import org.moire.ultrasonic.activity.SubsonicTabActivity;
|
|||
public abstract class TabActivityBackgroundTask<T> extends BackgroundTask<T>
|
||||
{
|
||||
|
||||
private final SubsonicTabActivity tabActivity;
|
||||
private final boolean changeProgress;
|
||||
private final SwipeRefreshLayout swipe;
|
||||
private CancellationToken cancel;
|
||||
|
||||
public TabActivityBackgroundTask(SubsonicTabActivity activity, boolean changeProgress)
|
||||
// TODO: Try to remove this constructor
|
||||
public TabActivityBackgroundTask(Activity activity, boolean changeProgress)
|
||||
{
|
||||
super(activity);
|
||||
tabActivity = activity;
|
||||
this.changeProgress = changeProgress;
|
||||
this.swipe = null;
|
||||
}
|
||||
|
||||
public TabActivityBackgroundTask(Activity activity, boolean changeProgress,
|
||||
SwipeRefreshLayout swipe, CancellationToken cancel)
|
||||
{
|
||||
super(activity);
|
||||
this.changeProgress = changeProgress;
|
||||
this.swipe = swipe;
|
||||
this.cancel = cancel;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -24,7 +37,7 @@ public abstract class TabActivityBackgroundTask<T> extends BackgroundTask<T>
|
|||
{
|
||||
if (changeProgress)
|
||||
{
|
||||
tabActivity.setProgressVisible(true);
|
||||
if (swipe != null) swipe.setRefreshing(true);
|
||||
}
|
||||
|
||||
new Thread()
|
||||
|
@ -35,7 +48,7 @@ public abstract class TabActivityBackgroundTask<T> extends BackgroundTask<T>
|
|||
try
|
||||
{
|
||||
final T result = doInBackground();
|
||||
if (isCancelled())
|
||||
if (cancel.isCancellationRequested())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -47,7 +60,7 @@ public abstract class TabActivityBackgroundTask<T> extends BackgroundTask<T>
|
|||
{
|
||||
if (changeProgress)
|
||||
{
|
||||
tabActivity.setProgressVisible(false);
|
||||
if (swipe != null) swipe.setRefreshing(false);
|
||||
}
|
||||
|
||||
done(result);
|
||||
|
@ -56,7 +69,7 @@ public abstract class TabActivityBackgroundTask<T> extends BackgroundTask<T>
|
|||
}
|
||||
catch (final Throwable t)
|
||||
{
|
||||
if (isCancelled())
|
||||
if (cancel.isCancellationRequested())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -67,7 +80,7 @@ public abstract class TabActivityBackgroundTask<T> extends BackgroundTask<T>
|
|||
{
|
||||
if (changeProgress)
|
||||
{
|
||||
tabActivity.setProgressVisible(false);
|
||||
if (swipe != null) swipe.setRefreshing(false);
|
||||
}
|
||||
|
||||
error(t);
|
||||
|
@ -78,20 +91,16 @@ public abstract class TabActivityBackgroundTask<T> extends BackgroundTask<T>
|
|||
}.start();
|
||||
}
|
||||
|
||||
private boolean isCancelled()
|
||||
{
|
||||
return tabActivity.getIsDestroyed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateProgress(final String message)
|
||||
{
|
||||
// TODO: Remove
|
||||
getHandler().post(new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
tabActivity.updateProgress(message);
|
||||
//activity.updateProgress(message);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
package org.moire.ultrasonic.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.preference.DialogPreference;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.preference.DialogPreference;
|
||||
|
||||
import org.moire.ultrasonic.R;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
@ -14,9 +15,7 @@ import java.util.regex.Pattern;
|
|||
*/
|
||||
public class TimeSpanPreference extends DialogPreference
|
||||
{
|
||||
private static final Pattern COMPILE = Pattern.compile(":");
|
||||
Context context;
|
||||
TimeSpanPicker picker;
|
||||
|
||||
public TimeSpanPreference(Context context, AttributeSet attrs)
|
||||
{
|
||||
|
@ -27,6 +26,7 @@ public class TimeSpanPreference extends DialogPreference
|
|||
setNegativeButtonText(android.R.string.cancel);
|
||||
|
||||
setDialogIcon(null);
|
||||
|
||||
}
|
||||
|
||||
public String getText()
|
||||
|
@ -40,59 +40,4 @@ public class TimeSpanPreference extends DialogPreference
|
|||
|
||||
return this.context.getResources().getString(R.string.time_span_disabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateDialogView()
|
||||
{
|
||||
picker = new TimeSpanPicker(this.context);
|
||||
picker.setTimeSpanDisableText(this.context.getResources().getString(R.string.no_expiration));
|
||||
|
||||
String persisted = getPersistedString("");
|
||||
|
||||
if (!"".equals(persisted))
|
||||
{
|
||||
String[] split = COMPILE.split(persisted);
|
||||
|
||||
if (split.length == 2)
|
||||
{
|
||||
String amount = split[0];
|
||||
|
||||
if ("0".equals(amount) || "".equals(amount))
|
||||
{
|
||||
picker.setTimeSpanDisableCheckboxChecked(true);
|
||||
}
|
||||
|
||||
picker.setTimeSpanAmount(amount);
|
||||
picker.setTimeSpanType(split[1]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
picker.setTimeSpanDisableCheckboxChecked(true);
|
||||
}
|
||||
|
||||
return picker;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDialogClosed(boolean positiveResult)
|
||||
{
|
||||
super.onDialogClosed(positiveResult);
|
||||
|
||||
String persisted = "";
|
||||
|
||||
if (picker.getTimeSpanEnabled())
|
||||
{
|
||||
int tsAmount = picker.getTimeSpanAmount();
|
||||
|
||||
if (tsAmount > 0)
|
||||
{
|
||||
String tsType = picker.getTimeSpanType();
|
||||
|
||||
persisted = String.format("%d:%s", tsAmount, tsType);
|
||||
}
|
||||
}
|
||||
|
||||
persistString(persisted);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
package org.moire.ultrasonic.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.preference.DialogPreference;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceDialogFragmentCompat;
|
||||
|
||||
import org.moire.ultrasonic.R;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Created by Joshua Bahnsen on 12/22/13.
|
||||
*/
|
||||
public class TimeSpanPreferenceDialogFragmentCompat extends PreferenceDialogFragmentCompat implements DialogPreference.TargetFragment
|
||||
{
|
||||
private static final Pattern COMPILE = Pattern.compile(":");
|
||||
Context context;
|
||||
TimeSpanPicker picker;
|
||||
|
||||
@Override
|
||||
protected View onCreateDialogView(Context context) {
|
||||
picker = new TimeSpanPicker(context);
|
||||
this.context = context;
|
||||
|
||||
picker.setTimeSpanDisableText(this.context.getResources().getString(R.string.no_expiration));
|
||||
|
||||
Preference preference = getPreference();
|
||||
String persisted = preference.getSharedPreferences().getString(preference.getKey(), "");
|
||||
|
||||
if (!"".equals(persisted))
|
||||
{
|
||||
String[] split = COMPILE.split(persisted);
|
||||
|
||||
if (split.length == 2)
|
||||
{
|
||||
String amount = split[0];
|
||||
|
||||
if ("0".equals(amount) || "".equals(amount))
|
||||
{
|
||||
picker.setTimeSpanDisableCheckboxChecked(true);
|
||||
}
|
||||
|
||||
picker.setTimeSpanAmount(amount);
|
||||
picker.setTimeSpanType(split[1]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
picker.setTimeSpanDisableCheckboxChecked(true);
|
||||
}
|
||||
|
||||
return picker;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onDialogClosed(boolean positiveResult)
|
||||
{
|
||||
String persisted = "";
|
||||
|
||||
if (picker.getTimeSpanEnabled())
|
||||
{
|
||||
int tsAmount = picker.getTimeSpanAmount();
|
||||
|
||||
if (tsAmount > 0)
|
||||
{
|
||||
String tsType = picker.getTimeSpanType();
|
||||
|
||||
persisted = String.format("%d:%s", tsAmount, tsType);
|
||||
}
|
||||
}
|
||||
|
||||
Preference preference = getPreference();
|
||||
preference.getSharedPreferences().edit().putString(preference.getKey(), persisted).apply();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Preference findPreference(@NonNull CharSequence key) {
|
||||
return getPreference();
|
||||
}
|
||||
}
|
|
@ -53,7 +53,6 @@ import androidx.annotation.ColorInt;
|
|||
import org.moire.ultrasonic.R;
|
||||
import org.moire.ultrasonic.activity.DownloadActivity;
|
||||
import org.moire.ultrasonic.activity.MainActivity;
|
||||
import org.moire.ultrasonic.activity.SettingsActivity;
|
||||
import org.moire.ultrasonic.data.ActiveServerProvider;
|
||||
import org.moire.ultrasonic.domain.*;
|
||||
import org.moire.ultrasonic.domain.MusicDirectory.Entry;
|
||||
|
@ -604,19 +603,6 @@ public class Util
|
|||
showDialog(context, android.R.drawable.ic_dialog_info, titleId, messageId);
|
||||
}
|
||||
|
||||
public static void showWelcomeDialog(final Context context, final MainActivity activity, int titleId, int messageId)
|
||||
{
|
||||
new AlertDialog.Builder(context).setIcon(android.R.drawable.ic_dialog_info).setTitle(titleId).setMessage(messageId).setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int i)
|
||||
{
|
||||
dialog.dismiss();
|
||||
activity.startActivityForResultWithoutTransition(activity, SettingsActivity.class);
|
||||
}
|
||||
}).show();
|
||||
}
|
||||
|
||||
private static void showDialog(Context context, int icon, int titleId, int messageId)
|
||||
{
|
||||
new AlertDialog.Builder(context).setIcon(icon).setTitle(titleId).setMessage(messageId).setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener()
|
||||
|
|
|
@ -20,6 +20,7 @@ package org.moire.ultrasonic.util;
|
|||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
|
@ -38,27 +39,27 @@ public enum VideoPlayerType
|
|||
MX("mx")
|
||||
{
|
||||
@Override
|
||||
public void playVideo(final Activity activity, MusicDirectory.Entry entry) throws Exception
|
||||
public void playVideo(final Context context, MusicDirectory.Entry entry) throws Exception
|
||||
{
|
||||
|
||||
// Check if MX Player is installed.
|
||||
boolean installedAd = Util.isPackageInstalled(activity, PACKAGE_NAME_MX_AD);
|
||||
boolean installedPro = Util.isPackageInstalled(activity, PACKAGE_NAME_MX_PRO);
|
||||
boolean installedAd = Util.isPackageInstalled(context, PACKAGE_NAME_MX_AD);
|
||||
boolean installedPro = Util.isPackageInstalled(context, PACKAGE_NAME_MX_PRO);
|
||||
|
||||
if (!installedAd && !installedPro)
|
||||
{
|
||||
new AlertDialog.Builder(activity).setMessage(R.string.video_get_mx_player_text).setPositiveButton(R.string.video_get_mx_player_button, new DialogInterface.OnClickListener()
|
||||
new AlertDialog.Builder(context).setMessage(R.string.video_get_mx_player_text).setPositiveButton(R.string.video_get_mx_player_button, new DialogInterface.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int i)
|
||||
{
|
||||
try
|
||||
{
|
||||
activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(String.format("market://details?id=%s", PACKAGE_NAME_MX_AD))));
|
||||
context.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(String.format("market://details?id=%s", PACKAGE_NAME_MX_AD))));
|
||||
}
|
||||
catch (android.content.ActivityNotFoundException x)
|
||||
{
|
||||
activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(String.format("http://play.google.com/store/apps/details?id=%s", PACKAGE_NAME_MX_AD))));
|
||||
context.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(String.format("http://play.google.com/store/apps/details?id=%s", PACKAGE_NAME_MX_AD))));
|
||||
}
|
||||
|
||||
dialog.dismiss();
|
||||
|
@ -79,8 +80,8 @@ public enum VideoPlayerType
|
|||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setPackage(installedPro ? PACKAGE_NAME_MX_PRO : PACKAGE_NAME_MX_AD);
|
||||
intent.putExtra("title", entry.getTitle());
|
||||
intent.setDataAndType(Uri.parse(MusicServiceFactory.getMusicService(activity).getVideoUrl(activity, entry.getId(), false)), "video/*");
|
||||
activity.startActivity(intent);
|
||||
intent.setDataAndType(Uri.parse(MusicServiceFactory.getMusicService(context).getVideoUrl(context, entry.getId(), false)), "video/*");
|
||||
context.startActivity(intent);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -88,22 +89,22 @@ public enum VideoPlayerType
|
|||
FLASH("flash")
|
||||
{
|
||||
@Override
|
||||
public void playVideo(Activity activity, MusicDirectory.Entry entry) throws Exception
|
||||
public void playVideo(Context context, MusicDirectory.Entry entry) throws Exception
|
||||
{
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setData(Uri.parse(MusicServiceFactory.getMusicService(activity).getVideoUrl(activity, entry.getId(), true)));
|
||||
activity.startActivity(intent);
|
||||
intent.setData(Uri.parse(MusicServiceFactory.getMusicService(context).getVideoUrl(context, entry.getId(), true)));
|
||||
context.startActivity(intent);
|
||||
}
|
||||
},
|
||||
|
||||
DEFAULT("default")
|
||||
{
|
||||
@Override
|
||||
public void playVideo(Activity activity, MusicDirectory.Entry entry) throws Exception
|
||||
public void playVideo(Context context, MusicDirectory.Entry entry) throws Exception
|
||||
{
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setDataAndType(Uri.parse(MusicServiceFactory.getMusicService(activity).getVideoUrl(activity, entry.getId(), false)), "video/*");
|
||||
activity.startActivity(intent);
|
||||
intent.setDataAndType(Uri.parse(MusicServiceFactory.getMusicService(context).getVideoUrl(context, entry.getId(), false)), "video/*");
|
||||
context.startActivity(intent);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -131,7 +132,7 @@ public enum VideoPlayerType
|
|||
return null;
|
||||
}
|
||||
|
||||
public abstract void playVideo(Activity activity, MusicDirectory.Entry entry) throws Exception;
|
||||
public abstract void playVideo(Context context, MusicDirectory.Entry entry) throws Exception;
|
||||
|
||||
private static final String PACKAGE_NAME_MX_AD = "com.mxtech.videoplayer.ad";
|
||||
private static final String PACKAGE_NAME_MX_PRO = "com.mxtech.videoplayer.pro";
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package org.moire.ultrasonic.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.text.util.Linkify;
|
||||
import android.view.LayoutInflater;
|
||||
|
@ -9,11 +10,10 @@ import android.widget.ArrayAdapter;
|
|||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import org.moire.ultrasonic.R;
|
||||
import org.moire.ultrasonic.activity.SubsonicTabActivity;
|
||||
import org.moire.ultrasonic.data.ActiveServerProvider;
|
||||
import org.moire.ultrasonic.domain.ChatMessage;
|
||||
import org.moire.ultrasonic.subsonic.ImageLoaderProvider;
|
||||
import org.moire.ultrasonic.util.ImageLoader;
|
||||
import org.moire.ultrasonic.util.Util;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.util.Date;
|
||||
|
@ -26,18 +26,19 @@ import static org.koin.java.KoinJavaComponent.inject;
|
|||
|
||||
public class ChatAdapter extends ArrayAdapter<ChatMessage>
|
||||
{
|
||||
private final SubsonicTabActivity activity;
|
||||
private final Context context;
|
||||
private List<ChatMessage> messages;
|
||||
|
||||
private static final String phoneRegex = "1?\\W*([2-9][0-8][0-9])\\W*([2-9][0-9]{2})\\W*([0-9]{4})";
|
||||
private static final Pattern phoneMatcher = Pattern.compile(phoneRegex);
|
||||
|
||||
private Lazy<ActiveServerProvider> activeServerProvider = inject(ActiveServerProvider.class);
|
||||
private Lazy<ImageLoaderProvider> imageLoader = inject(ImageLoaderProvider.class);
|
||||
|
||||
public ChatAdapter(SubsonicTabActivity activity, List<ChatMessage> messages)
|
||||
public ChatAdapter(Context context, List<ChatMessage> messages)
|
||||
{
|
||||
super(activity, R.layout.chat_item, messages);
|
||||
this.activity = activity;
|
||||
super(context, R.layout.chat_item, messages);
|
||||
this.context = context;
|
||||
this.messages = messages;
|
||||
}
|
||||
|
||||
|
@ -91,14 +92,14 @@ public class ChatAdapter extends ArrayAdapter<ChatMessage>
|
|||
|
||||
holder.chatMessage = message;
|
||||
|
||||
DateFormat timeFormat = android.text.format.DateFormat.getTimeFormat(activity);
|
||||
DateFormat timeFormat = android.text.format.DateFormat.getTimeFormat(context);
|
||||
String messageTimeFormatted = String.format("[%s]", timeFormat.format(messageTime));
|
||||
|
||||
ImageLoader imageLoader = activity.getImageLoader();
|
||||
ImageLoader imageLoaderInstance = imageLoader.getValue().getImageLoader();
|
||||
|
||||
if (imageLoader != null)
|
||||
if (imageLoaderInstance != null)
|
||||
{
|
||||
imageLoader.loadAvatarImage(holder.avatar, messageUser, false, holder.avatar.getWidth(), false, true);
|
||||
imageLoaderInstance.loadAvatarImage(holder.avatar, messageUser, false, holder.avatar.getWidth(), false, true);
|
||||
}
|
||||
|
||||
holder.username.setText(messageUser);
|
||||
|
@ -110,7 +111,7 @@ public class ChatAdapter extends ArrayAdapter<ChatMessage>
|
|||
|
||||
private View inflateView(int layout, ViewGroup parent)
|
||||
{
|
||||
return LayoutInflater.from(activity).inflate(layout, parent, false);
|
||||
return LayoutInflater.from(context).inflate(layout, parent, false);
|
||||
}
|
||||
|
||||
private static ViewHolder createViewHolder(int layout, View convertView)
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
*/
|
||||
package org.moire.ultrasonic.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
|
@ -25,7 +26,7 @@ import android.widget.CheckedTextView;
|
|||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
import org.moire.ultrasonic.activity.SubsonicTabActivity;
|
||||
|
||||
import org.moire.ultrasonic.domain.MusicDirectory.Entry;
|
||||
import org.moire.ultrasonic.util.ImageLoader;
|
||||
|
||||
|
@ -36,15 +37,15 @@ import java.util.List;
|
|||
*/
|
||||
public class EntryAdapter extends ArrayAdapter<Entry>
|
||||
{
|
||||
private final SubsonicTabActivity activity;
|
||||
private final Context context;
|
||||
private final ImageLoader imageLoader;
|
||||
private final boolean checkable;
|
||||
|
||||
public EntryAdapter(SubsonicTabActivity activity, ImageLoader imageLoader, List<Entry> entries, boolean checkable)
|
||||
public EntryAdapter(Context context, ImageLoader imageLoader, List<Entry> entries, boolean checkable)
|
||||
{
|
||||
super(activity, android.R.layout.simple_list_item_1, entries);
|
||||
super(context, android.R.layout.simple_list_item_1, entries);
|
||||
|
||||
this.activity = activity;
|
||||
this.context = context;
|
||||
this.imageLoader = imageLoader;
|
||||
this.checkable = checkable;
|
||||
}
|
||||
|
@ -75,7 +76,7 @@ public class EntryAdapter extends ArrayAdapter<Entry>
|
|||
}
|
||||
else
|
||||
{
|
||||
view = new AlbumView(activity, imageLoader);
|
||||
view = new AlbumView(context, imageLoader);
|
||||
view.setLayout();
|
||||
}
|
||||
|
||||
|
@ -104,7 +105,7 @@ public class EntryAdapter extends ArrayAdapter<Entry>
|
|||
}
|
||||
else
|
||||
{
|
||||
view = new SongView(activity);
|
||||
view = new SongView(context);
|
||||
view.setLayout(entry);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
package org.moire.ultrasonic.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.moire.ultrasonic.R;
|
||||
import org.moire.ultrasonic.activity.SubsonicTabActivity;
|
||||
import org.moire.ultrasonic.domain.Playlist;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
@ -20,12 +20,12 @@ import java.util.List;
|
|||
public class PlaylistAdapter extends ArrayAdapter<Playlist>
|
||||
{
|
||||
|
||||
private final SubsonicTabActivity activity;
|
||||
private final Context context;
|
||||
|
||||
public PlaylistAdapter(SubsonicTabActivity activity, List<Playlist> Playlists)
|
||||
public PlaylistAdapter(Context context, List<Playlist> Playlists)
|
||||
{
|
||||
super(activity, R.layout.playlist_list_item, Playlists);
|
||||
this.activity = activity;
|
||||
super(context, R.layout.playlist_list_item, Playlists);
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -44,7 +44,7 @@ public class PlaylistAdapter extends ArrayAdapter<Playlist>
|
|||
}
|
||||
else
|
||||
{
|
||||
view = new PlaylistView(activity);
|
||||
view = new PlaylistView(context);
|
||||
view.setLayout();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
package org.moire.ultrasonic.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.moire.ultrasonic.R;
|
||||
import org.moire.ultrasonic.activity.SubsonicTabActivity;
|
||||
import org.moire.ultrasonic.domain.Share;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
@ -20,12 +20,12 @@ import java.util.List;
|
|||
public class ShareAdapter extends ArrayAdapter<Share>
|
||||
{
|
||||
|
||||
private final SubsonicTabActivity activity;
|
||||
private final Context context;
|
||||
|
||||
public ShareAdapter(SubsonicTabActivity activity, List<Share> Shares)
|
||||
public ShareAdapter(Context context, List<Share> Shares)
|
||||
{
|
||||
super(activity, R.layout.share_list_item, Shares);
|
||||
this.activity = activity;
|
||||
super(context, R.layout.share_list_item, Shares);
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -44,7 +44,7 @@ public class ShareAdapter extends ArrayAdapter<Share>
|
|||
}
|
||||
else
|
||||
{
|
||||
view = new ShareView(activity);
|
||||
view = new ShareView(context);
|
||||
view.setLayout();
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,161 @@
|
|||
package org.moire.ultrasonic.activity
|
||||
|
||||
import android.app.AlertDialog
|
||||
import android.content.res.Resources
|
||||
import android.os.Bundle
|
||||
import android.preference.PreferenceManager
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.drawerlayout.widget.DrawerLayout
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.findNavController
|
||||
import androidx.navigation.fragment.NavHostFragment
|
||||
import androidx.navigation.ui.AppBarConfiguration
|
||||
import androidx.navigation.ui.navigateUp
|
||||
import androidx.navigation.ui.onNavDestinationSelected
|
||||
import androidx.navigation.ui.setupActionBarWithNavController
|
||||
import androidx.navigation.ui.setupWithNavController
|
||||
import com.google.android.material.navigation.NavigationView
|
||||
import org.koin.android.ext.android.inject
|
||||
import org.koin.android.viewmodel.ext.android.viewModel
|
||||
import org.moire.ultrasonic.R
|
||||
import org.moire.ultrasonic.service.MediaPlayerController
|
||||
import org.moire.ultrasonic.service.MediaPlayerLifecycleSupport
|
||||
import org.moire.ultrasonic.subsonic.ImageLoaderProvider
|
||||
import org.moire.ultrasonic.util.Constants
|
||||
import org.moire.ultrasonic.util.FileUtil
|
||||
import org.moire.ultrasonic.util.Util
|
||||
import timber.log.Timber
|
||||
|
||||
|
||||
/**
|
||||
* A simple activity demonstrating use of a NavHostFragment with a navigation drawer.
|
||||
*/
|
||||
class NavigationActivity : AppCompatActivity() {
|
||||
private lateinit var appBarConfiguration : AppBarConfiguration
|
||||
private val serverSettingsModel: ServerSettingsModel by viewModel()
|
||||
private val lifecycleSupport: MediaPlayerLifecycleSupport by inject()
|
||||
private val mediaPlayerController: MediaPlayerController by inject()
|
||||
private val imageLoaderProvider: ImageLoaderProvider by inject()
|
||||
|
||||
private var infoDialogDisplayed = false
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.navigation_activity)
|
||||
|
||||
val toolbar = findViewById<Toolbar>(R.id.toolbar)
|
||||
setSupportActionBar(toolbar)
|
||||
|
||||
val host: NavHostFragment = supportFragmentManager
|
||||
.findFragmentById(R.id.nav_host_fragment) as NavHostFragment? ?: return
|
||||
|
||||
val navController = host.navController
|
||||
|
||||
val drawerLayout : DrawerLayout? = findViewById(R.id.drawer_layout)
|
||||
appBarConfiguration = AppBarConfiguration(
|
||||
setOf(R.id.mainFragment, R.id.selectArtistFragment, R.id.searchFragment,
|
||||
R.id.playlistsFragment, R.id.sharesFragment, R.id.bookmarksFragment,
|
||||
R.id.chatFragment, R.id.podcastFragment, R.id.settingsFragment,
|
||||
R.id.aboutFragment),
|
||||
drawerLayout)
|
||||
|
||||
setupActionBar(navController, appBarConfiguration)
|
||||
|
||||
setupNavigationMenu(navController)
|
||||
|
||||
navController.addOnDestinationChangedListener { _, destination, _ ->
|
||||
val dest: String = try {
|
||||
resources.getResourceName(destination.id)
|
||||
} catch (e: Resources.NotFoundException) {
|
||||
Integer.toString(destination.id)
|
||||
}
|
||||
Timber.d("Navigated to $dest")
|
||||
}
|
||||
|
||||
// Determine first run and migrate server settings to DB as early as possible
|
||||
var showWelcomeScreen = Util.isFirstRun(this)
|
||||
val areServersMigrated: Boolean = serverSettingsModel.migrateFromPreferences()
|
||||
|
||||
// If there are any servers in the DB, do not show the welcome screen
|
||||
showWelcomeScreen = showWelcomeScreen and !areServersMigrated
|
||||
|
||||
loadSettings()
|
||||
showInfoDialog(showWelcomeScreen)
|
||||
}
|
||||
|
||||
private fun setupNavigationMenu(navController: NavController) {
|
||||
val sideNavView = findViewById<NavigationView>(R.id.nav_view)
|
||||
sideNavView?.setupWithNavController(navController)
|
||||
|
||||
// The exit menu is handled here manually
|
||||
val exitItem: MenuItem = sideNavView.menu.findItem(R.id.menu_exit)
|
||||
exitItem.setOnMenuItemClickListener { item ->
|
||||
if (item.itemId == R.id.menu_exit) {
|
||||
setResult(Constants.RESULT_CLOSE_ALL)
|
||||
mediaPlayerController.stopJukeboxService()
|
||||
imageLoaderProvider.getImageLoader().stopImageLoader()
|
||||
finish()
|
||||
exit()
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupActionBar(navController: NavController, appBarConfig: AppBarConfiguration) {
|
||||
setupActionBarWithNavController(navController, appBarConfig)
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
val retValue = super.onCreateOptionsMenu(menu)
|
||||
val navigationView = findViewById<NavigationView>(R.id.nav_view)
|
||||
if (navigationView == null) {
|
||||
menuInflater.inflate(R.menu.navigation, menu)
|
||||
return true
|
||||
}
|
||||
return retValue
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
return item.onNavDestinationSelected(findNavController(R.id.nav_host_fragment))
|
||||
|| super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
override fun onSupportNavigateUp(): Boolean {
|
||||
return findNavController(R.id.nav_host_fragment).navigateUp(appBarConfiguration)
|
||||
}
|
||||
|
||||
private fun loadSettings() {
|
||||
PreferenceManager.setDefaultValues(this, R.xml.settings, false)
|
||||
val preferences = Util.getPreferences(this)
|
||||
if (!preferences.contains(Constants.PREFERENCES_KEY_CACHE_LOCATION)) {
|
||||
val editor = preferences.edit()
|
||||
editor.putString(Constants.PREFERENCES_KEY_CACHE_LOCATION, FileUtil.getDefaultMusicDirectory(this).path)
|
||||
editor.apply()
|
||||
}
|
||||
}
|
||||
|
||||
private fun exit() {
|
||||
lifecycleSupport.onDestroy()
|
||||
Util.unregisterMediaButtonEventReceiver(this, false)
|
||||
finish()
|
||||
}
|
||||
|
||||
private fun showInfoDialog(show: Boolean) {
|
||||
if (!infoDialogDisplayed) {
|
||||
infoDialogDisplayed = true
|
||||
if (show) {
|
||||
AlertDialog.Builder(this)
|
||||
.setIcon(android.R.drawable.ic_dialog_info)
|
||||
.setTitle(R.string.main_welcome_title)
|
||||
.setMessage(R.string.main_welcome_text)
|
||||
.setPositiveButton(R.string.common_ok) { dialog, i ->
|
||||
dialog.dismiss()
|
||||
findNavController(R.id.nav_host_fragment).navigate(R.id.settingsFragment)
|
||||
}.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,6 +9,7 @@ import org.moire.ultrasonic.activity.ServerSettingsModel
|
|||
import org.moire.ultrasonic.data.ActiveServerProvider
|
||||
import org.moire.ultrasonic.data.AppDatabase
|
||||
import org.moire.ultrasonic.data.MIGRATION_1_2
|
||||
import org.moire.ultrasonic.subsonic.ImageLoaderProvider
|
||||
import org.moire.ultrasonic.util.Util
|
||||
|
||||
const val SP_NAME = "Default_SP"
|
||||
|
@ -32,4 +33,6 @@ val appPermanentStorage = module {
|
|||
viewModel { ServerSettingsModel(get(), get(), androidContext()) }
|
||||
|
||||
single { ActiveServerProvider(get(), androidContext()) }
|
||||
|
||||
single { ImageLoaderProvider(androidContext()) }
|
||||
}
|
||||
|
|
|
@ -20,6 +20,10 @@ import org.moire.ultrasonic.service.CachedMusicService
|
|||
import org.moire.ultrasonic.service.MusicService
|
||||
import org.moire.ultrasonic.service.OfflineMusicService
|
||||
import org.moire.ultrasonic.service.RESTMusicService
|
||||
import org.moire.ultrasonic.subsonic.DownloadHandler
|
||||
import org.moire.ultrasonic.subsonic.NetworkAndStorageChecker
|
||||
import org.moire.ultrasonic.subsonic.ShareHandler
|
||||
import org.moire.ultrasonic.subsonic.VideoPlayer
|
||||
import org.moire.ultrasonic.subsonic.loader.image.SubsonicImageLoader
|
||||
import org.moire.ultrasonic.util.Constants
|
||||
|
||||
|
@ -75,4 +79,9 @@ val musicServiceModule = module {
|
|||
single { SubsonicImageLoader(androidContext(), get()) }
|
||||
|
||||
viewModel { ArtistListModel(get(), androidContext()) }
|
||||
|
||||
single { DownloadHandler(get(), get()) }
|
||||
single { NetworkAndStorageChecker(androidContext()) }
|
||||
single { VideoPlayer(androidContext()) }
|
||||
single { ShareHandler(androidContext()) }
|
||||
}
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
package org.moire.ultrasonic.fragment
|
||||
|
||||
import androidx.fragment.app.Fragment
|
||||
|
||||
class DownloadFragment: Fragment() {
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package org.moire.ultrasonic.fragment
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.fragment.app.Fragment
|
||||
|
||||
class FragmentTitle {
|
||||
companion object {
|
||||
fun setTitle(fragment: Fragment, title: CharSequence?) {
|
||||
(fragment.activity as AppCompatActivity).supportActionBar?.setTitle(title)
|
||||
}
|
||||
|
||||
fun setTitle(fragment: Fragment, id: Int) {
|
||||
(fragment.activity as AppCompatActivity).supportActionBar?.setTitle(id)
|
||||
}
|
||||
|
||||
fun getTitle(fragment: Fragment): CharSequence? {
|
||||
return (fragment.activity as AppCompatActivity).supportActionBar?.title
|
||||
}
|
||||
|
||||
fun setSubtitle(fragment: Fragment, title: CharSequence?) {
|
||||
(fragment.activity as AppCompatActivity).supportActionBar?.setSubtitle(title)
|
||||
}
|
||||
|
||||
fun setSubtitle(fragment: Fragment, id: Int) {
|
||||
(fragment.activity as AppCompatActivity).supportActionBar?.setSubtitle(id)
|
||||
}
|
||||
|
||||
fun getSubtitle(fragment: Fragment): CharSequence? {
|
||||
return (fragment.activity as AppCompatActivity).supportActionBar?.subtitle
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,49 +1,38 @@
|
|||
/*
|
||||
This file is part of Subsonic.
|
||||
package org.moire.ultrasonic.fragment
|
||||
|
||||
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 2020 (C) Jozsef Varga
|
||||
*/
|
||||
package org.moire.ultrasonic.activity
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.PopupMenu
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
import org.koin.android.ext.android.inject
|
||||
import org.koin.android.viewmodel.ext.android.viewModel
|
||||
import org.moire.ultrasonic.R
|
||||
import org.moire.ultrasonic.activity.ArtistListModel
|
||||
import org.moire.ultrasonic.activity.ArtistRowAdapter
|
||||
import org.moire.ultrasonic.activity.ServerSettingsModel
|
||||
import org.moire.ultrasonic.data.ActiveServerProvider
|
||||
import org.moire.ultrasonic.data.ActiveServerProvider.Companion.isOffline
|
||||
import org.moire.ultrasonic.domain.Artist
|
||||
import org.moire.ultrasonic.domain.MusicFolder
|
||||
import org.moire.ultrasonic.fragment.FragmentTitle.Companion.setTitle
|
||||
import org.moire.ultrasonic.subsonic.DownloadHandler
|
||||
import org.moire.ultrasonic.subsonic.ImageLoaderProvider
|
||||
import org.moire.ultrasonic.util.Constants
|
||||
import org.moire.ultrasonic.util.Util
|
||||
|
||||
/**
|
||||
* Displays the available Artists in a list
|
||||
*/
|
||||
class SelectArtistActivity : SubsonicTabActivity() {
|
||||
class SelectArtistFragment : Fragment() {
|
||||
private val activeServerProvider: ActiveServerProvider by inject()
|
||||
private val serverSettingsModel: ServerSettingsModel by viewModel()
|
||||
private val artistListModel: ArtistListModel by viewModel()
|
||||
private val imageLoaderProvider: ImageLoaderProvider by inject()
|
||||
private val downloadHandler: DownloadHandler by inject()
|
||||
|
||||
private var refreshArtistListView: SwipeRefreshLayout? = null
|
||||
private var artistListView: RecyclerView? = null
|
||||
|
@ -54,35 +43,44 @@ class SelectArtistActivity : SubsonicTabActivity() {
|
|||
/**
|
||||
* Called when the activity is first created.
|
||||
*/
|
||||
public override fun onCreate(savedInstanceState: Bundle?) {
|
||||
@Override
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
Util.applyTheme(this.context)
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.select_artist)
|
||||
}
|
||||
|
||||
refreshArtistListView = findViewById(R.id.select_artist_refresh)
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?): View? {
|
||||
return inflater.inflate(R.layout.select_artist, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
refreshArtistListView = view.findViewById(R.id.select_artist_refresh)
|
||||
refreshArtistListView!!.setOnRefreshListener {
|
||||
artistListModel.refresh(refreshArtistListView!!)
|
||||
}
|
||||
|
||||
val shouldShowHeader = (!isOffline(this) && !Util.getShouldUseId3Tags(this))
|
||||
val shouldShowHeader = (!ActiveServerProvider.isOffline(this.context) && !Util.getShouldUseId3Tags(this.context))
|
||||
|
||||
val title = arguments?.getString(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TITLE)
|
||||
|
||||
val title = intent.getStringExtra(Constants.INTENT_EXTRA_NAME_ALBUM_LIST_TITLE)
|
||||
if (title == null) {
|
||||
setActionBarSubtitle(
|
||||
if (isOffline(this)) R.string.music_library_label_offline
|
||||
setTitle(
|
||||
this,
|
||||
if (ActiveServerProvider.isOffline(this.context)) R.string.music_library_label_offline
|
||||
else R.string.music_library_label
|
||||
)
|
||||
} else {
|
||||
actionBarSubtitle = title
|
||||
setTitle(this, title)
|
||||
}
|
||||
val browseMenuItem = findViewById<View>(R.id.menu_browse)
|
||||
menuDrawer.setActiveView(browseMenuItem)
|
||||
|
||||
musicFolders = null
|
||||
|
||||
val refresh = intent.getBooleanExtra(Constants.INTENT_EXTRA_NAME_REFRESH, false)
|
||||
val refresh = arguments?.getBoolean(Constants.INTENT_EXTRA_NAME_REFRESH) ?: false
|
||||
|
||||
artistListModel.getMusicFolders()
|
||||
.observe(
|
||||
this,
|
||||
viewLifecycleOwner,
|
||||
Observer { changedFolders ->
|
||||
if (changedFolders != null) {
|
||||
musicFolders = changedFolders
|
||||
|
@ -93,41 +91,26 @@ class SelectArtistActivity : SubsonicTabActivity() {
|
|||
|
||||
val artists = artistListModel.getArtists(refresh, refreshArtistListView!!)
|
||||
artists.observe(
|
||||
this, Observer { changedArtists -> viewAdapter.setData(changedArtists) }
|
||||
viewLifecycleOwner, Observer { changedArtists -> viewAdapter.setData(changedArtists) }
|
||||
)
|
||||
|
||||
viewManager = LinearLayoutManager(this)
|
||||
viewManager = LinearLayoutManager(this.context)
|
||||
viewAdapter = ArtistRowAdapter(
|
||||
artists.value ?: listOf(),
|
||||
getText(R.string.select_artist_all_folders).toString(),
|
||||
shouldShowHeader,
|
||||
{ artist -> onItemClick(artist) },
|
||||
{ menuItem, artist -> onArtistMenuItemSelected(menuItem, artist) },
|
||||
{ view -> onFolderClick(view) },
|
||||
imageLoader
|
||||
{ onFolderClick(it) },
|
||||
imageLoaderProvider.getImageLoader()
|
||||
)
|
||||
|
||||
artistListView = findViewById<RecyclerView>(R.id.select_artist_list).apply {
|
||||
artistListView = view.findViewById<RecyclerView>(R.id.select_artist_list).apply {
|
||||
setHasFixedSize(true)
|
||||
layoutManager = viewManager
|
||||
adapter = viewAdapter
|
||||
}
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
android.R.id.home -> {
|
||||
menuDrawer.toggleMenu()
|
||||
return true
|
||||
}
|
||||
R.id.main_shuffle -> {
|
||||
val intent = Intent(this, DownloadActivity::class.java)
|
||||
intent.putExtra(Constants.INTENT_EXTRA_NAME_SHUFFLE, true)
|
||||
startActivityForResultWithoutTransition(this, intent)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
}
|
||||
|
||||
private fun getMusicFolderName(musicFolders: List<MusicFolder>): String {
|
||||
|
@ -143,16 +126,16 @@ class SelectArtistActivity : SubsonicTabActivity() {
|
|||
}
|
||||
|
||||
private fun onItemClick(artist: Artist) {
|
||||
val intent = Intent(this, SelectAlbumActivity::class.java)
|
||||
intent.putExtra(Constants.INTENT_EXTRA_NAME_ID, artist.id)
|
||||
intent.putExtra(Constants.INTENT_EXTRA_NAME_NAME, artist.name)
|
||||
intent.putExtra(Constants.INTENT_EXTRA_NAME_PARENT_ID, artist.id)
|
||||
intent.putExtra(Constants.INTENT_EXTRA_NAME_ARTIST, true)
|
||||
startActivityForResultWithoutTransition(this, intent)
|
||||
val bundle = Bundle()
|
||||
bundle.putString(Constants.INTENT_EXTRA_NAME_ID, artist.id)
|
||||
bundle.putString(Constants.INTENT_EXTRA_NAME_NAME, artist.name)
|
||||
bundle.putString(Constants.INTENT_EXTRA_NAME_PARENT_ID, artist.id)
|
||||
bundle.putBoolean(Constants.INTENT_EXTRA_NAME_ARTIST, true)
|
||||
findNavController().navigate(R.id.selectArtistToSelectAlbum, bundle)
|
||||
}
|
||||
|
||||
private fun onFolderClick(view: View) {
|
||||
val popup = PopupMenu(this, view)
|
||||
val popup = PopupMenu(this.context, view)
|
||||
|
||||
val musicFolderId = activeServerProvider.getActiveServer().musicFolderId
|
||||
var menuItem = popup.menu.add(
|
||||
|
@ -179,17 +162,17 @@ class SelectArtistActivity : SubsonicTabActivity() {
|
|||
private fun onArtistMenuItemSelected(menuItem: MenuItem, artist: Artist): Boolean {
|
||||
when (menuItem.itemId) {
|
||||
R.id.artist_menu_play_now ->
|
||||
downloadRecursively(artist.id, false, false, true, false, false, false, false, true)
|
||||
downloadHandler.downloadRecursively(this, artist.id, false, false, true, false, false, false, false, true)
|
||||
R.id.artist_menu_play_next ->
|
||||
downloadRecursively(artist.id, false, false, true, true, false, true, false, true)
|
||||
downloadHandler.downloadRecursively(this, artist.id, false, false, true, true, false, true, false, true)
|
||||
R.id.artist_menu_play_last ->
|
||||
downloadRecursively(artist.id, false, true, false, false, false, false, false, true)
|
||||
downloadHandler.downloadRecursively(this, artist.id, false, true, false, false, false, false, false, true)
|
||||
R.id.artist_menu_pin ->
|
||||
downloadRecursively(artist.id, true, true, false, false, false, false, false, true)
|
||||
downloadHandler.downloadRecursively(this, artist.id, true, true, false, false, false, false, false, true)
|
||||
R.id.artist_menu_unpin ->
|
||||
downloadRecursively(artist.id, false, false, false, false, false, false, true, true)
|
||||
downloadHandler.downloadRecursively(this, artist.id, false, false, false, false, false, false, true, true)
|
||||
R.id.artist_menu_download ->
|
||||
downloadRecursively(artist.id, false, false, false, false, true, false, false, true)
|
||||
downloadHandler.downloadRecursively(this, artist.id, false, false, false, false, true, false, false, true)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
@ -199,7 +182,7 @@ class SelectArtistActivity : SubsonicTabActivity() {
|
|||
val musicFolderId = selectedFolder?.id
|
||||
val musicFolderName = selectedFolder?.name
|
||||
?: getString(R.string.select_artist_all_folders)
|
||||
if (!isOffline(this)) {
|
||||
if (!ActiveServerProvider.isOffline(this.context)) {
|
||||
val currentSetting = activeServerProvider.getActiveServer()
|
||||
currentSetting.musicFolderId = musicFolderId
|
||||
serverSettingsModel.updateItem(currentSetting)
|
||||
|
@ -212,4 +195,4 @@ class SelectArtistActivity : SubsonicTabActivity() {
|
|||
companion object {
|
||||
private const val MENU_GROUP_MUSIC_FOLDER = 10
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,210 @@
|
|||
package org.moire.ultrasonic.subsonic
|
||||
|
||||
import android.app.Activity
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import org.moire.ultrasonic.R
|
||||
import org.moire.ultrasonic.data.ActiveServerProvider.Companion.isOffline
|
||||
import org.moire.ultrasonic.domain.MusicDirectory
|
||||
import org.moire.ultrasonic.service.MediaPlayerController
|
||||
import org.moire.ultrasonic.service.MusicServiceFactory.getMusicService
|
||||
import org.moire.ultrasonic.util.Constants
|
||||
import org.moire.ultrasonic.util.EntryByDiscAndTrackComparator
|
||||
import org.moire.ultrasonic.util.ModalBackgroundTask
|
||||
import org.moire.ultrasonic.util.Util
|
||||
import java.util.*
|
||||
|
||||
class DownloadHandler(
|
||||
val mediaPlayerController: MediaPlayerController,
|
||||
val networkAndStorageChecker: NetworkAndStorageChecker
|
||||
) {
|
||||
private val MAX_SONGS = 500
|
||||
|
||||
fun download(fragment: Fragment, append: Boolean, save: Boolean, autoPlay: Boolean, playNext: Boolean, shuffle: Boolean, songs: List<MusicDirectory.Entry?>) {
|
||||
val onValid = Runnable {
|
||||
if (!append && !playNext) {
|
||||
mediaPlayerController.clear()
|
||||
}
|
||||
networkAndStorageChecker.warnIfNetworkOrStorageUnavailable()
|
||||
mediaPlayerController.download(songs, save, autoPlay, playNext, shuffle, false)
|
||||
val playlistName: String? = fragment.arguments?.getString(Constants.INTENT_EXTRA_NAME_PLAYLIST_NAME)
|
||||
if (playlistName != null) {
|
||||
mediaPlayerController.suggestedPlaylistName = playlistName
|
||||
}
|
||||
if (autoPlay) {
|
||||
if (Util.getShouldTransitionOnPlaybackPreference(fragment.activity)) {
|
||||
fragment.findNavController().popBackStack(R.id.downloadFragment, true)
|
||||
fragment.findNavController().navigate(R.id.downloadFragment)
|
||||
//startActivityForResultWithoutTransition(this@SubsonicTabActivity, DownloadActivity::class.java)
|
||||
}
|
||||
} else if (save) {
|
||||
Util.toast(fragment.context, fragment.resources.getQuantityString(R.plurals.select_album_n_songs_pinned, songs.size, songs.size))
|
||||
} else if (playNext) {
|
||||
Util.toast(fragment.context, fragment.resources.getQuantityString(R.plurals.select_album_n_songs_play_next, songs.size, songs.size))
|
||||
} else if (append) {
|
||||
Util.toast(fragment.context, fragment.resources.getQuantityString(R.plurals.select_album_n_songs_added, songs.size, songs.size))
|
||||
}
|
||||
}
|
||||
onValid.run()
|
||||
}
|
||||
|
||||
fun downloadPlaylist(fragment: Fragment, id: String, name: String?, save: Boolean, append: Boolean, autoplay: Boolean, shuffle: Boolean, background: Boolean, playNext: Boolean, unpin: Boolean) {
|
||||
downloadRecursively(fragment, id, name, false, false, save, append, autoplay, shuffle, background, playNext, unpin, false)
|
||||
}
|
||||
|
||||
fun downloadShare(fragment: Fragment, id: String, name: String?, save: Boolean, append: Boolean, autoplay: Boolean, shuffle: Boolean, background: Boolean, playNext: Boolean, unpin: Boolean) {
|
||||
downloadRecursively(fragment, id, name, true, false, save, append, autoplay, shuffle, background, playNext, unpin, false)
|
||||
}
|
||||
|
||||
fun downloadRecursively(
|
||||
fragment: Fragment,
|
||||
id: String?,
|
||||
save: Boolean,
|
||||
append: Boolean,
|
||||
autoPlay: Boolean,
|
||||
shuffle: Boolean,
|
||||
background: Boolean,
|
||||
playNext: Boolean,
|
||||
unpin: Boolean,
|
||||
isArtist: Boolean
|
||||
) {
|
||||
if (id.isNullOrEmpty()) return
|
||||
downloadRecursively(
|
||||
fragment,
|
||||
id,
|
||||
"",
|
||||
isShare = false,
|
||||
isDirectory = true,
|
||||
save = save,
|
||||
append = append,
|
||||
autoPlay = autoPlay,
|
||||
shuffle = shuffle,
|
||||
background = background,
|
||||
playNext = playNext,
|
||||
unpin = unpin,
|
||||
isArtist = isArtist)
|
||||
}
|
||||
|
||||
fun downloadRecursively(
|
||||
fragment: Fragment,
|
||||
id: String,
|
||||
name: String?,
|
||||
isShare: Boolean,
|
||||
isDirectory: Boolean,
|
||||
save: Boolean,
|
||||
append: Boolean,
|
||||
autoPlay: Boolean,
|
||||
shuffle: Boolean,
|
||||
background: Boolean,
|
||||
playNext: Boolean,
|
||||
unpin: Boolean,
|
||||
isArtist: Boolean
|
||||
) {
|
||||
val activity = fragment.activity as Activity
|
||||
val task = object: ModalBackgroundTask<List<MusicDirectory.Entry>>(
|
||||
activity,
|
||||
false
|
||||
) {
|
||||
|
||||
@Throws(Throwable::class)
|
||||
override fun doInBackground(): List<MusicDirectory.Entry> {
|
||||
// TODO: Handle swipe spinner here instead of the ProgressListener
|
||||
val musicService = getMusicService(activity)
|
||||
val songs: MutableList<MusicDirectory.Entry> = LinkedList()
|
||||
val root: MusicDirectory
|
||||
if (!isOffline(activity) && isArtist && Util.getShouldUseId3Tags(activity)) {
|
||||
getSongsForArtist(id, songs)
|
||||
} else {
|
||||
if (isDirectory) {
|
||||
root = if (!isOffline(activity) && Util.getShouldUseId3Tags(activity))
|
||||
musicService.getAlbum(id, name, false, activity, null)
|
||||
else
|
||||
musicService.getMusicDirectory(id, name, false, activity, null)
|
||||
} else if (isShare) {
|
||||
root = MusicDirectory()
|
||||
val shares = musicService.getShares(true, activity, null)
|
||||
for (share in shares) {
|
||||
if (share.id == id) {
|
||||
for (entry in share.getEntries()) {
|
||||
root.addChild(entry)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
root = musicService.getPlaylist(id, name, activity, null)
|
||||
}
|
||||
getSongsRecursively(root, songs)
|
||||
}
|
||||
return songs
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
private fun getSongsRecursively(parent: MusicDirectory, songs: MutableList<MusicDirectory.Entry>) {
|
||||
if (songs.size > MAX_SONGS) {
|
||||
return
|
||||
}
|
||||
for (song in parent.getChildren(false, true)) {
|
||||
if (!song.isVideo) {
|
||||
songs.add(song)
|
||||
}
|
||||
}
|
||||
val musicService = getMusicService(activity)
|
||||
for ((id1, _, _, title) in parent.getChildren(true, false)) {
|
||||
var root: MusicDirectory
|
||||
root = if (!isOffline(activity) && Util.getShouldUseId3Tags(activity)) musicService.getAlbum(id1, title, false, activity, null)
|
||||
else musicService.getMusicDirectory(id1, title, false, activity, null)
|
||||
getSongsRecursively(root, songs)
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
private fun getSongsForArtist(id: String, songs: MutableCollection<MusicDirectory.Entry>) {
|
||||
if (songs.size > MAX_SONGS) {
|
||||
return
|
||||
}
|
||||
val musicService = getMusicService(activity)
|
||||
val artist = musicService.getArtist(id, "", false, activity, null)
|
||||
for ((id1) in artist.getChildren()) {
|
||||
val albumDirectory = musicService.getAlbum(id1, "", false, activity, null)
|
||||
for (song in albumDirectory.getChildren()) {
|
||||
if (!song.isVideo) {
|
||||
songs.add(song)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun done(songs: List<MusicDirectory.Entry>) {
|
||||
if (Util.getShouldSortByDisc(activity)) {
|
||||
Collections.sort(songs, EntryByDiscAndTrackComparator())
|
||||
}
|
||||
if (songs.isNotEmpty()) {
|
||||
if (!append && !playNext && !unpin && !background) {
|
||||
mediaPlayerController.clear()
|
||||
}
|
||||
networkAndStorageChecker.warnIfNetworkOrStorageUnavailable()
|
||||
if (!background) {
|
||||
if (unpin) {
|
||||
mediaPlayerController.unpin(songs)
|
||||
} else {
|
||||
mediaPlayerController.download(songs, save, autoPlay, playNext, shuffle, false)
|
||||
if (!append && Util.getShouldTransitionOnPlaybackPreference(activity)) {
|
||||
fragment.findNavController().popBackStack(R.id.downloadFragment, true)
|
||||
fragment.findNavController().navigate(R.id.downloadFragment)
|
||||
//startActivityForResultWithoutTransition(activity, DownloadActivity::class.java)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (unpin) {
|
||||
mediaPlayerController.unpin(songs)
|
||||
} else {
|
||||
mediaPlayerController.downloadBackground(songs, save)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
task.execute()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
package org.moire.ultrasonic.subsonic
|
||||
|
||||
import android.content.Context
|
||||
import org.koin.java.KoinJavaComponent.get
|
||||
import org.moire.ultrasonic.featureflags.Feature
|
||||
import org.moire.ultrasonic.featureflags.FeatureStorage
|
||||
import org.moire.ultrasonic.subsonic.loader.image.SubsonicImageLoader
|
||||
import org.moire.ultrasonic.util.ImageLoader
|
||||
import org.moire.ultrasonic.util.LegacyImageLoader
|
||||
import org.moire.ultrasonic.util.Util
|
||||
|
||||
class ImageLoaderProvider (val context: Context) {
|
||||
private var imageLoader: ImageLoader? = null
|
||||
|
||||
@Synchronized
|
||||
fun clearImageLoader() {
|
||||
if (imageLoader != null &&
|
||||
imageLoader!!.isRunning) {
|
||||
imageLoader!!.clear()
|
||||
}
|
||||
imageLoader = null
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun getImageLoader(): ImageLoader {
|
||||
if (imageLoader == null || !imageLoader!!.isRunning) {
|
||||
val legacyImageLoader = LegacyImageLoader(
|
||||
context,
|
||||
Util.getImageLoaderConcurrency(context)
|
||||
)
|
||||
val isNewImageLoaderEnabled = get(FeatureStorage::class.java)
|
||||
.isFeatureEnabled(Feature.NEW_IMAGE_DOWNLOADER)
|
||||
if (isNewImageLoaderEnabled) {
|
||||
imageLoader = SubsonicImageLoaderProxy(
|
||||
legacyImageLoader,
|
||||
get(SubsonicImageLoader::class.java)
|
||||
)
|
||||
} else {
|
||||
imageLoader = legacyImageLoader
|
||||
}
|
||||
imageLoader!!.startImageLoader()
|
||||
}
|
||||
return imageLoader!!
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package org.moire.ultrasonic.subsonic
|
||||
|
||||
import android.content.Context
|
||||
import org.moire.ultrasonic.R
|
||||
import org.moire.ultrasonic.data.ActiveServerProvider.Companion.isOffline
|
||||
import org.moire.ultrasonic.util.Util
|
||||
|
||||
class NetworkAndStorageChecker(val context: Context) {
|
||||
fun warnIfNetworkOrStorageUnavailable() {
|
||||
if (!Util.isExternalStoragePresent()) {
|
||||
Util.toast(context, R.string.select_album_no_sdcard)
|
||||
} else if (!isOffline(context) && !Util.isNetworkConnected(context)) {
|
||||
Util.toast(context, R.string.select_album_no_network)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,139 @@
|
|||
package org.moire.ultrasonic.subsonic
|
||||
|
||||
import android.app.AlertDialog
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.widget.CheckBox
|
||||
import android.widget.EditText
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
import org.moire.ultrasonic.R
|
||||
import org.moire.ultrasonic.domain.MusicDirectory
|
||||
import org.moire.ultrasonic.domain.Share
|
||||
import org.moire.ultrasonic.service.MusicServiceFactory.getMusicService
|
||||
import org.moire.ultrasonic.util.BackgroundTask
|
||||
import org.moire.ultrasonic.util.CancellationToken
|
||||
import org.moire.ultrasonic.util.Constants
|
||||
import org.moire.ultrasonic.util.ShareDetails
|
||||
import org.moire.ultrasonic.util.TabActivityBackgroundTask
|
||||
import org.moire.ultrasonic.util.TimeSpan
|
||||
import org.moire.ultrasonic.util.TimeSpanPicker
|
||||
import org.moire.ultrasonic.util.Util
|
||||
import java.util.*
|
||||
import java.util.regex.Pattern
|
||||
|
||||
class ShareHandler(val context: Context) {
|
||||
private var shareDescription: EditText? = null
|
||||
private var timeSpanPicker: TimeSpanPicker? = null
|
||||
private var hideDialogCheckBox: CheckBox? = null
|
||||
private var noExpirationCheckBox: CheckBox? = null
|
||||
private var saveAsDefaultsCheckBox: CheckBox? = null
|
||||
private val pattern = Pattern.compile(":")
|
||||
|
||||
fun createShare(fragment: Fragment, entries: List<MusicDirectory.Entry?>?, swipe: SwipeRefreshLayout, cancellationToken: CancellationToken) {
|
||||
val askForDetails = Util.getShouldAskForShareDetails(context)
|
||||
val shareDetails = ShareDetails()
|
||||
shareDetails.Entries = entries
|
||||
if (askForDetails) {
|
||||
showDialog(fragment, shareDetails, swipe, cancellationToken)
|
||||
} else {
|
||||
shareDetails.Description = Util.getDefaultShareDescription(context)
|
||||
shareDetails.Expiration = TimeSpan.getCurrentTime().add(Util.getDefaultShareExpirationInMillis(context)).totalMilliseconds
|
||||
share(fragment, shareDetails, swipe, cancellationToken)
|
||||
}
|
||||
}
|
||||
|
||||
fun share(fragment: Fragment, shareDetails: ShareDetails, swipe: SwipeRefreshLayout, cancellationToken: CancellationToken) {
|
||||
val task: BackgroundTask<Share> = object : TabActivityBackgroundTask<Share>(fragment.requireActivity(), true, swipe, cancellationToken) {
|
||||
@Throws(Throwable::class)
|
||||
override fun doInBackground(): Share {
|
||||
val ids: MutableList<String?> = ArrayList()
|
||||
if (shareDetails.Entries.isEmpty()) {
|
||||
ids.add(fragment.arguments?.getString(Constants.INTENT_EXTRA_NAME_ID))
|
||||
} else {
|
||||
for ((id) in shareDetails.Entries) {
|
||||
ids.add(id)
|
||||
}
|
||||
}
|
||||
val musicService = getMusicService(context)
|
||||
var timeInMillis: Long = 0
|
||||
if (shareDetails.Expiration != 0L) {
|
||||
timeInMillis = shareDetails.Expiration
|
||||
}
|
||||
val shares = musicService.createShare(ids, shareDetails.Description, timeInMillis, context, this)
|
||||
return shares[0]
|
||||
}
|
||||
|
||||
override fun done(result: Share) {
|
||||
val intent = Intent(Intent.ACTION_SEND)
|
||||
intent.type = "text/plain"
|
||||
intent.putExtra(Intent.EXTRA_TEXT, String.format("%s\n\n%s", Util.getShareGreeting(context), result.url))
|
||||
fragment.activity?.startActivity(Intent.createChooser(intent, context.getResources().getString(R.string.share_via)))
|
||||
}
|
||||
}
|
||||
task.execute()
|
||||
}
|
||||
|
||||
private fun showDialog(fragment: Fragment, shareDetails: ShareDetails, swipe: SwipeRefreshLayout, cancellationToken: CancellationToken) {
|
||||
val layout = LayoutInflater.from(fragment.context).inflate(R.layout.share_details, null)
|
||||
|
||||
if (layout != null) {
|
||||
shareDescription = layout.findViewById<View>(R.id.share_description) as EditText
|
||||
hideDialogCheckBox = layout.findViewById<View>(R.id.hide_dialog) as CheckBox
|
||||
noExpirationCheckBox = layout.findViewById<View>(R.id.timeSpanDisableCheckBox) as CheckBox
|
||||
saveAsDefaultsCheckBox = layout.findViewById<View>(R.id.save_as_defaults) as CheckBox
|
||||
timeSpanPicker = layout.findViewById<View>(R.id.date_picker) as TimeSpanPicker
|
||||
}
|
||||
val builder = AlertDialog.Builder(fragment.context)
|
||||
builder.setTitle(R.string.share_set_share_options)
|
||||
builder.setPositiveButton(R.string.common_save) { dialog, clickId ->
|
||||
if (!noExpirationCheckBox!!.isChecked) {
|
||||
val timeSpan: TimeSpan = timeSpanPicker!!.timeSpan
|
||||
val now = TimeSpan.getCurrentTime()
|
||||
shareDetails.Expiration = now.add(timeSpan).totalMilliseconds
|
||||
}
|
||||
shareDetails.Description = shareDescription!!.text.toString()
|
||||
if (hideDialogCheckBox!!.isChecked) {
|
||||
Util.setShouldAskForShareDetails(context, false)
|
||||
}
|
||||
if (saveAsDefaultsCheckBox!!.isChecked) {
|
||||
val timeSpanType: String = timeSpanPicker!!.timeSpanType
|
||||
val timeSpanAmount: Int = timeSpanPicker!!.timeSpanAmount
|
||||
Util.setDefaultShareExpiration(context, if (!noExpirationCheckBox!!.isChecked && timeSpanAmount > 0) String.format("%d:%s", timeSpanAmount, timeSpanType) else "")
|
||||
Util.setDefaultShareDescription(context, shareDetails.Description)
|
||||
}
|
||||
share(fragment, shareDetails, swipe, cancellationToken)
|
||||
}
|
||||
builder.setNegativeButton(R.string.common_cancel) { dialog, clickId ->
|
||||
dialog.cancel()
|
||||
}
|
||||
builder.setView(layout)
|
||||
builder.setCancelable(true)
|
||||
timeSpanPicker!!.setTimeSpanDisableText(context.resources.getString(R.string.no_expiration))
|
||||
noExpirationCheckBox!!.setOnCheckedChangeListener { _, b -> timeSpanPicker!!.isEnabled = !b }
|
||||
val defaultDescription = Util.getDefaultShareDescription(context)
|
||||
val timeSpan = Util.getDefaultShareExpiration(context)
|
||||
val split = pattern.split(timeSpan)
|
||||
if (split.size == 2) {
|
||||
val timeSpanAmount = split[0].toInt()
|
||||
val timeSpanType = split[1]
|
||||
if (timeSpanAmount > 0) {
|
||||
noExpirationCheckBox!!.isChecked = false
|
||||
timeSpanPicker!!.isEnabled = true
|
||||
timeSpanPicker!!.setTimeSpanAmount(timeSpanAmount.toString())
|
||||
timeSpanPicker!!.setTimeSpanType(timeSpanType)
|
||||
} else {
|
||||
noExpirationCheckBox!!.isChecked = true
|
||||
timeSpanPicker!!.isEnabled = false
|
||||
}
|
||||
} else {
|
||||
noExpirationCheckBox!!.isChecked = true
|
||||
timeSpanPicker!!.isEnabled = false
|
||||
}
|
||||
shareDescription!!.setText(defaultDescription)
|
||||
builder.create()
|
||||
builder.show()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package org.moire.ultrasonic.subsonic
|
||||
|
||||
import android.content.Context
|
||||
import org.moire.ultrasonic.R
|
||||
import org.moire.ultrasonic.domain.MusicDirectory
|
||||
import org.moire.ultrasonic.util.Util
|
||||
|
||||
class VideoPlayer(val context: Context) {
|
||||
fun playVideo(entry: MusicDirectory.Entry?) {
|
||||
if (!Util.isNetworkConnected(context)) {
|
||||
Util.toast(context, R.string.select_album_no_network)
|
||||
return
|
||||
}
|
||||
val player = Util.getVideoPlayerType(context)
|
||||
try {
|
||||
player.playVideo(context, entry)
|
||||
} catch (e: Exception) {
|
||||
Util.toast(context, e.message, false)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
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 2021 (C) Jozsef Varga
|
||||
*/
|
||||
package org.moire.ultrasonic.util
|
||||
|
||||
/**
|
||||
* This class contains a very simple implementation of a CancellationToken
|
||||
*/
|
||||
class CancellationToken {
|
||||
var isCancellationRequested: Boolean = false
|
||||
|
||||
/**
|
||||
* Requests that this token be cancelled
|
||||
*/
|
||||
fun cancel() {
|
||||
isCancellationRequested = true;
|
||||
}
|
||||
}
|
|
@ -29,12 +29,18 @@
|
|||
|
||||
</LinearLayout>
|
||||
|
||||
<WebView
|
||||
a:id="@+id/help_contents"
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
a:id="@+id/help_refresh"
|
||||
a:layout_width="fill_parent"
|
||||
a:layout_height="match_parent"
|
||||
a:layout_below="@id/help_buttons"
|
||||
a:fadingEdge="vertical"
|
||||
a:fadingEdgeLength="12dip" />
|
||||
a:layout_below="@id/help_buttons">
|
||||
|
||||
<WebView
|
||||
a:id="@+id/help_contents"
|
||||
a:layout_width="fill_parent"
|
||||
a:layout_height="match_parent"
|
||||
a:fadingEdge="vertical"
|
||||
a:fadingEdgeLength="12dip" />
|
||||
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
</RelativeLayout>
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.drawerlayout.widget.DrawerLayout
|
||||
xmlns:a="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
a:id="@+id/drawer_layout"
|
||||
a:layout_width="match_parent"
|
||||
a:layout_height="match_parent"
|
||||
tools:context="org.moire.ultrasonic.activity.NavigationActivity">
|
||||
|
||||
<LinearLayout
|
||||
a:layout_width="match_parent"
|
||||
a:layout_height="match_parent"
|
||||
a:orientation="vertical">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
a:id="@+id/toolbar"
|
||||
a:layout_width="match_parent"
|
||||
a:layout_height="wrap_content" />
|
||||
|
||||
<fragment
|
||||
a:id="@+id/nav_host_fragment"
|
||||
a:name="androidx.navigation.fragment.NavHostFragment"
|
||||
a:layout_width="match_parent"
|
||||
a:layout_height="match_parent"
|
||||
app:defaultNavHost="true"
|
||||
app:navGraph="@navigation/navigation_graph" />
|
||||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.navigation.NavigationView
|
||||
a:id="@+id/nav_view"
|
||||
a:layout_width="wrap_content"
|
||||
a:layout_height="match_parent"
|
||||
a:layout_gravity="start"
|
||||
a:fitsSystemWindows="true"
|
||||
a:theme="@style/ThemeOverlay.AppCompat.navTheme"
|
||||
app:headerLayout="@layout/navigation_header"
|
||||
app:menu="@menu/navigation"/>
|
||||
</androidx.drawerlayout.widget.DrawerLayout>
|
|
@ -0,0 +1,41 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:a="http://schemas.android.com/apk/res/android"
|
||||
a:id="@+id/view_container"
|
||||
a:layout_width="match_parent"
|
||||
a:layout_height="96dp"
|
||||
a:gravity="bottom"
|
||||
a:orientation="vertical"
|
||||
a:theme="@style/ThemeOverlay.AppCompat.Dark">
|
||||
|
||||
<ImageView
|
||||
a:id="@+id/img_header_bg"
|
||||
a:layout_width="match_parent"
|
||||
a:layout_height="match_parent"
|
||||
a:scaleType="fitXY"
|
||||
a:src="@drawable/ic_launcher_background" />
|
||||
|
||||
<LinearLayout
|
||||
a:layout_width="wrap_content"
|
||||
a:layout_height="wrap_content"
|
||||
a:layout_centerVertical="true"
|
||||
a:orientation="horizontal"
|
||||
a:padding="16dp">
|
||||
|
||||
<ImageView
|
||||
a:id="@+id/img_profile"
|
||||
a:layout_width="52dp"
|
||||
a:layout_height="52dp"
|
||||
a:src="@drawable/ic_stat_ultrasonic" />
|
||||
|
||||
<TextView
|
||||
a:id="@+id/name"
|
||||
a:layout_width="wrap_content"
|
||||
a:layout_height="match_parent"
|
||||
a:gravity="center_vertical"
|
||||
a:paddingStart="16dp"
|
||||
a:paddingLeft="16dp"
|
||||
a:text="@string/common.appname"
|
||||
a:textAppearance="@style/TextAppearance.AppCompat.Title" />
|
||||
|
||||
</LinearLayout>
|
||||
</RelativeLayout>
|
|
@ -4,7 +4,11 @@
|
|||
a:layout_width="fill_parent"
|
||||
a:layout_height="fill_parent">
|
||||
|
||||
<include layout="@layout/tab_progress"/>
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
a:id="@+id/search_entries_refresh"
|
||||
a:layout_width="fill_parent"
|
||||
a:layout_height="0dip"
|
||||
a:layout_weight="1.0">
|
||||
|
||||
<ListView
|
||||
a:id="@+id/search_list"
|
||||
|
@ -12,6 +16,8 @@
|
|||
a:layout_height="0dip"
|
||||
a:layout_weight="1.0"/>
|
||||
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
|
||||
<include layout="@layout/now_playing" />
|
||||
|
||||
</LinearLayout>
|
|
@ -9,8 +9,6 @@
|
|||
a:layout_height="1dp"
|
||||
a:background="@color/dividerColor" />
|
||||
|
||||
<include layout="@layout/tab_progress" />
|
||||
|
||||
<TextView
|
||||
a:id="@+id/select_album_empty"
|
||||
a:layout_width="fill_parent"
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:a="http://schemas.android.com/apk/res/android">
|
||||
<group
|
||||
a:checkableBehavior="none"
|
||||
a:enabled="true"
|
||||
a:visible="true">
|
||||
<item
|
||||
a:id="@+id/mainFragment"
|
||||
a:checkable="true"
|
||||
a:icon="?attr/home"
|
||||
a:title="@string/button_bar.home" />
|
||||
<item
|
||||
a:id="@+id/selectArtistFragment"
|
||||
a:checkable="true"
|
||||
a:icon="?attr/browse"
|
||||
a:title="@string/button_bar.browse" />
|
||||
<item
|
||||
a:id="@+id/searchFragment"
|
||||
a:checkable="true"
|
||||
a:icon="?attr/search"
|
||||
a:title="@string/button_bar.search" />
|
||||
<item
|
||||
a:id="@+id/playlistsFragment"
|
||||
a:checkable="true"
|
||||
a:icon="?attr/playlists"
|
||||
a:title="@string/button_bar.playlists" />
|
||||
<item
|
||||
a:id="@+id/sharesFragment"
|
||||
a:checkable="true"
|
||||
a:icon="?attr/share"
|
||||
a:title="@string/button_bar.shares" />
|
||||
<item
|
||||
a:id="@+id/bookmarksFragment"
|
||||
a:checkable="true"
|
||||
a:icon="?attr/bookmark"
|
||||
a:title="@string/button_bar.bookmarks" />
|
||||
<item
|
||||
a:id="@+id/chatFragment"
|
||||
a:checkable="true"
|
||||
a:icon="?attr/chat"
|
||||
a:title="@string/button_bar.chat" />
|
||||
<item
|
||||
a:id="@+id/menu_now_playing"
|
||||
a:checkable="true"
|
||||
a:icon="?attr/media_play"
|
||||
a:title="@string/button_bar.now_playing" />
|
||||
<item
|
||||
a:id="@+id/podcastFragment"
|
||||
a:checkable="true"
|
||||
a:icon="?attr/podcasts"
|
||||
a:title="@string/button_bar.podcasts" />
|
||||
</group>
|
||||
<item a:title="@string/menu.common">
|
||||
<menu>
|
||||
<item
|
||||
a:id="@+id/settingsFragment"
|
||||
a:checkable="true"
|
||||
a:icon="?attr/settings"
|
||||
a:title="@string/menu.settings" />
|
||||
<item
|
||||
a:id="@+id/aboutFragment"
|
||||
a:checkable="true"
|
||||
a:icon="?attr/about"
|
||||
a:title="@string/menu.about" />
|
||||
<item
|
||||
a:id="@+id/menu_exit"
|
||||
a:checkable="true"
|
||||
a:icon="?attr/exit"
|
||||
a:title="@string/menu.exit" />
|
||||
</menu>
|
||||
</item>
|
||||
</menu>
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:a="http://schemas.android.com/apk/res/android">
|
||||
<item a:id="@+id/search_item"
|
||||
a:title="@string/search.label"
|
||||
a:icon="?attr/searchIcon"
|
||||
app:showAsAction="always"
|
||||
app:actionViewClass="androidx.appcompat.widget.SearchView" />
|
||||
</menu>
|
|
@ -0,0 +1,85 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/navigation_graph"
|
||||
app:startDestination="@id/mainFragment">
|
||||
|
||||
<fragment
|
||||
android:id="@+id/mainFragment"
|
||||
android:name="org.moire.ultrasonic.fragment.MainFragment"
|
||||
android:label="@string/common.appname" >
|
||||
<action
|
||||
android:id="@+id/mainToSelectAlbum"
|
||||
app:destination="@id/selectAlbumFragment" />
|
||||
<action
|
||||
android:id="@+id/mainToSelectGenre"
|
||||
app:destination="@id/selectGenreFragment" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/selectArtistFragment"
|
||||
android:name="org.moire.ultrasonic.fragment.SelectArtistFragment"
|
||||
android:label="@string/music_library.label" >
|
||||
<action
|
||||
android:id="@+id/selectArtistToSelectAlbum"
|
||||
app:destination="@id/selectAlbumFragment" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/downloadFragment"
|
||||
android:name="org.moire.ultrasonic.fragment.DownloadFragment" />
|
||||
<fragment
|
||||
android:id="@+id/selectAlbumFragment"
|
||||
android:name="org.moire.ultrasonic.fragment.SelectAlbumFragment"
|
||||
android:label="SelectAlbumFragment" >
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/searchFragment"
|
||||
android:name="org.moire.ultrasonic.fragment.SearchFragment"
|
||||
android:label="SearchFragment" >
|
||||
<action
|
||||
android:id="@+id/searchToSelectAlbum"
|
||||
app:destination="@id/selectAlbumFragment" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/playlistsFragment"
|
||||
android:name="org.moire.ultrasonic.fragment.PlaylistsFragment"
|
||||
android:label="PlaylistsFragment" >
|
||||
<action
|
||||
android:id="@+id/playlistsToSelectAlbum"
|
||||
app:destination="@id/selectAlbumFragment" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/sharesFragment"
|
||||
android:name="org.moire.ultrasonic.fragment.SharesFragment"
|
||||
android:label="SharesFragment" >
|
||||
<action
|
||||
android:id="@+id/sharesToSelectAlbum"
|
||||
app:destination="@id/selectAlbumFragment" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/bookmarksFragment"
|
||||
android:name="org.moire.ultrasonic.fragment.BookmarksFragment"
|
||||
android:label="BookmarksFragment" />
|
||||
<fragment
|
||||
android:id="@+id/chatFragment"
|
||||
android:name="org.moire.ultrasonic.fragment.ChatFragment"
|
||||
android:label="ChatFragment" />
|
||||
<fragment
|
||||
android:id="@+id/podcastFragment"
|
||||
android:name="org.moire.ultrasonic.fragment.PodcastFragment"
|
||||
android:label="PodcastFragment" >
|
||||
<action
|
||||
android:id="@+id/podcastToSelectAlbum"
|
||||
app:destination="@id/selectAlbumFragment" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/settingsFragment"
|
||||
android:name="org.moire.ultrasonic.fragment.SettingsFragment"
|
||||
android:label="SettingsFragment" />
|
||||
<fragment
|
||||
android:id="@+id/aboutFragment"
|
||||
android:name="org.moire.ultrasonic.fragment.AboutFragment"
|
||||
android:label="AboutFragment" />
|
||||
<fragment
|
||||
android:id="@+id/selectGenreFragment"
|
||||
android:name="org.moire.ultrasonic.fragment.SelectGenreFragment"
|
||||
android:label="SelectGenreFragment" />
|
||||
</navigation>
|
|
@ -9,6 +9,11 @@
|
|||
<item name="mdAllowIndicatorAnimation">true</item>
|
||||
</style>
|
||||
|
||||
<style name="NoActionBar" parent="@style/Theme.AppCompat">
|
||||
<item name="windowActionBar">false</item>
|
||||
<item name="windowNoTitle">true</item>
|
||||
</style>
|
||||
|
||||
<style name="MenuDrawer"/>
|
||||
|
||||
<style name="MenuDrawer.Widget"/>
|
||||
|
@ -40,6 +45,11 @@
|
|||
<item name="cornerSize">8dp</item>
|
||||
</style>
|
||||
|
||||
<style name="ThemeOverlay.AppCompat.navTheme">
|
||||
<item name="colorPrimary">@color/cyan</item>
|
||||
<item name="colorControlHighlight">?attr/color_selected</item>
|
||||
</style>
|
||||
|
||||
<attr name="star_hollow" format="reference"/>
|
||||
<attr name="star_full" format="reference"/>
|
||||
<attr name="about" format="reference"/>
|
||||
|
|
|
@ -1,327 +1,404 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<PreferenceScreen xmlns:a="http://schemas.android.com/apk/res/android"
|
||||
a:title="@string/common.appname">
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto" a:title="@string/common.appname">
|
||||
|
||||
<PreferenceCategory
|
||||
a:key="serversKey"
|
||||
a:title="@string/settings.servers_title">
|
||||
a:title="@string/settings.servers_title"
|
||||
app:iconSpaceReserved="false">
|
||||
<Preference
|
||||
a:key="editServers"
|
||||
a:title="@string/settings.server_manage_servers"
|
||||
app:iconSpaceReserved="false"/>
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory a:title="@string/settings.appearance_title">
|
||||
<PreferenceCategory
|
||||
a:title="@string/settings.appearance_title"
|
||||
app:iconSpaceReserved="false">
|
||||
<ListPreference
|
||||
a:defaultValue="@string/preferences_key_theme_dark"
|
||||
a:entries="@array/themeNames"
|
||||
a:entryValues="@array/themeValues"
|
||||
a:key="theme"
|
||||
a:title="@string/settings.theme_title"/>
|
||||
a:title="@string/settings.theme_title"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<CheckBoxPreference
|
||||
a:defaultValue="true"
|
||||
a:key="serverScaling"
|
||||
a:summary="@string/settings.server_scaling_summary"
|
||||
a:title="@string/settings.server_scaling_title"/>
|
||||
a:title="@string/settings.server_scaling_title"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<CheckBoxPreference
|
||||
a:defaultValue="true"
|
||||
a:key="displayBitrateWithArtist"
|
||||
a:summary="@string/settings.display_bitrate_summary"
|
||||
a:title="@string/settings.display_bitrate"/>
|
||||
a:title="@string/settings.display_bitrate"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<CheckBoxPreference
|
||||
a:defaultValue="false"
|
||||
a:key="useFolderForAlbumArtist"
|
||||
a:summary="@string/settings.use_folder_for_album_artist_summary"
|
||||
a:title="@string/settings.use_folder_for_album_artist"/>
|
||||
a:title="@string/settings.use_folder_for_album_artist"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<CheckBoxPreference
|
||||
a:defaultValue="false"
|
||||
a:key="showAllSongsByArtist"
|
||||
a:summary="@string/settings.show_all_songs_by_artist_summary"
|
||||
a:title="@string/settings.show_all_songs_by_artist"/>
|
||||
a:title="@string/settings.show_all_songs_by_artist"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<CheckBoxPreference
|
||||
a:defaultValue="true"
|
||||
a:key="showTrackNumber"
|
||||
a:summary="@string/settings.show_track_number_summary"
|
||||
a:title="@string/settings.show_track_number"/>
|
||||
a:title="@string/settings.show_track_number"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<CheckBoxPreference
|
||||
a:defaultValue="false"
|
||||
a:key="discAndTrackSort"
|
||||
a:summary="@string/settings.disc_sort_summary"
|
||||
a:title="@string/settings.disc_sort"/>
|
||||
a:title="@string/settings.disc_sort"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<ListPreference
|
||||
a:defaultValue="1000"
|
||||
a:entries="@array/viewRefreshNames"
|
||||
a:entryValues="@array/viewRefreshValues"
|
||||
a:key="viewRefresh"
|
||||
a:title="@string/settings.view_refresh"/>
|
||||
a:title="@string/settings.view_refresh"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<ListPreference
|
||||
a:defaultValue="5"
|
||||
a:entries="@array/imageConcurrencyNames"
|
||||
a:entryValues="@array/imageConcurrencyValues"
|
||||
a:key="imageLoaderConcurrency"
|
||||
a:title="@string/settings.image_loader_concurrency"/>
|
||||
a:title="@string/settings.image_loader_concurrency"
|
||||
app:iconSpaceReserved="false"/>
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory
|
||||
a:title="@string/settings.playback_control_title"
|
||||
a:key="playbackControlSettings">
|
||||
a:key="playbackControlSettings"
|
||||
app:iconSpaceReserved="false">
|
||||
<CheckBoxPreference
|
||||
a:defaultValue="false"
|
||||
a:key="useId3Tags"
|
||||
a:summary="@string/settings.use_id3_summary"
|
||||
a:title="@string/settings.use_id3"/>
|
||||
a:title="@string/settings.use_id3"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<CheckBoxPreference
|
||||
a:defaultValue="false"
|
||||
a:key="showArtistPicture"
|
||||
a:summary="@string/settings.show_artist_picture_summary"
|
||||
a:title="@string/settings.show_artist_picture"/>
|
||||
a:title="@string/settings.show_artist_picture"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<CheckBoxPreference
|
||||
a:defaultValue="true"
|
||||
a:key="mediaButtons"
|
||||
a:summary="@string/settings.media_button_summary"
|
||||
a:title="@string/settings.media_button_title"/>
|
||||
a:title="@string/settings.media_button_title"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<CheckBoxPreference
|
||||
a:defaultValue="true"
|
||||
a:key="transitionToDownloadOnPlay"
|
||||
a:summary="@string/settings.download_transition_summary"
|
||||
a:title="@string/settings.download_transition"/>
|
||||
a:title="@string/settings.download_transition"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<CheckBoxPreference
|
||||
a:defaultValue="false"
|
||||
a:key="gaplessPlayback"
|
||||
a:summary="@string/settings.gapless_playback_summary"
|
||||
a:title="@string/settings.gapless_playback"/>
|
||||
a:title="@string/settings.gapless_playback"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<CheckBoxPreference
|
||||
a:defaultValue="false"
|
||||
a:key="clearPlaylist"
|
||||
a:summary="@string/settings.clear_playlist_summary"
|
||||
a:title="@string/settings.clear_playlist"/>
|
||||
a:title="@string/settings.clear_playlist"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<CheckBoxPreference
|
||||
a:defaultValue="false"
|
||||
a:key="clearBookmark"
|
||||
a:summary="@string/settings.clear_bookmark_summary"
|
||||
a:title="@string/settings.clear_bookmark"/>
|
||||
a:title="@string/settings.clear_bookmark"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<CheckBoxPreference
|
||||
a:defaultValue="false"
|
||||
a:key="scanMedia"
|
||||
a:summary="@string/settings.scan_media_summary"
|
||||
a:title="@string/settings.scan_media"/>
|
||||
a:title="@string/settings.scan_media"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<ListPreference
|
||||
a:defaultValue="5000"
|
||||
a:entries="@array/bufferLengthNames"
|
||||
a:entryValues="@array/incrementTimeValues"
|
||||
a:key="incrementTime"
|
||||
a:title="@string/settings.increment_time"/>
|
||||
a:title="@string/settings.increment_time"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<CheckBoxPreference
|
||||
a:defaultValue="false"
|
||||
a:key="@string/settings.playback.resume_play_on_headphones_plug"
|
||||
a:title="@string/settings.playback.resume_play_on_headphones_plug.title"
|
||||
a:summary="@string/settings.playback.resume_play_on_headphones_plug.summary" />
|
||||
a:summary="@string/settings.playback.resume_play_on_headphones_plug.summary"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<Preference
|
||||
a:key="resumeOnBluetoothDevice"
|
||||
a:title="@string/settings.playback.resume_on_bluetooth_device"/>
|
||||
a:title="@string/settings.playback.resume_on_bluetooth_device"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<Preference
|
||||
a:key="pauseOnBluetoothDevice"
|
||||
a:title="@string/settings.playback.pause_on_bluetooth_device"/>
|
||||
a:title="@string/settings.playback.pause_on_bluetooth_device"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<CheckBoxPreference
|
||||
a:defaultValue="false"
|
||||
a:key="singleButtonPlayPause"
|
||||
a:summary="@string/settings.playback.single_button_bluetooth_device_summary"
|
||||
a:title="@string/settings.playback.single_button_bluetooth_device"/>
|
||||
a:title="@string/settings.playback.single_button_bluetooth_device"
|
||||
app:iconSpaceReserved="false"/>
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory
|
||||
a:title="@string/settings.notifications_title"
|
||||
a:key="notificationsCategory">
|
||||
a:key="notificationsCategory"
|
||||
app:iconSpaceReserved="false">
|
||||
<CheckBoxPreference
|
||||
a:defaultValue="true"
|
||||
a:key="showNowPlaying"
|
||||
a:summary="@string/settings.show_now_playing_summary"
|
||||
a:title="@string/settings.show_now_playing"/>
|
||||
a:title="@string/settings.show_now_playing"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<CheckBoxPreference
|
||||
a:defaultValue="true"
|
||||
a:key="showNotification"
|
||||
a:summary="@string/settings.show_notification_summary"
|
||||
a:title="@string/settings.show_notification"/>
|
||||
a:title="@string/settings.show_notification"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<CheckBoxPreference
|
||||
a:defaultValue="true"
|
||||
a:key="alwaysShowNotification"
|
||||
a:summary="@string/settings.show_notification_always_summary"
|
||||
a:title="@string/settings.show_notification_always"/>
|
||||
a:title="@string/settings.show_notification_always"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<CheckBoxPreference
|
||||
a:defaultValue="true"
|
||||
a:key="showLockScreen"
|
||||
a:summary="@string/settings.show_lockscreen_controls_summary"
|
||||
a:title="@string/settings.show_lockscreen_controls"/>
|
||||
a:title="@string/settings.show_lockscreen_controls"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<CheckBoxPreference
|
||||
a:defaultValue="true"
|
||||
a:key="sendBluetoothNotifications"
|
||||
a:summary="@string/settings.send_bluetooth_notification_summary"
|
||||
a:title="@string/settings.send_bluetooth_notification"/>
|
||||
a:title="@string/settings.send_bluetooth_notification"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<CheckBoxPreference
|
||||
a:defaultValue="false"
|
||||
a:key="sendBluetoothAlbumArt"
|
||||
a:summary="@string/settings.send_bluetooth_album_art_summary"
|
||||
a:title="@string/settings.send_bluetooth_album_art"/>
|
||||
a:title="@string/settings.send_bluetooth_album_art"
|
||||
app:iconSpaceReserved="false"/>
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory a:title="@string/settings.video_title">
|
||||
<PreferenceCategory
|
||||
a:title="@string/settings.video_title"
|
||||
app:iconSpaceReserved="false">
|
||||
<ListPreference
|
||||
a:defaultValue="default"
|
||||
a:entries="@array/videoPlayerNames"
|
||||
a:entryValues="@array/videoPlayerValues"
|
||||
a:key="videoPlayer"
|
||||
a:title="@string/settings.video_player"/>
|
||||
a:title="@string/settings.video_player"
|
||||
app:iconSpaceReserved="false"/>
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory a:title="@string/settings.sharing_title">
|
||||
<PreferenceCategory
|
||||
a:title="@string/settings.sharing_title"
|
||||
app:iconSpaceReserved="false">
|
||||
<EditTextPreference
|
||||
a:key="sharingDefaultDescription"
|
||||
a:title="@string/settings.share_description_default"/>
|
||||
a:title="@string/settings.share_description_default"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<EditTextPreference
|
||||
a:key="sharingDefaultGreeting"
|
||||
a:title="@string/settings.share_greeting_default"/>
|
||||
a:title="@string/settings.share_greeting_default"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<org.moire.ultrasonic.util.TimeSpanPreference
|
||||
a:defaultValue="0"
|
||||
a:key="sharingDefaultExpiration"
|
||||
a:title="@string/settings.share_expiration_default"/>
|
||||
a:title="@string/settings.share_expiration_default"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<CheckBoxPreference
|
||||
a:defaultValue="true"
|
||||
a:key="sharingAlwaysAskForDetails"
|
||||
a:summary="@string/settings.sharing_always_ask_for_details_summary"
|
||||
a:title="@string/settings.sharing_always_ask_for_details"/>
|
||||
a:title="@string/settings.sharing_always_ask_for_details"
|
||||
app:iconSpaceReserved="false"/>
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory a:title="@string/settings.network_title">
|
||||
<PreferenceCategory
|
||||
a:title="@string/settings.network_title"
|
||||
app:iconSpaceReserved="false">
|
||||
<ListPreference
|
||||
a:defaultValue="0"
|
||||
a:entries="@array/maxBitrateNames"
|
||||
a:entryValues="@array/maxBitrateValues"
|
||||
a:key="maxBitrateWifi"
|
||||
a:title="@string/settings.max_bitrate_wifi"/>
|
||||
a:title="@string/settings.max_bitrate_wifi"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<ListPreference
|
||||
a:defaultValue="0"
|
||||
a:entries="@array/maxBitrateNames"
|
||||
a:entryValues="@array/maxBitrateValues"
|
||||
a:key="maxBitrateMobile"
|
||||
a:title="@string/settings.max_bitrate_mobile"/>
|
||||
a:title="@string/settings.max_bitrate_mobile"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<CheckBoxPreference
|
||||
a:defaultValue="false"
|
||||
a:key="wifiRequiredForDownload"
|
||||
a:summary="@string/settings.wifi_required_summary"
|
||||
a:title="@string/settings.wifi_required_title"/>
|
||||
a:title="@string/settings.wifi_required_title"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<ListPreference
|
||||
a:defaultValue="5"
|
||||
a:entries="@array/bufferLengthNames"
|
||||
a:entryValues="@array/bufferLengthValues"
|
||||
a:key="bufferLength"
|
||||
a:title="@string/settings.buffer_length"/>
|
||||
a:title="@string/settings.buffer_length"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<ListPreference
|
||||
a:defaultValue="15000"
|
||||
a:entries="@array/networkTimeoutNames"
|
||||
a:entryValues="@array/networkTimeoutValues"
|
||||
a:key="networkTimeout"
|
||||
a:title="@string/settings.network_timeout"/>
|
||||
a:title="@string/settings.network_timeout"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<ListPreference
|
||||
a:defaultValue="5000"
|
||||
a:entries="@array/chatRefreshNames"
|
||||
a:entryValues="@array/chatRefreshValues"
|
||||
a:key="chatRefreshInterval"
|
||||
a:title="@string/settings.chat_refresh"/>
|
||||
a:title="@string/settings.chat_refresh"
|
||||
app:iconSpaceReserved="false"/>
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory a:title="@string/settings.cache_title">
|
||||
<PreferenceCategory
|
||||
a:title="@string/settings.cache_title"
|
||||
app:iconSpaceReserved="false">
|
||||
<ListPreference
|
||||
a:defaultValue="500"
|
||||
a:entries="@array/cacheSizeNames"
|
||||
a:entryValues="@array/cacheSizeValues"
|
||||
a:key="cacheSize"
|
||||
a:title="@string/settings.cache_size"/>
|
||||
a:title="@string/settings.cache_size"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<Preference
|
||||
a:key="cacheLocation"
|
||||
a:title="@string/settings.cache_location"/>
|
||||
a:title="@string/settings.cache_location"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<ListPreference
|
||||
a:defaultValue="3"
|
||||
a:entries="@array/preloadCountNames"
|
||||
a:entryValues="@array/preloadCountValues"
|
||||
a:key="preloadCount"
|
||||
a:title="@string/settings.preload"/>
|
||||
a:title="@string/settings.preload"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<ListPreference
|
||||
a:defaultValue="300"
|
||||
a:entries="@array/directoryCacheTimeNames"
|
||||
a:entryValues="@array/directoryCacheTimeValues"
|
||||
a:key="directoryCacheTime"
|
||||
a:title="@string/settings.directory_cache_time"/>
|
||||
a:title="@string/settings.directory_cache_time"
|
||||
app:iconSpaceReserved="false"/>
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory a:title="@string/settings.search_title">
|
||||
<PreferenceCategory
|
||||
a:title="@string/settings.search_title"
|
||||
app:iconSpaceReserved="false">
|
||||
<ListPreference
|
||||
a:defaultValue="3"
|
||||
a:entries="@array/searchNames"
|
||||
a:entryValues="@array/searchValues"
|
||||
a:key="defaultArtists"
|
||||
a:title="@string/settings.default_artists"/>
|
||||
a:title="@string/settings.default_artists"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<ListPreference
|
||||
a:defaultValue="10"
|
||||
a:entries="@array/searchNames"
|
||||
a:entryValues="@array/searchValues"
|
||||
a:key="maxArtists"
|
||||
a:title="@string/settings.max_artists"/>
|
||||
a:title="@string/settings.max_artists"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<ListPreference
|
||||
a:defaultValue="5"
|
||||
a:entries="@array/searchNames"
|
||||
a:entryValues="@array/searchValues"
|
||||
a:key="defaultAlbums"
|
||||
a:title="@string/settings.default_albums"/>
|
||||
a:title="@string/settings.default_albums"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<ListPreference
|
||||
a:defaultValue="20"
|
||||
a:entries="@array/searchNames"
|
||||
a:entryValues="@array/searchValues"
|
||||
a:key="maxAlbums"
|
||||
a:title="@string/settings.max_albums"/>
|
||||
a:title="@string/settings.max_albums"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<ListPreference
|
||||
a:defaultValue="10"
|
||||
a:entries="@array/searchNames"
|
||||
a:entryValues="@array/searchValues"
|
||||
a:key="defaultSongs"
|
||||
a:title="@string/settings.default_songs"/>
|
||||
a:title="@string/settings.default_songs"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<ListPreference
|
||||
a:defaultValue="25"
|
||||
a:entries="@array/searchNames"
|
||||
a:entryValues="@array/searchValues"
|
||||
a:key="maxSongs"
|
||||
a:title="@string/settings.max_songs"/>
|
||||
|
||||
a:title="@string/settings.max_songs"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<Preference
|
||||
a:key="clearSearchHistory"
|
||||
a:persistent="false"
|
||||
a:title="@string/settings.clear_search_history"/>
|
||||
a:title="@string/settings.clear_search_history"
|
||||
app:iconSpaceReserved="false"/>
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory a:title="@string/settings.other_title">
|
||||
<PreferenceCategory
|
||||
a:title="@string/settings.other_title"
|
||||
app:iconSpaceReserved="false">
|
||||
<CheckBoxPreference
|
||||
a:defaultValue="false"
|
||||
a:key="scrobble"
|
||||
a:summary="@string/settings.scrobble_summary"
|
||||
a:title="@string/settings.scrobble_title"/>
|
||||
a:title="@string/settings.scrobble_title"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<CheckBoxPreference
|
||||
a:defaultValue="false"
|
||||
a:key="hideMedia"
|
||||
a:summary="@string/settings.hide_media_summary"
|
||||
a:title="@string/settings.hide_media_title"/>
|
||||
a:title="@string/settings.hide_media_title"
|
||||
app:iconSpaceReserved="false"/>
|
||||
<CheckBoxPreference
|
||||
a:defaultValue="true"
|
||||
a:key="screenLitOnDownload"
|
||||
a:summary="@string/settings.screen_lit_summary"
|
||||
a:title="@string/settings.screen_lit_title"/>
|
||||
a:title="@string/settings.screen_lit_title"
|
||||
app:iconSpaceReserved="false"/>
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory a:title="@string/feature_flags_category_title">
|
||||
<PreferenceCategory
|
||||
a:title="@string/feature_flags_category_title"
|
||||
app:iconSpaceReserved="false">
|
||||
<CheckBoxPreference
|
||||
a:key="ff_new_image_loader"
|
||||
a:persistent="false"
|
||||
a:title="@string/feature_flags_image_loader_title"
|
||||
a:summary="@string/feature_flags_image_loader_description"
|
||||
/>
|
||||
app:iconSpaceReserved="false"/>
|
||||
<CheckBoxPreference
|
||||
a:key="use_five_star_rating"
|
||||
a:persistent="false"
|
||||
a:title="@string/feature_flags_five_star_rating_title"
|
||||
a:summary="@string/feature_flags_five_star_rating_description"
|
||||
/>
|
||||
app:iconSpaceReserved="false"/>
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory a:title="@string/settings.debug.title">
|
||||
<PreferenceCategory
|
||||
a:title="@string/settings.debug.title"
|
||||
app:iconSpaceReserved="false">
|
||||
<CheckBoxPreference
|
||||
a:defaultValue="false"
|
||||
a:key="debugLogToFile"
|
||||
a:title="@string/settings.debug.log_to_file"
|
||||
a:summary=""
|
||||
/>
|
||||
app:iconSpaceReserved="false"/>
|
||||
</PreferenceCategory>
|
||||
|
||||
</PreferenceScreen>
|
Loading…
Reference in New Issue