mirror of
https://gitlab.com/ultrasonic/ultrasonic.git
synced 2025-04-15 08:50:35 +03:00
Update dependency org.jetbrains.kotlinx:kotlinx-coroutines-guava to v1.6.4
This commit is contained in:
parent
008a07b438
commit
2b7a3b0488
@ -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"
|
||||
|
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<issues format="6" by="lint 7.2.2" type="baseline" client="gradle" dependencies="false" name="AGP (7.2.2)" variant="all" version="7.2.2">
|
||||
<issues format="6" by="lint 7.3.0" type="baseline" client="gradle" dependencies="false" name="AGP (7.3.0)" variant="all" version="7.3.0">
|
||||
|
||||
<issue
|
||||
id="PluralsCandidate"
|
||||
@ -737,7 +737,7 @@
|
||||
errorLine2=" ~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/equalizer_bar.xml"
|
||||
line="19"
|
||||
line="26"
|
||||
column="13"/>
|
||||
</issue>
|
||||
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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<CharSequence> 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)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
@ -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<CharSequence>
|
||||
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<View>(R.id.timeSpanEditText) as EditText
|
||||
timeSpanEditText.setText("0")
|
||||
timeSpanSpinner = dialog.findViewById<View>(R.id.timeSpanSpinner) as Spinner
|
||||
timeSpanDisableCheckbox =
|
||||
dialog.findViewById<View>(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<View>(R.id.timeSpanEditText) as EditText
|
||||
val timeSpanSpinner = dialog.findViewById<View>(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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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 <T : Preference?> findPreference(p0: CharSequence): T {
|
||||
return preference as T
|
||||
}
|
||||
}
|
@ -192,6 +192,9 @@ class NavigationActivity : AppCompatActivity() {
|
||||
showWelcomeDialog()
|
||||
}
|
||||
|
||||
// Ask for permission to send notifications
|
||||
Util.ensurePermissionToPostNotification(this)
|
||||
|
||||
RxBus.dismissNowPlayingCommandObservable.subscribe {
|
||||
nowPlayingHidden = true
|
||||
hideNowPlaying()
|
||||
|
@ -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<View>(R.id.equalizer_frequency) as TextView
|
||||
val levelTextView = bandBar.findViewById<View>(R.id.equalizer_level) as TextView
|
||||
val bar = bandBar.findViewById<View>(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)
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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<Any?>(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()
|
||||
|
@ -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()
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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()
|
||||
}
|
||||
|
||||
|
@ -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<KeyEvent>(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<KeyEvent>(Intent.EXTRA_KEY_EVENT)
|
||||
}
|
||||
when (event?.keyCode) {
|
||||
KEYCODE_MEDIA_PLAY -> play()
|
||||
KEYCODE_MEDIA_PAUSE -> stop()
|
||||
|
@ -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,
|
||||
|
@ -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<MediaItem>) {
|
||||
|
@ -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<MediaItem>
|
||||
* to be usable as a Media3 Timeline.
|
||||
*/
|
||||
@SuppressLint("UnsafeOptInUsageError")
|
||||
class PlaylistTimeline @JvmOverloads constructor(
|
||||
mediaItems: List<MediaItem>,
|
||||
shuffledIndices: IntArray = createUnshuffledIndices(
|
||||
|
@ -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<View>(R.id.share_description) as EditText
|
||||
hideDialogCheckBox = layout.findViewById<View>(R.id.hide_dialog) as CheckBox
|
||||
shareOnServerCheckBox = layout.findViewById<View>(R.id.share_on_server) as CheckBox
|
||||
noExpirationCheckBox = layout.findViewById<View>(
|
||||
R.id.timeSpanDisableCheckBox
|
||||
) as CheckBox
|
||||
saveAsDefaultsCheckBox = layout.findViewById<View>(R.id.save_as_defaults) as CheckBox
|
||||
timeSpanPicker = layout.findViewById<View>(R.id.date_picker) as TimeSpanPicker
|
||||
noExpirationCheckBox = timeSpanPicker!!.findViewById<View>(
|
||||
R.id.timeSpanDisableCheckBox
|
||||
) as CheckBox
|
||||
textViewComment = layout.findViewById<View>(R.id.textViewComment) as TextView
|
||||
textViewExpiration = layout.findViewById<View>(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
|
||||
|
@ -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(":")
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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? {
|
||||
|
@ -1,11 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ equalizer_bar.xml
|
||||
~ Copyright (C) 2009-2022 Ultrasonic developers
|
||||
~
|
||||
~ Distributed under terms of the GNU GPLv3 license.
|
||||
-->
|
||||
|
||||
<RelativeLayout xmlns:a="http://schemas.android.com/apk/res/android"
|
||||
a:orientation="vertical"
|
||||
a:layout_width="fill_parent"
|
||||
a:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
a:id="@+id/equalizer.frequency"
|
||||
a:id="@+id/equalizer_frequency"
|
||||
a:textSize="12sp"
|
||||
a:textColor="#c0c0c0"
|
||||
a:layout_width="wrap_content"
|
||||
@ -15,7 +22,7 @@
|
||||
/>
|
||||
|
||||
<TextView
|
||||
a:id="@+id/equalizer.level"
|
||||
a:id="@+id/equalizer_level"
|
||||
a:text="0 dB"
|
||||
a:textSize="12sp"
|
||||
a:textColor="#c0c0c0"
|
||||
@ -24,14 +31,14 @@
|
||||
a:layout_height="wrap_content"
|
||||
a:layout_marginTop="8dp"
|
||||
a:layout_alignParentRight="true"
|
||||
a:layout_toEndOf="@+id/equalizer.frequency"
|
||||
a:layout_toEndOf="@+id/equalizer_frequency"
|
||||
/>
|
||||
|
||||
<SeekBar
|
||||
a:id="@+id/equalizer.bar"
|
||||
a:id="@+id/equalizer_bar"
|
||||
a:layout_width="fill_parent"
|
||||
a:layout_height="wrap_content"
|
||||
a:layout_below="@+id/equalizer.frequency"
|
||||
a:layout_below="@+id/equalizer_frequency"
|
||||
/>
|
||||
|
||||
|
||||
|
@ -292,8 +292,6 @@
|
||||
<string name="select_share.empty">Na serveru nejsou žádná sdílení</string>
|
||||
<string name="menu_deleted_share">Smazaná sdílení %s</string>
|
||||
<string name="menu_deleted_share_error">Chyba vymazání sdílení %s</string>
|
||||
<string name="settings.share_milliseconds">Milisekund</string>
|
||||
<string name="settings.share_seconds">Sekund</string>
|
||||
<string name="settings.share_minutes">Minut</string>
|
||||
<string name="settings.share_hours">Hodin</string>
|
||||
<string name="settings.share_days">Dní</string>
|
||||
|
@ -351,8 +351,6 @@
|
||||
<string name="select_share.empty">Keine Freigaben auf dem Server verfügbar</string>
|
||||
<string name="menu_deleted_share">Freigabe %s gelöscht</string>
|
||||
<string name="menu_deleted_share_error">Löschen der Freigabe %s fehlgeschlagen</string>
|
||||
<string name="settings.share_milliseconds">Millisekunden</string>
|
||||
<string name="settings.share_seconds">Sekunden</string>
|
||||
<string name="settings.share_minutes">Minuten</string>
|
||||
<string name="settings.share_hours">Stunden</string>
|
||||
<string name="settings.share_days">Tage</string>
|
||||
|
@ -351,8 +351,6 @@
|
||||
<string name="select_share.empty">No hay compartidos disponibles en el servidor</string>
|
||||
<string name="menu_deleted_share">Eliminar compartido %s</string>
|
||||
<string name="menu_deleted_share_error">Fallo al eliminar compartido %s</string>
|
||||
<string name="settings.share_milliseconds">Milisegundos</string>
|
||||
<string name="settings.share_seconds">segundos</string>
|
||||
<string name="settings.share_minutes">Minutos</string>
|
||||
<string name="settings.share_hours">Horas</string>
|
||||
<string name="settings.share_days">Días</string>
|
||||
|
@ -334,8 +334,6 @@
|
||||
<string name="select_share.empty">Pas de partages disponibles sur le serveur</string>
|
||||
<string name="menu_deleted_share">Partage supprimé %s</string>
|
||||
<string name="menu_deleted_share_error">Échec de la suppression du partage %s</string>
|
||||
<string name="settings.share_milliseconds">Millisecondes</string>
|
||||
<string name="settings.share_seconds">Secondes</string>
|
||||
<string name="settings.share_minutes">Minutes</string>
|
||||
<string name="settings.share_hours">Heures</string>
|
||||
<string name="settings.share_days">Jours</string>
|
||||
|
@ -299,8 +299,6 @@
|
||||
<string name="select_share.empty">Nincs mentett megosztás a kiszolgálón.</string>
|
||||
<string name="menu_deleted_share">%s megosztás törölve</string>
|
||||
<string name="menu_deleted_share_error">%s megosztás törlése sikertelen!</string>
|
||||
<string name="settings.share_milliseconds">Ezredmásodperc</string>
|
||||
<string name="settings.share_seconds">Másodperc</string>
|
||||
<string name="settings.share_minutes">Perc</string>
|
||||
<string name="settings.share_hours">Óra</string>
|
||||
<string name="settings.share_days">Nap</string>
|
||||
|
@ -265,8 +265,6 @@
|
||||
<string name="widget.sdcard_missing">Nessuna scheda SD</string>
|
||||
<string name="settings.share_description_default">Descrizione Predefinita di Condivisione</string>
|
||||
<string name="download.menu_clear_playlist">Pulisci Playlist</string>
|
||||
<string name="settings.share_milliseconds">Millisecondi</string>
|
||||
<string name="settings.share_seconds">Secondi</string>
|
||||
<string name="settings.share_minutes">Minuti</string>
|
||||
<string name="settings.share_hours">Ore</string>
|
||||
<string name="settings.share_days">Giorni</string>
|
||||
|
@ -350,8 +350,6 @@
|
||||
<string name="select_share.empty">Geen delingen beschikbaar op server</string>
|
||||
<string name="menu_deleted_share">Deling %s verwijderd</string>
|
||||
<string name="menu_deleted_share_error">Kan deling %s niet verwijderen</string>
|
||||
<string name="settings.share_milliseconds">Milliseconden </string>
|
||||
<string name="settings.share_seconds">Seconden</string>
|
||||
<string name="settings.share_minutes">Minuten</string>
|
||||
<string name="settings.share_hours">Uur</string>
|
||||
<string name="settings.share_days">Dagen</string>
|
||||
|
@ -288,8 +288,6 @@
|
||||
<string name="select_share.empty">Brak udostępnień na serwerze</string>
|
||||
<string name="menu_deleted_share">Usunięto udostępnienie %s</string>
|
||||
<string name="menu_deleted_share_error">Nieudane usunięcie udostępnienia %s</string>
|
||||
<string name="settings.share_milliseconds">Milisekundy</string>
|
||||
<string name="settings.share_seconds">Sekundy</string>
|
||||
<string name="settings.share_minutes">Minuty</string>
|
||||
<string name="settings.share_hours">Godziny</string>
|
||||
<string name="settings.share_days">Dni</string>
|
||||
|
@ -341,8 +341,6 @@
|
||||
<string name="select_share.empty">Nenhum compartilhamento disponível no servidor</string>
|
||||
<string name="menu_deleted_share">Compartilhamento %s excluído</string>
|
||||
<string name="menu_deleted_share_error">Falha ao excluir o compartilhamento %s</string>
|
||||
<string name="settings.share_milliseconds">Milisegundos</string>
|
||||
<string name="settings.share_seconds">Segundos</string>
|
||||
<string name="settings.share_minutes">Minutos</string>
|
||||
<string name="settings.share_hours">Horas</string>
|
||||
<string name="settings.share_days">Dias</string>
|
||||
|
@ -288,8 +288,6 @@
|
||||
<string name="select_share.empty">Nenhum compartilhamento disponível no servidor</string>
|
||||
<string name="menu_deleted_share">Compartilhamento %s apagado</string>
|
||||
<string name="menu_deleted_share_error">Falha ao apagar o compartilhamento %s</string>
|
||||
<string name="settings.share_milliseconds">Milisegundos</string>
|
||||
<string name="settings.share_seconds">Segundos</string>
|
||||
<string name="settings.share_minutes">Minutos</string>
|
||||
<string name="settings.share_hours">Horas</string>
|
||||
<string name="settings.share_days">Dias</string>
|
||||
|
@ -318,8 +318,6 @@
|
||||
<string name="select_share.empty">Нет доступных ресурсов на сервере</string>
|
||||
<string name="menu_deleted_share">Удалить общий ресурс %s</string>
|
||||
<string name="menu_deleted_share_error">Не удалось удалить общий ресурс %s</string>
|
||||
<string name="settings.share_milliseconds">Миллисекунды</string>
|
||||
<string name="settings.share_seconds">Секунды</string>
|
||||
<string name="settings.share_minutes">Минуты</string>
|
||||
<string name="settings.share_hours">Часы</string>
|
||||
<string name="settings.share_days">Дни</string>
|
||||
|
@ -318,8 +318,6 @@
|
||||
<string name="select_share.empty">服务器上没有可用的共享</string>
|
||||
<string name="menu_deleted_share">删除分享 %s</string>
|
||||
<string name="menu_deleted_share_error">删除分享失败 %s</string>
|
||||
<string name="settings.share_milliseconds">毫秒</string>
|
||||
<string name="settings.share_seconds">秒</string>
|
||||
<string name="settings.share_minutes">分钟</string>
|
||||
<string name="settings.share_hours">小时</string>
|
||||
<string name="settings.share_days">天</string>
|
||||
|
@ -218,8 +218,6 @@
|
||||
<item>@string/settings.search_500</item>
|
||||
</string-array>
|
||||
<string-array name="shareExpirationNames" translatable="false">
|
||||
<item>@string/settings.share_milliseconds</item>
|
||||
<item>@string/settings.share_seconds</item>
|
||||
<item>@string/settings.share_minutes</item>
|
||||
<item>@string/settings.share_hours</item>
|
||||
<item>@string/settings.share_days</item>
|
||||
|
@ -353,8 +353,6 @@
|
||||
<string name="select_share.empty">No shares available on server</string>
|
||||
<string name="menu_deleted_share">Deleted share %s</string>
|
||||
<string name="menu_deleted_share_error">Failed to delete share %s</string>
|
||||
<string name="settings.share_milliseconds">Milliseconds</string>
|
||||
<string name="settings.share_seconds">Seconds</string>
|
||||
<string name="settings.share_minutes">Minutes</string>
|
||||
<string name="settings.share_hours">Hours</string>
|
||||
<string name="settings.share_days">Days</string>
|
||||
@ -383,7 +381,7 @@
|
||||
<string name="settings.debug.log_delete">Delete files</string>
|
||||
<string name="settings.debug.log_deleted">Deleted log files.</string>
|
||||
<string name="notification.downloading_title">Downloading media in the background…</string>
|
||||
|
||||
<string name="notification.permission_required">Notifications are required for media playback. You can grant the permission anytime in the Android settings.</string>
|
||||
<string name="server_selector.label">Configured servers</string>
|
||||
<string name="server_selector.delete_confirmation">Are you sure you want to delete the server?</string>
|
||||
<string name="server_editor.label">Editing server</string>
|
||||
|
Loading…
x
Reference in New Issue
Block a user