mirror of
https://github.com/ultrasonic/ultrasonic
synced 2025-02-16 19:50:35 +01:00
Merge branch 'develop' into refactor-events
This commit is contained in:
commit
fec2d78d30
@ -1,504 +0,0 @@
|
|||||||
package org.moire.ultrasonic.fragment;
|
|
||||||
|
|
||||||
import android.app.AlertDialog;
|
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.provider.SearchRecentSuggestions;
|
|
||||||
import android.view.View;
|
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.annotation.StringRes;
|
|
||||||
import androidx.fragment.app.DialogFragment;
|
|
||||||
import androidx.navigation.Navigation;
|
|
||||||
import androidx.preference.CheckBoxPreference;
|
|
||||||
import androidx.preference.EditTextPreference;
|
|
||||||
import androidx.preference.ListPreference;
|
|
||||||
import androidx.preference.Preference;
|
|
||||||
import androidx.preference.PreferenceCategory;
|
|
||||||
import androidx.preference.PreferenceFragmentCompat;
|
|
||||||
import androidx.preference.PreferenceManager;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.koin.java.KoinJavaComponent;
|
|
||||||
import org.moire.ultrasonic.R;
|
|
||||||
import org.moire.ultrasonic.featureflags.Feature;
|
|
||||||
import org.moire.ultrasonic.featureflags.FeatureStorage;
|
|
||||||
import org.moire.ultrasonic.filepicker.FilePickerDialog;
|
|
||||||
import org.moire.ultrasonic.filepicker.OnFileSelectedListener;
|
|
||||||
import org.moire.ultrasonic.log.FileLoggerTree;
|
|
||||||
import org.moire.ultrasonic.provider.SearchSuggestionProvider;
|
|
||||||
import org.moire.ultrasonic.service.Consumer;
|
|
||||||
import org.moire.ultrasonic.service.MediaPlayerController;
|
|
||||||
import org.moire.ultrasonic.service.RxBus;
|
|
||||||
import org.moire.ultrasonic.util.Constants;
|
|
||||||
import org.moire.ultrasonic.util.FileUtil;
|
|
||||||
import org.moire.ultrasonic.util.MediaSessionHandler;
|
|
||||||
import org.moire.ultrasonic.util.PermissionUtil;
|
|
||||||
import org.moire.ultrasonic.util.Settings;
|
|
||||||
import org.moire.ultrasonic.util.TimeSpanPreference;
|
|
||||||
import org.moire.ultrasonic.util.TimeSpanPreferenceDialogFragmentCompat;
|
|
||||||
import org.moire.ultrasonic.util.Util;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
import kotlin.Lazy;
|
|
||||||
import kotlin.Unit;
|
|
||||||
import timber.log.Timber;
|
|
||||||
|
|
||||||
import static org.koin.java.KoinJavaComponent.inject;
|
|
||||||
import static org.moire.ultrasonic.fragment.ServerSelectorFragment.SERVER_SELECTOR_MANAGE_MODE;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Shows main app settings.
|
|
||||||
*/
|
|
||||||
public class SettingsFragment extends PreferenceFragmentCompat
|
|
||||||
implements SharedPreferences.OnSharedPreferenceChangeListener {
|
|
||||||
|
|
||||||
private ListPreference theme;
|
|
||||||
private ListPreference maxBitrateWifi;
|
|
||||||
private ListPreference maxBitrateMobile;
|
|
||||||
private ListPreference cacheSize;
|
|
||||||
private Preference cacheLocation;
|
|
||||||
private ListPreference preloadCount;
|
|
||||||
private ListPreference bufferLength;
|
|
||||||
private ListPreference incrementTime;
|
|
||||||
private ListPreference networkTimeout;
|
|
||||||
private ListPreference maxAlbums;
|
|
||||||
private ListPreference maxSongs;
|
|
||||||
private ListPreference maxArtists;
|
|
||||||
private ListPreference defaultAlbums;
|
|
||||||
private ListPreference defaultSongs;
|
|
||||||
private ListPreference defaultArtists;
|
|
||||||
private ListPreference chatRefreshInterval;
|
|
||||||
private ListPreference directoryCacheTime;
|
|
||||||
private CheckBoxPreference mediaButtonsEnabled;
|
|
||||||
private CheckBoxPreference lockScreenEnabled;
|
|
||||||
private CheckBoxPreference sendBluetoothNotifications;
|
|
||||||
private CheckBoxPreference sendBluetoothAlbumArt;
|
|
||||||
private CheckBoxPreference showArtistPicture;
|
|
||||||
private ListPreference viewRefresh;
|
|
||||||
private EditTextPreference sharingDefaultDescription;
|
|
||||||
private EditTextPreference sharingDefaultGreeting;
|
|
||||||
private TimeSpanPreference sharingDefaultExpiration;
|
|
||||||
private Preference resumeOnBluetoothDevice;
|
|
||||||
private Preference pauseOnBluetoothDevice;
|
|
||||||
private CheckBoxPreference debugLogToFile;
|
|
||||||
|
|
||||||
private SharedPreferences settings;
|
|
||||||
|
|
||||||
private final Lazy<MediaPlayerController> mediaPlayerControllerLazy = inject(MediaPlayerController.class);
|
|
||||||
private final Lazy<PermissionUtil> permissionUtil = inject(PermissionUtil.class);
|
|
||||||
private final Lazy<MediaSessionHandler> mediaSessionHandler = inject(MediaSessionHandler.class);
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
|
|
||||||
settings = PreferenceManager.getDefaultSharedPreferences(getActivity());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
|
||||||
setPreferencesFromResource(R.xml.settings, rootKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onViewCreated(@NotNull View view, @Nullable Bundle savedInstanceState) {
|
|
||||||
super.onViewCreated(view, savedInstanceState);
|
|
||||||
FragmentTitle.Companion.setTitle(this, R.string.menu_settings);
|
|
||||||
|
|
||||||
theme = findPreference(Constants.PREFERENCES_KEY_THEME);
|
|
||||||
maxBitrateWifi = findPreference(Constants.PREFERENCES_KEY_MAX_BITRATE_WIFI);
|
|
||||||
maxBitrateMobile = findPreference(Constants.PREFERENCES_KEY_MAX_BITRATE_MOBILE);
|
|
||||||
cacheSize = findPreference(Constants.PREFERENCES_KEY_CACHE_SIZE);
|
|
||||||
cacheLocation = findPreference(Constants.PREFERENCES_KEY_CACHE_LOCATION);
|
|
||||||
preloadCount = findPreference(Constants.PREFERENCES_KEY_PRELOAD_COUNT);
|
|
||||||
bufferLength = findPreference(Constants.PREFERENCES_KEY_BUFFER_LENGTH);
|
|
||||||
incrementTime = findPreference(Constants.PREFERENCES_KEY_INCREMENT_TIME);
|
|
||||||
networkTimeout = findPreference(Constants.PREFERENCES_KEY_NETWORK_TIMEOUT);
|
|
||||||
maxAlbums = findPreference(Constants.PREFERENCES_KEY_MAX_ALBUMS);
|
|
||||||
maxSongs = findPreference(Constants.PREFERENCES_KEY_MAX_SONGS);
|
|
||||||
maxArtists = findPreference(Constants.PREFERENCES_KEY_MAX_ARTISTS);
|
|
||||||
defaultArtists = findPreference(Constants.PREFERENCES_KEY_DEFAULT_ARTISTS);
|
|
||||||
defaultSongs = findPreference(Constants.PREFERENCES_KEY_DEFAULT_SONGS);
|
|
||||||
defaultAlbums = findPreference(Constants.PREFERENCES_KEY_DEFAULT_ALBUMS);
|
|
||||||
chatRefreshInterval = findPreference(Constants.PREFERENCES_KEY_CHAT_REFRESH_INTERVAL);
|
|
||||||
directoryCacheTime = findPreference(Constants.PREFERENCES_KEY_DIRECTORY_CACHE_TIME);
|
|
||||||
mediaButtonsEnabled = findPreference(Constants.PREFERENCES_KEY_MEDIA_BUTTONS);
|
|
||||||
lockScreenEnabled = findPreference(Constants.PREFERENCES_KEY_SHOW_LOCK_SCREEN_CONTROLS);
|
|
||||||
sendBluetoothAlbumArt = findPreference(Constants.PREFERENCES_KEY_SEND_BLUETOOTH_ALBUM_ART);
|
|
||||||
sendBluetoothNotifications = findPreference(Constants.PREFERENCES_KEY_SEND_BLUETOOTH_NOTIFICATIONS);
|
|
||||||
viewRefresh = findPreference(Constants.PREFERENCES_KEY_VIEW_REFRESH);
|
|
||||||
sharingDefaultDescription = findPreference(Constants.PREFERENCES_KEY_DEFAULT_SHARE_DESCRIPTION);
|
|
||||||
sharingDefaultGreeting = findPreference(Constants.PREFERENCES_KEY_DEFAULT_SHARE_GREETING);
|
|
||||||
sharingDefaultExpiration = findPreference(Constants.PREFERENCES_KEY_DEFAULT_SHARE_EXPIRATION);
|
|
||||||
resumeOnBluetoothDevice = findPreference(Constants.PREFERENCES_KEY_RESUME_ON_BLUETOOTH_DEVICE);
|
|
||||||
pauseOnBluetoothDevice = findPreference(Constants.PREFERENCES_KEY_PAUSE_ON_BLUETOOTH_DEVICE);
|
|
||||||
debugLogToFile = findPreference(Constants.PREFERENCES_KEY_DEBUG_LOG_TO_FILE);
|
|
||||||
showArtistPicture = findPreference(Constants.PREFERENCES_KEY_SHOW_ARTIST_PICTURE);
|
|
||||||
|
|
||||||
sharingDefaultGreeting.setText(Settings.getShareGreeting());
|
|
||||||
setupClearSearchPreference();
|
|
||||||
setupFeatureFlagsPreferences();
|
|
||||||
setupCacheLocationPreference();
|
|
||||||
setupBluetoothDevicePreferences();
|
|
||||||
|
|
||||||
// After API26 foreground services must be used for music playback, and they must have a notification
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
||||||
PreferenceCategory notificationsCategory = findPreference(Constants.PREFERENCES_KEY_CATEGORY_NOTIFICATIONS);
|
|
||||||
Preference preferenceToRemove = findPreference(Constants.PREFERENCES_KEY_SHOW_NOTIFICATION);
|
|
||||||
if (preferenceToRemove != null) notificationsCategory.removePreference(preferenceToRemove);
|
|
||||||
preferenceToRemove = findPreference(Constants.PREFERENCES_KEY_ALWAYS_SHOW_NOTIFICATION);
|
|
||||||
if (preferenceToRemove != null) notificationsCategory.removePreference(preferenceToRemove);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
|
|
||||||
super.onActivityCreated(savedInstanceState);
|
|
||||||
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onResume() {
|
|
||||||
super.onResume();
|
|
||||||
SharedPreferences preferences = Settings.getPreferences();
|
|
||||||
preferences.registerOnSharedPreferenceChangeListener(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPause() {
|
|
||||||
super.onPause();
|
|
||||||
SharedPreferences prefs = Settings.getPreferences();
|
|
||||||
prefs.unregisterOnSharedPreferenceChangeListener(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
|
||||||
Timber.d("Preference changed: %s", key);
|
|
||||||
update();
|
|
||||||
|
|
||||||
if (Constants.PREFERENCES_KEY_HIDE_MEDIA.equals(key)) {
|
|
||||||
setHideMedia(sharedPreferences.getBoolean(key, false));
|
|
||||||
} else if (Constants.PREFERENCES_KEY_MEDIA_BUTTONS.equals(key)) {
|
|
||||||
setMediaButtonsEnabled(sharedPreferences.getBoolean(key, true));
|
|
||||||
} else if (Constants.PREFERENCES_KEY_SEND_BLUETOOTH_NOTIFICATIONS.equals(key)) {
|
|
||||||
setBluetoothPreferences(sharedPreferences.getBoolean(key, true));
|
|
||||||
} else if (Constants.PREFERENCES_KEY_DEBUG_LOG_TO_FILE.equals(key)) {
|
|
||||||
setDebugLogToFile(sharedPreferences.getBoolean(key, false));
|
|
||||||
} else if (Constants.PREFERENCES_KEY_ID3_TAGS.equals(key)) {
|
|
||||||
showArtistPicture.setEnabled(sharedPreferences.getBoolean(key, false));
|
|
||||||
} else if (Constants.PREFERENCES_KEY_THEME.equals(key)) {
|
|
||||||
RxBus.INSTANCE.getThemeChangedEventPublisher().onNext(Unit.INSTANCE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDisplayPreferenceDialog(Preference preference)
|
|
||||||
{
|
|
||||||
DialogFragment dialogFragment = null;
|
|
||||||
if (preference instanceof TimeSpanPreference)
|
|
||||||
{
|
|
||||||
dialogFragment = new TimeSpanPreferenceDialogFragmentCompat();
|
|
||||||
Bundle bundle = new Bundle(1);
|
|
||||||
bundle.putString("key", preference.getKey());
|
|
||||||
dialogFragment.setArguments(bundle);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dialogFragment != null)
|
|
||||||
{
|
|
||||||
dialogFragment.setTargetFragment(this, 0);
|
|
||||||
dialogFragment.show(this.getParentFragmentManager(), "android.support.v7.preference.PreferenceFragment.DIALOG");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
super.onDisplayPreferenceDialog(preference);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setupCacheLocationPreference() {
|
|
||||||
cacheLocation.setSummary(Settings.getCacheLocation());
|
|
||||||
|
|
||||||
cacheLocation.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
|
|
||||||
@Override
|
|
||||||
public boolean onPreferenceClick(Preference preference) {
|
|
||||||
// If the user tries to change the cache location, we must first check to see if we have write access.
|
|
||||||
PermissionUtil.requestInitialPermission(getActivity(), new PermissionUtil.PermissionRequestFinishedCallback() {
|
|
||||||
@Override
|
|
||||||
public void onPermissionRequestFinished(boolean hasPermission) {
|
|
||||||
if (hasPermission) {
|
|
||||||
FilePickerDialog filePickerDialog = FilePickerDialog.Companion.createFilePickerDialog(getContext());
|
|
||||||
filePickerDialog.setDefaultDirectory(FileUtil.getDefaultMusicDirectory().getPath());
|
|
||||||
filePickerDialog.setInitialDirectory(cacheLocation.getSummary().toString());
|
|
||||||
filePickerDialog.setOnFileSelectedListener(new OnFileSelectedListener() {
|
|
||||||
@Override
|
|
||||||
public void onFileSelected(File file, String path) {
|
|
||||||
SharedPreferences.Editor editor = cacheLocation.getSharedPreferences().edit();
|
|
||||||
editor.putString(Constants.PREFERENCES_KEY_CACHE_LOCATION, path);
|
|
||||||
editor.apply();
|
|
||||||
|
|
||||||
setCacheLocation(path);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
filePickerDialog.show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setupBluetoothDevicePreferences() {
|
|
||||||
final int resumeSetting = Settings.getResumeOnBluetoothDevice();
|
|
||||||
final int pauseSetting = Settings.getPauseOnBluetoothDevice();
|
|
||||||
|
|
||||||
resumeOnBluetoothDevice.setSummary(bluetoothDevicePreferenceToString(resumeSetting));
|
|
||||||
pauseOnBluetoothDevice.setSummary(bluetoothDevicePreferenceToString(pauseSetting));
|
|
||||||
|
|
||||||
resumeOnBluetoothDevice.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
|
|
||||||
@Override
|
|
||||||
public boolean onPreferenceClick(Preference preference) {
|
|
||||||
showBluetoothDevicePreferenceDialog(
|
|
||||||
R.string.settings_playback_resume_on_bluetooth_device,
|
|
||||||
Settings.getResumeOnBluetoothDevice(),
|
|
||||||
new Consumer<Integer>() {
|
|
||||||
@Override
|
|
||||||
public void accept(Integer choice) {
|
|
||||||
SharedPreferences.Editor editor = resumeOnBluetoothDevice.getSharedPreferences().edit();
|
|
||||||
editor.putInt(Constants.PREFERENCES_KEY_RESUME_ON_BLUETOOTH_DEVICE, choice);
|
|
||||||
editor.apply();
|
|
||||||
resumeOnBluetoothDevice.setSummary(bluetoothDevicePreferenceToString(choice));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
pauseOnBluetoothDevice.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
|
|
||||||
@Override
|
|
||||||
public boolean onPreferenceClick(Preference preference) {
|
|
||||||
showBluetoothDevicePreferenceDialog(
|
|
||||||
R.string.settings_playback_pause_on_bluetooth_device,
|
|
||||||
Settings.getPauseOnBluetoothDevice(),
|
|
||||||
new Consumer<Integer>() {
|
|
||||||
@Override
|
|
||||||
public void accept(Integer choice) {
|
|
||||||
SharedPreferences.Editor editor = pauseOnBluetoothDevice.getSharedPreferences().edit();
|
|
||||||
editor.putInt(Constants.PREFERENCES_KEY_PAUSE_ON_BLUETOOTH_DEVICE, choice);
|
|
||||||
editor.apply();
|
|
||||||
pauseOnBluetoothDevice.setSummary(bluetoothDevicePreferenceToString(choice));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showBluetoothDevicePreferenceDialog(@StringRes int title, int defaultChoice, final Consumer<Integer> onChosen) {
|
|
||||||
final int[] choice = {defaultChoice};
|
|
||||||
new AlertDialog.Builder(getActivity()).setTitle(title)
|
|
||||||
.setSingleChoiceItems(R.array.bluetoothDeviceSettingNames, defaultChoice,
|
|
||||||
new DialogInterface.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialogInterface, int i) {
|
|
||||||
choice[0] = i;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.setNegativeButton(R.string.common_cancel, new DialogInterface.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialogInterface, int i) {
|
|
||||||
dialogInterface.cancel();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialogInterface, int i) {
|
|
||||||
onChosen.accept(choice[0]);
|
|
||||||
dialogInterface.dismiss();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.create().show();
|
|
||||||
}
|
|
||||||
|
|
||||||
private String bluetoothDevicePreferenceToString(int preferenceValue) {
|
|
||||||
switch (preferenceValue) {
|
|
||||||
case Constants.PREFERENCE_VALUE_ALL: return getString(R.string.settings_playback_bluetooth_all);
|
|
||||||
case Constants.PREFERENCE_VALUE_A2DP: return getString(R.string.settings_playback_bluetooth_a2dp);
|
|
||||||
case Constants.PREFERENCE_VALUE_DISABLED: return getString(R.string.settings_playback_bluetooth_disabled);
|
|
||||||
default: return "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setupClearSearchPreference() {
|
|
||||||
Preference clearSearchPreference = findPreference(Constants.PREFERENCES_KEY_CLEAR_SEARCH_HISTORY);
|
|
||||||
|
|
||||||
if (clearSearchPreference != null) {
|
|
||||||
clearSearchPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
|
|
||||||
@Override
|
|
||||||
public boolean onPreferenceClick(Preference preference) {
|
|
||||||
SearchRecentSuggestions suggestions =
|
|
||||||
new SearchRecentSuggestions(getActivity(),
|
|
||||||
SearchSuggestionProvider.AUTHORITY,
|
|
||||||
SearchSuggestionProvider.MODE);
|
|
||||||
suggestions.clearHistory();
|
|
||||||
Util.toast(getActivity(), R.string.settings_search_history_cleared);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setupFeatureFlagsPreferences() {
|
|
||||||
final FeatureStorage featureStorage = KoinJavaComponent.get(FeatureStorage.class);
|
|
||||||
|
|
||||||
CheckBoxPreference useFiveStarRating = (CheckBoxPreference) findPreference(
|
|
||||||
Constants.PREFERENCES_KEY_USE_FIVE_STAR_RATING);
|
|
||||||
|
|
||||||
if (useFiveStarRating != null) {
|
|
||||||
useFiveStarRating.setChecked(featureStorage.isFeatureEnabled(Feature.FIVE_STAR_RATING));
|
|
||||||
useFiveStarRating.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
|
||||||
@Override
|
|
||||||
public boolean onPreferenceChange(Preference preference, Object o) {
|
|
||||||
featureStorage.changeFeatureFlag(Feature.FIVE_STAR_RATING, (Boolean) o);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void update() {
|
|
||||||
theme.setSummary(theme.getEntry());
|
|
||||||
maxBitrateWifi.setSummary(maxBitrateWifi.getEntry());
|
|
||||||
maxBitrateMobile.setSummary(maxBitrateMobile.getEntry());
|
|
||||||
cacheSize.setSummary(cacheSize.getEntry());
|
|
||||||
preloadCount.setSummary(preloadCount.getEntry());
|
|
||||||
bufferLength.setSummary(bufferLength.getEntry());
|
|
||||||
incrementTime.setSummary(incrementTime.getEntry());
|
|
||||||
networkTimeout.setSummary(networkTimeout.getEntry());
|
|
||||||
maxAlbums.setSummary(maxAlbums.getEntry());
|
|
||||||
maxArtists.setSummary(maxArtists.getEntry());
|
|
||||||
maxSongs.setSummary(maxSongs.getEntry());
|
|
||||||
defaultAlbums.setSummary(defaultAlbums.getEntry());
|
|
||||||
defaultArtists.setSummary(defaultArtists.getEntry());
|
|
||||||
defaultSongs.setSummary(defaultSongs.getEntry());
|
|
||||||
chatRefreshInterval.setSummary(chatRefreshInterval.getEntry());
|
|
||||||
directoryCacheTime.setSummary(directoryCacheTime.getEntry());
|
|
||||||
viewRefresh.setSummary(viewRefresh.getEntry());
|
|
||||||
sharingDefaultExpiration.setSummary(sharingDefaultExpiration.getText());
|
|
||||||
sharingDefaultDescription.setSummary(sharingDefaultDescription.getText());
|
|
||||||
sharingDefaultGreeting.setSummary(sharingDefaultGreeting.getText());
|
|
||||||
cacheLocation.setSummary(Settings.getCacheLocation());
|
|
||||||
|
|
||||||
if (!mediaButtonsEnabled.isChecked()) {
|
|
||||||
lockScreenEnabled.setChecked(false);
|
|
||||||
lockScreenEnabled.setEnabled(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!sendBluetoothNotifications.isChecked()) {
|
|
||||||
sendBluetoothAlbumArt.setChecked(false);
|
|
||||||
sendBluetoothAlbumArt.setEnabled(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (debugLogToFile.isChecked()) {
|
|
||||||
debugLogToFile.setSummary(getString(R.string.settings_debug_log_path,
|
|
||||||
FileUtil.getUltrasonicDirectory(), FileLoggerTree.FILENAME));
|
|
||||||
} else {
|
|
||||||
debugLogToFile.setSummary("");
|
|
||||||
}
|
|
||||||
|
|
||||||
showArtistPicture.setEnabled(Settings.getShouldUseId3Tags());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void setHideMedia(boolean hide) {
|
|
||||||
File nomediaDir = new File(FileUtil.getUltrasonicDirectory(), ".nomedia");
|
|
||||||
if (hide && !nomediaDir.exists()) {
|
|
||||||
if (!nomediaDir.mkdir()) {
|
|
||||||
Timber.w("Failed to create %s", nomediaDir);
|
|
||||||
}
|
|
||||||
} else if (nomediaDir.exists()) {
|
|
||||||
if (!nomediaDir.delete()) {
|
|
||||||
Timber.w("Failed to delete %s", nomediaDir);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Util.toast(getActivity(), R.string.settings_hide_media_toast, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setMediaButtonsEnabled(boolean enabled) {
|
|
||||||
lockScreenEnabled.setEnabled(enabled);
|
|
||||||
mediaSessionHandler.getValue().updateMediaButtonReceiver();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setBluetoothPreferences(boolean enabled) {
|
|
||||||
sendBluetoothAlbumArt.setEnabled(enabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setCacheLocation(String path) {
|
|
||||||
File dir = new File(path);
|
|
||||||
|
|
||||||
if (!FileUtil.ensureDirectoryExistsAndIsReadWritable(dir)) {
|
|
||||||
permissionUtil.getValue().handlePermissionFailed(new PermissionUtil.PermissionRequestFinishedCallback() {
|
|
||||||
@Override
|
|
||||||
public void onPermissionRequestFinished(boolean hasPermission) {
|
|
||||||
String currentPath = settings.getString(Constants.PREFERENCES_KEY_CACHE_LOCATION,
|
|
||||||
FileUtil.getDefaultMusicDirectory().getPath());
|
|
||||||
cacheLocation.setSummary(currentPath);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
cacheLocation.setSummary(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear download queue.
|
|
||||||
mediaPlayerControllerLazy.getValue().clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setDebugLogToFile(boolean writeLog) {
|
|
||||||
if (writeLog) {
|
|
||||||
FileLoggerTree.Companion.plantToTimberForest();
|
|
||||||
Timber.i("Enabled debug logging to file");
|
|
||||||
} else {
|
|
||||||
FileLoggerTree.Companion.uprootFromTimberForest();
|
|
||||||
Timber.i("Disabled debug logging to file");
|
|
||||||
|
|
||||||
int fileNum = FileLoggerTree.Companion.getLogFileNumber();
|
|
||||||
long fileSize = FileLoggerTree.Companion.getLogFileSizes();
|
|
||||||
String message = getString(R.string.settings_debug_log_summary,
|
|
||||||
String.valueOf(fileNum),
|
|
||||||
String.valueOf(Math.ceil(fileSize / 1000000d)),
|
|
||||||
FileUtil.getUltrasonicDirectory());
|
|
||||||
|
|
||||||
new AlertDialog.Builder(getActivity())
|
|
||||||
.setMessage(message)
|
|
||||||
.setIcon(android.R.drawable.ic_dialog_info)
|
|
||||||
.setNegativeButton(R.string.settings_debug_log_keep, new DialogInterface.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialogInterface, int i) {
|
|
||||||
dialogInterface.cancel();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.setPositiveButton(R.string.settings_debug_log_delete, new DialogInterface.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialogInterface, int i) {
|
|
||||||
FileLoggerTree.Companion.deleteLogFiles();
|
|
||||||
Timber.i("Deleted debug log files");
|
|
||||||
dialogInterface.dismiss();
|
|
||||||
new AlertDialog.Builder(getActivity()).setMessage(R.string.settings_debug_log_deleted)
|
|
||||||
.setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialogInterface, int i) {
|
|
||||||
dialogInterface.dismiss();
|
|
||||||
}
|
|
||||||
}).create().show();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.create().show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
package org.moire.ultrasonic.service;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deprecated: Should be replaced with lambdas
|
|
||||||
* Abstract class for consumers with one parameter
|
|
||||||
* @param <T> The type of the object to consume
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public abstract class Consumer<T>
|
|
||||||
{
|
|
||||||
public abstract void accept(T t);
|
|
||||||
}
|
|
@ -193,8 +193,7 @@ class ActiveServerProvider(
|
|||||||
* Queries the Id of the Active Server
|
* Queries the Id of the Active Server
|
||||||
*/
|
*/
|
||||||
fun getActiveServerId(): Int {
|
fun getActiveServerId(): Int {
|
||||||
val preferences = Settings.preferences
|
return Settings.activeServer
|
||||||
return preferences.getInt(Constants.PREFERENCES_KEY_SERVER_INSTANCE, -1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -203,11 +202,7 @@ class ActiveServerProvider(
|
|||||||
fun setActiveServerId(serverId: Int) {
|
fun setActiveServerId(serverId: Int) {
|
||||||
resetMusicService()
|
resetMusicService()
|
||||||
|
|
||||||
val preferences = Settings.preferences
|
Settings.activeServer = serverId
|
||||||
val editor = preferences.edit()
|
|
||||||
editor.putInt(Constants.PREFERENCES_KEY_SERVER_INSTANCE, serverId)
|
|
||||||
editor.apply()
|
|
||||||
|
|
||||||
liveActiveServerId.postValue(serverId)
|
liveActiveServerId.postValue(serverId)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -229,8 +224,7 @@ class ActiveServerProvider(
|
|||||||
if (isOffline()) {
|
if (isOffline()) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
val preferences = Settings.preferences
|
return Settings.serverScaling
|
||||||
return preferences.getBoolean(Constants.PREFERENCES_KEY_SERVER_SCALING, false)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
package org.moire.ultrasonic.fragment
|
package org.moire.ultrasonic.fragment
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.Color
|
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import android.os.Build
|
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
@ -109,10 +107,8 @@ internal class ServerRowAdapter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set colors
|
// Set colors
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
icon?.setTint(ServerColor.getForegroundColor(context, setting?.color))
|
||||||
icon?.setTint(ServerColor.getForegroundColor(context, setting?.color))
|
background?.setTint(ServerColor.getBackgroundColor(context, setting?.color))
|
||||||
background?.setTint(ServerColor.getBackgroundColor(context, setting?.color))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the final drawables
|
// Set the final drawables
|
||||||
image?.setImageDrawable(icon)
|
image?.setImageDrawable(icon)
|
||||||
@ -120,32 +116,16 @@ internal class ServerRowAdapter(
|
|||||||
|
|
||||||
// Highlight the Active Server's row by changing its background
|
// Highlight the Active Server's row by changing its background
|
||||||
if (index == activeServerProvider.getActiveServer().index) {
|
if (index == activeServerProvider.getActiveServer().index) {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
layout?.background = ContextCompat.getDrawable(context, R.drawable.select_ripple)
|
||||||
layout?.background = ContextCompat.getDrawable(context, R.drawable.select_ripple)
|
|
||||||
} else {
|
|
||||||
layout?.setBackgroundResource(
|
|
||||||
Util.getResourceFromAttribute(context, R.attr.list_selector_holo_selected)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
layout?.background = ContextCompat.getDrawable(context, R.drawable.default_ripple)
|
||||||
layout?.background = ContextCompat.getDrawable(context, R.drawable.default_ripple)
|
|
||||||
} else {
|
|
||||||
layout?.setBackgroundResource(
|
|
||||||
Util.getResourceFromAttribute(context, R.attr.list_selector_holo)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the context menu for the row
|
// Add the context menu for the row
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
serverMenu?.background = ContextCompat.getDrawable(
|
||||||
serverMenu?.background = ContextCompat.getDrawable(
|
context,
|
||||||
context,
|
R.drawable.select_ripple_circle
|
||||||
R.drawable.select_ripple_circle
|
)
|
||||||
)
|
|
||||||
} else {
|
|
||||||
serverMenu?.setBackgroundColor(Color.TRANSPARENT)
|
|
||||||
}
|
|
||||||
|
|
||||||
serverMenu?.setOnClickListener { view -> serverMenuClick(view, index) }
|
serverMenu?.setOnClickListener { view -> serverMenuClick(view, index) }
|
||||||
|
|
||||||
|
@ -0,0 +1,473 @@
|
|||||||
|
package org.moire.ultrasonic.fragment
|
||||||
|
|
||||||
|
import android.app.AlertDialog
|
||||||
|
import android.content.DialogInterface
|
||||||
|
import android.content.SharedPreferences
|
||||||
|
import android.content.SharedPreferences.OnSharedPreferenceChangeListener
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.provider.SearchRecentSuggestions
|
||||||
|
import android.view.View
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
import androidx.fragment.app.DialogFragment
|
||||||
|
import androidx.preference.CheckBoxPreference
|
||||||
|
import androidx.preference.EditTextPreference
|
||||||
|
import androidx.preference.ListPreference
|
||||||
|
import androidx.preference.Preference
|
||||||
|
import androidx.preference.PreferenceCategory
|
||||||
|
import androidx.preference.PreferenceFragmentCompat
|
||||||
|
import java.io.File
|
||||||
|
import kotlin.math.ceil
|
||||||
|
import org.koin.core.component.KoinComponent
|
||||||
|
import org.koin.java.KoinJavaComponent.get
|
||||||
|
import org.koin.java.KoinJavaComponent.inject
|
||||||
|
import org.moire.ultrasonic.R
|
||||||
|
import org.moire.ultrasonic.featureflags.Feature
|
||||||
|
import org.moire.ultrasonic.featureflags.FeatureStorage
|
||||||
|
import org.moire.ultrasonic.filepicker.FilePickerDialog.Companion.createFilePickerDialog
|
||||||
|
import org.moire.ultrasonic.filepicker.OnFileSelectedListener
|
||||||
|
import org.moire.ultrasonic.fragment.FragmentTitle.Companion.setTitle
|
||||||
|
import org.moire.ultrasonic.log.FileLoggerTree
|
||||||
|
import org.moire.ultrasonic.log.FileLoggerTree.Companion.deleteLogFiles
|
||||||
|
import org.moire.ultrasonic.log.FileLoggerTree.Companion.getLogFileNumber
|
||||||
|
import org.moire.ultrasonic.log.FileLoggerTree.Companion.getLogFileSizes
|
||||||
|
import org.moire.ultrasonic.log.FileLoggerTree.Companion.plantToTimberForest
|
||||||
|
import org.moire.ultrasonic.log.FileLoggerTree.Companion.uprootFromTimberForest
|
||||||
|
import org.moire.ultrasonic.provider.SearchSuggestionProvider
|
||||||
|
import org.moire.ultrasonic.service.MediaPlayerController
|
||||||
|
import org.moire.ultrasonic.service.RxBus
|
||||||
|
import org.moire.ultrasonic.util.Constants
|
||||||
|
import org.moire.ultrasonic.util.FileUtil.defaultMusicDirectory
|
||||||
|
import org.moire.ultrasonic.util.FileUtil.ensureDirectoryExistsAndIsReadWritable
|
||||||
|
import org.moire.ultrasonic.util.FileUtil.ultrasonicDirectory
|
||||||
|
import org.moire.ultrasonic.util.MediaSessionHandler
|
||||||
|
import org.moire.ultrasonic.util.PermissionUtil
|
||||||
|
import org.moire.ultrasonic.util.PermissionUtil.Companion.requestInitialPermission
|
||||||
|
import org.moire.ultrasonic.util.Settings
|
||||||
|
import org.moire.ultrasonic.util.Settings.preferences
|
||||||
|
import org.moire.ultrasonic.util.Settings.shareGreeting
|
||||||
|
import org.moire.ultrasonic.util.Settings.shouldUseId3Tags
|
||||||
|
import org.moire.ultrasonic.util.TimeSpanPreference
|
||||||
|
import org.moire.ultrasonic.util.TimeSpanPreferenceDialogFragmentCompat
|
||||||
|
import org.moire.ultrasonic.util.Util.toast
|
||||||
|
import timber.log.Timber
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows main app settings.
|
||||||
|
*/
|
||||||
|
class SettingsFragment :
|
||||||
|
PreferenceFragmentCompat(),
|
||||||
|
OnSharedPreferenceChangeListener,
|
||||||
|
KoinComponent {
|
||||||
|
private var theme: ListPreference? = null
|
||||||
|
private var maxBitrateWifi: ListPreference? = null
|
||||||
|
private var maxBitrateMobile: ListPreference? = null
|
||||||
|
private var cacheSize: ListPreference? = null
|
||||||
|
private var cacheLocation: Preference? = null
|
||||||
|
private var preloadCount: ListPreference? = null
|
||||||
|
private var bufferLength: ListPreference? = null
|
||||||
|
private var incrementTime: ListPreference? = null
|
||||||
|
private var networkTimeout: ListPreference? = null
|
||||||
|
private var maxAlbums: ListPreference? = null
|
||||||
|
private var maxSongs: ListPreference? = null
|
||||||
|
private var maxArtists: ListPreference? = null
|
||||||
|
private var defaultAlbums: ListPreference? = null
|
||||||
|
private var defaultSongs: ListPreference? = null
|
||||||
|
private var defaultArtists: ListPreference? = null
|
||||||
|
private var chatRefreshInterval: ListPreference? = null
|
||||||
|
private var directoryCacheTime: ListPreference? = null
|
||||||
|
private var mediaButtonsEnabled: CheckBoxPreference? = null
|
||||||
|
private var lockScreenEnabled: CheckBoxPreference? = null
|
||||||
|
private var sendBluetoothNotifications: CheckBoxPreference? = null
|
||||||
|
private var sendBluetoothAlbumArt: CheckBoxPreference? = null
|
||||||
|
private var showArtistPicture: CheckBoxPreference? = null
|
||||||
|
private var viewRefresh: ListPreference? = null
|
||||||
|
private var sharingDefaultDescription: EditTextPreference? = null
|
||||||
|
private var sharingDefaultGreeting: EditTextPreference? = null
|
||||||
|
private var sharingDefaultExpiration: TimeSpanPreference? = null
|
||||||
|
private var resumeOnBluetoothDevice: Preference? = null
|
||||||
|
private var pauseOnBluetoothDevice: Preference? = null
|
||||||
|
private var debugLogToFile: CheckBoxPreference? = null
|
||||||
|
|
||||||
|
private val mediaPlayerControllerLazy = inject<MediaPlayerController>(
|
||||||
|
MediaPlayerController::class.java
|
||||||
|
)
|
||||||
|
|
||||||
|
private val permissionUtil = inject<PermissionUtil>(
|
||||||
|
PermissionUtil::class.java
|
||||||
|
)
|
||||||
|
|
||||||
|
private val mediaSessionHandler = inject<MediaSessionHandler>(
|
||||||
|
MediaSessionHandler::class.java
|
||||||
|
)
|
||||||
|
|
||||||
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||||
|
setPreferencesFromResource(R.xml.settings, rootKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
setTitle(this, R.string.menu_settings)
|
||||||
|
theme = findPreference(Constants.PREFERENCES_KEY_THEME)
|
||||||
|
maxBitrateWifi = findPreference(Constants.PREFERENCES_KEY_MAX_BITRATE_WIFI)
|
||||||
|
maxBitrateMobile = findPreference(Constants.PREFERENCES_KEY_MAX_BITRATE_MOBILE)
|
||||||
|
cacheSize = findPreference(Constants.PREFERENCES_KEY_CACHE_SIZE)
|
||||||
|
cacheLocation = findPreference(Constants.PREFERENCES_KEY_CACHE_LOCATION)
|
||||||
|
preloadCount = findPreference(Constants.PREFERENCES_KEY_PRELOAD_COUNT)
|
||||||
|
bufferLength = findPreference(Constants.PREFERENCES_KEY_BUFFER_LENGTH)
|
||||||
|
incrementTime = findPreference(Constants.PREFERENCES_KEY_INCREMENT_TIME)
|
||||||
|
networkTimeout = findPreference(Constants.PREFERENCES_KEY_NETWORK_TIMEOUT)
|
||||||
|
maxAlbums = findPreference(Constants.PREFERENCES_KEY_MAX_ALBUMS)
|
||||||
|
maxSongs = findPreference(Constants.PREFERENCES_KEY_MAX_SONGS)
|
||||||
|
maxArtists = findPreference(Constants.PREFERENCES_KEY_MAX_ARTISTS)
|
||||||
|
defaultArtists = findPreference(Constants.PREFERENCES_KEY_DEFAULT_ARTISTS)
|
||||||
|
defaultSongs = findPreference(Constants.PREFERENCES_KEY_DEFAULT_SONGS)
|
||||||
|
defaultAlbums = findPreference(Constants.PREFERENCES_KEY_DEFAULT_ALBUMS)
|
||||||
|
chatRefreshInterval = findPreference(Constants.PREFERENCES_KEY_CHAT_REFRESH_INTERVAL)
|
||||||
|
directoryCacheTime = findPreference(Constants.PREFERENCES_KEY_DIRECTORY_CACHE_TIME)
|
||||||
|
mediaButtonsEnabled = findPreference(Constants.PREFERENCES_KEY_MEDIA_BUTTONS)
|
||||||
|
lockScreenEnabled = findPreference(Constants.PREFERENCES_KEY_SHOW_LOCK_SCREEN_CONTROLS)
|
||||||
|
sendBluetoothAlbumArt = findPreference(Constants.PREFERENCES_KEY_SEND_BLUETOOTH_ALBUM_ART)
|
||||||
|
sendBluetoothNotifications =
|
||||||
|
findPreference(Constants.PREFERENCES_KEY_SEND_BLUETOOTH_NOTIFICATIONS)
|
||||||
|
viewRefresh = findPreference(Constants.PREFERENCES_KEY_VIEW_REFRESH)
|
||||||
|
sharingDefaultDescription =
|
||||||
|
findPreference(Constants.PREFERENCES_KEY_DEFAULT_SHARE_DESCRIPTION)
|
||||||
|
sharingDefaultGreeting = findPreference(Constants.PREFERENCES_KEY_DEFAULT_SHARE_GREETING)
|
||||||
|
sharingDefaultExpiration =
|
||||||
|
findPreference(Constants.PREFERENCES_KEY_DEFAULT_SHARE_EXPIRATION)
|
||||||
|
resumeOnBluetoothDevice =
|
||||||
|
findPreference(Constants.PREFERENCES_KEY_RESUME_ON_BLUETOOTH_DEVICE)
|
||||||
|
pauseOnBluetoothDevice = findPreference(Constants.PREFERENCES_KEY_PAUSE_ON_BLUETOOTH_DEVICE)
|
||||||
|
debugLogToFile = findPreference(Constants.PREFERENCES_KEY_DEBUG_LOG_TO_FILE)
|
||||||
|
showArtistPicture = findPreference(Constants.PREFERENCES_KEY_SHOW_ARTIST_PICTURE)
|
||||||
|
sharingDefaultGreeting!!.text = shareGreeting
|
||||||
|
setupClearSearchPreference()
|
||||||
|
setupFeatureFlagsPreferences()
|
||||||
|
setupCacheLocationPreference()
|
||||||
|
setupBluetoothDevicePreferences()
|
||||||
|
|
||||||
|
// After API26 foreground services must be used for music playback, and they must have a notification
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
val notificationsCategory =
|
||||||
|
findPreference<PreferenceCategory>(Constants.PREFERENCES_KEY_CATEGORY_NOTIFICATIONS)
|
||||||
|
var preferenceToRemove =
|
||||||
|
findPreference<Preference>(Constants.PREFERENCES_KEY_SHOW_NOTIFICATION)
|
||||||
|
if (preferenceToRemove != null) notificationsCategory!!.removePreference(
|
||||||
|
preferenceToRemove
|
||||||
|
)
|
||||||
|
preferenceToRemove = findPreference(Constants.PREFERENCES_KEY_ALWAYS_SHOW_NOTIFICATION)
|
||||||
|
if (preferenceToRemove != null) notificationsCategory!!.removePreference(
|
||||||
|
preferenceToRemove
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||||
|
super.onActivityCreated(savedInstanceState)
|
||||||
|
update()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
val preferences = preferences
|
||||||
|
preferences.registerOnSharedPreferenceChangeListener(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPause() {
|
||||||
|
super.onPause()
|
||||||
|
val prefs = preferences
|
||||||
|
prefs.unregisterOnSharedPreferenceChangeListener(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
|
||||||
|
Timber.d("Preference changed: %s", key)
|
||||||
|
update()
|
||||||
|
when (key) {
|
||||||
|
Constants.PREFERENCES_KEY_HIDE_MEDIA -> {
|
||||||
|
setHideMedia(sharedPreferences.getBoolean(key, false))
|
||||||
|
}
|
||||||
|
Constants.PREFERENCES_KEY_MEDIA_BUTTONS -> {
|
||||||
|
setMediaButtonsEnabled(sharedPreferences.getBoolean(key, true))
|
||||||
|
}
|
||||||
|
Constants.PREFERENCES_KEY_SEND_BLUETOOTH_NOTIFICATIONS -> {
|
||||||
|
setBluetoothPreferences(sharedPreferences.getBoolean(key, true))
|
||||||
|
}
|
||||||
|
Constants.PREFERENCES_KEY_DEBUG_LOG_TO_FILE -> {
|
||||||
|
setDebugLogToFile(sharedPreferences.getBoolean(key, false))
|
||||||
|
}
|
||||||
|
Constants.PREFERENCES_KEY_ID3_TAGS -> {
|
||||||
|
showArtistPicture!!.isEnabled = sharedPreferences.getBoolean(key, false)
|
||||||
|
}
|
||||||
|
Constants.PREFERENCES_KEY_THEME -> {
|
||||||
|
RxBus.themeChangedEventPublisher.onNext(Unit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDisplayPreferenceDialog(preference: Preference) {
|
||||||
|
var dialogFragment: DialogFragment? = null
|
||||||
|
if (preference is TimeSpanPreference) {
|
||||||
|
dialogFragment = TimeSpanPreferenceDialogFragmentCompat()
|
||||||
|
val bundle = Bundle(1)
|
||||||
|
bundle.putString("key", preference.getKey())
|
||||||
|
dialogFragment.setArguments(bundle)
|
||||||
|
}
|
||||||
|
if (dialogFragment != null) {
|
||||||
|
dialogFragment.setTargetFragment(this, 0)
|
||||||
|
dialogFragment.show(
|
||||||
|
this.parentFragmentManager,
|
||||||
|
"android.support.v7.preference.PreferenceFragment.DIALOG"
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
super.onDisplayPreferenceDialog(preference)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupCacheLocationPreference() {
|
||||||
|
cacheLocation!!.summary = Settings.cacheLocation
|
||||||
|
cacheLocation!!.onPreferenceClickListener =
|
||||||
|
Preference.OnPreferenceClickListener {
|
||||||
|
// If the user tries to change the cache location,
|
||||||
|
// we must first check to see if we have write access.
|
||||||
|
requestInitialPermission(
|
||||||
|
requireActivity()
|
||||||
|
) {
|
||||||
|
if (it) {
|
||||||
|
val filePickerDialog = createFilePickerDialog(
|
||||||
|
requireContext()
|
||||||
|
)
|
||||||
|
filePickerDialog.setDefaultDirectory(defaultMusicDirectory.path)
|
||||||
|
filePickerDialog.setInitialDirectory(cacheLocation!!.summary.toString())
|
||||||
|
filePickerDialog.setOnFileSelectedListener(object :
|
||||||
|
OnFileSelectedListener {
|
||||||
|
override fun onFileSelected(file: File?, path: String?) {
|
||||||
|
if (path != null) {
|
||||||
|
Settings.cacheLocation = path
|
||||||
|
setCacheLocation(path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
filePickerDialog.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupBluetoothDevicePreferences() {
|
||||||
|
val resumeSetting = Settings.resumeOnBluetoothDevice
|
||||||
|
val pauseSetting = Settings.pauseOnBluetoothDevice
|
||||||
|
resumeOnBluetoothDevice!!.summary = bluetoothDevicePreferenceToString(resumeSetting)
|
||||||
|
pauseOnBluetoothDevice!!.summary = bluetoothDevicePreferenceToString(pauseSetting)
|
||||||
|
resumeOnBluetoothDevice!!.onPreferenceClickListener =
|
||||||
|
Preference.OnPreferenceClickListener {
|
||||||
|
showBluetoothDevicePreferenceDialog(
|
||||||
|
R.string.settings_playback_resume_on_bluetooth_device,
|
||||||
|
Settings.resumeOnBluetoothDevice
|
||||||
|
) { choice: Int ->
|
||||||
|
val editor = resumeOnBluetoothDevice!!.sharedPreferences.edit()
|
||||||
|
editor.putInt(Constants.PREFERENCES_KEY_RESUME_ON_BLUETOOTH_DEVICE, choice)
|
||||||
|
editor.apply()
|
||||||
|
resumeOnBluetoothDevice!!.summary = bluetoothDevicePreferenceToString(choice)
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
pauseOnBluetoothDevice!!.onPreferenceClickListener =
|
||||||
|
Preference.OnPreferenceClickListener {
|
||||||
|
showBluetoothDevicePreferenceDialog(
|
||||||
|
R.string.settings_playback_pause_on_bluetooth_device,
|
||||||
|
Settings.pauseOnBluetoothDevice
|
||||||
|
) { choice: Int ->
|
||||||
|
Settings.pauseOnBluetoothDevice = choice
|
||||||
|
pauseOnBluetoothDevice!!.summary = bluetoothDevicePreferenceToString(choice)
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showBluetoothDevicePreferenceDialog(
|
||||||
|
@StringRes title: Int,
|
||||||
|
defaultChoice: Int,
|
||||||
|
onChosen: (Int) -> Unit
|
||||||
|
) {
|
||||||
|
val choice = intArrayOf(defaultChoice)
|
||||||
|
AlertDialog.Builder(activity).setTitle(title)
|
||||||
|
.setSingleChoiceItems(
|
||||||
|
R.array.bluetoothDeviceSettingNames, defaultChoice
|
||||||
|
) { _: DialogInterface?, i: Int -> choice[0] = i }
|
||||||
|
.setNegativeButton(R.string.common_cancel) { dialogInterface: DialogInterface, _: Int ->
|
||||||
|
dialogInterface.cancel()
|
||||||
|
}
|
||||||
|
.setPositiveButton(R.string.common_ok) { dialogInterface: DialogInterface, _: Int ->
|
||||||
|
onChosen(choice[0])
|
||||||
|
dialogInterface.dismiss()
|
||||||
|
}
|
||||||
|
.create().show()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun bluetoothDevicePreferenceToString(preferenceValue: Int): String {
|
||||||
|
return when (preferenceValue) {
|
||||||
|
Constants.PREFERENCE_VALUE_ALL -> {
|
||||||
|
getString(R.string.settings_playback_bluetooth_all)
|
||||||
|
}
|
||||||
|
Constants.PREFERENCE_VALUE_A2DP -> {
|
||||||
|
getString(R.string.settings_playback_bluetooth_a2dp)
|
||||||
|
}
|
||||||
|
Constants.PREFERENCE_VALUE_DISABLED -> {
|
||||||
|
getString(R.string.settings_playback_bluetooth_disabled)
|
||||||
|
}
|
||||||
|
else -> ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupClearSearchPreference() {
|
||||||
|
val clearSearchPreference =
|
||||||
|
findPreference<Preference>(Constants.PREFERENCES_KEY_CLEAR_SEARCH_HISTORY)
|
||||||
|
if (clearSearchPreference != null) {
|
||||||
|
clearSearchPreference.onPreferenceClickListener =
|
||||||
|
Preference.OnPreferenceClickListener {
|
||||||
|
val suggestions = SearchRecentSuggestions(
|
||||||
|
activity,
|
||||||
|
SearchSuggestionProvider.AUTHORITY,
|
||||||
|
SearchSuggestionProvider.MODE
|
||||||
|
)
|
||||||
|
suggestions.clearHistory()
|
||||||
|
toast(activity, R.string.settings_search_history_cleared)
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupFeatureFlagsPreferences() {
|
||||||
|
val featureStorage = get<FeatureStorage>(FeatureStorage::class.java)
|
||||||
|
val useFiveStarRating = findPreference<Preference>(
|
||||||
|
Constants.PREFERENCES_KEY_USE_FIVE_STAR_RATING
|
||||||
|
) as CheckBoxPreference?
|
||||||
|
if (useFiveStarRating != null) {
|
||||||
|
useFiveStarRating.isChecked = featureStorage.isFeatureEnabled(Feature.FIVE_STAR_RATING)
|
||||||
|
useFiveStarRating.onPreferenceChangeListener =
|
||||||
|
Preference.OnPreferenceChangeListener { _: Preference?, o: Any? ->
|
||||||
|
featureStorage.changeFeatureFlag(Feature.FIVE_STAR_RATING, (o as Boolean?)!!)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun update() {
|
||||||
|
theme!!.summary = theme!!.entry
|
||||||
|
maxBitrateWifi!!.summary = maxBitrateWifi!!.entry
|
||||||
|
maxBitrateMobile!!.summary = maxBitrateMobile!!.entry
|
||||||
|
cacheSize!!.summary = cacheSize!!.entry
|
||||||
|
preloadCount!!.summary = preloadCount!!.entry
|
||||||
|
bufferLength!!.summary = bufferLength!!.entry
|
||||||
|
incrementTime!!.summary = incrementTime!!.entry
|
||||||
|
networkTimeout!!.summary = networkTimeout!!.entry
|
||||||
|
maxAlbums!!.summary = maxAlbums!!.entry
|
||||||
|
maxArtists!!.summary = maxArtists!!.entry
|
||||||
|
maxSongs!!.summary = maxSongs!!.entry
|
||||||
|
defaultAlbums!!.summary = defaultAlbums!!.entry
|
||||||
|
defaultArtists!!.summary = defaultArtists!!.entry
|
||||||
|
defaultSongs!!.summary = defaultSongs!!.entry
|
||||||
|
chatRefreshInterval!!.summary = chatRefreshInterval!!.entry
|
||||||
|
directoryCacheTime!!.summary = directoryCacheTime!!.entry
|
||||||
|
viewRefresh!!.summary = viewRefresh!!.entry
|
||||||
|
sharingDefaultExpiration!!.summary = sharingDefaultExpiration!!.text
|
||||||
|
sharingDefaultDescription!!.summary = sharingDefaultDescription!!.text
|
||||||
|
sharingDefaultGreeting!!.summary = sharingDefaultGreeting!!.text
|
||||||
|
cacheLocation!!.summary = Settings.cacheLocation
|
||||||
|
if (!mediaButtonsEnabled!!.isChecked) {
|
||||||
|
lockScreenEnabled!!.isChecked = false
|
||||||
|
lockScreenEnabled!!.isEnabled = false
|
||||||
|
}
|
||||||
|
if (!sendBluetoothNotifications!!.isChecked) {
|
||||||
|
sendBluetoothAlbumArt!!.isChecked = false
|
||||||
|
sendBluetoothAlbumArt!!.isEnabled = false
|
||||||
|
}
|
||||||
|
if (debugLogToFile!!.isChecked) {
|
||||||
|
debugLogToFile!!.summary = getString(
|
||||||
|
R.string.settings_debug_log_path,
|
||||||
|
ultrasonicDirectory, FileLoggerTree.FILENAME
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
debugLogToFile!!.summary = ""
|
||||||
|
}
|
||||||
|
showArtistPicture!!.isEnabled = shouldUseId3Tags
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setHideMedia(hide: Boolean) {
|
||||||
|
val nomediaDir = File(ultrasonicDirectory, ".nomedia")
|
||||||
|
if (hide && !nomediaDir.exists()) {
|
||||||
|
if (!nomediaDir.mkdir()) {
|
||||||
|
Timber.w("Failed to create %s", nomediaDir)
|
||||||
|
}
|
||||||
|
} else if (nomediaDir.exists()) {
|
||||||
|
if (!nomediaDir.delete()) {
|
||||||
|
Timber.w("Failed to delete %s", nomediaDir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
toast(activity, R.string.settings_hide_media_toast, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setMediaButtonsEnabled(enabled: Boolean) {
|
||||||
|
lockScreenEnabled!!.isEnabled = enabled
|
||||||
|
mediaSessionHandler.value.updateMediaButtonReceiver()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setBluetoothPreferences(enabled: Boolean) {
|
||||||
|
sendBluetoothAlbumArt!!.isEnabled = enabled
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setCacheLocation(path: String) {
|
||||||
|
val dir = File(path)
|
||||||
|
if (!ensureDirectoryExistsAndIsReadWritable(dir)) {
|
||||||
|
permissionUtil.value.handlePermissionFailed {
|
||||||
|
val currentPath = Settings.cacheLocation
|
||||||
|
cacheLocation!!.summary = currentPath
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cacheLocation!!.summary = path
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear download queue.
|
||||||
|
mediaPlayerControllerLazy.value.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setDebugLogToFile(writeLog: Boolean) {
|
||||||
|
if (writeLog) {
|
||||||
|
plantToTimberForest()
|
||||||
|
Timber.i("Enabled debug logging to file")
|
||||||
|
} else {
|
||||||
|
uprootFromTimberForest()
|
||||||
|
Timber.i("Disabled debug logging to file")
|
||||||
|
val fileNum = getLogFileNumber()
|
||||||
|
val fileSize = getLogFileSizes()
|
||||||
|
val message = getString(
|
||||||
|
R.string.settings_debug_log_summary,
|
||||||
|
fileNum.toString(),
|
||||||
|
ceil(fileSize / 1000 * 1000.0).toString(),
|
||||||
|
ultrasonicDirectory
|
||||||
|
)
|
||||||
|
val keep = R.string.settings_debug_log_keep
|
||||||
|
val delete = R.string.settings_debug_log_delete
|
||||||
|
AlertDialog.Builder(activity)
|
||||||
|
.setMessage(message)
|
||||||
|
.setIcon(android.R.drawable.ic_dialog_info)
|
||||||
|
.setNegativeButton(keep) { dIf: DialogInterface, _: Int ->
|
||||||
|
dIf.cancel()
|
||||||
|
}
|
||||||
|
.setPositiveButton(delete) { dIf: DialogInterface, _: Int ->
|
||||||
|
deleteLogFiles()
|
||||||
|
Timber.i("Deleted debug log files")
|
||||||
|
dIf.dismiss()
|
||||||
|
AlertDialog.Builder(activity)
|
||||||
|
.setMessage(R.string.settings_debug_log_deleted)
|
||||||
|
.setPositiveButton(R.string.common_ok) { dIf2: DialogInterface, _: Int ->
|
||||||
|
dIf2.dismiss()
|
||||||
|
}
|
||||||
|
.create().show()
|
||||||
|
}
|
||||||
|
.create().show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,14 +4,11 @@ import android.content.Context
|
|||||||
import android.media.AudioAttributes
|
import android.media.AudioAttributes
|
||||||
import android.media.AudioManager
|
import android.media.AudioManager
|
||||||
import android.media.AudioManager.OnAudioFocusChangeListener
|
import android.media.AudioManager.OnAudioFocusChangeListener
|
||||||
import android.os.Build
|
|
||||||
import androidx.annotation.RequiresApi
|
|
||||||
import androidx.media.AudioAttributesCompat
|
import androidx.media.AudioAttributesCompat
|
||||||
import androidx.media.AudioFocusRequestCompat
|
import androidx.media.AudioFocusRequestCompat
|
||||||
import androidx.media.AudioManagerCompat
|
import androidx.media.AudioManagerCompat
|
||||||
import org.koin.java.KoinJavaComponent.inject
|
import org.koin.java.KoinJavaComponent.inject
|
||||||
import org.moire.ultrasonic.domain.PlayerState
|
import org.moire.ultrasonic.domain.PlayerState
|
||||||
import org.moire.ultrasonic.util.Constants
|
|
||||||
import org.moire.ultrasonic.util.Settings
|
import org.moire.ultrasonic.util.Settings
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
@ -25,12 +22,8 @@ class AudioFocusHandler(private val context: Context) {
|
|||||||
context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
|
context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
|
||||||
}
|
}
|
||||||
|
|
||||||
private val preferences by lazy {
|
|
||||||
Settings.preferences
|
|
||||||
}
|
|
||||||
|
|
||||||
private val lossPref: Int
|
private val lossPref: Int
|
||||||
get() = preferences.getString(Constants.PREFERENCES_KEY_TEMP_LOSS, "1")!!.toInt()
|
get() = Settings.tempLoss
|
||||||
|
|
||||||
private val audioAttributesCompat by lazy {
|
private val audioAttributesCompat by lazy {
|
||||||
AudioAttributesCompat.Builder()
|
AudioAttributesCompat.Builder()
|
||||||
@ -114,7 +107,6 @@ class AudioFocusHandler(private val context: Context) {
|
|||||||
private var lowerFocus = false
|
private var lowerFocus = false
|
||||||
|
|
||||||
// TODO: This can be removed if we switch to androidx.media2.player
|
// TODO: This can be removed if we switch to androidx.media2.player
|
||||||
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
|
|
||||||
fun getAudioAttributes(): AudioAttributes {
|
fun getAudioAttributes(): AudioAttributes {
|
||||||
return AudioAttributes.Builder()
|
return AudioAttributes.Builder()
|
||||||
.setUsage(AudioAttributes.USAGE_MEDIA)
|
.setUsage(AudioAttributes.USAGE_MEDIA)
|
||||||
|
@ -10,11 +10,9 @@ package org.moire.ultrasonic.service
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Context.POWER_SERVICE
|
import android.content.Context.POWER_SERVICE
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.media.AudioManager
|
|
||||||
import android.media.MediaPlayer
|
import android.media.MediaPlayer
|
||||||
import android.media.MediaPlayer.OnCompletionListener
|
import android.media.MediaPlayer.OnCompletionListener
|
||||||
import android.media.audiofx.AudioEffect
|
import android.media.audiofx.AudioEffect
|
||||||
import android.os.Build
|
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
import android.os.PowerManager
|
import android.os.PowerManager
|
||||||
@ -447,12 +445,7 @@ class LocalMediaPlayer : KoinComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun setAudioAttributes(player: MediaPlayer) {
|
private fun setAudioAttributes(player: MediaPlayer) {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
player.setAudioAttributes(AudioFocusHandler.getAudioAttributes())
|
||||||
player.setAudioAttributes(AudioFocusHandler.getAudioAttributes())
|
|
||||||
} else {
|
|
||||||
@Suppress("DEPRECATION")
|
|
||||||
player.setAudioStreamType(AudioManager.STREAM_MUSIC)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("ComplexCondition")
|
@Suppress("ComplexCondition")
|
||||||
|
@ -12,7 +12,6 @@ import android.content.Context
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.IntentFilter
|
import android.content.IntentFilter
|
||||||
import android.media.AudioManager
|
import android.media.AudioManager
|
||||||
import android.os.Build
|
|
||||||
import android.view.KeyEvent
|
import android.view.KeyEvent
|
||||||
import io.reactivex.rxjava3.disposables.Disposable
|
import io.reactivex.rxjava3.disposables.Disposable
|
||||||
import org.koin.core.component.KoinComponent
|
import org.koin.core.component.KoinComponent
|
||||||
|
@ -22,10 +22,6 @@ import timber.log.Timber
|
|||||||
class PermissionUtil(private val applicationContext: Context) {
|
class PermissionUtil(private val applicationContext: Context) {
|
||||||
private var activityContext: Context? = null
|
private var activityContext: Context? = null
|
||||||
|
|
||||||
interface PermissionRequestFinishedCallback {
|
|
||||||
fun onPermissionRequestFinished(hasPermission: Boolean)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onForegroundApplicationStarted(context: Context?) {
|
fun onForegroundApplicationStarted(context: Context?) {
|
||||||
activityContext = context
|
activityContext = context
|
||||||
}
|
}
|
||||||
@ -42,7 +38,7 @@ class PermissionUtil(private val applicationContext: Context) {
|
|||||||
*
|
*
|
||||||
* @param callback callback function to execute after the permission request is finished
|
* @param callback callback function to execute after the permission request is finished
|
||||||
*/
|
*/
|
||||||
fun handlePermissionFailed(callback: PermissionRequestFinishedCallback?) {
|
fun handlePermissionFailed(callback: ((Boolean) -> Unit)?) {
|
||||||
val currentCachePath = Settings.cacheLocation
|
val currentCachePath = Settings.cacheLocation
|
||||||
val defaultCachePath = defaultMusicDirectory.path
|
val defaultCachePath = defaultMusicDirectory.path
|
||||||
|
|
||||||
@ -76,7 +72,7 @@ class PermissionUtil(private val applicationContext: Context) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
callback?.onPermissionRequestFinished(false)
|
callback?.invoke(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,7 +87,7 @@ class PermissionUtil(private val applicationContext: Context) {
|
|||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun requestInitialPermission(
|
fun requestInitialPermission(
|
||||||
context: Context,
|
context: Context,
|
||||||
callback: PermissionRequestFinishedCallback?
|
callback: ((Boolean) -> Unit)?
|
||||||
) {
|
) {
|
||||||
Dexter.withContext(context)
|
Dexter.withContext(context)
|
||||||
.withPermissions(
|
.withPermissions(
|
||||||
@ -102,7 +98,7 @@ class PermissionUtil(private val applicationContext: Context) {
|
|||||||
override fun onPermissionsChecked(report: MultiplePermissionsReport) {
|
override fun onPermissionsChecked(report: MultiplePermissionsReport) {
|
||||||
if (report.areAllPermissionsGranted()) {
|
if (report.areAllPermissionsGranted()) {
|
||||||
Timber.i("R/W permission granted for external storage")
|
Timber.i("R/W permission granted for external storage")
|
||||||
callback?.onPermissionRequestFinished(true)
|
callback?.invoke(true)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (report.isAnyPermissionPermanentlyDenied) {
|
if (report.isAnyPermissionPermanentlyDenied) {
|
||||||
@ -110,7 +106,7 @@ class PermissionUtil(private val applicationContext: Context) {
|
|||||||
"R/W permission is permanently denied for external storage"
|
"R/W permission is permanently denied for external storage"
|
||||||
)
|
)
|
||||||
showSettingsDialog(context)
|
showSettingsDialog(context)
|
||||||
callback?.onPermissionRequestFinished(false)
|
callback?.invoke(false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
Timber.i("R/W permission is missing for external storage")
|
Timber.i("R/W permission is missing for external storage")
|
||||||
@ -120,7 +116,7 @@ class PermissionUtil(private val applicationContext: Context) {
|
|||||||
context.getString(R.string.permissions_rationale_description_initial),
|
context.getString(R.string.permissions_rationale_description_initial),
|
||||||
null
|
null
|
||||||
)
|
)
|
||||||
callback?.onPermissionRequestFinished(false)
|
callback?.invoke(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPermissionRationaleShouldBeShown(
|
override fun onPermissionRationaleShouldBeShown(
|
||||||
@ -146,7 +142,7 @@ class PermissionUtil(private val applicationContext: Context) {
|
|||||||
private fun requestFailedPermission(
|
private fun requestFailedPermission(
|
||||||
context: Context,
|
context: Context,
|
||||||
cacheLocation: String?,
|
cacheLocation: String?,
|
||||||
callback: PermissionRequestFinishedCallback?
|
callback: ((Boolean) -> Unit)?
|
||||||
) {
|
) {
|
||||||
Dexter.withContext(context)
|
Dexter.withContext(context)
|
||||||
.withPermissions(
|
.withPermissions(
|
||||||
@ -161,7 +157,7 @@ class PermissionUtil(private val applicationContext: Context) {
|
|||||||
if (cacheLocation != null) {
|
if (cacheLocation != null) {
|
||||||
Settings.cacheLocation = cacheLocation
|
Settings.cacheLocation = cacheLocation
|
||||||
}
|
}
|
||||||
callback?.onPermissionRequestFinished(true)
|
callback?.invoke(true)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (report.isAnyPermissionPermanentlyDenied) {
|
if (report.isAnyPermissionPermanentlyDenied) {
|
||||||
@ -170,7 +166,7 @@ class PermissionUtil(private val applicationContext: Context) {
|
|||||||
cacheLocation
|
cacheLocation
|
||||||
)
|
)
|
||||||
showSettingsDialog(context)
|
showSettingsDialog(context)
|
||||||
callback?.onPermissionRequestFinished(false)
|
callback?.invoke(false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
Timber.i(
|
Timber.i(
|
||||||
@ -182,7 +178,7 @@ class PermissionUtil(private val applicationContext: Context) {
|
|||||||
context, context.getString(R.string.permissions_message_box_title),
|
context, context.getString(R.string.permissions_message_box_title),
|
||||||
context.getString(R.string.permissions_permission_missing), null
|
context.getString(R.string.permissions_permission_missing), null
|
||||||
)
|
)
|
||||||
callback?.onPermissionRequestFinished(false)
|
callback?.invoke(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPermissionRationaleShouldBeShown(
|
override fun onPermissionRationaleShouldBeShown(
|
||||||
|
@ -185,6 +185,15 @@ object Settings {
|
|||||||
var shouldUseId3Tags
|
var shouldUseId3Tags
|
||||||
by BooleanSetting(Constants.PREFERENCES_KEY_ID3_TAGS, false)
|
by BooleanSetting(Constants.PREFERENCES_KEY_ID3_TAGS, false)
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
var tempLoss by StringIntSetting(Constants.PREFERENCES_KEY_TEMP_LOSS, "1")
|
||||||
|
|
||||||
|
var activeServer by IntSetting(Constants.PREFERENCES_KEY_SERVER_INSTANCE, -1)
|
||||||
|
|
||||||
|
var serverScaling by BooleanSetting(Constants.PREFERENCES_KEY_SERVER_SCALING, false)
|
||||||
|
|
||||||
|
var firstRunExecuted by BooleanSetting(Constants.PREFERENCES_KEY_FIRST_RUN_EXECUTED, false)
|
||||||
|
|
||||||
val shouldShowArtistPicture: Boolean
|
val shouldShowArtistPicture: Boolean
|
||||||
get() {
|
get() {
|
||||||
val preferences = preferences
|
val preferences = preferences
|
||||||
|
@ -95,21 +95,18 @@ object Util {
|
|||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun applyTheme(context: Context?) {
|
fun applyTheme(context: Context?) {
|
||||||
val theme = Settings.theme
|
when (Settings.theme.lowercase()) {
|
||||||
if (Constants.PREFERENCES_KEY_THEME_DARK.equals(
|
Constants.PREFERENCES_KEY_THEME_DARK,
|
||||||
theme,
|
"fullscreen" -> {
|
||||||
ignoreCase = true
|
context!!.setTheme(R.style.UltrasonicTheme)
|
||||||
) || "fullscreen".equals(theme, ignoreCase = true)
|
}
|
||||||
) {
|
Constants.PREFERENCES_KEY_THEME_BLACK -> {
|
||||||
context!!.setTheme(R.style.UltrasonicTheme)
|
context!!.setTheme(R.style.UltrasonicTheme_Black)
|
||||||
} else if (Constants.PREFERENCES_KEY_THEME_BLACK.equals(theme, ignoreCase = true)) {
|
}
|
||||||
context!!.setTheme(R.style.UltrasonicTheme_Black)
|
Constants.PREFERENCES_KEY_THEME_LIGHT,
|
||||||
} else if (Constants.PREFERENCES_KEY_THEME_LIGHT.equals(
|
"fullscreenlight" -> {
|
||||||
theme,
|
context!!.setTheme(R.style.UltrasonicTheme_Light)
|
||||||
ignoreCase = true
|
}
|
||||||
) || "fullscreenlight".equals(theme, ignoreCase = true)
|
|
||||||
) {
|
|
||||||
context!!.setTheme(R.style.UltrasonicTheme_Light)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -794,13 +791,9 @@ object Util {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun isFirstRun(): Boolean {
|
fun isFirstRun(): Boolean {
|
||||||
val preferences = Settings.preferences
|
if (Settings.firstRunExecuted) return false
|
||||||
val firstExecuted =
|
|
||||||
preferences.getBoolean(Constants.PREFERENCES_KEY_FIRST_RUN_EXECUTED, false)
|
Settings.firstRunExecuted = true
|
||||||
if (firstExecuted) return false
|
|
||||||
val editor = preferences.edit()
|
|
||||||
editor.putBoolean(Constants.PREFERENCES_KEY_FIRST_RUN_EXECUTED, true)
|
|
||||||
editor.apply()
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -925,7 +918,7 @@ object Util {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getConnectivityManager(): ConnectivityManager {
|
fun getConnectivityManager(): ConnectivityManager {
|
||||||
val context = Util.appContext()
|
val context = appContext()
|
||||||
return context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
return context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -400,7 +400,7 @@
|
|||||||
<string name="settings.playback.bluetooth_all">All Bluetooth devices</string>
|
<string name="settings.playback.bluetooth_all">All Bluetooth devices</string>
|
||||||
<string name="settings.playback.bluetooth_a2dp">Only audio (A2DP) devices</string>
|
<string name="settings.playback.bluetooth_a2dp">Only audio (A2DP) devices</string>
|
||||||
<string name="settings.playback.bluetooth_disabled">Disabled</string>
|
<string name="settings.playback.bluetooth_disabled">Disabled</string>
|
||||||
<string name="settings.playback.single_button_bluetooth_device">Single button Play/Pause on Bluetooth device</string>
|
<string name="settings.playback.single_button_bluetooth_device">Bluetooth device with only a single Play/Pause button</string>
|
||||||
<string name="settings.playback.single_button_bluetooth_device_summary">Enabling this may help with older Bluetooth devices when Play/Pause doesn\'t work correctly</string>
|
<string name="settings.playback.single_button_bluetooth_device_summary">Enabling this may help with older Bluetooth devices when Play/Pause doesn\'t work correctly</string>
|
||||||
<string name="settings.debug.title">Debug options</string>
|
<string name="settings.debug.title">Debug options</string>
|
||||||
<string name="settings.debug.log_to_file">Write debug log to file</string>
|
<string name="settings.debug.log_to_file">Write debug log to file</string>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user