From 2b7a3b0488d203b417aee08a44415c046b89c594 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Sat, 24 Sep 2022 13:59:43 +0000 Subject: [PATCH] Update dependency org.jetbrains.kotlinx:kotlinx-coroutines-guava to v1.6.4 --- gradle/libs.versions.toml | 4 +- ultrasonic/lint-baseline.xml | 4 +- ultrasonic/src/main/AndroidManifest.xml | 1 + .../org/moire/ultrasonic/util/TimeSpan.java | 166 ------------ .../moire/ultrasonic/util/TimeSpanPicker.java | 246 ------------------ .../moire/ultrasonic/util/TimeSpanPicker.kt | 166 ++++++++++++ ...imeSpanPreferenceDialogFragmentCompat.java | 86 ------ .../TimeSpanPreferenceDialogFragmentCompat.kt | 58 +++++ .../ultrasonic/activity/NavigationActivity.kt | 3 + .../ultrasonic/fragment/EqualizerFragment.kt | 8 +- .../ultrasonic/fragment/SettingsFragment.kt | 7 +- .../fragment/legacy/SharesFragment.kt | 66 ++--- .../imageloader/ArtworkBitmapLoader.kt | 3 + .../playback/MediaNotificationProvider.kt | 4 +- .../ultrasonic/playback/PlaybackService.kt | 9 +- .../ultrasonic/service/JukeboxMediaPlayer.kt | 11 +- .../JukeboxNotificationActionFactory.kt | 2 + .../service/JukeboxUnimplementedFunctions.kt | 4 +- .../ultrasonic/service/PlaylistTimeline.kt | 2 + .../moire/ultrasonic/subsonic/ShareHandler.kt | 19 +- .../org/moire/ultrasonic/util/Settings.kt | 15 +- .../org/moire/ultrasonic/util/StorageFile.kt | 6 +- .../kotlin/org/moire/ultrasonic/util/Util.kt | 23 ++ .../src/main/res/layout/equalizer_bar.xml | 17 +- ultrasonic/src/main/res/values-cs/strings.xml | 2 - ultrasonic/src/main/res/values-de/strings.xml | 2 - ultrasonic/src/main/res/values-es/strings.xml | 2 - ultrasonic/src/main/res/values-fr/strings.xml | 2 - ultrasonic/src/main/res/values-hu/strings.xml | 2 - ultrasonic/src/main/res/values-it/strings.xml | 2 - ultrasonic/src/main/res/values-nl/strings.xml | 2 - ultrasonic/src/main/res/values-pl/strings.xml | 2 - .../src/main/res/values-pt-rBR/strings.xml | 2 - ultrasonic/src/main/res/values-pt/strings.xml | 2 - ultrasonic/src/main/res/values-ru/strings.xml | 2 - .../src/main/res/values-zh-rCN/strings.xml | 2 - ultrasonic/src/main/res/values/arrays.xml | 2 - ultrasonic/src/main/res/values/strings.xml | 4 +- 38 files changed, 355 insertions(+), 605 deletions(-) delete mode 100644 ultrasonic/src/main/java/org/moire/ultrasonic/util/TimeSpan.java delete mode 100644 ultrasonic/src/main/java/org/moire/ultrasonic/util/TimeSpanPicker.java create mode 100644 ultrasonic/src/main/java/org/moire/ultrasonic/util/TimeSpanPicker.kt delete mode 100644 ultrasonic/src/main/java/org/moire/ultrasonic/util/TimeSpanPreferenceDialogFragmentCompat.java create mode 100644 ultrasonic/src/main/java/org/moire/ultrasonic/util/TimeSpanPreferenceDialogFragmentCompat.kt diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 1fce19c6..5a4a5e41 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -19,8 +19,8 @@ constraintLayout = "2.1.4" multidex = "2.0.1" room = "2.4.3" kotlin = "1.7.10" -kotlinxCoroutines = "1.6.3-native-mt" -kotlinxGuava = "1.6.3" +kotlinxCoroutines = "1.6.4-native-mt" +kotlinxGuava = "1.6.4" viewModelKtx = "2.5.1" retrofit = "2.9.0" diff --git a/ultrasonic/lint-baseline.xml b/ultrasonic/lint-baseline.xml index 669d9037..f3794053 100644 --- a/ultrasonic/lint-baseline.xml +++ b/ultrasonic/lint-baseline.xml @@ -1,5 +1,5 @@ - + diff --git a/ultrasonic/src/main/AndroidManifest.xml b/ultrasonic/src/main/AndroidManifest.xml index cad7cdc9..e574a93a 100644 --- a/ultrasonic/src/main/AndroidManifest.xml +++ b/ultrasonic/src/main/AndroidManifest.xml @@ -5,6 +5,7 @@ + diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/util/TimeSpan.java b/ultrasonic/src/main/java/org/moire/ultrasonic/util/TimeSpan.java deleted file mode 100644 index e4ac46c9..00000000 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/util/TimeSpan.java +++ /dev/null @@ -1,166 +0,0 @@ -package org.moire.ultrasonic.util; - -import java.util.Calendar; -import java.util.Date; - -public class TimeSpan -{ - public static final int MILLISECONDS_IN_DAY = 86400000; - public static final int MILLISECONDS_IN_HOUR = 3600000; - public static final int MILLISECONDS_IN_MINUTE = 60000; - public static final int MILLISECONDS_IN_SECOND = 1000; - public static final int SECONDS_IN_MINUTE = 60; - public static final int MINUTES_IN_HOUR = 60; - public static final int HOURS_IN_DAY = 24; - - private long totalMilliseconds; - private int milliseconds; - private int seconds; - private int minutes; - private int hours; - private int days; - - public TimeSpan(long milliseconds) - { - this.totalMilliseconds = milliseconds; - this.milliseconds = (int) (milliseconds % MILLISECONDS_IN_SECOND); - milliseconds /= MILLISECONDS_IN_SECOND; - this.seconds = (int) (milliseconds % SECONDS_IN_MINUTE); - milliseconds /= SECONDS_IN_MINUTE; - this.minutes = (int) (milliseconds % MINUTES_IN_HOUR); - milliseconds /= MINUTES_IN_HOUR; - this.hours = (int) (milliseconds % HOURS_IN_DAY); - milliseconds /= HOURS_IN_DAY; - this.days = (int) milliseconds; - } - - public static TimeSpan create(int minutes, int seconds) - { - long totalMilliseconds = (seconds * MILLISECONDS_IN_SECOND); - totalMilliseconds += (minutes * MILLISECONDS_IN_MINUTE); - - return new TimeSpan(totalMilliseconds); - } - - public static TimeSpan create(int hours, int minutes, int seconds) - { - long totalMilliseconds = (seconds * MILLISECONDS_IN_SECOND); - totalMilliseconds += (minutes * MILLISECONDS_IN_MINUTE); - totalMilliseconds += (hours * MILLISECONDS_IN_HOUR); - - return new TimeSpan(totalMilliseconds); - } - - public static TimeSpan create(long days, long hours, long minutes, long seconds) - { - long totalMilliseconds = (seconds * MILLISECONDS_IN_SECOND); - totalMilliseconds += (minutes * MILLISECONDS_IN_MINUTE); - totalMilliseconds += (hours * MILLISECONDS_IN_HOUR); - totalMilliseconds += (days * MILLISECONDS_IN_DAY); - - return new TimeSpan(totalMilliseconds); - } - - public static TimeSpan create(long days, long hours, long minutes, long seconds, long milliseconds) - { - long totalMilliseconds = milliseconds; - totalMilliseconds += (seconds * MILLISECONDS_IN_SECOND); - totalMilliseconds += (minutes * MILLISECONDS_IN_MINUTE); - totalMilliseconds += (hours * MILLISECONDS_IN_HOUR); - totalMilliseconds += (days * MILLISECONDS_IN_DAY); - - return new TimeSpan(totalMilliseconds); - } - - public static TimeSpan create(Calendar cal) - { - return new TimeSpan(cal.getTimeInMillis()); - } - - public static TimeSpan create(Date date) - { - return new TimeSpan(date.getTime()); - } - - public static TimeSpan getCurrentTime() - { - return new TimeSpan(System.currentTimeMillis()); - } - - public int getDays() - { - return days; - } - - public int getHours() - { - return hours; - } - - public int getMinutes() - { - return minutes; - } - - public int getSeconds() - { - return seconds; - } - - public int getMilliseconds() - { - return milliseconds; - } - - public double getTotalDays() - { - return totalMilliseconds / (double) (MILLISECONDS_IN_DAY); - } - - public double getTotalHours() - { - return totalMilliseconds / (double) (MILLISECONDS_IN_HOUR); - } - - public double getTotalMinutes() - { - return totalMilliseconds / (double) (MILLISECONDS_IN_MINUTE); - } - - public double getTotalSeconds() - { - return totalMilliseconds / (double) (MILLISECONDS_IN_SECOND); - } - - public long getTotalMilliseconds() - { - return totalMilliseconds; - } - - public TimeSpan add(TimeSpan ts) - { - return new TimeSpan(this.totalMilliseconds + ts.totalMilliseconds); - } - - public TimeSpan add(long milliseconds) - { - return new TimeSpan(this.totalMilliseconds + milliseconds); - } - - public TimeSpan subtract(TimeSpan ts) - { - return new TimeSpan(this.totalMilliseconds - ts.totalMilliseconds); - } - - public static int compare(TimeSpan t1, TimeSpan t2) - { - if (t1.totalMilliseconds < t2.totalMilliseconds) - { - return -1; - } - else - { - return t1.totalMilliseconds == t2.totalMilliseconds ? 0 : 1; - } - } -} \ No newline at end of file diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/util/TimeSpanPicker.java b/ultrasonic/src/main/java/org/moire/ultrasonic/util/TimeSpanPicker.java deleted file mode 100644 index 2b294861..00000000 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/util/TimeSpanPicker.java +++ /dev/null @@ -1,246 +0,0 @@ -package org.moire.ultrasonic.util; - -import android.content.Context; -import android.content.res.Resources; -import android.text.Editable; -import android.util.AttributeSet; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.AdapterView; -import android.widget.ArrayAdapter; -import android.widget.CheckBox; -import android.widget.CompoundButton; -import android.widget.EditText; -import android.widget.LinearLayout; -import android.widget.Spinner; - -import org.moire.ultrasonic.R; - -/** - * Created by Joshua Bahnsen on 12/22/13. - */ -public class TimeSpanPicker extends LinearLayout implements AdapterView.OnItemSelectedListener -{ - private EditText timeSpanEditText; - private Spinner timeSpanSpinner; - private CheckBox timeSpanDisableCheckbox; - private TimeSpan timeSpan; - private ArrayAdapter adapter; - private Context context; - private View dialog; - - public TimeSpanPicker(Context context) - { - this(context, null); - - this.context = context; - } - - public TimeSpanPicker(Context context, AttributeSet attrs) - { - this(context, attrs, 0); - - this.context = context; - } - - public TimeSpanPicker(Context context, AttributeSet attrs, int defStyle) - { - super(context, attrs, defStyle); - - this.context = context; - - final LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - dialog = inflater.inflate(R.layout.time_span_dialog, this, true); - - timeSpan = new TimeSpan(-1); - - timeSpanEditText = (EditText) dialog.findViewById(R.id.timeSpanEditText); - timeSpanEditText.setText("0"); - - timeSpanSpinner = (Spinner) dialog.findViewById(R.id.timeSpanSpinner); - timeSpanDisableCheckbox = (CheckBox) dialog.findViewById(R.id.timeSpanDisableCheckBox); - - timeSpanDisableCheckbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() - { - @Override - public void onCheckedChanged(CompoundButton compoundButton, boolean b) - { - timeSpanEditText.setEnabled(!b); - timeSpanSpinner.setEnabled(!b); - } - }); - - adapter = ArrayAdapter.createFromResource(context, R.array.shareExpirationNames, android.R.layout.simple_spinner_item); - adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - timeSpanSpinner.setAdapter(adapter); - - timeSpanSpinner.setOnItemSelectedListener(this); - } - - @Override - public void setEnabled(boolean enabled) - { - timeSpanEditText.setEnabled(enabled); - timeSpanSpinner.setEnabled(enabled); - } - - public TimeSpan getTimeSpan() - { - this.timeSpan = !timeSpanDisableCheckbox.isChecked() ? getTimeSpanFromDialog(this.context, dialog) : new TimeSpan(0); - - return timeSpan; - } - - public boolean getTimeSpanEnabled() - { - return !timeSpanDisableCheckbox.isChecked(); - } - - public String getTimeSpanType() - { - EditText timeSpanEditText = (EditText) dialog.findViewById(R.id.timeSpanEditText); - - if (timeSpanEditText == null) - { - return null; - } - - return (String) timeSpanSpinner.getSelectedItem(); - } - - public int getTimeSpanAmount() - { - Spinner timeSpanSpinner = (Spinner) dialog.findViewById(R.id.timeSpanSpinner); - - if (timeSpanSpinner == null) - { - return -1; - } - - Editable text = timeSpanEditText.getText(); - - String timeSpanAmountString = null; - - if (text != null) - { - timeSpanAmountString = text.toString(); - } - - int timeSpanAmount = 0; - - if (timeSpanAmountString != null && !"".equals(timeSpanAmountString)) - { - timeSpanAmount = Integer.parseInt(timeSpanAmountString); - } - - return timeSpanAmount; - } - - public void setTimeSpanAmount(CharSequence amount) - { - timeSpanEditText.setText(amount); - } - - public void setTimeSpanType(CharSequence type) - { - timeSpanSpinner.setSelection(adapter.getPosition(type)); - } - - public void setTimeSpanDisableText(CharSequence text) - { - timeSpanDisableCheckbox.setText(text); - } - - public void setTimeSpanDisableCheckboxChecked(boolean checked) - { - timeSpanDisableCheckbox.setChecked(checked); - } - - public static TimeSpan getTimeSpanFromDialog(Context context, View dialog) - { - EditText timeSpanEditText = (EditText) dialog.findViewById(R.id.timeSpanEditText); - Spinner timeSpanSpinner = (Spinner) dialog.findViewById(R.id.timeSpanSpinner); - - if (timeSpanEditText == null || timeSpanSpinner == null) - { - return new TimeSpan(-1); - } - - String timeSpanType = (String) timeSpanSpinner.getSelectedItem(); - - Editable text = timeSpanEditText.getText(); - String timeSpanAmountString = null; - - if (text != null) - { - timeSpanAmountString = text.toString(); - } - - int timeSpanAmount = 0; - - if (timeSpanAmountString != null && !"".equals(timeSpanAmountString)) - { - timeSpanAmount = Integer.parseInt(timeSpanAmountString); - } - - return calculateTimeSpan(context, timeSpanType, timeSpanAmount); - } - - public static TimeSpan calculateTimeSpan(Context context, String timeSpanType, int timeSpanAmount) - { - TimeSpan timeSpan = null; - - Resources resources = context.getResources(); - - if (resources.getText(R.string.settings_share_milliseconds).equals(timeSpanType)) - { - timeSpan = new TimeSpan(timeSpanAmount); - } - else if (resources.getText(R.string.settings_share_seconds).equals(timeSpanType)) - { - timeSpan = TimeSpan.create(0, timeSpanAmount); - } - else if (resources.getText(R.string.settings_share_minutes).equals(timeSpanType)) - { - timeSpan = TimeSpan.create(timeSpanAmount, 0); - } - else if (resources.getText(R.string.settings_share_hours).equals(timeSpanType)) - { - timeSpan = TimeSpan.create(timeSpanAmount, 0, 0); - } - else if (resources.getText(R.string.settings_share_days).equals(timeSpanType)) - { - timeSpan = TimeSpan.create(timeSpanAmount, 0, 0, 0); - } - - return timeSpan; - } - - @Override - public void onItemSelected(AdapterView parent, View view, int pos, long id) - { - String timeSpanType = (String) parent.getItemAtPosition(pos); - Editable text = timeSpanEditText.getText(); - if (text == null) - { - return; - } - - String timeSpanAmountString = text.toString(); - - int timeSpanAmount = 0; - - if (timeSpanAmountString != null && !"".equals(timeSpanAmountString)) - { - timeSpanAmount = Integer.parseInt(timeSpanAmountString); - } - - this.timeSpan = calculateTimeSpan(this.context, timeSpanType, timeSpanAmount); - } - - @Override - public void onNothingSelected(AdapterView adapterView) - { - - } -} diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/util/TimeSpanPicker.kt b/ultrasonic/src/main/java/org/moire/ultrasonic/util/TimeSpanPicker.kt new file mode 100644 index 00000000..63566313 --- /dev/null +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/util/TimeSpanPicker.kt @@ -0,0 +1,166 @@ +/* + * TimeSpanPicker.kt + * Copyright (C) 2009-2022 Ultrasonic developers + * + * Distributed under terms of the GNU GPLv3 license. + */ + +package org.moire.ultrasonic.util + +import android.content.Context +import android.util.AttributeSet +import android.view.LayoutInflater +import android.view.View +import android.widget.AdapterView +import android.widget.ArrayAdapter +import android.widget.CheckBox +import android.widget.EditText +import android.widget.LinearLayout +import android.widget.Spinner +import java.util.concurrent.TimeUnit +import org.moire.ultrasonic.R +import timber.log.Timber + +/** + * UI Dialog which allow the User to pick a duration ranging from milliseconds to month + */ +class TimeSpanPicker(private var mContext: Context, attrs: AttributeSet?, defStyle: Int) : + LinearLayout( + mContext, attrs, defStyle + ), + AdapterView.OnItemSelectedListener { + private val timeSpanEditText: EditText + private val timeSpanSpinner: Spinner + private val timeSpanDisableCheckbox: CheckBox + private var mTimeSpan: Long = -1L + private val adapter: ArrayAdapter + private val dialog: View + + constructor(context: Context) : this(context, null) { + this.mContext = context + } + + constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0) { + this.mContext = context + } + + init { + val inflater = mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater + dialog = inflater.inflate(R.layout.time_span_dialog, this, true) + timeSpanEditText = dialog.findViewById(R.id.timeSpanEditText) as EditText + timeSpanEditText.setText("0") + timeSpanSpinner = dialog.findViewById(R.id.timeSpanSpinner) as Spinner + timeSpanDisableCheckbox = + dialog.findViewById(R.id.timeSpanDisableCheckBox) as CheckBox + timeSpanDisableCheckbox.setOnCheckedChangeListener { _, b -> + timeSpanEditText.isEnabled = !b + timeSpanSpinner.isEnabled = !b + } + adapter = ArrayAdapter.createFromResource( + mContext, + R.array.shareExpirationNames, + android.R.layout.simple_spinner_item + ) + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) + timeSpanSpinner.adapter = adapter + timeSpanSpinner.onItemSelectedListener = this + } + + override fun setEnabled(enabled: Boolean) { + timeSpanEditText.isEnabled = enabled + timeSpanSpinner.isEnabled = enabled + } + + fun getTimeSpan(): Long { + return if (!timeSpanDisableCheckbox.isChecked) getTimeSpanFromDialog( + mContext, dialog + ) else -1L + } + + val timeSpanEnabled: Boolean + get() = !timeSpanDisableCheckbox.isChecked + var timeSpanType: String? + get() { + return timeSpanSpinner.selectedItem as String + } + set(type) { + timeSpanSpinner.setSelection(adapter.getPosition(type)) + } + val timeSpanAmount: Int + get() { + val text = timeSpanEditText.text + var timeSpanAmountString: String? = null + if (text != null) { + timeSpanAmountString = text.toString() + } + var timeSpanAmount = 0 + if (timeSpanAmountString != null && "" != timeSpanAmountString) { + timeSpanAmount = timeSpanAmountString.toInt() + } + return timeSpanAmount + } + + fun setTimeSpanAmount(amount: CharSequence?) { + timeSpanEditText.setText(amount) + } + + fun setTimeSpanDisableText(text: CharSequence?) { + timeSpanDisableCheckbox.text = text + } + + fun setTimeSpanDisableCheckboxChecked(checked: Boolean) { + timeSpanDisableCheckbox.isChecked = checked + } + + override fun onItemSelected(parent: AdapterView<*>, view: View, pos: Int, id: Long) { + val timeSpanType = parent.getItemAtPosition(pos) as String + val text = timeSpanEditText.text ?: return + val timeSpanAmountString = text.toString() + var timeSpanAmount = 0L + if ("" != timeSpanAmountString) { + timeSpanAmount = timeSpanAmountString.toLong() + } + mTimeSpan = calculateTimeSpan(mContext, timeSpanType, timeSpanAmount) + } + + override fun onNothingSelected(adapterView: AdapterView<*>?) {} + + companion object { + fun getTimeSpanFromDialog(context: Context, dialog: View): Long { + val timeSpanEditText = dialog.findViewById(R.id.timeSpanEditText) as EditText + val timeSpanSpinner = dialog.findViewById(R.id.timeSpanSpinner) as Spinner + val timeSpanType = timeSpanSpinner.selectedItem as String + Timber.i("SELECTED ITEM: %d", timeSpanSpinner.selectedItemId) + val text = timeSpanEditText.text + val timeSpanAmountString: String? = text?.toString() + var timeSpanAmount = 0L + + if (timeSpanAmountString != null && timeSpanAmountString != "") { + timeSpanAmount = timeSpanAmountString.toLong() + } + + return calculateTimeSpan(context, timeSpanType, timeSpanAmount) + } + + fun calculateTimeSpan( + context: Context, + timeSpanType: String, + timeSpanAmount: Long + ): Long { + val resources = context.resources + + return when (timeSpanType) { + resources.getText(R.string.settings_share_minutes) -> { + TimeUnit.MINUTES.toMillis(timeSpanAmount) + } + resources.getText(R.string.settings_share_hours) -> { + TimeUnit.HOURS.toMillis(timeSpanAmount) + } + resources.getText(R.string.settings_share_days) -> { + TimeUnit.DAYS.toMillis(timeSpanAmount) + } + else -> TimeUnit.MINUTES.toMillis(0L) + } + } + } +} diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/util/TimeSpanPreferenceDialogFragmentCompat.java b/ultrasonic/src/main/java/org/moire/ultrasonic/util/TimeSpanPreferenceDialogFragmentCompat.java deleted file mode 100644 index c5c3eb4f..00000000 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/util/TimeSpanPreferenceDialogFragmentCompat.java +++ /dev/null @@ -1,86 +0,0 @@ -package org.moire.ultrasonic.util; - -import android.content.Context; -import android.view.View; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.preference.DialogPreference; -import androidx.preference.Preference; -import androidx.preference.PreferenceDialogFragmentCompat; -import org.moire.ultrasonic.R; - -import java.util.Locale; -import java.util.regex.Pattern; - -/** - * Created by Joshua Bahnsen on 12/22/13. - */ -public class TimeSpanPreferenceDialogFragmentCompat extends PreferenceDialogFragmentCompat implements DialogPreference.TargetFragment -{ - private static final Pattern COMPILE = Pattern.compile(":"); - Context context; - TimeSpanPicker picker; - - @Override - protected View onCreateDialogView(Context context) { - picker = new TimeSpanPicker(context); - this.context = context; - - picker.setTimeSpanDisableText(this.context.getResources().getString(R.string.no_expiration)); - - Preference preference = getPreference(); - String persisted = preference.getSharedPreferences().getString(preference.getKey(), ""); - - if (!"".equals(persisted)) - { - String[] split = COMPILE.split(persisted); - - if (split.length == 2) - { - String amount = split[0]; - - if ("0".equals(amount) || "".equals(amount)) - { - picker.setTimeSpanDisableCheckboxChecked(true); - } - - picker.setTimeSpanAmount(amount); - picker.setTimeSpanType(split[1]); - } - } - else - { - picker.setTimeSpanDisableCheckboxChecked(true); - } - - return picker; - } - - - @Override - public void onDialogClosed(boolean positiveResult) - { - String persisted = ""; - - if (picker.getTimeSpanEnabled()) - { - int tsAmount = picker.getTimeSpanAmount(); - - if (tsAmount > 0) - { - String tsType = picker.getTimeSpanType(); - - persisted = String.format(Locale.US, "%d:%s", tsAmount, tsType); - } - } - - Preference preference = getPreference(); - preference.getSharedPreferences().edit().putString(preference.getKey(), persisted).apply(); - } - - @Nullable - @Override - public Preference findPreference(@NonNull CharSequence key) { - return getPreference(); - } -} diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/util/TimeSpanPreferenceDialogFragmentCompat.kt b/ultrasonic/src/main/java/org/moire/ultrasonic/util/TimeSpanPreferenceDialogFragmentCompat.kt new file mode 100644 index 00000000..7700a7d7 --- /dev/null +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/util/TimeSpanPreferenceDialogFragmentCompat.kt @@ -0,0 +1,58 @@ +/* + * TimeSpanPreferenceDialogFragmentCompat.kt + * Copyright (C) 2009-2022 Ultrasonic developers + * + * Distributed under terms of the GNU GPLv3 license. + */ + +package org.moire.ultrasonic.util + +import android.content.Context +import android.view.View +import androidx.preference.PreferenceDialogFragmentCompat +import androidx.preference.DialogPreference.TargetFragment +import androidx.preference.Preference +import org.moire.ultrasonic.R +import java.util.Locale + +class TimeSpanPreferenceDialogFragmentCompat : PreferenceDialogFragmentCompat(), TargetFragment { + var picker: TimeSpanPicker? = null + + override fun onCreateDialogView(context: Context): View? { + picker = TimeSpanPicker(context) + picker!!.setTimeSpanDisableText(requireContext().resources.getString(R.string.no_expiration)) + val persisted = Settings.defaultShareExpiration + if ("" != persisted) { + val split = Settings.COLON_PATTERN.split(persisted) + if (split.size == 2) { + val amount = split[0] + if ("0" == amount || "" == amount) { + picker!!.setTimeSpanDisableCheckboxChecked(true) + } + picker!!.setTimeSpanAmount(amount) + picker!!.timeSpanType = split[1] + } + } else { + picker!!.setTimeSpanDisableCheckboxChecked(true) + } + return picker + } + + override fun onDialogClosed(positiveResult: Boolean) { + var persisted = "" + if (picker!!.timeSpanEnabled) { + val tsAmount = picker!!.timeSpanAmount + if (tsAmount > 0) { + val tsType = picker!!.timeSpanType + persisted = String.format(Locale.US, "%d:%s", tsAmount, tsType) + } + } + val preference: Preference = preference + preference.sharedPreferences!!.edit().putString(preference.key, persisted).apply() + } + + @Suppress("UNCHECKED_CAST") + override fun findPreference(p0: CharSequence): T { + return preference as T + } +} \ No newline at end of file diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/activity/NavigationActivity.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/activity/NavigationActivity.kt index a259a51f..ae29ad70 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/activity/NavigationActivity.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/activity/NavigationActivity.kt @@ -192,6 +192,9 @@ class NavigationActivity : AppCompatActivity() { showWelcomeDialog() } + // Ask for permission to send notifications + Util.ensurePermissionToPostNotification(this) + RxBus.dismissNowPlayingCommandObservable.subscribe { nowPlayingHidden = true hideNowPlaying() diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/EqualizerFragment.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/EqualizerFragment.kt index e1d48e79..b27750a2 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/EqualizerFragment.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/EqualizerFragment.kt @@ -28,6 +28,7 @@ import org.moire.ultrasonic.R import org.moire.ultrasonic.audiofx.EqualizerController import org.moire.ultrasonic.fragment.FragmentTitle.Companion.setTitle import org.moire.ultrasonic.util.Util.applyTheme +import org.w3c.dom.Text import timber.log.Timber /** @@ -168,10 +169,9 @@ class EqualizerFragment : Fragment() { val bandBar = LayoutInflater.from(context) .inflate(R.layout.equalizer_bar, equalizerLayout, false) - val freqTextView: TextView = - bandBar.findViewById(R.id.equalizer_frequency) as TextView - val levelTextView = bandBar.findViewById(R.id.equalizer_level) as TextView - val bar = bandBar.findViewById(R.id.equalizer_bar) as SeekBar + val freqTextView: TextView = bandBar.findViewById(R.id.equalizer_frequency) + val levelTextView: TextView = bandBar.findViewById(R.id.equalizer_level) + val bar: SeekBar = bandBar.findViewById(R.id.equalizer_bar) val range = equalizer!!.getBandFreqRange(band) diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/SettingsFragment.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/SettingsFragment.kt index c099457b..75973908 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/SettingsFragment.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/SettingsFragment.kt @@ -247,18 +247,15 @@ class SettingsFragment : } override fun onDisplayPreferenceDialog(preference: Preference) { - var dialogFragment: DialogFragment? = null if (preference is TimeSpanPreference) { - dialogFragment = TimeSpanPreferenceDialogFragmentCompat() + val 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" + "androidx.preference.PreferenceFragment.DIALOG" ) } else { super.onDisplayPreferenceDialog(preference) diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/legacy/SharesFragment.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/legacy/SharesFragment.kt index 7753d079..e53b6f4e 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/legacy/SharesFragment.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/legacy/SharesFragment.kt @@ -40,7 +40,6 @@ import org.moire.ultrasonic.util.BackgroundTask import org.moire.ultrasonic.util.CancellationToken import org.moire.ultrasonic.util.FragmentBackgroundTask import org.moire.ultrasonic.util.LoadingTask -import org.moire.ultrasonic.util.TimeSpan import org.moire.ultrasonic.util.TimeSpanPicker import org.moire.ultrasonic.util.Util import org.moire.ultrasonic.view.ShareAdapter @@ -79,16 +78,16 @@ class SharesFragment : Fragment() { sharesListView = view.findViewById(R.id.select_share_list) refreshSharesListView!!.setOnRefreshListener { load(true) } emptyTextView = view.findViewById(R.id.select_share_empty) - sharesListView!!.onItemClickListener = AdapterView.OnItemClickListener { - parent, _, position, _ -> - val share = parent.getItemAtPosition(position) as Share + sharesListView!!.onItemClickListener = + AdapterView.OnItemClickListener { parent, _, position, _ -> + val share = parent.getItemAtPosition(position) as Share - val action = SharesFragmentDirections.sharesToTrackCollection( - shareId = share.id, - shareName = share.name - ) - findNavController().navigate(action) - } + val action = SharesFragmentDirections.sharesToTrackCollection( + shareId = share.id, + shareName = share.name + ) + findNavController().navigate(action) + } registerForContextMenu(sharesListView!!) FragmentTitle.setTitle(this, R.string.button_bar_shares) load(false) @@ -273,19 +272,19 @@ class SharesFragment : Fragment() { Entry Count: ${share.getEntries().size} Visit Count: ${share.visitCount} """.trimIndent() + - ( - if (share.created == null) "" else """ + ( + if (share.created == null) "" else """ Creation Date: ${share.created!!.replace('T', ' ')} """.trimIndent() - ) + - ( - if (share.lastVisited == null) "" else """ + ) + + ( + if (share.lastVisited == null) "" else """ Last Visited Date: ${share.lastVisited!!.replace('T', ' ')} """.trimIndent() - ) + - if (share.expires == null) "" else """ + ) + + if (share.expires == null) "" else """ Expiration Date: ${share.expires!!.replace('T', ' ')} """.trimIndent() @@ -321,9 +320,9 @@ class SharesFragment : Fragment() { object : LoadingTask(activity, refreshSharesListView, cancellationToken) { @Throws(Throwable::class) override fun doInBackground(): Any? { - var millis = timeSpanPicker.timeSpan.totalMilliseconds + var millis = timeSpanPicker.getTimeSpan() if (millis > 0) { - millis = TimeSpan.getCurrentTime().add(millis).totalMilliseconds + millis += System.currentTimeMillis() } val shareDescriptionText = shareDescription.text val description = shareDescriptionText?.toString() @@ -341,19 +340,22 @@ class SharesFragment : Fragment() { } override fun error(error: Throwable) { - val msg: String - msg = if (error is OfflineException || error is ApiNotSupportedException) { - getErrorMessage( - error - ) - } else { - String.format( - Locale.ROOT, - "%s %s", - resources.getString(R.string.playlist_updated_info_error, share.name), - getErrorMessage(error) - ) - } + val msg: String = + if (error is OfflineException || error is ApiNotSupportedException) { + getErrorMessage( + error + ) + } else { + String.format( + Locale.ROOT, + "%s %s", + resources.getString( + R.string.playlist_updated_info_error, + share.name + ), + getErrorMessage(error) + ) + } Util.toast(context, msg, false) } }.execute() diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/imageloader/ArtworkBitmapLoader.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/imageloader/ArtworkBitmapLoader.kt index 120b70bf..be92785b 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/imageloader/ArtworkBitmapLoader.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/imageloader/ArtworkBitmapLoader.kt @@ -7,9 +7,11 @@ package org.moire.ultrasonic.imageloader +import android.annotation.SuppressLint import android.graphics.Bitmap import android.graphics.BitmapFactory import android.net.Uri +import androidx.media3.common.util.UnstableApi import androidx.media3.session.BitmapLoader import com.google.common.util.concurrent.ListenableFuture import com.google.common.util.concurrent.ListeningExecutorService @@ -19,6 +21,7 @@ import java.util.concurrent.Executors import org.koin.core.component.KoinComponent import org.koin.core.component.inject +@SuppressLint("UnsafeOptInUsageError") class ArtworkBitmapLoader : BitmapLoader, KoinComponent { private val imageLoader: ImageLoader by inject() diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/MediaNotificationProvider.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/MediaNotificationProvider.kt index 502ca0fa..88dd6946 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/MediaNotificationProvider.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/MediaNotificationProvider.kt @@ -7,11 +7,11 @@ package org.moire.ultrasonic.playback +import android.annotation.SuppressLint import android.content.Context import androidx.core.app.NotificationCompat import androidx.media3.common.HeartRating import androidx.media3.common.Player -import androidx.media3.common.util.UnstableApi import androidx.media3.session.CommandButton import androidx.media3.session.DefaultMediaNotificationProvider import androidx.media3.session.MediaNotification @@ -24,7 +24,7 @@ import org.moire.ultrasonic.imageloader.ArtworkBitmapLoader import org.moire.ultrasonic.service.MediaPlayerController import org.moire.ultrasonic.util.toTrack -@UnstableApi +@SuppressLint("UnsafeOptInUsageError") class MediaNotificationProvider(context: Context) : DefaultMediaNotificationProvider(context, ArtworkBitmapLoader()), KoinComponent { diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/PlaybackService.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/PlaybackService.kt index 8c2eb083..aaebe797 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/PlaybackService.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/playback/PlaybackService.kt @@ -6,6 +6,7 @@ */ package org.moire.ultrasonic.playback +import android.annotation.SuppressLint import android.app.PendingIntent import android.content.Intent import android.os.Build @@ -39,6 +40,7 @@ import org.moire.ultrasonic.util.Util import org.moire.ultrasonic.util.toTrack import timber.log.Timber +@SuppressLint("UnsafeOptInUsageError") class PlaybackService : MediaLibraryService(), KoinComponent { private lateinit var player: ExoPlayer private lateinit var mediaLibrarySession: MediaLibrarySession @@ -84,7 +86,12 @@ class PlaybackService : MediaLibraryService(), KoinComponent { mediaLibrarySession.release() rxBusSubscription.dispose() isStarted = false - stopForeground(true) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + stopForeground(STOP_FOREGROUND_REMOVE) + } else { + @Suppress("DEPRECATION") + stopForeground(true) + } stopSelf() } diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/JukeboxMediaPlayer.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/JukeboxMediaPlayer.kt index f95e2e09..20024844 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/JukeboxMediaPlayer.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/JukeboxMediaPlayer.kt @@ -39,7 +39,6 @@ import androidx.media3.common.Timeline import androidx.media3.common.TrackSelectionParameters import androidx.media3.common.VideoSize import androidx.media3.common.text.CueGroup -import androidx.media3.common.util.Util import androidx.media3.session.MediaSession import com.google.common.collect.ImmutableList import com.google.common.util.concurrent.ListenableFuture @@ -76,6 +75,7 @@ private const val SEEK_START_AFTER_SECONDS = 5 * TODO: Minimize status updates. */ @Suppress("TooManyFunctions") +@SuppressLint("UnsafeOptInUsageError") class JukeboxMediaPlayer : JukeboxUnimplementedFunctions(), Player { private val tasks = TaskQueue() private val executorService = Executors.newSingleThreadScheduledExecutor() @@ -119,7 +119,7 @@ class JukeboxMediaPlayer : JukeboxUnimplementedFunctions(), Player { JukeboxNotificationActionFactory() ) {} - if (Util.SDK_INT >= 29) { + if (Build.VERSION.SDK_INT >= 29) { startForeground( notification.notificationId, notification.notification, @@ -140,7 +140,12 @@ class JukeboxMediaPlayer : JukeboxUnimplementedFunctions(), Player { val extras = intent.extras if ((extras != null) && extras.containsKey(Intent.EXTRA_KEY_EVENT)) { - val event = extras.getParcelable(Intent.EXTRA_KEY_EVENT) + val event = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + extras.getParcelable(Intent.EXTRA_KEY_EVENT, KeyEvent::class.java) + } else { + @Suppress("DEPRECATION") + extras.getParcelable(Intent.EXTRA_KEY_EVENT) + } when (event?.keyCode) { KEYCODE_MEDIA_PLAY -> play() KEYCODE_MEDIA_PAUSE -> stop() diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/JukeboxNotificationActionFactory.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/JukeboxNotificationActionFactory.kt index 26930a4e..73bad2cb 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/JukeboxNotificationActionFactory.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/JukeboxNotificationActionFactory.kt @@ -7,6 +7,7 @@ package org.moire.ultrasonic.service +import android.annotation.SuppressLint import android.app.PendingIntent import android.content.ComponentName import android.content.Intent @@ -25,6 +26,7 @@ import org.moire.ultrasonic.app.UApp * This class creates Intents and Actions to be used with the Media Notification * of the Jukebox Service */ +@SuppressLint("UnsafeOptInUsageError") class JukeboxNotificationActionFactory : MediaNotification.ActionFactory { override fun createMediaAction( mediaSession: MediaSession, diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/JukeboxUnimplementedFunctions.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/JukeboxUnimplementedFunctions.kt index 82e38d17..7ac1b543 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/JukeboxUnimplementedFunctions.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/JukeboxUnimplementedFunctions.kt @@ -1,5 +1,5 @@ /* - * JukeboxUnimplemented.kt + * JukeboxUnimplementedFunctions.kt * Copyright (C) 2009-2022 Ultrasonic developers * * Distributed under terms of the GNU GPLv3 license. @@ -7,6 +7,7 @@ package org.moire.ultrasonic.service +import android.annotation.SuppressLint import android.app.Service import android.view.Surface import android.view.SurfaceHolder @@ -24,6 +25,7 @@ import androidx.media3.common.Tracks * of the crowded Player interface, so the JukeboxMediaPlayer class can be a bit clearer. */ @Suppress("TooManyFunctions") +@SuppressLint("UnsafeOptInUsageError") abstract class JukeboxUnimplementedFunctions : Service(), Player { override fun setMediaItems(mediaItems: MutableList) { diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/PlaylistTimeline.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/PlaylistTimeline.kt index 5de98ca6..fb32dd84 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/PlaylistTimeline.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/PlaylistTimeline.kt @@ -7,6 +7,7 @@ package org.moire.ultrasonic.service +import android.annotation.SuppressLint import androidx.media3.common.C import androidx.media3.common.MediaItem import androidx.media3.common.Player @@ -20,6 +21,7 @@ import java.util.Arrays * This class wraps a simple playlist provided as List * to be usable as a Media3 Timeline. */ +@SuppressLint("UnsafeOptInUsageError") class PlaylistTimeline @JvmOverloads constructor( mediaItems: List, shuffledIndices: IntArray = createUnshuffledIndices( diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/ShareHandler.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/ShareHandler.kt index f60ef15f..64b9c4ec 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/ShareHandler.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/ShareHandler.kt @@ -30,7 +30,6 @@ import org.moire.ultrasonic.util.CancellationToken import org.moire.ultrasonic.util.FragmentBackgroundTask import org.moire.ultrasonic.util.Settings import org.moire.ultrasonic.util.ShareDetails -import org.moire.ultrasonic.util.TimeSpan import org.moire.ultrasonic.util.TimeSpanPicker import org.moire.ultrasonic.util.Util.ifNotNull @@ -145,9 +144,8 @@ class ShareHandler(val context: Context) { showDialog(fragment, shareDetails, swipe, cancellationToken, additionalId) } else { shareDetails.Description = Settings.defaultShareDescription - shareDetails.Expiration = TimeSpan.getCurrentTime().add( + shareDetails.Expiration = System.currentTimeMillis() + Settings.defaultShareExpirationInMillis - ).totalMilliseconds share(fragment, shareDetails, swipe, cancellationToken, additionalId) } } @@ -165,11 +163,11 @@ class ShareHandler(val context: Context) { shareDescription = layout.findViewById(R.id.share_description) as EditText hideDialogCheckBox = layout.findViewById(R.id.hide_dialog) as CheckBox shareOnServerCheckBox = layout.findViewById(R.id.share_on_server) as CheckBox - noExpirationCheckBox = layout.findViewById( - R.id.timeSpanDisableCheckBox - ) as CheckBox saveAsDefaultsCheckBox = layout.findViewById(R.id.save_as_defaults) as CheckBox timeSpanPicker = layout.findViewById(R.id.date_picker) as TimeSpanPicker + noExpirationCheckBox = timeSpanPicker!!.findViewById( + R.id.timeSpanDisableCheckBox + ) as CheckBox textViewComment = layout.findViewById(R.id.textViewComment) as TextView textViewExpiration = layout.findViewById(R.id.textViewExpiration) as TextView } @@ -191,9 +189,8 @@ class ShareHandler(val context: Context) { builder.setPositiveButton(R.string.menu_share) { _, _ -> if (!noExpirationCheckBox!!.isChecked) { - val timeSpan: TimeSpan = timeSpanPicker!!.timeSpan - val now = TimeSpan.getCurrentTime() - shareDetails.Expiration = now.add(timeSpan).totalMilliseconds + val timeSpan: Long = timeSpanPicker!!.getTimeSpan() + shareDetails.Expiration = System.currentTimeMillis() + timeSpan } shareDetails.Description = shareDescription!!.text.toString() @@ -204,7 +201,7 @@ class ShareHandler(val context: Context) { } if (saveAsDefaultsCheckBox!!.isChecked) { - val timeSpanType: String = timeSpanPicker!!.timeSpanType + val timeSpanType: String = timeSpanPicker!!.timeSpanType!! val timeSpanAmount: Int = timeSpanPicker!!.timeSpanAmount Settings.defaultShareExpiration = if (!noExpirationCheckBox!!.isChecked && timeSpanAmount > 0) @@ -240,7 +237,7 @@ class ShareHandler(val context: Context) { noExpirationCheckBox!!.isChecked = false timeSpanPicker!!.isEnabled = true timeSpanPicker!!.setTimeSpanAmount(timeSpanAmount.toString()) - timeSpanPicker!!.setTimeSpanType(timeSpanType) + timeSpanPicker!!.timeSpanType = timeSpanType } else { noExpirationCheckBox!!.isChecked = true timeSpanPicker!!.isEnabled = false diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/Settings.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/Settings.kt index 5eca5beb..09886b7d 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/Settings.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/Settings.kt @@ -18,7 +18,7 @@ import org.moire.ultrasonic.app.UApp * Contains convenience functions for reading and writing preferences */ object Settings { - private val PATTERN = Pattern.compile(":") + @JvmStatic var theme by StringSetting( @@ -229,15 +229,12 @@ object Settings { val defaultShareExpirationInMillis: Long get() { - val preference = - preferences.getString(getKey(R.string.setting_key_default_share_expiration), "0")!! - val split = PATTERN.split(preference) + val preference = defaultShareExpiration + val split = COLON_PATTERN.split(preference) if (split.size == 2) { - val timeSpanAmount = split[0].toInt() + val timeSpanAmount = split[0].toLong() val timeSpanType = split[1] - val timeSpan = - TimeSpanPicker.calculateTimeSpan(appContext, timeSpanType, timeSpanAmount) - return timeSpan.totalMilliseconds + return TimeSpanPicker.calculateTimeSpan(appContext, timeSpanType, timeSpanAmount) } return 0 } @@ -292,4 +289,6 @@ object Settings { private val appContext: Context get() = UApp.applicationContext() + + val COLON_PATTERN = Pattern.compile(":") } diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/StorageFile.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/StorageFile.kt index 714c6df9..d97a538c 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/StorageFile.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/StorageFile.kt @@ -1,6 +1,6 @@ /* * StorageFile.kt - * Copyright (C) 2009-2021 Ultrasonic developers + * Copyright (C) 2009-2022 Ultrasonic developers * * Distributed under terms of the GNU GPLv3 license. */ @@ -91,8 +91,10 @@ class StorageFile( uri, mode ) - return descriptor?.createOutputStream() + val stream = descriptor?.createOutputStream() ?: throw IOException("Couldn't retrieve OutputStream") + descriptor.close() + return stream } override fun getFileInputStream(): InputStream { diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/Util.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/Util.kt index 41a5c6e2..511039f5 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/Util.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/Util.kt @@ -7,6 +7,7 @@ package org.moire.ultrasonic.util +import android.Manifest.permission.POST_NOTIFICATIONS import android.annotation.SuppressLint import android.app.Activity import android.app.PendingIntent @@ -32,7 +33,10 @@ import android.util.TypedValue import android.view.Gravity import android.view.inputmethod.InputMethodManager import android.widget.Toast +import androidx.activity.result.contract.ActivityResultContracts import androidx.annotation.AnyRes +import androidx.appcompat.app.AppCompatActivity +import androidx.core.content.ContextCompat import androidx.media3.common.C import androidx.media3.common.MediaItem import androidx.media3.common.Player @@ -508,6 +512,25 @@ object Util { } } + fun ensurePermissionToPostNotification(fragment: AppCompatActivity) { + if (ContextCompat.checkSelfPermission( + applicationContext(), + POST_NOTIFICATIONS, + ) != PackageManager.PERMISSION_GRANTED && + Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU + ) { + + val requestPermissionLauncher = + fragment.registerForActivityResult(ActivityResultContracts.RequestPermission()) { + if (!it) { + toast(applicationContext(), R.string.notification_permission_required) + } + } + + requestPermissionLauncher.launch(POST_NOTIFICATIONS) + } + } + @JvmStatic @Suppress("DEPRECATION") fun getVersionName(context: Context): String? { diff --git a/ultrasonic/src/main/res/layout/equalizer_bar.xml b/ultrasonic/src/main/res/layout/equalizer_bar.xml index b76d8e75..6e00cf83 100644 --- a/ultrasonic/src/main/res/layout/equalizer_bar.xml +++ b/ultrasonic/src/main/res/layout/equalizer_bar.xml @@ -1,11 +1,18 @@ + + diff --git a/ultrasonic/src/main/res/values-cs/strings.xml b/ultrasonic/src/main/res/values-cs/strings.xml index 58b67d1b..9602c7e8 100644 --- a/ultrasonic/src/main/res/values-cs/strings.xml +++ b/ultrasonic/src/main/res/values-cs/strings.xml @@ -292,8 +292,6 @@ Na serveru nejsou žádná sdílení Smazaná sdílení %s Chyba vymazání sdílení %s - Milisekund - Sekund Minut Hodin Dní diff --git a/ultrasonic/src/main/res/values-de/strings.xml b/ultrasonic/src/main/res/values-de/strings.xml index 6d93e4c6..77c79a7b 100644 --- a/ultrasonic/src/main/res/values-de/strings.xml +++ b/ultrasonic/src/main/res/values-de/strings.xml @@ -351,8 +351,6 @@ Keine Freigaben auf dem Server verfügbar Freigabe %s gelöscht Löschen der Freigabe %s fehlgeschlagen - Millisekunden - Sekunden Minuten Stunden Tage diff --git a/ultrasonic/src/main/res/values-es/strings.xml b/ultrasonic/src/main/res/values-es/strings.xml index 77256758..f5c74254 100644 --- a/ultrasonic/src/main/res/values-es/strings.xml +++ b/ultrasonic/src/main/res/values-es/strings.xml @@ -351,8 +351,6 @@ No hay compartidos disponibles en el servidor Eliminar compartido %s Fallo al eliminar compartido %s - Milisegundos - segundos Minutos Horas Días diff --git a/ultrasonic/src/main/res/values-fr/strings.xml b/ultrasonic/src/main/res/values-fr/strings.xml index 53fae809..a42f2062 100644 --- a/ultrasonic/src/main/res/values-fr/strings.xml +++ b/ultrasonic/src/main/res/values-fr/strings.xml @@ -334,8 +334,6 @@ Pas de partages disponibles sur le serveur Partage supprimé %s Échec de la suppression du partage %s - Millisecondes - Secondes Minutes Heures Jours diff --git a/ultrasonic/src/main/res/values-hu/strings.xml b/ultrasonic/src/main/res/values-hu/strings.xml index 071f982f..a0d91a28 100644 --- a/ultrasonic/src/main/res/values-hu/strings.xml +++ b/ultrasonic/src/main/res/values-hu/strings.xml @@ -299,8 +299,6 @@ Nincs mentett megosztás a kiszolgálón. %s megosztás törölve %s megosztás törlése sikertelen! - Ezredmásodperc - Másodperc Perc Óra Nap diff --git a/ultrasonic/src/main/res/values-it/strings.xml b/ultrasonic/src/main/res/values-it/strings.xml index fdc47882..a9896b3d 100644 --- a/ultrasonic/src/main/res/values-it/strings.xml +++ b/ultrasonic/src/main/res/values-it/strings.xml @@ -265,8 +265,6 @@ Nessuna scheda SD Descrizione Predefinita di Condivisione Pulisci Playlist - Millisecondi - Secondi Minuti Ore Giorni diff --git a/ultrasonic/src/main/res/values-nl/strings.xml b/ultrasonic/src/main/res/values-nl/strings.xml index fb9dbc2c..9402b488 100644 --- a/ultrasonic/src/main/res/values-nl/strings.xml +++ b/ultrasonic/src/main/res/values-nl/strings.xml @@ -350,8 +350,6 @@ Geen delingen beschikbaar op server Deling %s verwijderd Kan deling %s niet verwijderen - Milliseconden - Seconden Minuten Uur Dagen diff --git a/ultrasonic/src/main/res/values-pl/strings.xml b/ultrasonic/src/main/res/values-pl/strings.xml index 37ece52e..0bfc178f 100644 --- a/ultrasonic/src/main/res/values-pl/strings.xml +++ b/ultrasonic/src/main/res/values-pl/strings.xml @@ -288,8 +288,6 @@ Brak udostępnień na serwerze Usunięto udostępnienie %s Nieudane usunięcie udostępnienia %s - Milisekundy - Sekundy Minuty Godziny Dni diff --git a/ultrasonic/src/main/res/values-pt-rBR/strings.xml b/ultrasonic/src/main/res/values-pt-rBR/strings.xml index dec7f516..8e03824e 100644 --- a/ultrasonic/src/main/res/values-pt-rBR/strings.xml +++ b/ultrasonic/src/main/res/values-pt-rBR/strings.xml @@ -341,8 +341,6 @@ Nenhum compartilhamento disponível no servidor Compartilhamento %s excluído Falha ao excluir o compartilhamento %s - Milisegundos - Segundos Minutos Horas Dias diff --git a/ultrasonic/src/main/res/values-pt/strings.xml b/ultrasonic/src/main/res/values-pt/strings.xml index 03f44c0c..1b40d37a 100644 --- a/ultrasonic/src/main/res/values-pt/strings.xml +++ b/ultrasonic/src/main/res/values-pt/strings.xml @@ -288,8 +288,6 @@ Nenhum compartilhamento disponível no servidor Compartilhamento %s apagado Falha ao apagar o compartilhamento %s - Milisegundos - Segundos Minutos Horas Dias diff --git a/ultrasonic/src/main/res/values-ru/strings.xml b/ultrasonic/src/main/res/values-ru/strings.xml index b2c8e258..eb4ac301 100644 --- a/ultrasonic/src/main/res/values-ru/strings.xml +++ b/ultrasonic/src/main/res/values-ru/strings.xml @@ -318,8 +318,6 @@ Нет доступных ресурсов на сервере Удалить общий ресурс %s Не удалось удалить общий ресурс %s - Миллисекунды - Секунды Минуты Часы Дни diff --git a/ultrasonic/src/main/res/values-zh-rCN/strings.xml b/ultrasonic/src/main/res/values-zh-rCN/strings.xml index c4c6ced7..3d30191e 100644 --- a/ultrasonic/src/main/res/values-zh-rCN/strings.xml +++ b/ultrasonic/src/main/res/values-zh-rCN/strings.xml @@ -318,8 +318,6 @@ 服务器上没有可用的共享 删除分享 %s 删除分享失败 %s - 毫秒 - 分钟 小时 diff --git a/ultrasonic/src/main/res/values/arrays.xml b/ultrasonic/src/main/res/values/arrays.xml index bfa8b643..93ae1e75 100644 --- a/ultrasonic/src/main/res/values/arrays.xml +++ b/ultrasonic/src/main/res/values/arrays.xml @@ -218,8 +218,6 @@ @string/settings.search_500 - @string/settings.share_milliseconds - @string/settings.share_seconds @string/settings.share_minutes @string/settings.share_hours @string/settings.share_days diff --git a/ultrasonic/src/main/res/values/strings.xml b/ultrasonic/src/main/res/values/strings.xml index e74adfcd..d29e31ff 100644 --- a/ultrasonic/src/main/res/values/strings.xml +++ b/ultrasonic/src/main/res/values/strings.xml @@ -353,8 +353,6 @@ No shares available on server Deleted share %s Failed to delete share %s - Milliseconds - Seconds Minutes Hours Days @@ -383,7 +381,7 @@ Delete files Deleted log files. Downloading media in the background… - + Notifications are required for media playback. You can grant the permission anytime in the Android settings. Configured servers Are you sure you want to delete the server? Editing server