Compare commits
160 Commits
Author | SHA1 | Date |
---|---|---|
Andrew Rabert | 4c1bb5375d | |
Andrew Rabert | f1a9686eb4 | |
Andrew Rabert | 279adc166b | |
Andrew Rabert | c20a64ab15 | |
Andrew Rabert | aa4c877046 | |
Jordi Masip | 09c5d7ee51 | |
Andrew Rabert | 27b0dc742c | |
Andrew Rabert | 63df26cdfd | |
Andrew Rabert | 49f15f2a0d | |
Andrew Rabert | 2c3c9ede14 | |
Andrew Rabert | ef2e19ebf2 | |
Andrew Rabert | 4c5406c916 | |
Andrew Rabert | 072a6d7870 | |
Andrew Rabert | 5ae04b3643 | |
Andrew Rabert | 79ea6cb082 | |
Robert Robinson | 8cfae03550 | |
Andrew Rabert | 298a06ab5b | |
anasofiagribeiro | 41d17f960e | |
Andrew Rabert | a861e035ab | |
Morgan Lim | 4598f849ff | |
Morgan Lim | d37a72b2a0 | |
Andrew Rabert | e102dea3d3 | |
Andrew Rabert | a69b1385c0 | |
Morgan Lim | f506bfb14a | |
Morgan Lim | 0f525befca | |
Morgan Lim | d3d6186fb7 | |
Andrew Rabert | d10ea92edd | |
Andrew Rabert | d1f1331f35 | |
Andrew Rabert | 4fd53e74c5 | |
Andrew Rabert | 6704e05da2 | |
Andrew Rabert | e79aff9e98 | |
Andrew Rabert | ffa048177e | |
Andrew Rabert | f6d308c37c | |
Simão Mata | 3c17e91ca8 | |
Andrew Rabert | ec083cd79d | |
Simão Mata | cfae0d30b5 | |
Andrew Rabert | ecdae8c6e3 | |
Andrew Rabert | 76efc2c62c | |
Andrew Rabert | 26848852ac | |
Simão Mata | 377f21d732 | |
Simão Mata | 8af307da28 | |
Andrew Rabert | 3d7b173cb1 | |
Andrew Rabert | e7ea8d4dfc | |
dddddd-mmmmmm | e2280304e4 | |
emaiannone | 061c318e05 | |
Andrew Rabert | 8fc54ac68c | |
Andrew Rabert | 8f4d3be90b | |
Andrew Rabert | a6a784037e | |
dddddd-mmmmmm | 2ed89ab72f | |
Andrew Rabert | d5b56eecdd | |
dddddd-mmmmmm | 81086c4a3a | |
dddddd-mmmmmm | 02d94e1a6c | |
dddddd-mmmmmm | 5141eb6e81 | |
dddddd-mmmmmm | 4254c8ad9f | |
Andrew Rabert | bf9e9ad1e6 | |
Andrew Rabert | 02a8b1909a | |
Morgan Lim | ff4a1f3b13 | |
Morgan Lim | 153ccdd46a | |
Morgan Lim | 507c9008aa | |
Morgan Lim | 35a112509e | |
Andrew Rabert | af89b8ea76 | |
Morgan Lim | 0a84e9376a | |
Morgan Lim | 717cab5dd5 | |
Andrew Rabert | 13810a07a5 | |
Andrew Rabert | b147cc10dc | |
mlim15 | 357cbd2ba1 | |
Andrew Rabert | 435d770716 | |
Andrew Rabert | 4503ae4133 | |
Andrew Rabert | d43cfb1ce3 | |
mlim15 | cc0faa19ce | |
mlim15 | 8078eeff74 | |
mlim15 | d176ed4036 | |
mlim15 | cf4ab6bf05 | |
Andrew Rabert | 0239248b23 | |
Andrew Rabert | 8ee8858098 | |
Andrew Rabert | 23054ffab0 | |
Andrew Rabert | f623ad2b63 | |
Andrew Rabert | 4eac05e57f | |
Andrew Rabert | 1c269aac53 | |
Andrew Rabert | c5c4185e2f | |
Andrew Rabert | f56c976de7 | |
Andrew Rabert | 572024da52 | |
Andrew Rabert | 97507a25a1 | |
Andrew Rabert | e7628a97c7 | |
Andrew Rabert | f8a9d8ad34 | |
Andrew Rabert | 92c41fd463 | |
Andrew Rabert | 22c98d9606 | |
Andrew Rabert | bf3233d6b2 | |
Andrew Rabert | 93658f32d3 | |
Andrew Rabert | c0ee2d908e | |
Andrew Rabert | e4febf7260 | |
Andrew Rabert | 70f6e22be7 | |
Andrew Rabert | 54d6269a9e | |
Andrew Rabert | eaef36849a | |
Andrew Rabert | e28a70a6e6 | |
Andrew Rabert | 2ca504d7e9 | |
Andrew Rabert | 5e34f2d8e9 | |
Andrew Rabert | 9e57cf1433 | |
Andrew Rabert | 687f64c115 | |
Andrew Rabert | 3ebb48682b | |
Andrew Rabert | a334494186 | |
Andrew Rabert | d43cca9436 | |
Andrew Rabert | a6fa354622 | |
Andrew Rabert | 89ede8ff47 | |
Andrew Rabert | 3243f320b1 | |
Andrew Rabert | e12fbf68ad | |
Andrew Rabert | b943580fa8 | |
Andrew Rabert | a24faf6fae | |
Andrew Rabert | 5144f82051 | |
Andrew Rabert | 63fc23d9fb | |
Andrew Rabert | 11223bd44d | |
Andrew Rabert | 17091f8f86 | |
Andrew Rabert | 3648f64fa4 | |
Andrew Rabert | b0750949f0 | |
Andrew Rabert | 66db8db769 | |
Andrew Rabert | d62e010894 | |
Andrew Rabert | 39a7c39b94 | |
Andrew Rabert | 963aace2c5 | |
Andrew Rabert | 2e49bcadb2 | |
Andrew Rabert | f3b91bff2d | |
Andrew Rabert | 9d9ff7b728 | |
Andrew Rabert | ca15682a5e | |
Andrew Rabert | b213a99e7c | |
Andrew Rabert | cf579043f2 | |
Andrew Rabert | c6729e427b | |
Andrew Rabert | 514eb00797 | |
Andrew Rabert | 5fc74d7cfc | |
Andrew Rabert | 3dffd84f18 | |
Andrew Rabert | 49d5c007d6 | |
Andrew Rabert | 8e2bdadef5 | |
Andrew Rabert | 9acf64c42b | |
Andrew Rabert | 02ffb3b8f0 | |
Andrew Rabert | f4fbdc2bb1 | |
Andrew Rabert | 625f7ba97e | |
dddddd-mmmmmm | 01d4b270cf | |
dddddd-mmmmmm | 05ce1509ec | |
dddddd-mmmmmm | 13ffabdb20 | |
dddddd-mmmmmm | 2015039c3d | |
dddddd-mmmmmm | 2301e2e015 | |
Andrew Rabert | ae55b32ac5 | |
Andrew Rabert | b86cc33bbf | |
Andrew Rabert | 94bf3cbb57 | |
Andrew Rabert | 20cc63f152 | |
Andrew Rabert | e5702c1761 | |
Andrew Rabert | 665bbb3788 | |
Andrew Rabert | 7b1f690d5f | |
Andrew Rabert | cd6a3b0054 | |
Andrew Rabert | 3a4fda9b7c | |
Andrew Rabert | 81a1a44b87 | |
Andrew Rabert | b6f0d7ccc9 | |
Andrew Rabert | 9614cf9445 | |
Andrew Rabert | f1fe5a087b | |
Andrew Rabert | c7f1fa8665 | |
Andrew Rabert | e7953958da | |
Andrew Rabert | 115f211e9f | |
Andrew Rabert | d3a317dc9e | |
Andrew Rabert | 2054ce5427 | |
Andrew Rabert | 405fc8cafd | |
Andrew Rabert | 59c814cf16 | |
Andrew Rabert | 09e01d5641 |
51
CHANGELOG.md
|
@ -1,6 +1,57 @@
|
|||
Changelog
|
||||
=========
|
||||
|
||||
## Version 0.5.1
|
||||
_2020-04-30_
|
||||
* Add option to force server-side media scan
|
||||
* Change to local artist sorting (case-insensitive)
|
||||
* Fix crash while offline (#25)
|
||||
* Fix read timeout not being respected
|
||||
* Fix switching to playlist on app resume
|
||||
|
||||
## Version 0.5.0
|
||||
_2020-01-15_
|
||||
* Add 24kbps and 48kbps options
|
||||
* Add adaptive icon
|
||||
* Add support for p= authentication
|
||||
* Change to MediaStyle playback notification
|
||||
* Fix SSID selection
|
||||
* Fix keyboard being visible when switching to now playing
|
||||
* Fix now playing icon when using light theme
|
||||
|
||||
## Version 0.4.1
|
||||
_2019-12-28_
|
||||
* Revert attempt to fix infinite loop as it sometimes deleted valid files.
|
||||
|
||||
## Version 0.4.0
|
||||
_2019-12-22_
|
||||
* Add support for .opus files
|
||||
* Fix HTTP support
|
||||
* Fix infinite loop when playing contains only invalid files
|
||||
* Overhaul themes
|
||||
* Replace raster images with vector images
|
||||
|
||||
## Version 0.3.3
|
||||
_2019-03-17_
|
||||
* Fix [Funkwhale](https://funkwhale.audio/) (Subsonic API) support
|
||||
* Use query parameters instead of body
|
||||
* Disable most "now playing" swipe gestures - too sensitive and often
|
||||
accidentally triggered.
|
||||
Only swipe left/right to change track ramains.
|
||||
|
||||
|
||||
## Version 0.3.2
|
||||
_2018-07-05_
|
||||
* Prevent now playing from closing upon resuming
|
||||
* Only show save and delete playlist functions in menu
|
||||
* Stop hiding playlist on resume
|
||||
|
||||
|
||||
## Version 0.3.1
|
||||
_2018-06-03_
|
||||
* Fix crash on now playing & playlist while in landscape
|
||||
|
||||
|
||||
## Version 0.3.0
|
||||
_2018-05-12_
|
||||
* Center cover art on now playing screen
|
||||
|
|
11
README.md
|
@ -1,7 +1,14 @@
|
|||
# Audinaut
|
||||
⚠ No longer maintained.
|
||||
|
||||
Although not a direct replacement, I've since moved onto syncing an Opus version
|
||||
of my entire library to my phone using [harmonize](https://github.com/nvllsvm/harmonize)
|
||||
and [Syncthing](https://syncthing.net/).
|
||||
|
||||
---
|
||||
<img src="audinaut.png" align="left" width="200" hspace="10" vspace="10">
|
||||
|
||||
An [Airsonic] client for Android.
|
||||
A Subsonic client for Android.
|
||||
|
||||
|
||||
<a href="https://f-droid.org/app/net.nullsum.audinaut">
|
||||
|
@ -12,5 +19,3 @@ An [Airsonic] client for Android.
|
|||
<img src="https://play.google.com/intl/en_us/badges/images/generic/en_badge_web_generic.png"
|
||||
alt="Get it on Google Play" height="80" />
|
||||
</a>
|
||||
|
||||
[Airsonic]: https://airsonic.github.io
|
||||
|
|
|
@ -2,26 +2,16 @@ apply plugin: 'com.android.application'
|
|||
apply plugin: 'kotlin-android'
|
||||
|
||||
android {
|
||||
compileSdkVersion 27
|
||||
buildToolsVersion "27.0.3"
|
||||
compileSdkVersion 31
|
||||
buildToolsVersion "31.0.0"
|
||||
|
||||
defaultConfig {
|
||||
applicationId "net.nullsum.audinaut"
|
||||
minSdkVersion 19
|
||||
targetSdkVersion 27
|
||||
versionCode 195
|
||||
versionName '0.3.0'
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 30
|
||||
versionCode 202
|
||||
versionName '0.5.1'
|
||||
setProperty("archivesBaseName", "Audinaut $versionName")
|
||||
resConfigs "de", "es", "fr", "hu", "nl", "pt-rPT", "ru", "sv"
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
packagingOptions {
|
||||
exclude 'META-INF/beans.xml'
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
|
@ -36,15 +26,16 @@ android {
|
|||
|
||||
dependencies {
|
||||
implementation 'com.esotericsoftware:kryo:4.0.2'
|
||||
implementation "com.android.support:design:$android_support_version"
|
||||
implementation 'com.sothree.slidinguppanel:library:3.3.1'
|
||||
implementation 'com.squareup.okhttp3:okhttp:3.10.0'
|
||||
implementation 'com.google.android.material:material:1.5.0'
|
||||
implementation 'com.github.hannesa2:AndroidSlidingUpPanel:4.4.1'
|
||||
implementation 'com.squareup.okhttp3:okhttp:4.9.3'
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||
implementation "androidx.media:media:1.5.0"
|
||||
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
||||
}
|
||||
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.2.31'
|
||||
ext.android_support_version = '27.1.0'
|
||||
ext.kotlin_version = '1.6.0'
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
|
|
@ -13,9 +13,11 @@
|
|||
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
|
||||
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />
|
||||
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission
|
||||
android:name="android.permission.READ_PHONE_STATE"
|
||||
android:maxSdkVersion="22" />
|
||||
|
@ -44,6 +46,8 @@
|
|||
android:backupAgent="net.nullsum.audinaut.util.SettingsBackupAgent"
|
||||
android:icon="@drawable/launch"
|
||||
android:label="@string/common.appname"
|
||||
android:usesCleartextTraffic="true"
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
android:theme="@style/LaunchScreen">
|
||||
|
||||
<uses-library android:name="android.test.runner" />
|
||||
|
|
|
@ -15,11 +15,8 @@
|
|||
|
||||
package net.nullsum.audinaut.activity;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.widget.DrawerLayout;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
|
@ -30,6 +27,10 @@ import android.widget.CheckBox;
|
|||
import android.widget.EditText;
|
||||
import android.widget.Spinner;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.drawerlayout.widget.DrawerLayout;
|
||||
|
||||
import net.nullsum.audinaut.R;
|
||||
import net.nullsum.audinaut.domain.Genre;
|
||||
import net.nullsum.audinaut.service.MusicService;
|
||||
|
@ -58,7 +59,7 @@ public class EditPlayActionActivity extends SubsonicActivity {
|
|||
super.onCreate(savedInstanceState);
|
||||
setTitle(R.string.tasker_start_playing_title);
|
||||
setContentView(R.layout.edit_play_action);
|
||||
final Activity context = this;
|
||||
final AppCompatActivity context = this;
|
||||
doNothing = context.getResources().getString(R.string.tasker_edit_do_nothing);
|
||||
|
||||
shuffleCheckbox = findViewById(R.id.edit_shuffle_checkbox);
|
||||
|
@ -218,12 +219,12 @@ public class EditPlayActionActivity extends SubsonicActivity {
|
|||
|
||||
intent.putExtra(Constants.TASKER_EXTRA_BUNDLE, data);
|
||||
|
||||
setResult(Activity.RESULT_OK, intent);
|
||||
setResult(AppCompatActivity.RESULT_OK, intent);
|
||||
finish();
|
||||
}
|
||||
|
||||
private void cancel() {
|
||||
setResult(Activity.RESULT_CANCELED);
|
||||
setResult(AppCompatActivity.RESULT_CANCELED);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,11 +19,12 @@
|
|||
|
||||
package net.nullsum.audinaut.activity;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.SearchManager;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import net.nullsum.audinaut.util.Constants;
|
||||
import net.nullsum.audinaut.util.Util;
|
||||
|
||||
|
@ -32,7 +33,7 @@ import net.nullsum.audinaut.util.Util;
|
|||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class QueryReceiverActivity extends Activity {
|
||||
public class QueryReceiverActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
|
@ -64,10 +65,10 @@ public class QueryReceiverActivity extends Activity {
|
|||
intent.putExtra(Constants.INTENT_EXTRA_VIEW_ALBUM, true);
|
||||
if (albumId.indexOf("ar-") == 0) {
|
||||
intent.putExtra(Constants.INTENT_EXTRA_NAME_ARTIST, true);
|
||||
albumId = albumId.replace("ar-", "");
|
||||
albumId = albumId.replaceFirst("ar-", "");
|
||||
} else if (albumId.indexOf("so-") == 0) {
|
||||
intent.putExtra(Constants.INTENT_EXTRA_SEARCH_SONG, name);
|
||||
albumId = albumId.replace("so-", "");
|
||||
albumId = albumId.replaceFirst("so-", "");
|
||||
}
|
||||
intent.putExtra(Constants.INTENT_EXTRA_NAME_ID, albumId);
|
||||
if (name != null) {
|
||||
|
|
|
@ -19,7 +19,8 @@
|
|||
package net.nullsum.audinaut.activity;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
|
||||
import net.nullsum.audinaut.R;
|
||||
import net.nullsum.audinaut.fragments.PreferenceCompatFragment;
|
||||
|
|
|
@ -21,22 +21,11 @@ package net.nullsum.audinaut.activity;
|
|||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.media.AudioManager;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.os.Handler;
|
||||
import android.support.design.widget.NavigationView;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.support.v4.app.FragmentTransaction;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.support.v4.widget.DrawerLayout;
|
||||
import android.support.v7.app.ActionBarDrawerToggle;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.app.AppCompatDelegate;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
|
@ -53,6 +42,18 @@ import android.widget.ImageView;
|
|||
import android.widget.Spinner;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.appcompat.app.ActionBarDrawerToggle;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.app.AppCompatDelegate;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.drawerlayout.widget.DrawerLayout;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
|
||||
import com.google.android.material.navigation.NavigationView;
|
||||
|
||||
import net.nullsum.audinaut.R;
|
||||
import net.nullsum.audinaut.fragments.SubsonicFragment;
|
||||
import net.nullsum.audinaut.service.DownloadService;
|
||||
|
@ -66,8 +67,6 @@ import net.nullsum.audinaut.util.UserUtil;
|
|||
import net.nullsum.audinaut.util.Util;
|
||||
import net.nullsum.audinaut.view.UpdateView;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
@ -80,11 +79,16 @@ public class SubsonicActivity extends AppCompatActivity implements OnItemSelecte
|
|||
private static final int PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE = 1;
|
||||
private static String theme;
|
||||
private static boolean fullScreen;
|
||||
private static boolean actionbarColored;
|
||||
private static ImageLoader IMAGE_LOADER;
|
||||
|
||||
static {
|
||||
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_AUTO);
|
||||
// If Android Pie or older, set night mode by system clock
|
||||
if (Build.VERSION.SDK_INT<29) {
|
||||
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_AUTO);
|
||||
} else {
|
||||
// Else, for Android 10+, follow system dark mode setting
|
||||
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
|
||||
}
|
||||
}
|
||||
|
||||
final List<SubsonicFragment> backStack = new ArrayList<>();
|
||||
|
@ -123,7 +127,6 @@ public class SubsonicActivity extends AppCompatActivity implements OnItemSelecte
|
|||
touchscreen = false;
|
||||
}
|
||||
|
||||
setUncaughtExceptionHandler();
|
||||
applyTheme();
|
||||
applyFullscreen();
|
||||
super.onCreate(bundle);
|
||||
|
@ -144,7 +147,7 @@ public class SubsonicActivity extends AppCompatActivity implements OnItemSelecte
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
|
||||
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
|
||||
switch (requestCode) {
|
||||
case PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE: {
|
||||
// If request is cancelled, the result arrays are empty.
|
||||
|
@ -175,7 +178,7 @@ public class SubsonicActivity extends AppCompatActivity implements OnItemSelecte
|
|||
|
||||
private void createCustomActionBarView() {
|
||||
actionBarSpinner = (Spinner) getLayoutInflater().inflate(R.layout.actionbar_spinner, null);
|
||||
if ((this instanceof SubsonicFragmentActivity || this instanceof SettingsActivity) && (Util.getPreferences(this).getBoolean(Constants.PREFERENCES_KEY_COLOR_ACTION_BAR, true) || ThemeUtil.getThemeRes(this) != R.style.Theme_Audinaut_Light_No_Color)) {
|
||||
if ((this instanceof SubsonicFragmentActivity || this instanceof SettingsActivity) && ThemeUtil.getThemeRes(this) != R.style.Theme_Audinaut_Light) {
|
||||
actionBarSpinner.setBackground(DrawableTint.getTintedDrawableFromColor(this));
|
||||
}
|
||||
spinnerAdapter = new ArrayAdapter(this, android.R.layout.simple_spinner_item);
|
||||
|
@ -193,7 +196,7 @@ public class SubsonicActivity extends AppCompatActivity implements OnItemSelecte
|
|||
|
||||
// Make sure to update theme
|
||||
SharedPreferences prefs = Util.getPreferences(this);
|
||||
if (theme != null && !theme.equals(ThemeUtil.getTheme(this)) || fullScreen != prefs.getBoolean(Constants.PREFERENCES_KEY_FULL_SCREEN, false) || actionbarColored != prefs.getBoolean(Constants.PREFERENCES_KEY_COLOR_ACTION_BAR, true)) {
|
||||
if (theme != null && !theme.equals(ThemeUtil.getTheme(this)) || fullScreen != prefs.getBoolean(Constants.PREFERENCES_KEY_FULL_SCREEN, false)) {
|
||||
restart();
|
||||
overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
|
||||
DrawableTint.wipeTintCache();
|
||||
|
@ -482,7 +485,7 @@ public class SubsonicActivity extends AppCompatActivity implements OnItemSelecte
|
|||
item.setChecked(true);
|
||||
}
|
||||
}
|
||||
drawerHeaderToggle.setImageResource(R.drawable.main_select_server_dark);
|
||||
drawerHeaderToggle.setImageResource(R.drawable.main_select_server);
|
||||
|
||||
showingTabs = true;
|
||||
}
|
||||
|
@ -499,7 +502,7 @@ public class SubsonicActivity extends AppCompatActivity implements OnItemSelecte
|
|||
}
|
||||
}
|
||||
drawerList.getMenu().setGroupCheckable(MENU_GROUP_SERVER, true, true);
|
||||
drawerHeaderToggle.setImageResource(R.drawable.main_select_tabs_dark);
|
||||
drawerHeaderToggle.setImageResource(R.drawable.main_select_tabs);
|
||||
|
||||
showingTabs = false;
|
||||
}
|
||||
|
@ -741,7 +744,6 @@ public class SubsonicActivity extends AppCompatActivity implements OnItemSelecte
|
|||
}
|
||||
|
||||
ThemeUtil.applyTheme(this, theme);
|
||||
actionbarColored = Util.getPreferences(this).getBoolean(Constants.PREFERENCES_KEY_COLOR_ACTION_BAR, true);
|
||||
}
|
||||
|
||||
private void applyFullscreen() {
|
||||
|
@ -827,7 +829,7 @@ public class SubsonicActivity extends AppCompatActivity implements OnItemSelecte
|
|||
if (service != null) {
|
||||
new SilentBackgroundTask<Void>(this) {
|
||||
@Override
|
||||
protected Void doInBackground() throws Throwable {
|
||||
protected Void doInBackground() {
|
||||
service.clearIncomplete();
|
||||
return null;
|
||||
}
|
||||
|
@ -836,7 +838,6 @@ public class SubsonicActivity extends AppCompatActivity implements OnItemSelecte
|
|||
}
|
||||
Util.setActiveServer(this, instance);
|
||||
invalidate();
|
||||
UserUtil.refreshCurrentUser(this);
|
||||
updateDrawerHeader();
|
||||
}
|
||||
}
|
||||
|
@ -864,7 +865,6 @@ public class SubsonicActivity extends AppCompatActivity implements OnItemSelecte
|
|||
service.setOnline(isOffline);
|
||||
}
|
||||
|
||||
UserUtil.seedCurrentUser(this);
|
||||
this.updateDrawerHeader();
|
||||
drawer.closeDrawers();
|
||||
}
|
||||
|
@ -883,50 +883,4 @@ public class SubsonicActivity extends AppCompatActivity implements OnItemSelecte
|
|||
return R.id.drawer_library;
|
||||
}
|
||||
}
|
||||
|
||||
private void setUncaughtExceptionHandler() {
|
||||
Thread.UncaughtExceptionHandler handler = Thread.getDefaultUncaughtExceptionHandler();
|
||||
if (!(handler instanceof SubsonicActivity.SubsonicUncaughtExceptionHandler)) {
|
||||
Thread.setDefaultUncaughtExceptionHandler(new SubsonicActivity.SubsonicUncaughtExceptionHandler(this));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs the stack trace of uncaught exceptions to a file on the SD card.
|
||||
*/
|
||||
private static class SubsonicUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
|
||||
|
||||
private final Thread.UncaughtExceptionHandler defaultHandler;
|
||||
private final Context context;
|
||||
|
||||
private SubsonicUncaughtExceptionHandler(Context context) {
|
||||
this.context = context;
|
||||
defaultHandler = Thread.getDefaultUncaughtExceptionHandler();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uncaughtException(Thread thread, Throwable throwable) {
|
||||
File file = null;
|
||||
PrintWriter printWriter = null;
|
||||
try {
|
||||
|
||||
PackageInfo packageInfo = context.getPackageManager().getPackageInfo("net.nullsum.audinaut", 0);
|
||||
file = new File(Environment.getExternalStorageDirectory(), "audinaut-stacktrace.txt");
|
||||
printWriter = new PrintWriter(file);
|
||||
printWriter.println("Subsonic version name: " + packageInfo.versionName);
|
||||
printWriter.println("Subsonic version code: " + packageInfo.versionCode);
|
||||
printWriter.println();
|
||||
throwable.printStackTrace(printWriter);
|
||||
Log.i(TAG, "Stack trace written to " + file);
|
||||
} catch (Throwable x) {
|
||||
Log.e(TAG, "Failed to write stack trace to " + file, x);
|
||||
} finally {
|
||||
Util.close(printWriter);
|
||||
if (defaultHandler != null) {
|
||||
defaultHandler.uncaughtException(thread, throwable);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,17 +27,19 @@ import android.content.SharedPreferences;
|
|||
import android.content.res.TypedArray;
|
||||
import android.os.Bundle;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.support.v4.app.FragmentTransaction;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
|
||||
import com.sothree.slidinguppanel.PanelSlideListener;
|
||||
import com.sothree.slidinguppanel.PanelState;
|
||||
import com.sothree.slidinguppanel.SlidingUpPanelLayout;
|
||||
import com.sothree.slidinguppanel.SlidingUpPanelLayout.PanelState;
|
||||
|
||||
import net.nullsum.audinaut.R;
|
||||
import net.nullsum.audinaut.domain.MusicDirectory;
|
||||
|
@ -55,7 +57,6 @@ import net.nullsum.audinaut.updates.Updater;
|
|||
import net.nullsum.audinaut.util.Constants;
|
||||
import net.nullsum.audinaut.util.FileUtil;
|
||||
import net.nullsum.audinaut.util.SilentBackgroundTask;
|
||||
import net.nullsum.audinaut.util.UserUtil;
|
||||
import net.nullsum.audinaut.util.Util;
|
||||
|
||||
import java.io.File;
|
||||
|
@ -68,8 +69,9 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
|
|||
private static boolean infoDialogDisplayed;
|
||||
private static boolean sessionInitialized = false;
|
||||
private SlidingUpPanelLayout slideUpPanel;
|
||||
private SlidingUpPanelLayout.PanelSlideListener panelSlideListener;
|
||||
private PanelSlideListener panelSlideListener;
|
||||
private boolean isPanelClosing = false;
|
||||
private boolean resuming = false;
|
||||
private NowPlayingFragment nowPlayingFragment;
|
||||
private SubsonicFragment secondaryFragment;
|
||||
private Toolbar mainToolbar;
|
||||
|
@ -166,9 +168,11 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
|
|||
}
|
||||
|
||||
slideUpPanel = findViewById(R.id.slide_up_panel);
|
||||
panelSlideListener = new SlidingUpPanelLayout.PanelSlideListener() {
|
||||
panelSlideListener = new PanelSlideListener() {
|
||||
@Override
|
||||
public void onPanelSlide(View panel, float slideOffset) {}
|
||||
public void onPanelSlide(View panel, float slideOffset) {
|
||||
Util.hideKeyboard(panel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPanelStateChanged(View panel, PanelState previousState, PanelState newState) {
|
||||
|
@ -232,7 +236,7 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
|
|||
rewindButton = findViewById(R.id.download_rewind);
|
||||
rewindButton.setOnClickListener(v -> new SilentBackgroundTask<Void>(SubsonicFragmentActivity.this) {
|
||||
@Override
|
||||
protected Void doInBackground() throws Throwable {
|
||||
protected Void doInBackground() {
|
||||
if (getDownloadService() != null) {
|
||||
getDownloadService().rewind();
|
||||
}
|
||||
|
@ -243,7 +247,7 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
|
|||
previousButton = findViewById(R.id.download_previous);
|
||||
previousButton.setOnClickListener(v -> new SilentBackgroundTask<Void>(SubsonicFragmentActivity.this) {
|
||||
@Override
|
||||
protected Void doInBackground() throws Throwable {
|
||||
protected Void doInBackground() {
|
||||
if (getDownloadService() != null) {
|
||||
getDownloadService().previous();
|
||||
}
|
||||
|
@ -254,7 +258,7 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
|
|||
startButton = findViewById(R.id.download_start);
|
||||
startButton.setOnClickListener(v -> new SilentBackgroundTask<Void>(SubsonicFragmentActivity.this) {
|
||||
@Override
|
||||
protected Void doInBackground() throws Throwable {
|
||||
protected Void doInBackground() {
|
||||
PlayerState state = getDownloadService().getPlayerState();
|
||||
if (state == PlayerState.STARTED) {
|
||||
getDownloadService().pause();
|
||||
|
@ -268,7 +272,7 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
|
|||
nextButton = findViewById(R.id.download_next);
|
||||
nextButton.setOnClickListener(v -> new SilentBackgroundTask<Void>(SubsonicFragmentActivity.this) {
|
||||
@Override
|
||||
protected Void doInBackground() throws Throwable {
|
||||
protected Void doInBackground() {
|
||||
if (getDownloadService() != null) {
|
||||
getDownloadService().next();
|
||||
}
|
||||
|
@ -279,7 +283,7 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
|
|||
fastforwardButton = findViewById(R.id.download_fastforward);
|
||||
fastforwardButton.setOnClickListener(v -> new SilentBackgroundTask<Void>(SubsonicFragmentActivity.this) {
|
||||
@Override
|
||||
protected Void doInBackground() throws Throwable {
|
||||
protected Void doInBackground() {
|
||||
if (getDownloadService() == null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -310,7 +314,7 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
|
|||
super.onNewIntent(intent);
|
||||
|
||||
if (currentFragment != null && intent.getStringExtra(Constants.INTENT_EXTRA_NAME_QUERY) != null) {
|
||||
if (slideUpPanel.getPanelState() == SlidingUpPanelLayout.PanelState.EXPANDED) {
|
||||
if (isNowPlayingOpen()) {
|
||||
closeNowPlaying();
|
||||
}
|
||||
|
||||
|
@ -329,7 +333,7 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
|
|||
replaceFragment(fragment, fragment.getSupportTag());
|
||||
}
|
||||
} else if (intent.getBooleanExtra(Constants.INTENT_EXTRA_NAME_DOWNLOAD, false)) {
|
||||
if (slideUpPanel.getPanelState() != SlidingUpPanelLayout.PanelState.EXPANDED) {
|
||||
if (!isNowPlayingOpen()) {
|
||||
openNowPlaying();
|
||||
}
|
||||
} else {
|
||||
|
@ -342,6 +346,7 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
|
|||
|
||||
@Override
|
||||
public void onResume() {
|
||||
resuming = true;
|
||||
super.onResume();
|
||||
|
||||
if (getIntent().hasExtra(Constants.INTENT_EXTRA_VIEW_ALBUM)) {
|
||||
|
@ -362,9 +367,9 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
|
|||
getIntent().removeExtra(Constants.INTENT_EXTRA_VIEW_ALBUM);
|
||||
}
|
||||
|
||||
UserUtil.seedCurrentUser(this);
|
||||
createAccount();
|
||||
runWhenServiceAvailable(() -> getDownloadService().addOnSongChangedListener(SubsonicFragmentActivity.this));
|
||||
resuming = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -410,17 +415,19 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
|
|||
drawerToggle.setDrawerIndicatorEnabled(false);
|
||||
}
|
||||
|
||||
if (savedInstanceState.getInt(Constants.MAIN_SLIDE_PANEL_STATE, -1) == SlidingUpPanelLayout.PanelState.EXPANDED.hashCode()) {
|
||||
if (savedInstanceState.getInt(Constants.MAIN_SLIDE_PANEL_STATE, -1) == PanelState.EXPANDED.hashCode()) {
|
||||
panelSlideListener.onPanelStateChanged(null, null, PanelState.EXPANDED);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (slideUpPanel.getPanelState() == SlidingUpPanelLayout.PanelState.EXPANDED && secondaryFragment == null) {
|
||||
slideUpPanel.setPanelState(SlidingUpPanelLayout.PanelState.COLLAPSED);
|
||||
} else if (slideUpPanel.getPanelState() == SlidingUpPanelLayout.PanelState.EXPANDED) {
|
||||
removeCurrent();
|
||||
if (isNowPlayingOpen()) {
|
||||
if (secondaryFragment == null) {
|
||||
closeNowPlaying();
|
||||
} else {
|
||||
removeCurrent();
|
||||
}
|
||||
} else {
|
||||
super.onBackPressed();
|
||||
}
|
||||
|
@ -428,7 +435,7 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
|
|||
|
||||
@Override
|
||||
public SubsonicFragment getCurrentFragment() {
|
||||
if (slideUpPanel.getPanelState() == SlidingUpPanelLayout.PanelState.EXPANDED) {
|
||||
if (isNowPlayingOpen()) {
|
||||
if (secondaryFragment == null) {
|
||||
return nowPlayingFragment;
|
||||
} else {
|
||||
|
@ -441,7 +448,7 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
|
|||
|
||||
@Override
|
||||
public void replaceFragment(SubsonicFragment fragment, int tag, boolean replaceCurrent) {
|
||||
if (slideUpPanel != null && slideUpPanel.getPanelState() == SlidingUpPanelLayout.PanelState.EXPANDED && !isPanelClosing) {
|
||||
if (slideUpPanel != null && isNowPlayingOpen() && !isPanelClosing) {
|
||||
secondaryFragment = fragment;
|
||||
nowPlayingFragment.setPrimaryFragment(false);
|
||||
secondaryFragment.setPrimaryFragment(true);
|
||||
|
@ -459,7 +466,7 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
|
|||
|
||||
@Override
|
||||
public void removeCurrent() {
|
||||
if (slideUpPanel.getPanelState() == SlidingUpPanelLayout.PanelState.EXPANDED && secondaryFragment != null) {
|
||||
if (isNowPlayingOpen() && secondaryFragment != null) {
|
||||
FragmentTransaction trans = getSupportFragmentManager().beginTransaction();
|
||||
trans.setCustomAnimations(R.anim.enter_from_left, R.anim.exit_to_right, R.anim.enter_from_right, R.anim.exit_to_left);
|
||||
trans.remove(secondaryFragment);
|
||||
|
@ -476,7 +483,7 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
|
|||
|
||||
@Override
|
||||
public void setTitle(CharSequence title) {
|
||||
if (slideUpPanel.getPanelState() == SlidingUpPanelLayout.PanelState.EXPANDED) {
|
||||
if (isNowPlayingOpen()) {
|
||||
getSupportActionBar().setTitle(title);
|
||||
} else {
|
||||
super.setTitle(title);
|
||||
|
@ -487,8 +494,8 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
|
|||
protected void drawerItemSelected(String fragmentType) {
|
||||
super.drawerItemSelected(fragmentType);
|
||||
|
||||
if (slideUpPanel.getPanelState() == SlidingUpPanelLayout.PanelState.EXPANDED) {
|
||||
slideUpPanel.setPanelState(SlidingUpPanelLayout.PanelState.COLLAPSED);
|
||||
if (isNowPlayingOpen() && !resuming) {
|
||||
closeNowPlaying();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -527,15 +534,19 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
|
|||
|
||||
@Override
|
||||
public void openNowPlaying() {
|
||||
slideUpPanel.setPanelState(SlidingUpPanelLayout.PanelState.EXPANDED);
|
||||
slideUpPanel.setPanelState(PanelState.EXPANDED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeNowPlaying() {
|
||||
slideUpPanel.setPanelState(SlidingUpPanelLayout.PanelState.COLLAPSED);
|
||||
slideUpPanel.setPanelState(PanelState.COLLAPSED);
|
||||
isPanelClosing = true;
|
||||
}
|
||||
|
||||
private boolean isNowPlayingOpen() {
|
||||
return slideUpPanel.getPanelState() == PanelState.EXPANDED;
|
||||
}
|
||||
|
||||
private SubsonicFragment getNewFragment(String fragmentType) {
|
||||
switch (fragmentType) {
|
||||
case "Playlist":
|
||||
|
@ -598,13 +609,13 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
|
|||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void createAccount() {
|
||||
final Context context = this;
|
||||
|
||||
new SilentBackgroundTask<Void>(this) {
|
||||
@Override
|
||||
protected Void doInBackground() throws Throwable {
|
||||
protected Void doInBackground() {
|
||||
AccountManager accountManager = (AccountManager) context.getSystemService(ACCOUNT_SERVICE);
|
||||
Account account = new Account(Constants.SYNC_ACCOUNT_NAME, Constants.SYNC_ACCOUNT_TYPE);
|
||||
accountManager.addAccountExplicitly(account, null, null);
|
||||
|
@ -693,7 +704,7 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo
|
|||
getImageLoader().loadImage(coverArtView, song, false, height, false);
|
||||
|
||||
// We need to update it immediately since it won't update if updater is not running for it
|
||||
if (nowPlayingFragment != null && slideUpPanel.getPanelState() == SlidingUpPanelLayout.PanelState.COLLAPSED) {
|
||||
if (nowPlayingFragment != null && slideUpPanel.getPanelState() == PanelState.COLLAPSED) {
|
||||
nowPlayingFragment.onMetadataUpdate(song, fieldChange);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,12 +19,13 @@
|
|||
|
||||
package net.nullsum.audinaut.activity;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.SearchManager;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.provider.MediaStore;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import net.nullsum.audinaut.util.Constants;
|
||||
import net.nullsum.audinaut.util.Util;
|
||||
|
||||
|
@ -35,7 +36,7 @@ import net.nullsum.audinaut.util.Util;
|
|||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class VoiceQueryReceiverActivity extends Activity {
|
||||
public class VoiceQueryReceiverActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
|
|
|
@ -16,12 +16,13 @@
|
|||
package net.nullsum.audinaut.adapter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.v7.widget.PopupMenu;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.appcompat.widget.PopupMenu;
|
||||
|
||||
import net.nullsum.audinaut.R;
|
||||
import net.nullsum.audinaut.domain.Artist;
|
||||
import net.nullsum.audinaut.domain.MusicDirectory.Entry;
|
||||
|
|
|
@ -17,21 +17,17 @@ package net.nullsum.audinaut.adapter;
|
|||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Build;
|
||||
import android.support.v7.view.ActionMode;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.util.Log;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.PopupMenu;
|
||||
|
||||
import androidx.appcompat.view.ActionMode;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import net.nullsum.audinaut.R;
|
||||
import net.nullsum.audinaut.activity.SubsonicFragmentActivity;
|
||||
import net.nullsum.audinaut.util.Constants;
|
||||
|
@ -390,17 +386,6 @@ public abstract class SectionAdapter<T> extends RecyclerView.Adapter<UpdateViewH
|
|||
MenuUtil.hideMenuItems(context, menu, updateView);
|
||||
|
||||
mode.setTitle(context.getResources().getString(R.string.select_album_n_selected, selected.size()));
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && Util.getPreferences(context).getBoolean(Constants.PREFERENCES_KEY_COLOR_ACTION_BAR, true)) {
|
||||
TypedValue typedValue = new TypedValue();
|
||||
Resources.Theme theme = context.getTheme();
|
||||
theme.resolveAttribute(R.attr.colorPrimaryDark, typedValue, true);
|
||||
int colorPrimaryDark = typedValue.data;
|
||||
|
||||
Window window = ((SubsonicFragmentActivity) context).getWindow();
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
|
||||
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
|
||||
window.setStatusBarColor(colorPrimaryDark);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -427,11 +412,6 @@ public abstract class SectionAdapter<T> extends RecyclerView.Adapter<UpdateViewH
|
|||
updateView.setChecked(false);
|
||||
}
|
||||
selectedViews.clear();
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && Util.getPreferences(context).getBoolean(Constants.PREFERENCES_KEY_COLOR_ACTION_BAR, true)) {
|
||||
Window window = ((SubsonicFragmentActivity) context).getWindow();
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@ package net.nullsum.audinaut.fragments;
|
|||
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.support.v7.widget.helper.ItemTouchHelper;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
|
@ -25,6 +24,8 @@ import android.view.MenuItem;
|
|||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||
|
||||
import net.nullsum.audinaut.R;
|
||||
import net.nullsum.audinaut.adapter.DownloadFileAdapter;
|
||||
import net.nullsum.audinaut.adapter.SectionAdapter;
|
||||
|
@ -91,7 +92,7 @@ public class DownloadFragment extends SelectRecyclerFragment<DownloadFile> imple
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<DownloadFile> getObjects(MusicService musicService, boolean refresh, ProgressListener listener) throws Exception {
|
||||
public List<DownloadFile> getObjects(MusicService musicService, boolean refresh, ProgressListener listener) {
|
||||
DownloadService downloadService = getDownloadService();
|
||||
if (downloadService == null) {
|
||||
return new ArrayList<>();
|
||||
|
@ -139,7 +140,7 @@ public class DownloadFragment extends SelectRecyclerFragment<DownloadFile> imple
|
|||
case R.id.menu_remove_all:
|
||||
Util.confirmDialog(context, R.string.download_menu_remove_all, "", (dialog, which) -> new SilentBackgroundTask<Void>(context) {
|
||||
@Override
|
||||
protected Void doInBackground() throws Throwable {
|
||||
protected Void doInBackground() {
|
||||
getDownloadService().clearBackground();
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -65,7 +65,7 @@ public class MainFragment extends SelectRecyclerFragment<Integer> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<Integer> getObjects(MusicService musicService, boolean refresh, ProgressListener listener) throws Exception {
|
||||
public List<Integer> getObjects(MusicService musicService, boolean refresh, ProgressListener listener) {
|
||||
return Collections.singletonList(0);
|
||||
}
|
||||
|
||||
|
|
|
@ -17,10 +17,6 @@ package net.nullsum.audinaut.fragments;
|
|||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.support.v7.widget.helper.ItemTouchHelper;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.GestureDetector.OnGestureListener;
|
||||
|
@ -38,6 +34,10 @@ import android.widget.SeekBar;
|
|||
import android.widget.TextView;
|
||||
import android.widget.ViewFlipper;
|
||||
|
||||
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import net.nullsum.audinaut.R;
|
||||
import net.nullsum.audinaut.activity.SubsonicFragmentActivity;
|
||||
import net.nullsum.audinaut.adapter.DownloadFileAdapter;
|
||||
|
@ -56,17 +56,12 @@ import net.nullsum.audinaut.util.MenuUtil;
|
|||
import net.nullsum.audinaut.util.SilentBackgroundTask;
|
||||
import net.nullsum.audinaut.util.Util;
|
||||
import net.nullsum.audinaut.view.AutoRepeatButton;
|
||||
import net.nullsum.audinaut.view.FadeOutAnimation;
|
||||
import net.nullsum.audinaut.view.FastScroller;
|
||||
import net.nullsum.audinaut.view.UpdateView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static net.nullsum.audinaut.domain.MusicDirectory.Entry;
|
||||
import static net.nullsum.audinaut.domain.PlayerState.COMPLETED;
|
||||
|
@ -86,7 +81,9 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
|
|||
private TextView emptyTextView;
|
||||
private TextView songTitleTextView;
|
||||
private ImageView albumArtImageView;
|
||||
private ImageView albumArtBackgroundView;
|
||||
private View albumArtBackgroundView;
|
||||
private ImageView albumArtBackgroundImageView;
|
||||
private View nowPlayingView;
|
||||
private RecyclerView playlistView;
|
||||
private TextView positionTextView;
|
||||
private TextView durationTextView;
|
||||
|
@ -102,11 +99,9 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
|
|||
private ImageButton repeatButton;
|
||||
private View toggleListButton;
|
||||
|
||||
private ScheduledExecutorService executorService;
|
||||
private DownloadFile currentPlaying;
|
||||
private int swipeDistance;
|
||||
private int swipeVelocity;
|
||||
private ScheduledFuture<?> hideControlsFuture;
|
||||
private List<DownloadFile> songList;
|
||||
private DownloadFileAdapter songListAdapter;
|
||||
private boolean seekInProgress = false;
|
||||
|
@ -154,6 +149,8 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
|
|||
songTitleTextView = rootView.findViewById(R.id.download_song_title);
|
||||
albumArtImageView = rootView.findViewById(R.id.download_album_art_image);
|
||||
albumArtBackgroundView = rootView.findViewById(R.id.download_album_art_background);
|
||||
albumArtBackgroundImageView = rootView.findViewById(R.id.download_album_art_background_image);
|
||||
nowPlayingView = rootView.findViewById(R.id.now_playing_top);
|
||||
positionTextView = rootView.findViewById(R.id.download_position);
|
||||
durationTextView = rootView.findViewById(R.id.download_duration);
|
||||
statusTextView = rootView.findViewById(R.id.download_status);
|
||||
|
@ -191,12 +188,11 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
|
|||
warnIfStorageUnavailable();
|
||||
new SilentBackgroundTask<Void>(context) {
|
||||
@Override
|
||||
protected Void doInBackground() throws Throwable {
|
||||
protected Void doInBackground() {
|
||||
getDownloadService().previous();
|
||||
return null;
|
||||
}
|
||||
}.execute();
|
||||
setControlsVisible(true);
|
||||
});
|
||||
previousButton.setOnRepeatListener(() -> changeProgress(true));
|
||||
|
||||
|
@ -204,12 +200,11 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
|
|||
warnIfStorageUnavailable();
|
||||
new SilentBackgroundTask<Boolean>(context) {
|
||||
@Override
|
||||
protected Boolean doInBackground() throws Throwable {
|
||||
protected Boolean doInBackground() {
|
||||
getDownloadService().next();
|
||||
return true;
|
||||
}
|
||||
}.execute();
|
||||
setControlsVisible(true);
|
||||
});
|
||||
nextButton.setOnRepeatListener(() -> changeProgress(false));
|
||||
|
||||
|
@ -222,7 +217,7 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
|
|||
|
||||
pauseButton.setOnClickListener(view -> new SilentBackgroundTask<Void>(context) {
|
||||
@Override
|
||||
protected Void doInBackground() throws Throwable {
|
||||
protected Void doInBackground() {
|
||||
getDownloadService().pause();
|
||||
return null;
|
||||
}
|
||||
|
@ -230,7 +225,7 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
|
|||
|
||||
stopButton.setOnClickListener(view -> new SilentBackgroundTask<Void>(context) {
|
||||
@Override
|
||||
protected Void doInBackground() throws Throwable {
|
||||
protected Void doInBackground() {
|
||||
getDownloadService().reset();
|
||||
return null;
|
||||
}
|
||||
|
@ -240,7 +235,7 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
|
|||
warnIfStorageUnavailable();
|
||||
new SilentBackgroundTask<Void>(context) {
|
||||
@Override
|
||||
protected Void doInBackground() throws Throwable {
|
||||
protected Void doInBackground() {
|
||||
start();
|
||||
return null;
|
||||
}
|
||||
|
@ -264,29 +259,23 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
|
|||
break;
|
||||
}
|
||||
updateRepeatButton();
|
||||
setControlsVisible(true);
|
||||
});
|
||||
|
||||
toggleListButton.setOnClickListener(view -> {
|
||||
toggleFullscreenAlbumArt();
|
||||
setControlsVisible(true);
|
||||
});
|
||||
|
||||
View overlay = rootView.findViewById(R.id.download_overlay_buttons);
|
||||
final int overlayHeight = overlay != null ? overlay.getHeight() : -1;
|
||||
albumArtImageView.setOnClickListener(view -> {
|
||||
if (overlayHeight == -1 || lastY < (view.getBottom() - overlayHeight)) {
|
||||
toggleFullscreenAlbumArt();
|
||||
setControlsVisible(true);
|
||||
}
|
||||
});
|
||||
// seems pointless, but allows swipe gestures to work
|
||||
albumArtImageView.setOnClickListener(view -> {});
|
||||
|
||||
progressBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
|
||||
@Override
|
||||
public void onStopTrackingTouch(final SeekBar seekBar) {
|
||||
new SilentBackgroundTask<Void>(context) {
|
||||
@Override
|
||||
protected Void doInBackground() throws Throwable {
|
||||
protected Void doInBackground() {
|
||||
getDownloadService().seekTo(progressBar.getProgress());
|
||||
return null;
|
||||
}
|
||||
|
@ -307,7 +296,6 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
|
|||
public void onProgressChanged(final SeekBar seekBar, final int position, final boolean fromUser) {
|
||||
if (fromUser) {
|
||||
positionTextView.setText(Util.formatDuration(position / 1000));
|
||||
setControlsVisible(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -318,16 +306,16 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
|
|||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
|
||||
DownloadService downloadService = getDownloadService();
|
||||
menuInflater.inflate(R.menu.nowplaying, menu);
|
||||
|
||||
if (Util.isOffline(context)) {
|
||||
menuInflater.inflate(R.menu.nowplaying_offline, menu);
|
||||
} else {
|
||||
menuInflater.inflate(R.menu.nowplaying, menu);
|
||||
}
|
||||
if (downloadService != null && downloadService.isRemovePlayed()) {
|
||||
menu.findItem(R.id.menu_remove_played).setChecked(true);
|
||||
menu.findItem(R.id.menu_save_playlist).setEnabled(false);
|
||||
}
|
||||
|
||||
if (downloadService != null) {
|
||||
if (downloadService.isRemovePlayed()) {
|
||||
menu.findItem(R.id.menu_remove_played).setChecked(true);
|
||||
}
|
||||
SharedPreferences prefs = Util.getPreferences(context);
|
||||
boolean equalizerOn = prefs.getBoolean(Constants.PREFERENCES_EQUALIZER_ON, false);
|
||||
if (equalizerOn) {
|
||||
|
@ -421,7 +409,7 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
|
|||
case R.id.menu_remove_all:
|
||||
Util.confirmDialog(context, R.string.download_menu_remove_all, "", (dialog, which) -> new SilentBackgroundTask<Void>(context) {
|
||||
@Override
|
||||
protected Void doInBackground() throws Throwable {
|
||||
protected Void doInBackground() {
|
||||
getDownloadService().setShufflePlayEnabled(false);
|
||||
getDownloadService().clear();
|
||||
return null;
|
||||
|
@ -444,7 +432,7 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
|
|||
case R.id.menu_shuffle:
|
||||
new SilentBackgroundTask<Void>(context) {
|
||||
@Override
|
||||
protected Void doInBackground() throws Throwable {
|
||||
protected Void doInBackground() {
|
||||
getDownloadService().shuffle();
|
||||
return null;
|
||||
}
|
||||
|
@ -472,7 +460,6 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
|
|||
if (controller != null) {
|
||||
SubsonicFragment fragment = new EqualizerFragment();
|
||||
replaceFragment(fragment);
|
||||
setControlsVisible(true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -509,19 +496,6 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
|
|||
}
|
||||
|
||||
private void onResumeHandlers() {
|
||||
executorService = Executors.newSingleThreadScheduledExecutor();
|
||||
setControlsVisible(true);
|
||||
|
||||
final DownloadService downloadService = getDownloadService();
|
||||
if (downloadService == null || downloadService.getCurrentPlaying() == null || startFlipped) {
|
||||
playlistFlipper.setDisplayedChild(1);
|
||||
startFlipped = false;
|
||||
}
|
||||
|
||||
if (currentPlaying == null && downloadService != null && null == downloadService.getCurrentPlaying()) {
|
||||
setAlbumArt(null, false);
|
||||
}
|
||||
|
||||
context.runWhenServiceAvailable(() -> {
|
||||
if (primaryFragment) {
|
||||
DownloadService downloadService1 = getDownloadService();
|
||||
|
@ -539,12 +513,9 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
|
|||
}
|
||||
|
||||
private void onPauseHandlers() {
|
||||
if (executorService != null) {
|
||||
DownloadService downloadService = getDownloadService();
|
||||
if (downloadService != null) {
|
||||
downloadService.removeOnSongChangeListener(this);
|
||||
}
|
||||
playlistFlipper.setDisplayedChild(0);
|
||||
DownloadService downloadService = getDownloadService();
|
||||
if (downloadService != null) {
|
||||
downloadService.removeOnSongChangeListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -581,29 +552,6 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
|
|||
return songListAdapter;
|
||||
}
|
||||
|
||||
private void scheduleHideControls() {
|
||||
if (hideControlsFuture != null) {
|
||||
hideControlsFuture.cancel(false);
|
||||
}
|
||||
|
||||
final Handler handler = new Handler();
|
||||
Runnable runnable = () -> handler.post(() -> setControlsVisible(false));
|
||||
hideControlsFuture = executorService.schedule(runnable, 3000L, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
private void setControlsVisible(boolean visible) {
|
||||
try {
|
||||
long duration = 1700L;
|
||||
FadeOutAnimation.createAndStart(rootView.findViewById(R.id.download_overlay_buttons), !visible, duration);
|
||||
|
||||
if (visible) {
|
||||
scheduleHideControls();
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Scroll to current playing/downloading.
|
||||
private void scrollToCurrent() {
|
||||
if (getDownloadService() == null || songListAdapter == null) {
|
||||
|
@ -675,7 +623,7 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
|
|||
int seekTo;
|
||||
|
||||
@Override
|
||||
protected Void doInBackground() throws Throwable {
|
||||
protected Void doInBackground() {
|
||||
if (rewind) {
|
||||
seekTo = downloadService.rewind();
|
||||
} else {
|
||||
|
@ -693,7 +641,6 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
|
|||
|
||||
@Override
|
||||
public boolean onDown(MotionEvent me) {
|
||||
setControlsVisible(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -713,6 +660,8 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
|
|||
else if (e2.getX() - e1.getX() > swipeDistance && Math.abs(velocityX) > swipeVelocity) {
|
||||
action = ACTION_PREVIOUS;
|
||||
}
|
||||
/*
|
||||
* too finnicky, no visual feedback
|
||||
// Top to Bottom swipe
|
||||
else if (e2.getY() - e1.getY() > swipeDistance && Math.abs(velocityY) > swipeVelocity) {
|
||||
action = ACTION_FORWARD;
|
||||
|
@ -721,13 +670,14 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
|
|||
else if (e1.getY() - e2.getY() > swipeDistance && Math.abs(velocityY) > swipeVelocity) {
|
||||
action = ACTION_REWIND;
|
||||
}
|
||||
*/
|
||||
|
||||
if (action > 0) {
|
||||
final int performAction = action;
|
||||
warnIfStorageUnavailable();
|
||||
new SilentBackgroundTask<Void>(context) {
|
||||
@Override
|
||||
protected Void doInBackground() throws Throwable {
|
||||
protected Void doInBackground() {
|
||||
switch (performAction) {
|
||||
case ACTION_NEXT:
|
||||
downloadService.next();
|
||||
|
@ -775,7 +725,7 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
|
|||
warnIfStorageUnavailable();
|
||||
new SilentBackgroundTask<Void>(context) {
|
||||
@Override
|
||||
protected Void doInBackground() throws Throwable {
|
||||
protected Void doInBackground() {
|
||||
getDownloadService().play(item);
|
||||
return null;
|
||||
}
|
||||
|
@ -810,11 +760,6 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
|
|||
setAlbumArt(song, true);
|
||||
|
||||
DownloadService downloadService = getDownloadService();
|
||||
if (downloadService.isShufflePlayEnabled()) {
|
||||
setSubtitle(context.getResources().getString(R.string.download_playerstate_playing_shuffle));
|
||||
} else {
|
||||
setSubtitle(context.getResources().getString(R.string.download_playing_out_of, currentPlayingIndex + 1, currentPlayingSize));
|
||||
}
|
||||
} else {
|
||||
songTitleTextView.setText(null);
|
||||
setAlbumArt(null, false);
|
||||
|
@ -943,7 +888,7 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis
|
|||
getImageLoader().loadImage(albumArtImageView, song, true, crossfade);
|
||||
if (Util.getPreferences(context).getBoolean(Constants.PREFERENCES_KEY_BLURRED_BACKGROUND, true)) {
|
||||
albumArtBackgroundView.setVisibility(ImageView.VISIBLE);
|
||||
getImageLoader().loadBlurImage(albumArtBackgroundView, song, true, crossfade);
|
||||
getImageLoader().loadBlurImage(albumArtBackgroundImageView, song, true, crossfade);
|
||||
} else {
|
||||
albumArtBackgroundView.setVisibility(ImageView.GONE);
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
package net.nullsum.audinaut.fragments;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.widget.GridLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
|
@ -10,6 +8,10 @@ import android.view.MenuItem;
|
|||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.core.view.MenuItemCompat;
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import net.nullsum.audinaut.R;
|
||||
import net.nullsum.audinaut.adapter.ArtistAdapter;
|
||||
import net.nullsum.audinaut.adapter.EntryGridAdapter;
|
||||
|
@ -203,7 +205,7 @@ public class SearchFragment extends SubsonicFragment implements SectionAdapter.O
|
|||
task.execute();
|
||||
|
||||
if (searchItem != null) {
|
||||
searchItem.collapseActionView();
|
||||
MenuItemCompat.collapseActionView(searchItem);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -169,7 +169,7 @@ public class SelectArtistFragment extends SelectRecyclerFragment<Serializable> i
|
|||
String musicFolderId = Util.getSelectedMusicFolderId(context);
|
||||
|
||||
Indexes indexes = musicService.getIndexes(musicFolderId, refresh, context, listener);
|
||||
indexes.sortChildren(context);
|
||||
indexes.sortChildren();
|
||||
items = new ArrayList<>(indexes.getShortcuts().size() + indexes.getArtists().size());
|
||||
items.addAll(indexes.getShortcuts());
|
||||
items.addAll(indexes.getArtists());
|
||||
|
@ -184,8 +184,7 @@ public class SelectArtistFragment extends SelectRecyclerFragment<Serializable> i
|
|||
}
|
||||
|
||||
Indexes indexes = new Indexes();
|
||||
//indexes.setArtists = artists;
|
||||
indexes.sortChildren(context);
|
||||
indexes.sortChildren();
|
||||
items = new ArrayList<>(indexes.getArtists());
|
||||
|
||||
entries = dir.getChildren(false, true);
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
package net.nullsum.audinaut.fragments;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.widget.GridLayoutManager;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
|
@ -11,6 +8,10 @@ import android.view.MenuItem;
|
|||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import net.nullsum.audinaut.R;
|
||||
import net.nullsum.audinaut.adapter.AlphabeticalAlbumAdapter;
|
||||
import net.nullsum.audinaut.adapter.EntryGridAdapter;
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package net.nullsum.audinaut.fragments;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
|
@ -9,6 +8,8 @@ import android.view.View;
|
|||
import android.widget.CheckBox;
|
||||
import android.widget.EditText;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
import net.nullsum.audinaut.R;
|
||||
import net.nullsum.audinaut.adapter.PlaylistAdapter;
|
||||
import net.nullsum.audinaut.adapter.SectionAdapter;
|
||||
|
|
|
@ -16,8 +16,6 @@
|
|||
package net.nullsum.audinaut.fragments;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.widget.GridLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
|
@ -25,6 +23,9 @@ import android.view.MenuInflater;
|
|||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import net.nullsum.audinaut.R;
|
||||
import net.nullsum.audinaut.adapter.SectionAdapter;
|
||||
import net.nullsum.audinaut.service.MusicService;
|
||||
|
@ -176,7 +177,7 @@ public abstract class SelectRecyclerFragment<T> extends SubsonicFragment impleme
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<T> doInBackground() throws Exception {
|
||||
public List<T> doInBackground() {
|
||||
MusicService musicService = MusicServiceFactory.getMusicService(context);
|
||||
|
||||
objects = new ArrayList<>();
|
||||
|
|
|
@ -51,7 +51,7 @@ public class SelectYearFragment extends SelectRecyclerFragment<String> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<String> getObjects(MusicService musicService, boolean refresh, ProgressListener listener) throws Exception {
|
||||
public List<String> getObjects(MusicService musicService, boolean refresh, ProgressListener listener) {
|
||||
List<String> decades = new ArrayList<>();
|
||||
for (int i = 2010; i >= 1800; i -= 10) {
|
||||
decades.add(String.valueOf(i));
|
||||
|
|
|
@ -28,6 +28,7 @@ import android.preference.ListPreference;
|
|||
import android.preference.Preference;
|
||||
import android.preference.PreferenceCategory;
|
||||
import android.preference.PreferenceScreen;
|
||||
import android.preference.SwitchPreference;
|
||||
import android.text.InputType;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
@ -224,7 +225,7 @@ public class SettingsFragment extends PreferenceCompatFragment implements Shared
|
|||
this.findPreference("clearCache").setOnPreferenceClickListener(preference -> {
|
||||
Util.confirmDialog(context, (dialog, which) -> new LoadingTask<Void>(context, false) {
|
||||
@Override
|
||||
protected Void doInBackground() throws Throwable {
|
||||
protected Void doInBackground() {
|
||||
FileUtil.deleteMusicDirectory(context);
|
||||
FileUtil.deleteSerializedCache(context);
|
||||
FileUtil.deleteArtworkCache(context);
|
||||
|
@ -469,6 +470,12 @@ public class SettingsFragment extends PreferenceCompatFragment implements Shared
|
|||
serverPasswordPreference.setSummary("***");
|
||||
serverPasswordPreference.setTitle(R.string.settings_server_password);
|
||||
|
||||
final SwitchPreference authMethodPreference = new SwitchPreference(context);
|
||||
authMethodPreference.setKey(Constants.PREFERENCES_KEY_AUTH_METHOD + instance);
|
||||
authMethodPreference.setSummary(R.string.settings_auth_summary);
|
||||
authMethodPreference.setDefaultValue(true); // use Token/Salt by default
|
||||
authMethodPreference.setTitle(R.string.settings_auth_method);
|
||||
|
||||
final Preference serverOpenBrowser = new Preference(context);
|
||||
serverOpenBrowser.setKey(Constants.PREFERENCES_KEY_OPEN_BROWSER);
|
||||
serverOpenBrowser.setPersistent(false);
|
||||
|
@ -523,13 +530,24 @@ public class SettingsFragment extends PreferenceCompatFragment implements Shared
|
|||
return false;
|
||||
});
|
||||
|
||||
Preference serverStartScanPreference = new Preference(context);
|
||||
serverStartScanPreference.setKey(Constants.PREFERENCES_KEY_START_SCAN + instance);
|
||||
serverStartScanPreference.setPersistent(false);
|
||||
serverStartScanPreference.setTitle(R.string.settings_start_scan_title);
|
||||
serverStartScanPreference.setOnPreferenceClickListener(preference -> {
|
||||
startScan(instance);
|
||||
return false;
|
||||
});
|
||||
|
||||
screen.addPreference(serverNamePreference);
|
||||
screen.addPreference(serverUrlPreference);
|
||||
screen.addPreference(serverInternalUrlPreference);
|
||||
screen.addPreference(serverLocalNetworkSSIDPreference);
|
||||
screen.addPreference(serverUsernamePreference);
|
||||
screen.addPreference(serverPasswordPreference);
|
||||
screen.addPreference(authMethodPreference);
|
||||
screen.addPreference(serverTestConnectionPreference);
|
||||
screen.addPreference(serverStartScanPreference);
|
||||
screen.addPreference(serverOpenBrowser);
|
||||
screen.addPreference(serverRemoveServerPreference);
|
||||
|
||||
|
@ -599,6 +617,42 @@ public class SettingsFragment extends PreferenceCompatFragment implements Shared
|
|||
}
|
||||
}
|
||||
|
||||
private void startScan(final int instance) {
|
||||
LoadingTask<Boolean> task = new LoadingTask<Boolean>(context) {
|
||||
@Override
|
||||
protected Boolean doInBackground() throws Throwable {
|
||||
MusicService musicService = MusicServiceFactory.getOnlineService();
|
||||
|
||||
try {
|
||||
musicService.setInstance(instance);
|
||||
musicService.startScan(context);
|
||||
return true;
|
||||
} finally {
|
||||
musicService.setInstance(null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done(Boolean licenseValid) {
|
||||
Log.d(TAG, "Finished media scan start");
|
||||
Util.toast(context, R.string.settings_media_scan_started);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
super.cancel();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void error(Throwable error) {
|
||||
Log.w(TAG, error.toString(), error);
|
||||
new ErrorDialog(context, getResources().getString(R.string.settings_media_scan_start_failed) +
|
||||
" " + getErrorMessage(error), false);
|
||||
}
|
||||
};
|
||||
task.execute();
|
||||
}
|
||||
|
||||
private void testConnection(final int instance) {
|
||||
LoadingTask<Boolean> task = new LoadingTask<Boolean>(context) {
|
||||
private int previousInstance;
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
*/
|
||||
package net.nullsum.audinaut.fragments;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.SearchManager;
|
||||
import android.app.SearchableInfo;
|
||||
import android.content.Context;
|
||||
|
@ -26,13 +25,6 @@ import android.content.SharedPreferences;
|
|||
import android.media.MediaMetadataRetriever;
|
||||
import android.os.Bundle;
|
||||
import android.os.StatFs;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.widget.SwipeRefreshLayout;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.support.v7.widget.GridLayoutManager;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.support.v7.widget.SearchView;
|
||||
import android.util.Log;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.Menu;
|
||||
|
@ -46,6 +38,16 @@ import android.widget.CheckBox;
|
|||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.SearchView;
|
||||
import androidx.core.view.MenuItemCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
|
||||
import net.nullsum.audinaut.R;
|
||||
import net.nullsum.audinaut.activity.SubsonicActivity;
|
||||
import net.nullsum.audinaut.adapter.SectionAdapter;
|
||||
|
@ -151,7 +153,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
|
|||
void onFinishSetupOptionsMenu(final Menu menu) {
|
||||
searchItem = menu.findItem(R.id.menu_global_search);
|
||||
if (searchItem != null) {
|
||||
searchView = (SearchView) searchItem.getActionView();
|
||||
searchView = (SearchView) MenuItemCompat.getActionView(searchItem);
|
||||
SearchManager searchManager = (SearchManager) context.getSystemService(Context.SEARCH_SERVICE);
|
||||
SearchableInfo searchableInfo = searchManager.getSearchableInfo(context.getComponentName());
|
||||
if (searchableInfo == null) {
|
||||
|
@ -1151,7 +1153,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
|
|||
|
||||
new LoadingTask<Void>(context) {
|
||||
@Override
|
||||
protected Void doInBackground() throws Throwable {
|
||||
protected Void doInBackground() {
|
||||
MediaStoreService mediaStore = new MediaStoreService(context);
|
||||
FileUtil.recursiveDelete(dir, mediaStore);
|
||||
return null;
|
||||
|
@ -1176,7 +1178,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
|
|||
private void deleteSongs(final List<Entry> songs) {
|
||||
new LoadingTask<Void>(context) {
|
||||
@Override
|
||||
protected Void doInBackground() throws Throwable {
|
||||
protected Void doInBackground() {
|
||||
getDownloadService().delete(songs);
|
||||
return null;
|
||||
}
|
||||
|
@ -1287,7 +1289,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
|
|||
private void playNow(final List<Entry> entries, final Entry song, final String playlistName, final String playlistId) {
|
||||
new LoadingTask<Void>(context) {
|
||||
@Override
|
||||
protected Void doInBackground() throws Throwable {
|
||||
protected Void doInBackground() {
|
||||
playNowInTask(entries, song, playlistName, playlistId);
|
||||
return null;
|
||||
}
|
||||
|
@ -1331,7 +1333,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
|
|||
return getCurrentAdapter().getSelected();
|
||||
}
|
||||
|
||||
void playNow(final boolean shuffle, final boolean append, final boolean playNext) {
|
||||
void playNow(final boolean shuffle, final boolean append, final boolean playNext) {
|
||||
List<Entry> songs = getSelectedEntries();
|
||||
if (!songs.isEmpty()) {
|
||||
download(songs, append, !append, playNext, shuffle);
|
||||
|
@ -1444,7 +1446,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR
|
|||
boolean playNowOverride = false;
|
||||
List<Entry> songs = new ArrayList<>();
|
||||
|
||||
public RecursiveLoader(Activity context) {
|
||||
public RecursiveLoader(AppCompatActivity context) {
|
||||
super(context);
|
||||
musicService = MusicServiceFactory.getMusicService(context);
|
||||
}
|
||||
|
|
|
@ -139,30 +139,6 @@ public class AudinautSearchProvider extends ContentProvider {
|
|||
cursor.addRow(new Object[]{artist.getId().hashCode(), artist.getName(), null, "ar-" + artist.getId(), artist.getName(), icon});
|
||||
} else {
|
||||
MusicDirectory.Entry entry = (MusicDirectory.Entry) obj;
|
||||
|
||||
if (entry.isDirectory()) {
|
||||
String icon = RESOURCE_PREFIX + R.drawable.ic_action_album;
|
||||
cursor.addRow(new Object[]{entry.getId().hashCode(), entry.getTitle(), entry.getArtist(), entry.getId(), entry.getTitle(), icon});
|
||||
} else {
|
||||
String icon = RESOURCE_PREFIX + R.drawable.ic_action_song;
|
||||
String id;
|
||||
id = entry.getAlbumId();
|
||||
|
||||
String artistDisplay;
|
||||
if (entry.getArtist() == null) {
|
||||
if (entry.getAlbum() != null) {
|
||||
artistDisplay = entry.getAlbumDisplay();
|
||||
} else {
|
||||
artistDisplay = "";
|
||||
}
|
||||
} else if (entry.getAlbum() != null) {
|
||||
artistDisplay = entry.getArtist() + " - " + entry.getAlbumDisplay();
|
||||
} else {
|
||||
artistDisplay = entry.getArtist();
|
||||
}
|
||||
|
||||
cursor.addRow(new Object[]{entry.getId().hashCode(), entry.getTitle(), artistDisplay, "so-" + id, entry.getTitle(), icon});
|
||||
}
|
||||
}
|
||||
}
|
||||
return cursor;
|
||||
|
|
|
@ -34,6 +34,7 @@ import android.graphics.PorterDuff.Mode;
|
|||
import android.graphics.PorterDuffXfermode;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.os.Build;
|
||||
import android.os.Environment;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
@ -239,11 +240,15 @@ public class AudinautWidgetProvider extends AppWidgetProvider {
|
|||
|
||||
// Set correct drawable for pause state
|
||||
if (playing) {
|
||||
views.setImageViewResource(R.id.control_play, R.drawable.media_pause_dark);
|
||||
views.setImageViewResource(R.id.control_play, R.drawable.widget_media_pause);
|
||||
} else {
|
||||
views.setImageViewResource(R.id.control_play, R.drawable.media_start_dark);
|
||||
views.setImageViewResource(R.id.control_play, R.drawable.widget_media_start);
|
||||
}
|
||||
|
||||
// Set next/back button images
|
||||
views.setImageViewResource(R.id.control_next, R.drawable.widget_media_forward);
|
||||
views.setImageViewResource(R.id.control_previous, R.drawable.widget_media_backward);
|
||||
|
||||
// Set the cover art
|
||||
try {
|
||||
boolean large = false;
|
||||
|
@ -286,19 +291,28 @@ public class AudinautWidgetProvider extends AppWidgetProvider {
|
|||
intent = new Intent("Audinaut.PLAY_PAUSE");
|
||||
intent.setComponent(new ComponentName(context, DownloadService.class));
|
||||
intent.setAction(DownloadService.CMD_TOGGLEPAUSE);
|
||||
pendingIntent = PendingIntent.getService(context, 0, intent, 0);
|
||||
if (Build.VERSION.SDK_INT >= 26)
|
||||
pendingIntent = PendingIntent.getForegroundService(context, 0, intent, 0);
|
||||
else
|
||||
pendingIntent = PendingIntent.getService(context, 0, intent, 0);
|
||||
views.setOnClickPendingIntent(R.id.control_play, pendingIntent);
|
||||
|
||||
intent = new Intent("Audinaut.NEXT"); // Use a unique action name to ensure a different PendingIntent to be created.
|
||||
intent.setComponent(new ComponentName(context, DownloadService.class));
|
||||
intent.setAction(DownloadService.CMD_NEXT);
|
||||
pendingIntent = PendingIntent.getService(context, 0, intent, 0);
|
||||
if (Build.VERSION.SDK_INT >= 26)
|
||||
pendingIntent = PendingIntent.getForegroundService(context, 0, intent, 0);
|
||||
else
|
||||
pendingIntent = PendingIntent.getService(context, 0, intent, 0);
|
||||
views.setOnClickPendingIntent(R.id.control_next, pendingIntent);
|
||||
|
||||
intent = new Intent("Audinaut.PREVIOUS"); // Use a unique action name to ensure a different PendingIntent to be created.
|
||||
intent.setComponent(new ComponentName(context, DownloadService.class));
|
||||
intent.setAction(DownloadService.CMD_PREVIOUS);
|
||||
pendingIntent = PendingIntent.getService(context, 0, intent, 0);
|
||||
if (Build.VERSION.SDK_INT >= 26)
|
||||
pendingIntent = PendingIntent.getForegroundService(context, 0, intent, 0);
|
||||
else
|
||||
pendingIntent = PendingIntent.getService(context, 0, intent, 0);
|
||||
views.setOnClickPendingIntent(R.id.control_previous, pendingIntent);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,21 +24,30 @@ import net.nullsum.audinaut.service.DownloadService;
|
|||
import net.nullsum.audinaut.util.Constants;
|
||||
|
||||
public class PlayActionReceiver extends BroadcastReceiver {
|
||||
private Bundle lastdata = null;
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (lastdata.equals(intent.getBundleExtra(Constants.TASKER_EXTRA_BUNDLE))) {
|
||||
// nothing has changed; we can safely return
|
||||
return;
|
||||
}
|
||||
updateValues(intent);
|
||||
if (intent.hasExtra(Constants.TASKER_EXTRA_BUNDLE)) {
|
||||
Bundle data = intent.getBundleExtra(Constants.TASKER_EXTRA_BUNDLE);
|
||||
Boolean startShuffled = data.getBoolean(Constants.INTENT_EXTRA_NAME_SHUFFLE);
|
||||
Boolean startShuffled = lastdata.getBoolean(Constants.INTENT_EXTRA_NAME_SHUFFLE);
|
||||
|
||||
Intent start = new Intent(context, DownloadService.class);
|
||||
start.setAction(DownloadService.START_PLAY);
|
||||
start.putExtra(Constants.INTENT_EXTRA_NAME_SHUFFLE, startShuffled);
|
||||
start.putExtra(Constants.PREFERENCES_KEY_SHUFFLE_START_YEAR, data.getString(Constants.PREFERENCES_KEY_SHUFFLE_START_YEAR));
|
||||
start.putExtra(Constants.PREFERENCES_KEY_SHUFFLE_END_YEAR, data.getString(Constants.PREFERENCES_KEY_SHUFFLE_END_YEAR));
|
||||
start.putExtra(Constants.PREFERENCES_KEY_SHUFFLE_GENRE, data.getString(Constants.PREFERENCES_KEY_SHUFFLE_GENRE));
|
||||
start.putExtra(Constants.PREFERENCES_KEY_OFFLINE, data.getInt(Constants.PREFERENCES_KEY_OFFLINE));
|
||||
start.putExtra(Constants.PREFERENCES_KEY_SHUFFLE_START_YEAR, lastdata.getString(Constants.PREFERENCES_KEY_SHUFFLE_START_YEAR));
|
||||
start.putExtra(Constants.PREFERENCES_KEY_SHUFFLE_END_YEAR, lastdata.getString(Constants.PREFERENCES_KEY_SHUFFLE_END_YEAR));
|
||||
start.putExtra(Constants.PREFERENCES_KEY_SHUFFLE_GENRE, lastdata.getString(Constants.PREFERENCES_KEY_SHUFFLE_GENRE));
|
||||
start.putExtra(Constants.PREFERENCES_KEY_OFFLINE, lastdata.getInt(Constants.PREFERENCES_KEY_OFFLINE));
|
||||
context.startService(start);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateValues(Intent intent) {
|
||||
lastdata = intent.getBundleExtra(Constants.TASKER_EXTRA_BUNDLE);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,7 +30,6 @@ import net.nullsum.audinaut.domain.MusicFolder;
|
|||
import net.nullsum.audinaut.domain.Playlist;
|
||||
import net.nullsum.audinaut.domain.SearchCritera;
|
||||
import net.nullsum.audinaut.domain.SearchResult;
|
||||
import net.nullsum.audinaut.domain.User;
|
||||
import net.nullsum.audinaut.util.FileUtil;
|
||||
import net.nullsum.audinaut.util.ProgressListener;
|
||||
import net.nullsum.audinaut.util.SilentBackgroundTask;
|
||||
|
@ -617,35 +616,22 @@ public class CachedMusicService implements MusicService {
|
|||
}
|
||||
|
||||
@Override
|
||||
public User getUser(boolean refresh, String username, Context context, ProgressListener progressListener) throws Exception {
|
||||
User result = null;
|
||||
|
||||
try {
|
||||
result = musicService.getUser(refresh, username, context, progressListener);
|
||||
FileUtil.serialize(context, result, getCacheName(context, "user-" + username));
|
||||
} catch (Exception e) {
|
||||
// Don't care
|
||||
}
|
||||
|
||||
if (result == null && !refresh) {
|
||||
result = FileUtil.deserialize(context, getCacheName(context, "user-" + username), User.class);
|
||||
}
|
||||
|
||||
return result;
|
||||
public void startScan(Context c) throws Exception {
|
||||
musicService.startScan(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInstance(Integer instance) throws Exception {
|
||||
public void setInstance(Integer instance) {
|
||||
musicService.setInstance(instance);
|
||||
}
|
||||
|
||||
private String getCacheName(Context context, String name, String id) {
|
||||
String s = musicService.getRestUrl(context, null, false) + id;
|
||||
String s = musicService.getRestUrl(context, null, false, null) + id;
|
||||
return name + "-" + s.hashCode() + ".ser";
|
||||
}
|
||||
|
||||
private String getCacheName(Context context, String name) {
|
||||
String s = musicService.getRestUrl(context, null, false);
|
||||
String s = musicService.getRestUrl(context, null, false, null);
|
||||
return name + "-" + s.hashCode() + ".ser";
|
||||
}
|
||||
|
||||
|
@ -672,7 +658,7 @@ public class CachedMusicService implements MusicService {
|
|||
|
||||
private void checkSettingsChanged(Context context) {
|
||||
int instance = musicService.getInstance(context);
|
||||
String newUrl = musicService.getRestUrl(context, null, false);
|
||||
String newUrl = musicService.getRestUrl(context, null, false, null);
|
||||
if (!Util.equals(newUrl, restUrl)) {
|
||||
cachedMusicFolders.clear();
|
||||
cachedIndexes.clear();
|
||||
|
|
|
@ -32,9 +32,10 @@ import android.os.Handler;
|
|||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.os.PowerManager;
|
||||
import android.support.v4.util.LruCache;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.collection.LruCache;
|
||||
|
||||
import net.nullsum.audinaut.R;
|
||||
import net.nullsum.audinaut.activity.SubsonicActivity;
|
||||
import net.nullsum.audinaut.audiofx.AudioEffectsController;
|
||||
|
@ -60,7 +61,6 @@ import java.util.Collections;
|
|||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import static net.nullsum.audinaut.domain.PlayerState.COMPLETED;
|
||||
import static net.nullsum.audinaut.domain.PlayerState.DOWNLOADING;
|
||||
import static net.nullsum.audinaut.domain.PlayerState.IDLE;
|
||||
import static net.nullsum.audinaut.domain.PlayerState.PAUSED;
|
||||
|
@ -949,8 +949,7 @@ public class DownloadService extends Service {
|
|||
|
||||
public synchronized void next(boolean forceStart) {
|
||||
// If only one song, just skip within song
|
||||
if (size() == 1 || (currentPlaying != null && !currentPlaying.isSong())) {
|
||||
fastForward();
|
||||
if (currentPlaying != null && !currentPlaying.isSong()) {
|
||||
return;
|
||||
} else if (playerState == PREPARING || playerState == PREPARED) {
|
||||
return;
|
||||
|
@ -958,8 +957,7 @@ public class DownloadService extends Service {
|
|||
|
||||
int index = getCurrentPlayingIndex();
|
||||
int nextPlayingIndex = getNextPlayingIndex();
|
||||
// Make sure to actually go to next when repeat song is on
|
||||
if (index == nextPlayingIndex) {
|
||||
if (index == nextPlayingIndex && size() > 1) {
|
||||
nextPlayingIndex++;
|
||||
}
|
||||
if (index != -1 && nextPlayingIndex < size()) {
|
||||
|
|
|
@ -28,7 +28,6 @@ import net.nullsum.audinaut.domain.MusicFolder;
|
|||
import net.nullsum.audinaut.domain.Playlist;
|
||||
import net.nullsum.audinaut.domain.SearchCritera;
|
||||
import net.nullsum.audinaut.domain.SearchResult;
|
||||
import net.nullsum.audinaut.domain.User;
|
||||
import net.nullsum.audinaut.util.ProgressListener;
|
||||
import net.nullsum.audinaut.util.SilentBackgroundTask;
|
||||
|
||||
|
@ -87,7 +86,7 @@ public interface MusicService {
|
|||
|
||||
MusicDirectory getSongsByGenre(String genre, int count, int offset, Context context, ProgressListener progressListener) throws Exception;
|
||||
|
||||
User getUser(boolean refresh, String username, Context context, ProgressListener progressListener) throws Exception;
|
||||
void startScan(Context c) throws Exception;
|
||||
|
||||
void setInstance(Integer instance) throws Exception;
|
||||
}
|
||||
|
|
|
@ -31,7 +31,6 @@ import net.nullsum.audinaut.domain.MusicFolder;
|
|||
import net.nullsum.audinaut.domain.Playlist;
|
||||
import net.nullsum.audinaut.domain.SearchCritera;
|
||||
import net.nullsum.audinaut.domain.SearchResult;
|
||||
import net.nullsum.audinaut.domain.User;
|
||||
import net.nullsum.audinaut.util.Constants;
|
||||
import net.nullsum.audinaut.util.FileUtil;
|
||||
import net.nullsum.audinaut.util.ProgressListener;
|
||||
|
@ -62,14 +61,15 @@ public class OfflineMusicService implements MusicService {
|
|||
private static final Random random = new Random();
|
||||
|
||||
@Override
|
||||
public void ping(Context context, ProgressListener progressListener) throws Exception {
|
||||
public void ping(Context context, ProgressListener progressListener) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Indexes getIndexes(String musicFolderId, boolean refresh, Context context, ProgressListener progressListener) throws Exception {
|
||||
public Indexes getIndexes(String musicFolderId, boolean refresh, Context context, ProgressListener progressListener) {
|
||||
List<Artist> artists = new ArrayList<>();
|
||||
List<Entry> entries = new ArrayList<>();
|
||||
|
||||
File root = FileUtil.getMusicDirectory(context);
|
||||
for (File file : FileUtil.listFiles(root)) {
|
||||
if (file.isDirectory()) {
|
||||
|
@ -87,11 +87,11 @@ public class OfflineMusicService implements MusicService {
|
|||
}
|
||||
|
||||
@Override
|
||||
public MusicDirectory getMusicDirectory(String id, String artistName, boolean refresh, Context context, ProgressListener progressListener) throws Exception {
|
||||
public MusicDirectory getMusicDirectory(String id, String artistName, boolean refresh, Context context, ProgressListener progressListener) {
|
||||
return getMusicDirectory(id, context);
|
||||
}
|
||||
|
||||
private MusicDirectory getMusicDirectory(String id, Context context) throws Exception {
|
||||
private MusicDirectory getMusicDirectory(String id, Context context) {
|
||||
File dir = new File(id);
|
||||
MusicDirectory result = new MusicDirectory();
|
||||
result.setName(dir.getName());
|
||||
|
@ -186,7 +186,7 @@ public class OfflineMusicService implements MusicService {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Bitmap getCoverArt(Context context, Entry entry, int size, ProgressListener progressListener, SilentBackgroundTask task) throws Exception {
|
||||
public Bitmap getCoverArt(Context context, Entry entry, int size, ProgressListener progressListener, SilentBackgroundTask task) {
|
||||
try {
|
||||
return FileUtil.getAlbumArtBitmap(context, entry, size);
|
||||
} catch (Exception e) {
|
||||
|
@ -205,7 +205,7 @@ public class OfflineMusicService implements MusicService {
|
|||
}
|
||||
|
||||
@Override
|
||||
public SearchResult search(SearchCritera criteria, Context context, ProgressListener progressListener) throws Exception {
|
||||
public SearchResult search(SearchCritera criteria, Context context, ProgressListener progressListener) {
|
||||
List<Artist> artists = new ArrayList<>();
|
||||
List<Entry> albums = new ArrayList<>();
|
||||
List<Entry> songs = new ArrayList<>();
|
||||
|
@ -294,7 +294,7 @@ public class OfflineMusicService implements MusicService {
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<Playlist> getPlaylists(boolean refresh, Context context, ProgressListener progressListener) throws Exception {
|
||||
public List<Playlist> getPlaylists(boolean refresh, Context context, ProgressListener progressListener) {
|
||||
List<Playlist> playlists = new ArrayList<>();
|
||||
File root = FileUtil.getPlaylistDirectory(context);
|
||||
String lastServer = null;
|
||||
|
@ -481,7 +481,7 @@ public class OfflineMusicService implements MusicService {
|
|||
}
|
||||
|
||||
@Override
|
||||
public MusicDirectory getRandomSongs(int size, String folder, String genre, String startYear, String endYear, Context context, ProgressListener progressListener) throws Exception {
|
||||
public MusicDirectory getRandomSongs(int size, String folder, String genre, String startYear, String endYear, Context context, ProgressListener progressListener) {
|
||||
File root = FileUtil.getMusicDirectory(context);
|
||||
List<File> children = new LinkedList<>();
|
||||
listFilesRecursively(root, children);
|
||||
|
@ -499,7 +499,7 @@ public class OfflineMusicService implements MusicService {
|
|||
}
|
||||
|
||||
@Override
|
||||
public User getUser(boolean refresh, String username, Context context, ProgressListener progressListener) throws Exception {
|
||||
public void startScan(Context c) throws Exception {
|
||||
throw new OfflineException();
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,9 @@ import android.content.SharedPreferences;
|
|||
import android.graphics.Bitmap;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import net.nullsum.audinaut.R;
|
||||
import net.nullsum.audinaut.domain.Genre;
|
||||
import net.nullsum.audinaut.domain.Indexes;
|
||||
import net.nullsum.audinaut.domain.MusicDirectory;
|
||||
|
@ -30,7 +33,6 @@ import net.nullsum.audinaut.domain.MusicFolder;
|
|||
import net.nullsum.audinaut.domain.Playlist;
|
||||
import net.nullsum.audinaut.domain.SearchCritera;
|
||||
import net.nullsum.audinaut.domain.SearchResult;
|
||||
import net.nullsum.audinaut.domain.User;
|
||||
import net.nullsum.audinaut.fragments.MainFragment;
|
||||
import net.nullsum.audinaut.service.parser.EntryListParser;
|
||||
import net.nullsum.audinaut.service.parser.ErrorParser;
|
||||
|
@ -42,7 +44,6 @@ import net.nullsum.audinaut.service.parser.PlaylistParser;
|
|||
import net.nullsum.audinaut.service.parser.PlaylistsParser;
|
||||
import net.nullsum.audinaut.service.parser.RandomSongsParser;
|
||||
import net.nullsum.audinaut.service.parser.SearchResult2Parser;
|
||||
import net.nullsum.audinaut.service.parser.UserParser;
|
||||
import net.nullsum.audinaut.util.Constants;
|
||||
import net.nullsum.audinaut.util.FileUtil;
|
||||
import net.nullsum.audinaut.util.Pair;
|
||||
|
@ -54,14 +55,13 @@ import net.nullsum.audinaut.util.Util;
|
|||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import okhttp3.FormBody;
|
||||
import okhttp3.FormBody.Builder;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
|
||||
/**
|
||||
|
@ -75,7 +75,7 @@ public class RESTMusicService implements MusicService {
|
|||
|
||||
@Override
|
||||
public void ping(Context context, ProgressListener progressListener) throws Exception {
|
||||
String url = getRestUrl(context, "ping");
|
||||
String url = getRestUrl(context, "ping", null);
|
||||
|
||||
Request request = new Request.Builder()
|
||||
.url(url)
|
||||
|
@ -87,7 +87,7 @@ public class RESTMusicService implements MusicService {
|
|||
}
|
||||
|
||||
public List<MusicFolder> getMusicFolders(boolean refresh, Context context, ProgressListener progressListener) throws Exception {
|
||||
String url = getRestUrl(context, "getMusicFolders");
|
||||
String url = getRestUrl(context, "getMusicFolders", null);
|
||||
|
||||
Request request = new Request.Builder()
|
||||
.url(url)
|
||||
|
@ -100,19 +100,21 @@ public class RESTMusicService implements MusicService {
|
|||
|
||||
@Override
|
||||
public Indexes getIndexes(String musicFolderId, boolean refresh, Context context, ProgressListener progressListener) throws Exception {
|
||||
String url = getRestUrl(context, "getArtists");
|
||||
|
||||
Builder builder = new FormBody.Builder();
|
||||
|
||||
|
||||
Map<String, String> parameters = new HashMap<>();
|
||||
|
||||
if (musicFolderId != null) {
|
||||
builder.add("musicFolderId", musicFolderId);
|
||||
parameters.put("musicFolderId", musicFolderId);
|
||||
} else {
|
||||
parameters = null;
|
||||
}
|
||||
|
||||
RequestBody formBody = builder.build();
|
||||
String url = getRestUrl(context, "getArtists", parameters);
|
||||
|
||||
Request request = new Request.Builder()
|
||||
.url(url)
|
||||
.post(formBody)
|
||||
.build();
|
||||
|
||||
try (Response response = client.newCall(request).execute()) {
|
||||
|
@ -158,15 +160,14 @@ public class RESTMusicService implements MusicService {
|
|||
}
|
||||
|
||||
private MusicDirectory getMusicDirectoryImpl(String id, String name, Context context) throws Exception {
|
||||
String url = getRestUrl(context, "getMusicDirectory");
|
||||
Map<String, String> parameters = new HashMap<>();
|
||||
|
||||
RequestBody formBody = new FormBody.Builder()
|
||||
.add("id", id)
|
||||
.build();
|
||||
parameters.put("id", id);
|
||||
|
||||
String url = getRestUrl(context, "getMusicDirectory", parameters);
|
||||
|
||||
Request request = new Request.Builder()
|
||||
.url(url)
|
||||
.post(formBody)
|
||||
.build();
|
||||
|
||||
try (Response response = client.newCall(request).execute()) {
|
||||
|
@ -176,15 +177,15 @@ public class RESTMusicService implements MusicService {
|
|||
|
||||
@Override
|
||||
public MusicDirectory getArtist(String id, String name, boolean refresh, Context context, ProgressListener progressListener) throws Exception {
|
||||
String url = getRestUrl(context, "getArtist");
|
||||
|
||||
RequestBody formBody = new FormBody.Builder()
|
||||
.add("id", id)
|
||||
.build();
|
||||
Map<String, String> parameters = new HashMap<>();
|
||||
|
||||
parameters.put("id", id);
|
||||
|
||||
String url = getRestUrl(context, "getArtist", parameters);
|
||||
|
||||
Request request = new Request.Builder()
|
||||
.url(url)
|
||||
.post(formBody)
|
||||
.build();
|
||||
|
||||
try (Response response = client.newCall(request).execute()) {
|
||||
|
@ -194,15 +195,14 @@ public class RESTMusicService implements MusicService {
|
|||
|
||||
@Override
|
||||
public MusicDirectory getAlbum(String id, String name, boolean refresh, Context context, ProgressListener progressListener) throws Exception {
|
||||
String url = getRestUrl(context, "getAlbum");
|
||||
Map<String, String> parameters = new HashMap<>();
|
||||
|
||||
RequestBody formBody = new FormBody.Builder()
|
||||
.add("id", id)
|
||||
.build();
|
||||
parameters.put("id", id);
|
||||
|
||||
String url = getRestUrl(context, "getAlbum", parameters);
|
||||
|
||||
Request request = new Request.Builder()
|
||||
.url(url)
|
||||
.post(formBody)
|
||||
.build();
|
||||
|
||||
try (Response response = client.newCall(request).execute()) {
|
||||
|
@ -212,20 +212,17 @@ public class RESTMusicService implements MusicService {
|
|||
|
||||
@Override
|
||||
public SearchResult search(SearchCritera critera, Context context, ProgressListener progressListener) throws Exception {
|
||||
String url = getRestUrl(context, "search3");
|
||||
Map<String, String> parameters = new HashMap<>();
|
||||
|
||||
Builder builder = new FormBody.Builder();
|
||||
parameters.put("query", critera.getQuery());
|
||||
parameters.put("artistCount", Integer.toString(critera.getArtistCount()));
|
||||
parameters.put("albumCount", Integer.toString(critera.getAlbumCount()));
|
||||
parameters.put("songCount", Integer.toString(critera.getSongCount()));
|
||||
|
||||
builder.add("query", critera.getQuery());
|
||||
builder.add("artistCount", Integer.toString(critera.getArtistCount()));
|
||||
builder.add("albumCount", Integer.toString(critera.getAlbumCount()));
|
||||
builder.add("songCount", Integer.toString(critera.getSongCount()));
|
||||
|
||||
RequestBody formBody = builder.build();
|
||||
String url = getRestUrl(context, "search3", parameters);
|
||||
|
||||
Request request = new Request.Builder()
|
||||
.url(url)
|
||||
.post(formBody)
|
||||
.build();
|
||||
|
||||
try (Response response = client.newCall(request).execute()) {
|
||||
|
@ -235,15 +232,14 @@ public class RESTMusicService implements MusicService {
|
|||
|
||||
@Override
|
||||
public MusicDirectory getPlaylist(boolean refresh, String id, String name, Context context, ProgressListener progressListener) throws Exception {
|
||||
String url = getRestUrl(context, "getPlaylist");
|
||||
Map<String, String> parameters = new HashMap<>();
|
||||
|
||||
RequestBody formBody = new FormBody.Builder()
|
||||
.add("id", id)
|
||||
.build();
|
||||
parameters.put("id", id);
|
||||
|
||||
String url = getRestUrl(context, "getPlaylist", parameters);
|
||||
|
||||
Request request = new Request.Builder()
|
||||
.url(url)
|
||||
.post(formBody)
|
||||
.build();
|
||||
|
||||
try (Response response = client.newCall(request).execute()) {
|
||||
|
@ -253,7 +249,7 @@ public class RESTMusicService implements MusicService {
|
|||
|
||||
@Override
|
||||
public List<Playlist> getPlaylists(boolean refresh, Context context, ProgressListener progressListener) throws Exception {
|
||||
String url = getRestUrl(context, "getPlaylists");
|
||||
String url = getRestUrl(context, "getPlaylists", null);
|
||||
|
||||
Request request = new Request.Builder()
|
||||
.url(url)
|
||||
|
@ -266,27 +262,24 @@ public class RESTMusicService implements MusicService {
|
|||
|
||||
@Override
|
||||
public void createPlaylist(String id, String name, List<MusicDirectory.Entry> entries, Context context, ProgressListener progressListener) throws Exception {
|
||||
String url = getRestUrl(context, "createPlaylist");
|
||||
|
||||
Builder builder = new FormBody.Builder();
|
||||
Map<String, String> parameters = new HashMap<>();
|
||||
|
||||
if (id != null) {
|
||||
builder.add("playlistId", id);
|
||||
parameters.put("playlistId", id);
|
||||
}
|
||||
|
||||
if (name != null) {
|
||||
builder.add("name", name);
|
||||
parameters.put("name", name);
|
||||
}
|
||||
|
||||
for (MusicDirectory.Entry entry : entries) {
|
||||
builder.add("songId", getOfflineSongId(entry.getId(), context, progressListener));
|
||||
parameters.put("songId", getOfflineSongId(entry.getId(), context, progressListener));
|
||||
}
|
||||
|
||||
RequestBody formBody = builder.build();
|
||||
String url = getRestUrl(context, "createPlaylist", parameters);
|
||||
|
||||
Request request = new Request.Builder()
|
||||
.url(url)
|
||||
.post(formBody)
|
||||
.build();
|
||||
|
||||
try (Response response = client.newCall(request).execute()) {
|
||||
|
@ -296,15 +289,14 @@ public class RESTMusicService implements MusicService {
|
|||
|
||||
@Override
|
||||
public void deletePlaylist(String id, Context context, ProgressListener progressListener) throws Exception {
|
||||
String url = getRestUrl(context, "deletePlaylist");
|
||||
Map<String, String> parameters = new HashMap<>();
|
||||
|
||||
RequestBody formBody = new FormBody.Builder()
|
||||
.add("id", id)
|
||||
.build();
|
||||
parameters.put("id", id);
|
||||
|
||||
String url = getRestUrl(context, "deletePlaylist", parameters);
|
||||
|
||||
Request request = new Request.Builder()
|
||||
.url(url)
|
||||
.post(formBody)
|
||||
.build();
|
||||
|
||||
try (Response response = client.newCall(request).execute()) {
|
||||
|
@ -314,19 +306,17 @@ public class RESTMusicService implements MusicService {
|
|||
|
||||
@Override
|
||||
public void addToPlaylist(String id, List<MusicDirectory.Entry> toAdd, Context context, ProgressListener progressListener) throws Exception {
|
||||
String url = getRestUrl(context, "updatePlaylist");
|
||||
Map<String, String> parameters = new HashMap<>();
|
||||
|
||||
Builder builder = new FormBody.Builder();
|
||||
builder.add("playlistId", id);
|
||||
parameters.put("playlistId", id);
|
||||
for (MusicDirectory.Entry song : toAdd) {
|
||||
builder.add("songIdToAdd", getOfflineSongId(song.getId(), context, progressListener));
|
||||
parameters.put("songIdToAdd", getOfflineSongId(song.getId(), context, progressListener));
|
||||
}
|
||||
|
||||
RequestBody formBody = builder.build();
|
||||
String url = getRestUrl(context, "updatePlaylist", parameters);
|
||||
|
||||
Request request = new Request.Builder()
|
||||
.url(url)
|
||||
.post(formBody)
|
||||
.build();
|
||||
|
||||
try (Response response = client.newCall(request).execute()) {
|
||||
|
@ -336,20 +326,18 @@ public class RESTMusicService implements MusicService {
|
|||
|
||||
@Override
|
||||
public void removeFromPlaylist(String id, List<Integer> toRemove, Context context, ProgressListener progressListener) throws Exception {
|
||||
String url = getRestUrl(context, "updatePlaylist");
|
||||
Map<String, String> parameters = new HashMap<>();
|
||||
|
||||
Builder builder = new FormBody.Builder();
|
||||
builder.add("playlistId", id);
|
||||
parameters.put("playlistId", id);
|
||||
|
||||
for (Integer song : toRemove) {
|
||||
builder.add("songIndexToRemove", Integer.toString(song));
|
||||
parameters.put("songIndexToRemove", Integer.toString(song));
|
||||
}
|
||||
|
||||
RequestBody formBody = builder.build();
|
||||
String url = getRestUrl(context, "updatePlaylist", parameters);
|
||||
|
||||
Request request = new Request.Builder()
|
||||
.url(url)
|
||||
.post(formBody)
|
||||
.build();
|
||||
|
||||
try (Response response = client.newCall(request).execute()) {
|
||||
|
@ -359,25 +347,23 @@ public class RESTMusicService implements MusicService {
|
|||
|
||||
@Override
|
||||
public void overwritePlaylist(String id, String name, int toRemove, List<MusicDirectory.Entry> toAdd, Context context, ProgressListener progressListener) throws Exception {
|
||||
String url = getRestUrl(context, "updatePlaylist");
|
||||
Map<String, String> parameters = new HashMap<>();
|
||||
|
||||
Builder builder = new FormBody.Builder();
|
||||
builder.add("playlistId", id);
|
||||
builder.add("name", name);
|
||||
parameters.put("playlistId", id);
|
||||
parameters.put("name", name);
|
||||
|
||||
for (MusicDirectory.Entry song : toAdd) {
|
||||
builder.add("songIdToAdd", getOfflineSongId(song.getId(), context, progressListener));
|
||||
parameters.put("songIdToAdd", getOfflineSongId(song.getId(), context, progressListener));
|
||||
}
|
||||
|
||||
for (int i = 0; i < toRemove; i++) {
|
||||
builder.add("songIndexToRemove", Integer.toString(i));
|
||||
parameters.put("songIndexToRemove", Integer.toString(i));
|
||||
}
|
||||
|
||||
RequestBody formBody = builder.build();
|
||||
String url = getRestUrl(context, "updatePlaylist", parameters);
|
||||
|
||||
Request request = new Request.Builder()
|
||||
.url(url)
|
||||
.post(formBody)
|
||||
.build();
|
||||
|
||||
try (Response response = client.newCall(request).execute()) {
|
||||
|
@ -387,19 +373,17 @@ public class RESTMusicService implements MusicService {
|
|||
|
||||
@Override
|
||||
public void updatePlaylist(String id, String name, String comment, boolean pub, Context context, ProgressListener progressListener) throws Exception {
|
||||
String url = getRestUrl(context, "updatePlaylist");
|
||||
Map<String, String> parameters = new HashMap<>();
|
||||
|
||||
Builder builder = new FormBody.Builder();
|
||||
builder.add("playlistId", id);
|
||||
builder.add("name", name);
|
||||
builder.add("comment", comment);
|
||||
builder.add("public", Boolean.toString(pub));
|
||||
parameters.put("playlistId", id);
|
||||
parameters.put("name", name);
|
||||
parameters.put("comment", comment);
|
||||
parameters.put("public", Boolean.toString(pub));
|
||||
|
||||
RequestBody formBody = builder.build();
|
||||
String url = getRestUrl(context, "updatePlaylist", parameters);
|
||||
|
||||
Request request = new Request.Builder()
|
||||
.url(url)
|
||||
.post(formBody)
|
||||
.build();
|
||||
|
||||
try (Response response = client.newCall(request).execute()) {
|
||||
|
@ -409,27 +393,25 @@ public class RESTMusicService implements MusicService {
|
|||
|
||||
@Override
|
||||
public MusicDirectory getAlbumList(String type, int size, int offset, boolean refresh, Context context, ProgressListener progressListener) throws Exception {
|
||||
String url = getRestUrl(context, "getAlbumList2");
|
||||
Map<String, String> parameters = new HashMap<>();
|
||||
|
||||
Builder builder = new FormBody.Builder();
|
||||
builder.add("type", type);
|
||||
builder.add("size", Integer.toString(size));
|
||||
builder.add("offset", Integer.toString(offset));
|
||||
parameters.put("type", type);
|
||||
parameters.put("size", Integer.toString(size));
|
||||
parameters.put("offset", Integer.toString(offset));
|
||||
|
||||
// Add folder if it was set and is non null
|
||||
int instance = getInstance(context);
|
||||
if (Util.getAlbumListsPerFolder(context, instance)) {
|
||||
String folderId = Util.getSelectedMusicFolderId(context, instance);
|
||||
if (folderId != null) {
|
||||
builder.add("musicFolderId", folderId);
|
||||
parameters.put("musicFolderId", folderId);
|
||||
}
|
||||
}
|
||||
|
||||
RequestBody formBody = builder.build();
|
||||
String url = getRestUrl(context, "getAlbumList2", parameters);
|
||||
|
||||
Request request = new Request.Builder()
|
||||
.url(url)
|
||||
.post(formBody)
|
||||
.build();
|
||||
|
||||
try (Response response = client.newCall(request).execute()) {
|
||||
|
@ -439,37 +421,35 @@ public class RESTMusicService implements MusicService {
|
|||
|
||||
@Override
|
||||
public MusicDirectory getAlbumList(String type, String extra, int size, int offset, boolean refresh, Context context, ProgressListener progressListener) throws Exception {
|
||||
String url = getRestUrl(context, "getAlbumList2");
|
||||
Map<String, String> parameters = new HashMap<>();
|
||||
|
||||
Builder builder = new FormBody.Builder();
|
||||
builder.add("size", Integer.toString(size));
|
||||
builder.add("offset", Integer.toString(offset));
|
||||
parameters.put("size", Integer.toString(size));
|
||||
parameters.put("offset", Integer.toString(offset));
|
||||
|
||||
int instance = getInstance(context);
|
||||
if ("genres".equals(type)) {
|
||||
builder.add("type", "byGenre");
|
||||
builder.add("genre", extra);
|
||||
parameters.put("type", "byGenre");
|
||||
parameters.put("genre", extra);
|
||||
} else if ("years".equals(type)) {
|
||||
int decade = Integer.parseInt(extra);
|
||||
|
||||
builder.add("type", "byYear");
|
||||
builder.add("fromYear", Integer.toString(decade + 9));
|
||||
builder.add("toYear", Integer.toString(decade));
|
||||
parameters.put("type", "byYear");
|
||||
parameters.put("fromYear", Integer.toString(decade + 9));
|
||||
parameters.put("toYear", Integer.toString(decade));
|
||||
}
|
||||
|
||||
// Add folder if it was set and is non null
|
||||
if (Util.getAlbumListsPerFolder(context, instance)) {
|
||||
String folderId = Util.getSelectedMusicFolderId(context, instance);
|
||||
if (folderId != null) {
|
||||
builder.add("musicFolderId", folderId);
|
||||
parameters.put("musicFolderId", folderId);
|
||||
}
|
||||
}
|
||||
|
||||
RequestBody formBody = builder.build();
|
||||
String url = getRestUrl(context, "getAlbumList2", parameters);
|
||||
|
||||
Request request = new Request.Builder()
|
||||
.url(url)
|
||||
.post(formBody)
|
||||
.build();
|
||||
|
||||
try (Response response = client.newCall(request).execute()) {
|
||||
|
@ -479,9 +459,10 @@ public class RESTMusicService implements MusicService {
|
|||
|
||||
@Override
|
||||
public MusicDirectory getSongList(String type, int size, int offset, Context context, ProgressListener progressListener) throws Exception {
|
||||
Builder builder = new FormBody.Builder();
|
||||
builder.add("size", Integer.toString(size));
|
||||
builder.add("offset", Integer.toString(offset));
|
||||
Map<String, String> parameters = new HashMap<>();
|
||||
|
||||
parameters.put("size", Integer.toString(size));
|
||||
parameters.put("offset", Integer.toString(offset));
|
||||
|
||||
String method;
|
||||
switch (type) {
|
||||
|
@ -501,13 +482,10 @@ public class RESTMusicService implements MusicService {
|
|||
method = "getNewaddedSongs";
|
||||
}
|
||||
|
||||
String url = getRestUrl(context, method);
|
||||
|
||||
RequestBody formBody = builder.build();
|
||||
String url = getRestUrl(context, method, parameters);
|
||||
|
||||
Request request = new Request.Builder()
|
||||
.url(url)
|
||||
.post(formBody)
|
||||
.build();
|
||||
|
||||
try (Response response = client.newCall(request).execute()) {
|
||||
|
@ -517,11 +495,12 @@ public class RESTMusicService implements MusicService {
|
|||
|
||||
@Override
|
||||
public MusicDirectory getRandomSongs(int size, String musicFolderId, String genre, String startYear, String endYear, Context context, ProgressListener progressListener) throws Exception {
|
||||
Builder builder = new FormBody.Builder();
|
||||
builder.add("size", Integer.toString(size));
|
||||
Map<String, String> parameters = new HashMap<>();
|
||||
|
||||
parameters.put("size", Integer.toString(size));
|
||||
|
||||
if (genre != null && !"".equals(genre)) {
|
||||
builder.add("genre", genre);
|
||||
parameters.put("genre", genre);
|
||||
}
|
||||
if (startYear != null && !"".equals(startYear)) {
|
||||
// Check to make sure user isn't doing 2015 -> 2010 since Subsonic will return no results
|
||||
|
@ -540,19 +519,16 @@ public class RESTMusicService implements MusicService {
|
|||
}
|
||||
}
|
||||
|
||||
builder.add("fromYear", startYear);
|
||||
parameters.put("fromYear", startYear);
|
||||
}
|
||||
if (endYear != null && !"".equals(endYear)) {
|
||||
builder.add("toYear", endYear);
|
||||
parameters.put("toYear", endYear);
|
||||
}
|
||||
|
||||
String url = getRestUrl(context, "getRandomSongs");
|
||||
|
||||
RequestBody formBody = builder.build();
|
||||
String url = getRestUrl(context, "getRandomSongs", parameters);
|
||||
|
||||
Request request = new Request.Builder()
|
||||
.url(url)
|
||||
.post(formBody)
|
||||
.build();
|
||||
|
||||
try (Response response = client.newCall(request).execute()) {
|
||||
|
@ -572,16 +548,14 @@ public class RESTMusicService implements MusicService {
|
|||
return bitmap;
|
||||
}
|
||||
|
||||
String url = getRestUrl(context, "getCoverArt");
|
||||
Map<String, String> parameters = new HashMap<>();
|
||||
|
||||
Builder builder = new FormBody.Builder();
|
||||
builder.add("id", entry.getCoverArt());
|
||||
parameters.put("id", entry.getCoverArt());
|
||||
|
||||
RequestBody formBody = builder.build();
|
||||
String url = getRestUrl(context, "getCoverArt", parameters);
|
||||
|
||||
Request request = new Request.Builder()
|
||||
.url(url)
|
||||
.post(formBody)
|
||||
.build();
|
||||
|
||||
try (Response response = client.newCall(request).execute()) {
|
||||
|
@ -616,16 +590,15 @@ public class RESTMusicService implements MusicService {
|
|||
public Response getDownloadInputStream(Context context, MusicDirectory.Entry song, long offset, int maxBitrate, SilentBackgroundTask task) throws Exception {
|
||||
|
||||
OkHttpClient eagerClient = client.newBuilder()
|
||||
.readTimeout(30, TimeUnit.SECONDS)
|
||||
.readTimeout(Util.getNetworkTimeoutMs(context), TimeUnit.MILLISECONDS)
|
||||
.build();
|
||||
|
||||
String url = getRestUrl(context, "stream");
|
||||
Map<String, String> parameters = new HashMap<>();
|
||||
|
||||
Builder builder = new FormBody.Builder();
|
||||
builder.add("id", song.getId());
|
||||
builder.add("maxBitRate", Integer.toString(maxBitrate));
|
||||
parameters.put("id", song.getId());
|
||||
parameters.put("maxBitRate", Integer.toString(maxBitrate));
|
||||
|
||||
RequestBody formBody = builder.build();
|
||||
String url = getRestUrl(context, "stream", parameters);
|
||||
|
||||
Request.Builder requestBuilder = new Request.Builder();
|
||||
if (offset > 0) {
|
||||
|
@ -633,7 +606,6 @@ public class RESTMusicService implements MusicService {
|
|||
}
|
||||
|
||||
requestBuilder.url(url);
|
||||
requestBuilder.post(formBody);
|
||||
|
||||
Request request = requestBuilder.build();
|
||||
|
||||
|
@ -643,7 +615,7 @@ public class RESTMusicService implements MusicService {
|
|||
|
||||
@Override
|
||||
public List<Genre> getGenres(boolean refresh, Context context, ProgressListener progressListener) throws Exception {
|
||||
String url = getRestUrl(context, "getGenres");
|
||||
String url = getRestUrl(context, "getGenres", null);
|
||||
|
||||
Request request = new Request.Builder()
|
||||
.url(url)
|
||||
|
@ -656,27 +628,25 @@ public class RESTMusicService implements MusicService {
|
|||
|
||||
@Override
|
||||
public MusicDirectory getSongsByGenre(String genre, int count, int offset, Context context, ProgressListener progressListener) throws Exception {
|
||||
Builder builder = new FormBody.Builder();
|
||||
builder.add("genre", genre);
|
||||
builder.add("count", Integer.toString(count));
|
||||
builder.add("offset", Integer.toString(offset));
|
||||
Map<String, String> parameters = new HashMap<>();
|
||||
|
||||
parameters.put("genre", genre);
|
||||
parameters.put("count", Integer.toString(count));
|
||||
parameters.put("offset", Integer.toString(offset));
|
||||
|
||||
// Add folder if it was set and is non null
|
||||
int instance = getInstance(context);
|
||||
if (Util.getAlbumListsPerFolder(context, instance)) {
|
||||
String folderId = Util.getSelectedMusicFolderId(context, instance);
|
||||
if (folderId != null) {
|
||||
builder.add("musicFolderId", folderId);
|
||||
parameters.put("musicFolderId", folderId);
|
||||
}
|
||||
}
|
||||
|
||||
String url = getRestUrl(context, "getSongsByGenre");
|
||||
|
||||
RequestBody formBody = builder.build();
|
||||
String url = getRestUrl(context, "getSongsByGenre", parameters);
|
||||
|
||||
Request request = new Request.Builder()
|
||||
.url(url)
|
||||
.post(formBody)
|
||||
.build();
|
||||
|
||||
try (Response response = client.newCall(request).execute()) {
|
||||
|
@ -685,25 +655,17 @@ public class RESTMusicService implements MusicService {
|
|||
}
|
||||
|
||||
@Override
|
||||
public User getUser(boolean refresh, String username, Context context, ProgressListener progressListener) throws Exception {
|
||||
String url = getRestUrl(context, "getUser");
|
||||
public void startScan(Context context) throws Exception {
|
||||
String url = getRestUrl(context, "startScan", null);
|
||||
|
||||
RequestBody formBody = new FormBody.Builder()
|
||||
.add("username", username)
|
||||
.build();
|
||||
|
||||
Request request = new Request.Builder()
|
||||
.url(url)
|
||||
.post(formBody)
|
||||
.build();
|
||||
Request request = new Request.Builder().url(url).build();
|
||||
|
||||
try (Response response = client.newCall(request).execute()) {
|
||||
List<User> users = new UserParser(context, getInstance(context)).parse(response.body().byteStream());
|
||||
if (users.size() > 0) {
|
||||
// Should only have returned one anyways
|
||||
return users.get(0);
|
||||
if (response.isSuccessful()) {
|
||||
Log.d(TAG, "Media scan started" + response.toString());
|
||||
} else {
|
||||
return null;
|
||||
Log.w(TAG, "media scan start failed" + response.toString());
|
||||
Util.toast(context, R.string.settings_media_scan_start_failed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -729,7 +691,7 @@ public class RESTMusicService implements MusicService {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void setInstance(Integer instance) throws Exception {
|
||||
public void setInstance(Integer instance) {
|
||||
this.instance = instance;
|
||||
}
|
||||
|
||||
|
@ -742,15 +704,15 @@ public class RESTMusicService implements MusicService {
|
|||
}
|
||||
|
||||
|
||||
public String getRestUrl(Context context, String method) {
|
||||
return getRestUrl(context, method, true);
|
||||
public String getRestUrl(Context context, String method, @Nullable Map<String, String> parameters) {
|
||||
return getRestUrl(context, method, true, parameters);
|
||||
}
|
||||
|
||||
public String getRestUrl(Context context, String method, boolean allowAltAddress) {
|
||||
public String getRestUrl(Context context, String method, boolean allowAltAddress, @Nullable Map<String, String> parameters) {
|
||||
if (instance == null) {
|
||||
return Util.getRestUrl(context, method, allowAltAddress);
|
||||
return Util.getRestUrl(context, method, allowAltAddress, parameters);
|
||||
} else {
|
||||
return Util.getRestUrl(context, method, instance, allowAltAddress);
|
||||
return Util.getRestUrl(context, method, instance, allowAltAddress, parameters);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,6 @@ package net.nullsum.audinaut.service.sync;
|
|||
import android.accounts.AbstractAccountAuthenticator;
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountAuthenticatorResponse;
|
||||
import android.accounts.NetworkErrorException;
|
||||
import android.app.Service;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
@ -58,17 +57,17 @@ public class AuthenticatorService extends Service {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options) throws NetworkErrorException {
|
||||
public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account, Bundle options) throws NetworkErrorException {
|
||||
public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account, Bundle options) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {
|
||||
public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -78,12 +77,12 @@ public class AuthenticatorService extends Service {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {
|
||||
public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account, String[] features) throws NetworkErrorException {
|
||||
public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account, String[] features) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,12 +18,13 @@
|
|||
*/
|
||||
package net.nullsum.audinaut.util;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import net.nullsum.audinaut.R;
|
||||
import net.nullsum.audinaut.view.ErrorDialog;
|
||||
|
||||
|
@ -79,8 +80,8 @@ public abstract class BackgroundTask<T> implements ProgressListener {
|
|||
}
|
||||
}
|
||||
|
||||
private Activity getActivity() {
|
||||
return (context instanceof Activity) ? ((Activity) context) : null;
|
||||
private AppCompatActivity getActivity() {
|
||||
return (context instanceof AppCompatActivity) ? ((AppCompatActivity) context) : null;
|
||||
}
|
||||
|
||||
Handler getHandler() {
|
||||
|
@ -95,7 +96,7 @@ public abstract class BackgroundTask<T> implements ProgressListener {
|
|||
|
||||
protected void error(Throwable error) {
|
||||
Log.w(TAG, "Got exception: " + error, error);
|
||||
Activity activity = getActivity();
|
||||
AppCompatActivity activity = getActivity();
|
||||
if (activity != null) {
|
||||
new ErrorDialog(activity, getErrorMessage(error), true);
|
||||
}
|
||||
|
|
|
@ -2,6 +2,10 @@ package net.nullsum.audinaut.util;
|
|||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.ColorMatrix;
|
||||
import android.graphics.ColorMatrixColorFilter;
|
||||
import android.graphics.Paint;
|
||||
import android.renderscript.Allocation;
|
||||
import android.renderscript.Element;
|
||||
import android.renderscript.RenderScript;
|
||||
|
@ -12,6 +16,15 @@ class BlurBuilder {
|
|||
private static final float BLUR_RADIUS = 25f;
|
||||
|
||||
public static Bitmap blur(Context context, Bitmap image) {
|
||||
Bitmap newImage = image;
|
||||
for(int i = 0; i<3; i++) {
|
||||
newImage = blur_real(context, newImage);
|
||||
}
|
||||
newImage = changeBitmapContrastBrightness(newImage, 0.5f, 48);
|
||||
return newImage;
|
||||
}
|
||||
|
||||
private static Bitmap blur_real(Context context, Bitmap image) {
|
||||
int width = Math.round(image.getWidth() * BITMAP_SCALE);
|
||||
int height = Math.round(image.getHeight() * BITMAP_SCALE);
|
||||
|
||||
|
@ -29,4 +42,25 @@ class BlurBuilder {
|
|||
|
||||
return outputBitmap;
|
||||
}
|
||||
|
||||
public static Bitmap changeBitmapContrastBrightness(Bitmap bmp, float contrast, float brightness)
|
||||
{
|
||||
ColorMatrix cm = new ColorMatrix(new float[]
|
||||
{
|
||||
contrast, 0, 0, 0, brightness,
|
||||
0, contrast, 0, 0, brightness,
|
||||
0, 0, contrast, 0, brightness,
|
||||
0, 0, 0, 1, 0
|
||||
});
|
||||
|
||||
Bitmap ret = Bitmap.createBitmap(bmp.getWidth(), bmp.getHeight(), bmp.getConfig());
|
||||
|
||||
Canvas canvas = new Canvas(ret);
|
||||
|
||||
Paint paint = new Paint();
|
||||
paint.setColorFilter(new ColorMatrixColorFilter(cm));
|
||||
canvas.drawBitmap(bmp, 0, 0, paint);
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,10 +67,12 @@ public final class Constants {
|
|||
public static final String PREFERENCES_KEY_SERVER_INTERNAL_URL = "serverInternalUrl";
|
||||
public static final String PREFERENCES_KEY_SERVER_LOCAL_NETWORK_SSID = "serverLocalNetworkSSID";
|
||||
public static final String PREFERENCES_KEY_TEST_CONNECTION = "serverTestConnection";
|
||||
public static final String PREFERENCES_KEY_START_SCAN = "serverStartScan";
|
||||
public static final String PREFERENCES_KEY_OPEN_BROWSER = "openBrowser";
|
||||
public static final String PREFERENCES_KEY_MUSIC_FOLDER_ID = "musicFolderId";
|
||||
public static final String PREFERENCES_KEY_USERNAME = "username";
|
||||
public static final String PREFERENCES_KEY_PASSWORD = "password";
|
||||
public static final String PREFERENCES_KEY_AUTH_METHOD = "authMethod";
|
||||
public static final String PREFERENCES_KEY_THEME = "theme";
|
||||
public static final String PREFERENCES_KEY_FULL_SCREEN = "fullScreen";
|
||||
public static final String PREFERENCES_KEY_DISPLAY_TRACK = "displayTrack";
|
||||
|
@ -121,7 +123,6 @@ public final class Constants {
|
|||
public static final String PREFERENCES_KEY_ALBUMS_PER_FOLDER = "albumsPerFolder";
|
||||
public static final String PREFERENCES_KEY_FIRST_LEVEL_ARTIST = "firstLevelArtist";
|
||||
public static final String PREFERENCES_KEY_START_ON_HEADPHONES = "startOnHeadphones";
|
||||
public static final String PREFERENCES_KEY_COLOR_ACTION_BAR = "colorActionBar";
|
||||
public static final String PREFERENCES_KEY_SHUFFLE_BY_ALBUM = "shuffleByAlbum";
|
||||
public static final String PREFERENCES_KEY_BATCH_MODE = "batchMode";
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package net.nullsum.audinaut.util;
|
||||
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.support.v7.widget.helper.ItemTouchHelper;
|
||||
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import net.nullsum.audinaut.adapter.SectionAdapter;
|
||||
import net.nullsum.audinaut.fragments.SubsonicFragment;
|
||||
|
@ -68,7 +68,7 @@ public class DownloadFileItemHelperCallback extends ItemTouchHelper.SimpleCallba
|
|||
|
||||
pendingTask = new SilentBackgroundTask<Void>(downloadService) {
|
||||
@Override
|
||||
protected Void doInBackground() throws Throwable {
|
||||
protected Void doInBackground() {
|
||||
boolean running = true;
|
||||
while (running) {
|
||||
Object nextOperation = null;
|
||||
|
|
|
@ -20,11 +20,12 @@ import android.content.res.Resources;
|
|||
import android.content.res.TypedArray;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.support.annotation.AttrRes;
|
||||
import android.support.annotation.DrawableRes;
|
||||
import android.util.SparseArray;
|
||||
import android.util.TypedValue;
|
||||
|
||||
import androidx.annotation.AttrRes;
|
||||
import androidx.annotation.DrawableRes;
|
||||
|
||||
import net.nullsum.audinaut.R;
|
||||
|
||||
import java.util.WeakHashMap;
|
||||
|
|
|
@ -23,9 +23,10 @@ import android.graphics.Bitmap;
|
|||
import android.graphics.BitmapFactory;
|
||||
import android.os.Build;
|
||||
import android.os.Environment;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.esotericsoftware.kryo.Kryo;
|
||||
import com.esotericsoftware.kryo.io.Input;
|
||||
import com.esotericsoftware.kryo.io.Output;
|
||||
|
@ -65,7 +66,7 @@ public class FileUtil {
|
|||
private static final String TAG = FileUtil.class.getSimpleName();
|
||||
private static final String[] FILE_SYSTEM_UNSAFE = {"/", "\\", "..", ":", "\"", "?", "*", "<", ">", "|"};
|
||||
private static final String[] FILE_SYSTEM_UNSAFE_DIR = {"\\", "..", ":", "\"", "?", "*", "<", ">", "|"};
|
||||
private static final List<String> MUSIC_FILE_EXTENSIONS = Arrays.asList("mp3", "ogg", "aac", "flac", "m4a", "wav", "wma");
|
||||
private static final List<String> MUSIC_FILE_EXTENSIONS = Arrays.asList("mp3", "ogg", "opus", "aac", "flac", "m4a", "wav", "wma");
|
||||
private static final List<String> PLAYLIST_FILE_EXTENSIONS = Collections.singletonList("m3u");
|
||||
private static final int MAX_FILENAME_LENGTH = 254 - ".complete.mp3".length();
|
||||
private static final Kryo kryo = new Kryo();
|
||||
|
@ -292,7 +293,7 @@ public class FileUtil {
|
|||
}
|
||||
|
||||
// Do a special lookup since 4.7+ doesn't match artist/album to entry.getPath
|
||||
String s = Util.getRestUrl(context, null, false) + entry.getId();
|
||||
String s = Util.getRestUrl(context, null, false, null) + entry.getId();
|
||||
String cacheName = "album-" + s.hashCode() + ".ser";
|
||||
MusicDirectory entryDir = FileUtil.deserialize(context, cacheName, MusicDirectory.class);
|
||||
|
||||
|
|
|
@ -30,13 +30,14 @@ import android.graphics.drawable.Drawable;
|
|||
import android.graphics.drawable.TransitionDrawable;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.support.v4.util.LruCache;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.collection.LruCache;
|
||||
|
||||
import net.nullsum.audinaut.R;
|
||||
import net.nullsum.audinaut.domain.MusicDirectory;
|
||||
import net.nullsum.audinaut.domain.Playlist;
|
||||
|
@ -99,7 +100,7 @@ public class ImageLoader {
|
|||
nowPlayingSmall = null;
|
||||
new SilentBackgroundTask<Void>(context) {
|
||||
@Override
|
||||
protected Void doInBackground() throws Throwable {
|
||||
protected Void doInBackground() {
|
||||
clearingCache = true;
|
||||
cache.evictAll();
|
||||
clearingCache = false;
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
package net.nullsum.audinaut.util;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.ProgressDialog;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import net.nullsum.audinaut.activity.SubsonicActivity;
|
||||
|
||||
/**
|
||||
|
@ -11,17 +12,17 @@ import net.nullsum.audinaut.activity.SubsonicActivity;
|
|||
*/
|
||||
public abstract class LoadingTask<T> extends BackgroundTask<T> {
|
||||
|
||||
private final Activity tabActivity;
|
||||
private final AppCompatActivity tabActivity;
|
||||
private final boolean cancellable;
|
||||
private ProgressDialog loading;
|
||||
|
||||
public LoadingTask(Activity activity) {
|
||||
public LoadingTask(AppCompatActivity activity) {
|
||||
super(activity);
|
||||
tabActivity = activity;
|
||||
this.cancellable = true;
|
||||
}
|
||||
|
||||
public LoadingTask(Activity activity, final boolean cancellable) {
|
||||
public LoadingTask(AppCompatActivity activity, final boolean cancellable) {
|
||||
super(activity);
|
||||
tabActivity = activity;
|
||||
this.cancellable = cancellable;
|
||||
|
|
|
@ -23,13 +23,16 @@ import android.content.ComponentName;
|
|||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.support.v4.media.MediaMetadataCompat;
|
||||
import android.support.v4.media.session.MediaSessionCompat;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
import android.widget.RemoteViews;
|
||||
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.media.app.NotificationCompat.MediaStyle;
|
||||
|
||||
import net.nullsum.audinaut.R;
|
||||
import net.nullsum.audinaut.activity.SubsonicActivity;
|
||||
|
@ -66,28 +69,52 @@ public final class Notifications {
|
|||
}
|
||||
|
||||
final boolean playing = downloadService.getPlayerState() == PlayerState.STARTED;
|
||||
|
||||
RemoteViews expandedContentView = new RemoteViews(context.getPackageName(), R.layout.notification_expanded);
|
||||
setupViews(expandedContentView, context, song, true, playing);
|
||||
|
||||
RemoteViews smallContentView = new RemoteViews(context.getPackageName(), R.layout.notification);
|
||||
setupViews(smallContentView, context, song, false, playing);
|
||||
|
||||
Intent notificationIntent = new Intent(context, SubsonicFragmentActivity.class);
|
||||
notificationIntent.putExtra(Constants.INTENT_EXTRA_NAME_DOWNLOAD, true);
|
||||
notificationIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
|
||||
final Notification notification = new NotificationCompat.Builder(context, CHANNEL_PLAYING_ID)
|
||||
Intent cancelIntent = new Intent("KEYCODE_MEDIA_STOP")
|
||||
.setComponent(new ComponentName(context, DownloadService.class))
|
||||
.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MEDIA_STOP));
|
||||
int[] compactActions = new int[]{0, 1, 2};
|
||||
|
||||
MediaSessionCompat mediaSession = new MediaSessionCompat(context, "Audinaut");
|
||||
MediaSessionCompat.Token mediaToken = mediaSession.getSessionToken();
|
||||
MediaMetadataCompat.Builder metadataBuilder = new MediaMetadataCompat.Builder();
|
||||
|
||||
mediaSession.setMetadata(metadataBuilder
|
||||
.putLong(MediaMetadataCompat.METADATA_KEY_DURATION, -1)
|
||||
.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, getAlbumArt(context, song))
|
||||
//.putBitmap(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON, R.drawable.notification_logo)
|
||||
.putString(MediaMetadataCompat.METADATA_KEY_TITLE, song.getTitle())
|
||||
.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, song.getArtist())
|
||||
.build() );
|
||||
|
||||
MediaStyle mediaStyle = new MediaStyle()
|
||||
.setShowActionsInCompactView(compactActions)
|
||||
.setShowCancelButton(true)
|
||||
.setCancelButtonIntent(PendingIntent.getService(context, 0, cancelIntent, 0))
|
||||
.setMediaSession(mediaToken);
|
||||
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, CHANNEL_PLAYING_ID)
|
||||
.setChannelId(CHANNEL_PLAYING_ID)
|
||||
.setSmallIcon(R.drawable.stat_notify_playing)
|
||||
.setContentTitle(song.getTitle())
|
||||
.setContentText(song.getTitle())
|
||||
.setContentText(song.getArtist())
|
||||
.setSubText(song.getAlbum())
|
||||
.setTicker(song.getTitle())
|
||||
.setOngoing(playing)
|
||||
.setVisibility(Notification.VISIBILITY_PUBLIC)
|
||||
.setCustomContentView(smallContentView)
|
||||
.setCustomBigContentView(expandedContentView)
|
||||
.setContentIntent(PendingIntent.getActivity(context, 0, notificationIntent, 0))
|
||||
.setPriority(NotificationCompat.PRIORITY_LOW).build();
|
||||
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||
.setShowWhen(false)
|
||||
.setLargeIcon(getAlbumArt(context, song))
|
||||
.setSmallIcon(R.drawable.notification_logo)
|
||||
.setStyle(mediaStyle)
|
||||
.setContentIntent(PendingIntent.getActivity(context, 0, notificationIntent, 0));
|
||||
addActions(context, builder, playing);
|
||||
final Notification notification = builder.build();
|
||||
|
||||
if(playing) {
|
||||
notification.flags |= Notification.FLAG_NO_CLEAR | Notification.FLAG_ONGOING_EVENT;
|
||||
}
|
||||
|
||||
playShowing = true;
|
||||
if (downloadForeground && downloadShowing) {
|
||||
|
@ -115,13 +142,7 @@ public final class Notifications {
|
|||
AudinautWidgetProvider.notifyInstances(context, downloadService, playing);
|
||||
}
|
||||
|
||||
private static void setupViews(RemoteViews rv, Context context, MusicDirectory.Entry song, boolean expanded, boolean playing) {
|
||||
// Use the same text for the ticker and the expanded notification
|
||||
String title = song.getTitle();
|
||||
String arist = song.getArtist();
|
||||
String album = song.getAlbum();
|
||||
|
||||
// Set the album art.
|
||||
private static Bitmap getAlbumArt(Context context, MusicDirectory.Entry song) {
|
||||
try {
|
||||
ImageLoader imageLoader = SubsonicActivity.getStaticImageLoader(context);
|
||||
Bitmap bitmap = null;
|
||||
|
@ -130,89 +151,42 @@ public final class Notifications {
|
|||
}
|
||||
if (bitmap == null) {
|
||||
// set default album art
|
||||
rv.setImageViewResource(R.id.notification_image, R.drawable.unknown_album);
|
||||
return BitmapFactory.decodeResource(context.getResources(), R.drawable.unknown_album);
|
||||
} else {
|
||||
imageLoader.setNowPlayingSmall(bitmap);
|
||||
rv.setImageViewBitmap(R.id.notification_image, bitmap);
|
||||
return bitmap;
|
||||
}
|
||||
} catch (Exception x) {
|
||||
Log.w(TAG, "Failed to get notification cover art", x);
|
||||
rv.setImageViewResource(R.id.notification_image, R.drawable.unknown_album);
|
||||
return BitmapFactory.decodeResource(context.getResources(), R.drawable.unknown_album);
|
||||
}
|
||||
}
|
||||
|
||||
// set the text for the notifications
|
||||
rv.setTextViewText(R.id.notification_title, title);
|
||||
rv.setTextViewText(R.id.notification_artist, arist);
|
||||
rv.setTextViewText(R.id.notification_album, album);
|
||||
|
||||
boolean persistent = Util.getPreferences(context).getBoolean(Constants.PREFERENCES_KEY_PERSISTENT_NOTIFICATION, false);
|
||||
if (persistent) {
|
||||
if (expanded) {
|
||||
rv.setImageViewResource(R.id.control_pause, playing ? R.drawable.notification_pause : R.drawable.notification_start);
|
||||
|
||||
rv.setImageViewResource(R.id.control_previous, R.drawable.notification_backward);
|
||||
rv.setImageViewResource(R.id.control_next, R.drawable.notification_forward);
|
||||
} else {
|
||||
rv.setImageViewResource(R.id.control_previous, playing ? R.drawable.notification_pause : R.drawable.notification_start);
|
||||
rv.setImageViewResource(R.id.control_pause, R.drawable.notification_forward);
|
||||
rv.setImageViewResource(R.id.control_next, R.drawable.notification_close);
|
||||
}
|
||||
} else {
|
||||
// Necessary for switching back since it appears to re-use the same layout
|
||||
rv.setImageViewResource(R.id.control_previous, R.drawable.notification_backward);
|
||||
rv.setImageViewResource(R.id.control_next, R.drawable.notification_forward);
|
||||
}
|
||||
|
||||
// Create actions for media buttons
|
||||
private static void addActions(final Context context, final NotificationCompat.Builder builder, final boolean playing) {
|
||||
PendingIntent pendingIntent;
|
||||
int previous = 0, pause, next, close = 0;
|
||||
if (persistent && !expanded) {
|
||||
pause = R.id.control_previous;
|
||||
next = R.id.control_pause;
|
||||
close = R.id.control_next;
|
||||
} else {
|
||||
previous = R.id.control_previous;
|
||||
pause = R.id.control_pause;
|
||||
next = R.id.control_next;
|
||||
}
|
||||
|
||||
if (persistent && close == 0 && expanded) {
|
||||
close = R.id.notification_close;
|
||||
rv.setViewVisibility(close, View.VISIBLE);
|
||||
}
|
||||
|
||||
if (previous > 0) {
|
||||
Intent prevIntent = new Intent("KEYCODE_MEDIA_PREVIOUS");
|
||||
prevIntent.setComponent(new ComponentName(context, DownloadService.class));
|
||||
prevIntent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MEDIA_PREVIOUS));
|
||||
pendingIntent = PendingIntent.getService(context, 0, prevIntent, 0);
|
||||
rv.setOnClickPendingIntent(previous, pendingIntent);
|
||||
}
|
||||
Intent prevIntent = new Intent("KEYCODE_MEDIA_PREVIOUS");
|
||||
prevIntent.setComponent(new ComponentName(context, DownloadService.class));
|
||||
prevIntent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MEDIA_PREVIOUS));
|
||||
pendingIntent = PendingIntent.getService(context, 0, prevIntent, 0);
|
||||
builder.addAction(R.drawable.notification_media_backward, "Previous", pendingIntent);
|
||||
if (playing) {
|
||||
Intent pauseIntent = new Intent("KEYCODE_MEDIA_PLAY_PAUSE");
|
||||
pauseIntent.setComponent(new ComponentName(context, DownloadService.class));
|
||||
pauseIntent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE));
|
||||
pendingIntent = PendingIntent.getService(context, 0, pauseIntent, 0);
|
||||
rv.setOnClickPendingIntent(pause, pendingIntent);
|
||||
builder.addAction(R.drawable.notification_media_pause, "Pause", pendingIntent);
|
||||
} else {
|
||||
Intent prevIntent = new Intent("KEYCODE_MEDIA_START");
|
||||
prevIntent.setComponent(new ComponentName(context, DownloadService.class));
|
||||
prevIntent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MEDIA_PLAY));
|
||||
pendingIntent = PendingIntent.getService(context, 0, prevIntent, 0);
|
||||
rv.setOnClickPendingIntent(pause, pendingIntent);
|
||||
Intent playIntent = new Intent("KEYCODE_MEDIA_PLAY");
|
||||
playIntent.setComponent(new ComponentName(context, DownloadService.class));
|
||||
playIntent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MEDIA_PLAY));
|
||||
pendingIntent = PendingIntent.getService(context, 0, playIntent, 0);
|
||||
builder.addAction(R.drawable.notification_media_start, "Play", pendingIntent);
|
||||
}
|
||||
Intent nextIntent = new Intent("KEYCODE_MEDIA_NEXT");
|
||||
nextIntent.setComponent(new ComponentName(context, DownloadService.class));
|
||||
nextIntent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MEDIA_NEXT));
|
||||
pendingIntent = PendingIntent.getService(context, 0, nextIntent, 0);
|
||||
rv.setOnClickPendingIntent(next, pendingIntent);
|
||||
if (close > 0) {
|
||||
Intent prevIntent = new Intent("KEYCODE_MEDIA_STOP");
|
||||
prevIntent.setComponent(new ComponentName(context, DownloadService.class));
|
||||
prevIntent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MEDIA_STOP));
|
||||
pendingIntent = PendingIntent.getService(context, 0, prevIntent, 0);
|
||||
rv.setOnClickPendingIntent(close, pendingIntent);
|
||||
}
|
||||
builder.addAction(R.drawable.notification_media_forward, "Next", pendingIntent);
|
||||
}
|
||||
|
||||
public static void hidePlayingNotification(final Context context, final DownloadService downloadService, Handler handler) {
|
||||
|
|
|
@ -15,7 +15,7 @@ public final class SyncUtil {
|
|||
|
||||
private static void checkRestURL(Context context) {
|
||||
int instance = Util.getActiveServer(context);
|
||||
String newURL = Util.getRestUrl(context, null, instance, false);
|
||||
String newURL = Util.getRestUrl(context, null, instance, false, null);
|
||||
if (url == null || !url.equals(newURL)) {
|
||||
syncedPlaylists = null;
|
||||
url = newURL;
|
||||
|
@ -72,7 +72,7 @@ public final class SyncUtil {
|
|||
}
|
||||
|
||||
private static String getPlaylistSyncFile(Context context, int instance) {
|
||||
return "sync-playlist-" + (Util.getRestUrl(context, null, instance, false)).hashCode() + ".ser";
|
||||
return "sync-playlist-" + (Util.getRestUrl(context, null, instance, false, null)).hashCode() + ".ser";
|
||||
}
|
||||
|
||||
public static void removeMostRecentSyncFiles(Context context) {
|
||||
|
@ -84,7 +84,7 @@ public final class SyncUtil {
|
|||
}
|
||||
|
||||
private static String getMostRecentSyncFile(Context context, int instance) {
|
||||
return "sync-most_recent-" + (Util.getRestUrl(context, null, instance, false)).hashCode() + ".ser";
|
||||
return "sync-most_recent-" + (Util.getRestUrl(context, null, instance, false, null)).hashCode() + ".ser";
|
||||
}
|
||||
|
||||
public static class SyncSet implements Serializable {
|
||||
|
|
|
@ -18,10 +18,9 @@ package net.nullsum.audinaut.util;
|
|||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Build;
|
||||
|
||||
import net.nullsum.audinaut.R;
|
||||
import net.nullsum.audinaut.activity.SettingsActivity;
|
||||
import net.nullsum.audinaut.activity.SubsonicFragmentActivity;
|
||||
|
||||
public final class ThemeUtil {
|
||||
private static final String THEME_DARK = "dark";
|
||||
|
@ -32,7 +31,15 @@ public final class ThemeUtil {
|
|||
|
||||
public static String getTheme(Context context) {
|
||||
SharedPreferences prefs = Util.getPreferences(context);
|
||||
String theme = prefs.getString(Constants.PREFERENCES_KEY_THEME, null);
|
||||
String theme;
|
||||
|
||||
if (Build.VERSION.SDK_INT<29) {
|
||||
// If Android Pie or older, default to null (handled below as light)
|
||||
theme = prefs.getString(Constants.PREFERENCES_KEY_THEME, null);
|
||||
} else {
|
||||
// Else, for Android 10+, default to follow system dark mode setting
|
||||
theme = prefs.getString(Constants.PREFERENCES_KEY_THEME, THEME_DAY_NIGHT);
|
||||
}
|
||||
|
||||
if (THEME_DAY_NIGHT.equals(theme)) {
|
||||
int currentNightMode = context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
|
||||
|
@ -58,35 +65,16 @@ public final class ThemeUtil {
|
|||
}
|
||||
|
||||
private static int getThemeRes(Context context, String theme) {
|
||||
if (context instanceof SubsonicFragmentActivity || context instanceof SettingsActivity) {
|
||||
if (Util.getPreferences(context).getBoolean(Constants.PREFERENCES_KEY_COLOR_ACTION_BAR, true)) {
|
||||
switch (theme) {
|
||||
case THEME_DARK:
|
||||
return R.style.Theme_Audinaut_Dark_No_Actionbar;
|
||||
case THEME_BLACK:
|
||||
return R.style.Theme_Audinaut_Black_No_Actionbar;
|
||||
default:
|
||||
return R.style.Theme_Audinaut_Light_No_Actionbar;
|
||||
}
|
||||
} else {
|
||||
switch (theme) {
|
||||
case THEME_DARK:
|
||||
return R.style.Theme_Audinaut_Dark_No_Color;
|
||||
case THEME_BLACK:
|
||||
return R.style.Theme_Audinaut_Black_No_Color;
|
||||
default:
|
||||
return R.style.Theme_Audinaut_Light_No_Color;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
switch (theme) {
|
||||
case THEME_DARK:
|
||||
return R.style.Theme_Audinaut_Dark;
|
||||
case THEME_BLACK:
|
||||
return R.style.Theme_Audinaut_Black;
|
||||
default:
|
||||
return R.style.Theme_Audinaut_Light;
|
||||
}
|
||||
if(theme == null)
|
||||
return R.style.Theme_Audinaut_Light;
|
||||
|
||||
switch (theme) {
|
||||
case THEME_DARK:
|
||||
return R.style.Theme_Audinaut_Dark;
|
||||
case THEME_BLACK:
|
||||
return R.style.Theme_Audinaut_Black;
|
||||
default:
|
||||
return R.style.Theme_Audinaut_Light;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -17,62 +17,8 @@ package net.nullsum.audinaut.util;
|
|||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.util.Log;
|
||||
|
||||
import net.nullsum.audinaut.domain.User;
|
||||
import net.nullsum.audinaut.service.MusicServiceFactory;
|
||||
|
||||
public final class UserUtil {
|
||||
private static final String TAG = UserUtil.class.getSimpleName();
|
||||
|
||||
private static int instance = -1;
|
||||
private static int instanceHash = -1;
|
||||
private static User currentUser;
|
||||
|
||||
public static void refreshCurrentUser(Context context) {
|
||||
currentUser = null;
|
||||
seedCurrentUser(context);
|
||||
}
|
||||
|
||||
public static void seedCurrentUser(Context context) {
|
||||
// Only try to seed if online
|
||||
if (Util.isOffline(context)) {
|
||||
currentUser = null;
|
||||
return;
|
||||
}
|
||||
|
||||
final int instance = Util.getActiveServer(context);
|
||||
final int instanceHash = (instance == 0) ? 0 : Util.getRestUrl(context).hashCode();
|
||||
if (UserUtil.instance == instance && UserUtil.instanceHash == instanceHash && currentUser != null) {
|
||||
return;
|
||||
} else {
|
||||
UserUtil.instance = instance;
|
||||
UserUtil.instanceHash = instanceHash;
|
||||
}
|
||||
|
||||
new SilentBackgroundTask<Void>(context) {
|
||||
@Override
|
||||
protected Void doInBackground() throws Throwable {
|
||||
currentUser = MusicServiceFactory.getMusicService(context).getUser(false, getCurrentUsername(context, instance), context, null);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done(Void result) {
|
||||
if (context instanceof AppCompatActivity) {
|
||||
((AppCompatActivity) context).supportInvalidateOptionsMenu();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void error(Throwable error) {
|
||||
// Don't do anything, supposed to be background pull
|
||||
Log.e(TAG, "Failed to seed user information");
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
private static String getCurrentUsername(Context context, int instance) {
|
||||
SharedPreferences prefs = Util.getPreferences(context);
|
||||
return prefs.getString(Constants.PREFERENCES_KEY_USERNAME + instance, null);
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
*/
|
||||
package net.nullsum.audinaut.util;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.ComponentName;
|
||||
|
@ -36,18 +35,23 @@ import android.net.NetworkInfo;
|
|||
import android.net.wifi.WifiManager;
|
||||
import android.os.Build;
|
||||
import android.os.Environment;
|
||||
import android.support.annotation.StringRes;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.text.SpannableString;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.text.util.Linkify;
|
||||
import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import net.nullsum.audinaut.R;
|
||||
import net.nullsum.audinaut.adapter.DetailsAdapter;
|
||||
import net.nullsum.audinaut.domain.MusicDirectory;
|
||||
|
@ -63,12 +67,14 @@ import java.io.IOException;
|
|||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.SecureRandom;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
import okhttp3.HttpUrl;
|
||||
|
@ -115,6 +121,11 @@ public final class Util {
|
|||
editor.apply();
|
||||
}
|
||||
|
||||
public static int getNetworkTimeoutMs(Context context) {
|
||||
SharedPreferences prefs = getPreferences(context);
|
||||
return Integer.parseInt(prefs.getString(Constants.PREFERENCES_KEY_NETWORK_TIMEOUT, "30000"));
|
||||
}
|
||||
|
||||
public static boolean isScreenLitOnDownload(Context context) {
|
||||
SharedPreferences prefs = getPreferences(context);
|
||||
return prefs.getBoolean(Constants.PREFERENCES_KEY_SCREEN_LIT_ON_DOWNLOAD, false);
|
||||
|
@ -276,23 +287,26 @@ public final class Util {
|
|||
}
|
||||
|
||||
public static String getRestUrl(Context context) {
|
||||
return getRestUrl(context, null, true);
|
||||
return getRestUrl(context, null, true, null);
|
||||
}
|
||||
|
||||
public static String getRestUrl(Context context, String method, boolean allowAltAddress) {
|
||||
// used
|
||||
public static String getRestUrl(Context context, String method, boolean allowAltAddress, @Nullable Map<String, String> parameters) {
|
||||
SharedPreferences prefs = getPreferences(context);
|
||||
int instance = prefs.getInt(Constants.PREFERENCES_KEY_SERVER_INSTANCE, 1);
|
||||
return getRestUrl(context, method, prefs, instance, allowAltAddress);
|
||||
return getRestUrl(context, method, prefs, instance, allowAltAddress, parameters);
|
||||
}
|
||||
|
||||
public static String getRestUrl(Context context, String method, int instance, boolean allowAltAddress) {
|
||||
public static String getRestUrl(Context context, String method, int instance, boolean allowAltAddress, @Nullable Map<String, String> parameters) {
|
||||
SharedPreferences prefs = getPreferences(context);
|
||||
return getRestUrl(context, method, prefs, instance, allowAltAddress);
|
||||
return getRestUrl(context, method, prefs, instance, allowAltAddress, parameters);
|
||||
}
|
||||
|
||||
private static String getRestUrl(Context context, String method, SharedPreferences prefs, int instance, boolean allowAltAddress) {
|
||||
private static String getRestUrl(Context context, String method, SharedPreferences prefs, int instance, boolean allowAltAddress, @Nullable Map<String, String> parameters) {
|
||||
String serverUrl = prefs.getString(Constants.PREFERENCES_KEY_SERVER_URL + instance, null);
|
||||
|
||||
if (serverUrl == null) return "OFFLINE";
|
||||
|
||||
HttpUrl.Builder builder;
|
||||
builder = HttpUrl.parse(serverUrl).newBuilder();
|
||||
|
||||
|
@ -318,21 +332,33 @@ public final class Util {
|
|||
builder.addPathSegment("rest");
|
||||
builder.addPathSegment(method + ".view");
|
||||
|
||||
int hash = (username + password).hashCode();
|
||||
Pair<String, String> values = tokens.get(hash);
|
||||
if (values == null) {
|
||||
String salt = new BigInteger(130, getRandom()).toString(32);
|
||||
String token = md5Hex(password + salt);
|
||||
values = new Pair<>(salt, token);
|
||||
tokens.put(hash, values);
|
||||
builder.addQueryParameter("u", username);
|
||||
|
||||
if (prefs.getBoolean(Constants.PREFERENCES_KEY_AUTH_METHOD + instance, true)) {
|
||||
int hash = (username + password).hashCode();
|
||||
Pair<String, String> values = tokens.get(hash);
|
||||
if (values == null) {
|
||||
String salt = new BigInteger(130, getRandom()).toString(32);
|
||||
String token = md5Hex(password + salt);
|
||||
values = new Pair<>(salt, token);
|
||||
tokens.put(hash, values);
|
||||
}
|
||||
|
||||
builder.addQueryParameter("s", values.getFirst());
|
||||
builder.addQueryParameter("t", values.getSecond());
|
||||
} else {
|
||||
builder.addQueryParameter("p", password);
|
||||
}
|
||||
|
||||
builder.addQueryParameter("u", username);
|
||||
builder.addQueryParameter("s", values.getFirst());
|
||||
builder.addQueryParameter("t", values.getSecond());
|
||||
builder.addQueryParameter("v", Constants.REST_PROTOCOL_VERSION_SUBSONIC);
|
||||
builder.addQueryParameter("c", Constants.REST_CLIENT_ID);
|
||||
|
||||
if (parameters != null) {
|
||||
for (Map.Entry<String, String> parameter : parameters.entrySet()) {
|
||||
builder.addQueryParameter(parameter.getKey(), parameter.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
return builder.build().toString();
|
||||
}
|
||||
|
||||
|
@ -351,7 +377,7 @@ public final class Util {
|
|||
}
|
||||
|
||||
private static String getBlockTokenUsePref(Context context, int instance) {
|
||||
return Constants.CACHE_BLOCK_TOKEN_USE + Util.getRestUrl(context, null, instance, false);
|
||||
return Constants.CACHE_BLOCK_TOKEN_USE + Util.getRestUrl(context, null, instance, false, null);
|
||||
}
|
||||
|
||||
public static void setBlockTokenUse(Context context, int instance) {
|
||||
|
@ -365,7 +391,7 @@ public final class Util {
|
|||
}
|
||||
|
||||
public static String getCacheName(Context context, String name, String id) {
|
||||
String s = getRestUrl(context, null, getActiveServer(context), false) + id;
|
||||
String s = getRestUrl(context, null, getActiveServer(context), false, null) + id;
|
||||
return name + "-" + s.hashCode() + ".ser";
|
||||
}
|
||||
|
||||
|
@ -714,7 +740,7 @@ public final class Util {
|
|||
|
||||
try {
|
||||
MessageDigest md5 = MessageDigest.getInstance("MD5");
|
||||
return hexEncode(md5.digest(s.getBytes(Constants.UTF_8)));
|
||||
return hexEncode(md5.digest(s.getBytes(StandardCharsets.UTF_8)));
|
||||
} catch (Exception x) {
|
||||
throw new RuntimeException(x.getMessage(), x);
|
||||
}
|
||||
|
@ -782,9 +808,9 @@ public final class Util {
|
|||
m = t.length();
|
||||
}
|
||||
|
||||
int p[] = new int[n + 1];
|
||||
int d[] = new int[n + 1];
|
||||
int _d[];
|
||||
int[] p = new int[n + 1];
|
||||
int[] d = new int[n + 1];
|
||||
int[] _d;
|
||||
|
||||
int i;
|
||||
int j;
|
||||
|
@ -935,7 +961,7 @@ public final class Util {
|
|||
}
|
||||
}
|
||||
|
||||
public static void startActivityWithoutTransition(Activity currentActivity, Intent intent) {
|
||||
public static void startActivityWithoutTransition(AppCompatActivity currentActivity, Intent intent) {
|
||||
currentActivity.startActivity(intent);
|
||||
}
|
||||
|
||||
|
@ -1124,4 +1150,10 @@ public final class Util {
|
|||
|
||||
return random;
|
||||
}
|
||||
|
||||
public static void hideKeyboard(View view) {
|
||||
InputMethodManager imm = (InputMethodManager) view.getContext().getSystemService(AppCompatActivity.INPUT_METHOD_SERVICE);
|
||||
|
||||
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
package net.nullsum.audinaut.util.tags;
|
||||
|
||||
import android.support.v4.util.LruCache;
|
||||
import androidx.collection.LruCache;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Vector;
|
||||
|
|
|
@ -31,8 +31,8 @@ class Common {
|
|||
}
|
||||
|
||||
/*
|
||||
** Returns a 32bit int from given byte offset in LE
|
||||
*/
|
||||
** Returns a 32bit int from given byte offset in LE
|
||||
*/
|
||||
private int b2le32(byte[] b, int off) {
|
||||
int r = 0;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
|
@ -50,8 +50,8 @@ class Common {
|
|||
}
|
||||
|
||||
/*
|
||||
** convert 'byte' value into unsigned int
|
||||
*/
|
||||
** convert 'byte' value into unsigned int
|
||||
*/
|
||||
int b2u(byte x) {
|
||||
return (x & 0xFF);
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ class FlacFile extends Common {
|
|||
public HashMap getTags(RandomAccessFile s) throws IOException {
|
||||
int xoff = 4; // skip file magic
|
||||
int retry = 64;
|
||||
int r[];
|
||||
int[] r;
|
||||
HashMap tags = new HashMap();
|
||||
|
||||
for (; retry > 0; retry--) {
|
||||
|
@ -52,8 +52,8 @@ class FlacFile extends Common {
|
|||
}
|
||||
|
||||
/* Parses the metadata block at 'offset' and returns
|
||||
** [header_size, payload_size, type, stop_after]
|
||||
*/
|
||||
** [header_size, payload_size, type, stop_after]
|
||||
*/
|
||||
private int[] parse_metadata_block(RandomAccessFile s, long offset) throws IOException {
|
||||
int[] result = new int[4];
|
||||
byte[] mb_head = new byte[4];
|
||||
|
|
|
@ -19,6 +19,7 @@ package net.nullsum.audinaut.util.tags;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
|
@ -56,8 +57,8 @@ class ID3v2File extends Common {
|
|||
}
|
||||
|
||||
/* Parses all ID3v2 frames at the current position up until payload_len
|
||||
** bytes were read
|
||||
*/
|
||||
** bytes were read
|
||||
*/
|
||||
private HashMap parse_v3_frames(RandomAccessFile s, long payload_len) throws IOException {
|
||||
HashMap tags = new HashMap();
|
||||
byte[] frame = new byte[10]; // a frame header is always 10 bytes
|
||||
|
@ -105,7 +106,7 @@ class ID3v2File extends Common {
|
|||
rv[1] = getDecodedString(v);
|
||||
} else if (k.equals("TXXX")) {
|
||||
/* A freestyle field, ieks! */
|
||||
String txData[] = getDecodedString(v).split(Character.toString('\0'), 2);
|
||||
String[] txData = getDecodedString(v).split(Character.toString('\0'), 2);
|
||||
/* Check if we got replaygain info in key\0value style */
|
||||
if (txData.length == 2) {
|
||||
if (txData[0].matches("^(?i)REPLAYGAIN_(ALBUM|TRACK)_GAIN$")) {
|
||||
|
@ -156,13 +157,13 @@ class ID3v2File extends Common {
|
|||
int ID3_ENC_UTF16BE = 0x02;
|
||||
int ID3_ENC_UTF16LE = 0x01;
|
||||
if (encid == ID3_ENC_LATIN) {
|
||||
v = new String(raw, 1, len - 1, "ISO-8859-1");
|
||||
v = new String(raw, 1, len - 1, StandardCharsets.ISO_8859_1);
|
||||
} else if (encid == ID3_ENC_UTF8) {
|
||||
v = new String(raw, 1, len - 1, "UTF-8");
|
||||
v = new String(raw, 1, len - 1, StandardCharsets.UTF_8);
|
||||
} else if (encid == ID3_ENC_UTF16LE) {
|
||||
v = new String(raw, 3, len - 3, "UTF-16LE");
|
||||
v = new String(raw, 3, len - 3, StandardCharsets.UTF_16LE);
|
||||
} else if (encid == ID3_ENC_UTF16BE) {
|
||||
v = new String(raw, 3, len - 3, "UTF-16BE");
|
||||
v = new String(raw, 3, len - 3, StandardCharsets.UTF_16BE);
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ package net.nullsum.audinaut.util.tags;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
|
||||
|
||||
|
@ -38,7 +39,7 @@ class LameHeader extends Common {
|
|||
s.seek(offset + 0x24);
|
||||
s.read(chunk);
|
||||
|
||||
String lameMark = new String(chunk, 0, chunk.length, "ISO-8859-1");
|
||||
String lameMark = new String(chunk, 0, chunk.length, StandardCharsets.ISO_8859_1);
|
||||
|
||||
if (lameMark.equals("Info") || lameMark.equals("Xing")) {
|
||||
s.seek(offset + 0xAB);
|
||||
|
|
|
@ -37,7 +37,7 @@ class OggFile extends Common {
|
|||
HashMap tags = new HashMap();
|
||||
|
||||
for (; retry > 0; retry--) {
|
||||
long res[] = parse_ogg_page(s, offset);
|
||||
long[] res = parse_ogg_page(s, offset);
|
||||
if (res[2] == OGG_TYPE_COMMENT) {
|
||||
tags = parse_ogg_vorbis_comment(s, offset + res[0], res[1]);
|
||||
break;
|
||||
|
@ -49,8 +49,8 @@ class OggFile extends Common {
|
|||
|
||||
|
||||
/* Parses the ogg page at offset 'offset' and returns
|
||||
** [header_size, payload_size, type]
|
||||
*/
|
||||
** [header_size, payload_size, type]
|
||||
*/
|
||||
private long[] parse_ogg_page(RandomAccessFile s, long offset) throws IOException {
|
||||
long[] result = new long[3]; // [header_size, payload_size]
|
||||
byte[] p_header = new byte[OGG_PAGE_SIZE]; // buffer for the page header
|
||||
|
@ -93,8 +93,8 @@ class OggFile extends Common {
|
|||
}
|
||||
|
||||
/* In 'vorbiscomment' field is prefixed with \3vorbis in OGG files
|
||||
** we check that this marker is present and call the generic comment
|
||||
** parset with the correct offset (+7) */
|
||||
** we check that this marker is present and call the generic comment
|
||||
** parset with the correct offset (+7) */
|
||||
private HashMap parse_ogg_vorbis_comment(RandomAccessFile s, long offset, long pl_len) throws IOException {
|
||||
final int pfx_len = 7;
|
||||
byte[] pfx = new byte[pfx_len];
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
package net.nullsum.audinaut.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.v7.widget.AppCompatImageButton;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import androidx.appcompat.widget.AppCompatImageButton;
|
||||
|
||||
public class AutoRepeatButton extends AppCompatImageButton {
|
||||
|
||||
private static final long initialRepeatDelay = 1000;
|
||||
|
|
|
@ -18,7 +18,6 @@ import android.content.Context;
|
|||
import android.os.Build;
|
||||
import android.os.Environment;
|
||||
import android.preference.EditTextPreference;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
|
@ -28,6 +27,8 @@ import android.widget.Button;
|
|||
import android.widget.EditText;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import net.nullsum.audinaut.R;
|
||||
|
||||
import java.io.File;
|
||||
|
|
|
@ -18,9 +18,10 @@
|
|||
*/
|
||||
package net.nullsum.audinaut.view;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import net.nullsum.audinaut.R;
|
||||
import net.nullsum.audinaut.activity.SubsonicFragmentActivity;
|
||||
|
@ -31,11 +32,11 @@ import net.nullsum.audinaut.util.Util;
|
|||
*/
|
||||
public class ErrorDialog {
|
||||
|
||||
public ErrorDialog(Activity activity, int messageId) {
|
||||
public ErrorDialog(AppCompatActivity activity, int messageId) {
|
||||
this(activity, activity.getResources().getString(messageId), false);
|
||||
}
|
||||
|
||||
public ErrorDialog(final Activity activity, String message, final boolean finishActivityOnClose) {
|
||||
public ErrorDialog(final AppCompatActivity activity, String message, final boolean finishActivityOnClose) {
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
|
||||
builder.setIcon(android.R.drawable.ic_dialog_alert);
|
||||
|
@ -60,7 +61,7 @@ public class ErrorDialog {
|
|||
}
|
||||
}
|
||||
|
||||
private void restart(Activity activity) {
|
||||
private void restart(AppCompatActivity activity) {
|
||||
Intent intent = new Intent(activity, SubsonicFragmentActivity.class);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
Util.startActivityWithoutTransition(activity, intent);
|
||||
|
|
|
@ -19,10 +19,6 @@ import android.animation.Animator;
|
|||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.support.v7.widget.RecyclerView.AdapterDataObserver;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.util.TypedValue;
|
||||
|
@ -32,9 +28,14 @@ import android.view.View;
|
|||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.recyclerview.widget.RecyclerView.AdapterDataObserver;
|
||||
|
||||
import net.nullsum.audinaut.R;
|
||||
|
||||
import static android.support.v7.widget.RecyclerView.OnScrollListener;
|
||||
import static androidx.recyclerview.widget.RecyclerView.OnScrollListener;
|
||||
|
||||
public class FastScroller extends LinearLayout {
|
||||
private static final String TAG = FastScroller.class.getSimpleName();
|
||||
|
|
|
@ -16,14 +16,15 @@
|
|||
package net.nullsum.audinaut.view;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.support.v7.widget.GridLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.util.TypedValue;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
public class GridSpacingDecoration extends RecyclerView.ItemDecoration {
|
||||
public static final int SPACING = 10;
|
||||
|
||||
|
|
|
@ -20,9 +20,10 @@ import android.graphics.Canvas;
|
|||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.TransitionDrawable;
|
||||
import android.support.v7.widget.AppCompatImageView;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import androidx.appcompat.widget.AppCompatImageView;
|
||||
|
||||
public class RecyclingImageView extends AppCompatImageView {
|
||||
private boolean invalidated = false;
|
||||
|
||||
|
@ -45,7 +46,7 @@ public class RecyclingImageView extends AppCompatImageView {
|
|||
if (drawable instanceof BitmapDrawable) {
|
||||
if (isBitmapRecycled(drawable)) {
|
||||
this.setImageDrawable(null);
|
||||
setInvalidated(true);
|
||||
this.invalidated = true;
|
||||
}
|
||||
} else if (drawable instanceof TransitionDrawable) {
|
||||
TransitionDrawable transitionDrawable = (TransitionDrawable) drawable;
|
||||
|
|
|
@ -177,9 +177,9 @@ public class SongView extends UpdateView2<MusicDirectory.Entry, Boolean> {
|
|||
moreButton.setImageResource(moreImage);
|
||||
this.moreImage = moreImage;
|
||||
}
|
||||
} else if (this.moreImage != R.drawable.download_none_light) {
|
||||
} else if (this.moreImage != R.drawable.download_none) {
|
||||
moreButton.setImageResource(DrawableTint.getDrawableRes(context, R.attr.download_none));
|
||||
this.moreImage = R.drawable.download_none_light;
|
||||
this.moreImage = R.drawable.download_none;
|
||||
}
|
||||
|
||||
if (downloadFile.isDownloading() && !downloadFile.isDownloadCancelled() && partialFileExists) {
|
||||
|
|
|
@ -22,7 +22,6 @@ import android.content.Context;
|
|||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
@ -30,6 +29,8 @@ import android.widget.AbsListView;
|
|||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import net.nullsum.audinaut.R;
|
||||
import net.nullsum.audinaut.domain.MusicDirectory;
|
||||
import net.nullsum.audinaut.util.DrawableTint;
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
package net.nullsum.audinaut.domain
|
||||
|
||||
import android.content.Context
|
||||
import java.io.Serializable
|
||||
import java.util.*
|
||||
|
||||
class Indexes(var shortcuts: MutableList<Artist> = mutableListOf(),
|
||||
var artists: MutableList<Artist> = mutableListOf(),
|
||||
var entries: MutableList<MusicDirectory.Entry> = mutableListOf()) : Serializable {
|
||||
fun sortChildren(context: Context) {
|
||||
|
||||
fun sortChildren() {
|
||||
shortcuts.sortBy { s -> s.id.toLowerCase(Locale.ROOT) }
|
||||
artists.sortBy { a -> a.name.toLowerCase(Locale.ROOT) }
|
||||
entries.sortBy { e -> e.artist.toLowerCase(Locale.ROOT) }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
<adaptive-icon
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
Before Width: | Height: | Size: 400 B |
Before Width: | Height: | Size: 421 B |
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 349 B After Width: | Height: | Size: 343 B |
Before Width: | Height: | Size: 905 B |
Before Width: | Height: | Size: 154 B |
Before Width: | Height: | Size: 167 B |
Before Width: | Height: | Size: 907 B |
Before Width: | Height: | Size: 263 B |
Before Width: | Height: | Size: 274 B |
Before Width: | Height: | Size: 138 B |
Before Width: | Height: | Size: 145 B |
Before Width: | Height: | Size: 407 B |
Before Width: | Height: | Size: 411 B |
Before Width: | Height: | Size: 241 B |
Before Width: | Height: | Size: 401 B |
Before Width: | Height: | Size: 425 B |
Before Width: | Height: | Size: 239 B |
Before Width: | Height: | Size: 255 B |
Before Width: | Height: | Size: 207 B |
Before Width: | Height: | Size: 222 B |
Before Width: | Height: | Size: 309 B |
Before Width: | Height: | Size: 323 B |
Before Width: | Height: | Size: 340 B |
Before Width: | Height: | Size: 370 B |
Before Width: | Height: | Size: 451 B |
Before Width: | Height: | Size: 494 B |
Before Width: | Height: | Size: 240 B |
Before Width: | Height: | Size: 256 B |
Before Width: | Height: | Size: 314 B |
Before Width: | Height: | Size: 335 B |
Before Width: | Height: | Size: 503 B |
Before Width: | Height: | Size: 555 B |