mirror of
https://gitlab.com/ultrasonic/ultrasonic.git
synced 2025-04-18 18:17:43 +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"
|
multidex = "2.0.1"
|
||||||
room = "2.4.3"
|
room = "2.4.3"
|
||||||
kotlin = "1.7.10"
|
kotlin = "1.7.10"
|
||||||
kotlinxCoroutines = "1.6.3-native-mt"
|
kotlinxCoroutines = "1.6.4-native-mt"
|
||||||
kotlinxGuava = "1.6.3"
|
kotlinxGuava = "1.6.4"
|
||||||
viewModelKtx = "2.5.1"
|
viewModelKtx = "2.5.1"
|
||||||
|
|
||||||
retrofit = "2.9.0"
|
retrofit = "2.9.0"
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?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
|
<issue
|
||||||
id="PluralsCandidate"
|
id="PluralsCandidate"
|
||||||
@ -737,7 +737,7 @@
|
|||||||
errorLine2=" ~~~~~~~~~~~~~">
|
errorLine2=" ~~~~~~~~~~~~~">
|
||||||
<location
|
<location
|
||||||
file="src/main/res/layout/equalizer_bar.xml"
|
file="src/main/res/layout/equalizer_bar.xml"
|
||||||
line="19"
|
line="26"
|
||||||
column="13"/>
|
column="13"/>
|
||||||
</issue>
|
</issue>
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
<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.WAKE_LOCK"/>
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||||
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
|
<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()
|
showWelcomeDialog()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ask for permission to send notifications
|
||||||
|
Util.ensurePermissionToPostNotification(this)
|
||||||
|
|
||||||
RxBus.dismissNowPlayingCommandObservable.subscribe {
|
RxBus.dismissNowPlayingCommandObservable.subscribe {
|
||||||
nowPlayingHidden = true
|
nowPlayingHidden = true
|
||||||
hideNowPlaying()
|
hideNowPlaying()
|
||||||
|
@ -28,6 +28,7 @@ import org.moire.ultrasonic.R
|
|||||||
import org.moire.ultrasonic.audiofx.EqualizerController
|
import org.moire.ultrasonic.audiofx.EqualizerController
|
||||||
import org.moire.ultrasonic.fragment.FragmentTitle.Companion.setTitle
|
import org.moire.ultrasonic.fragment.FragmentTitle.Companion.setTitle
|
||||||
import org.moire.ultrasonic.util.Util.applyTheme
|
import org.moire.ultrasonic.util.Util.applyTheme
|
||||||
|
import org.w3c.dom.Text
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -168,10 +169,9 @@ class EqualizerFragment : Fragment() {
|
|||||||
val bandBar = LayoutInflater.from(context)
|
val bandBar = LayoutInflater.from(context)
|
||||||
.inflate(R.layout.equalizer_bar, equalizerLayout, false)
|
.inflate(R.layout.equalizer_bar, equalizerLayout, false)
|
||||||
|
|
||||||
val freqTextView: TextView =
|
val freqTextView: TextView = bandBar.findViewById(R.id.equalizer_frequency)
|
||||||
bandBar.findViewById<View>(R.id.equalizer_frequency) as TextView
|
val levelTextView: TextView = bandBar.findViewById(R.id.equalizer_level)
|
||||||
val levelTextView = bandBar.findViewById<View>(R.id.equalizer_level) as TextView
|
val bar: SeekBar = bandBar.findViewById(R.id.equalizer_bar)
|
||||||
val bar = bandBar.findViewById<View>(R.id.equalizer_bar) as SeekBar
|
|
||||||
|
|
||||||
val range = equalizer!!.getBandFreqRange(band)
|
val range = equalizer!!.getBandFreqRange(band)
|
||||||
|
|
||||||
|
@ -247,18 +247,15 @@ class SettingsFragment :
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onDisplayPreferenceDialog(preference: Preference) {
|
override fun onDisplayPreferenceDialog(preference: Preference) {
|
||||||
var dialogFragment: DialogFragment? = null
|
|
||||||
if (preference is TimeSpanPreference) {
|
if (preference is TimeSpanPreference) {
|
||||||
dialogFragment = TimeSpanPreferenceDialogFragmentCompat()
|
val dialogFragment = TimeSpanPreferenceDialogFragmentCompat()
|
||||||
val bundle = Bundle(1)
|
val bundle = Bundle(1)
|
||||||
bundle.putString("key", preference.getKey())
|
bundle.putString("key", preference.getKey())
|
||||||
dialogFragment.setArguments(bundle)
|
dialogFragment.setArguments(bundle)
|
||||||
}
|
|
||||||
if (dialogFragment != null) {
|
|
||||||
dialogFragment.setTargetFragment(this, 0)
|
dialogFragment.setTargetFragment(this, 0)
|
||||||
dialogFragment.show(
|
dialogFragment.show(
|
||||||
this.parentFragmentManager,
|
this.parentFragmentManager,
|
||||||
"android.support.v7.preference.PreferenceFragment.DIALOG"
|
"androidx.preference.PreferenceFragment.DIALOG"
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
super.onDisplayPreferenceDialog(preference)
|
super.onDisplayPreferenceDialog(preference)
|
||||||
|
@ -40,7 +40,6 @@ import org.moire.ultrasonic.util.BackgroundTask
|
|||||||
import org.moire.ultrasonic.util.CancellationToken
|
import org.moire.ultrasonic.util.CancellationToken
|
||||||
import org.moire.ultrasonic.util.FragmentBackgroundTask
|
import org.moire.ultrasonic.util.FragmentBackgroundTask
|
||||||
import org.moire.ultrasonic.util.LoadingTask
|
import org.moire.ultrasonic.util.LoadingTask
|
||||||
import org.moire.ultrasonic.util.TimeSpan
|
|
||||||
import org.moire.ultrasonic.util.TimeSpanPicker
|
import org.moire.ultrasonic.util.TimeSpanPicker
|
||||||
import org.moire.ultrasonic.util.Util
|
import org.moire.ultrasonic.util.Util
|
||||||
import org.moire.ultrasonic.view.ShareAdapter
|
import org.moire.ultrasonic.view.ShareAdapter
|
||||||
@ -79,16 +78,16 @@ class SharesFragment : Fragment() {
|
|||||||
sharesListView = view.findViewById(R.id.select_share_list)
|
sharesListView = view.findViewById(R.id.select_share_list)
|
||||||
refreshSharesListView!!.setOnRefreshListener { load(true) }
|
refreshSharesListView!!.setOnRefreshListener { load(true) }
|
||||||
emptyTextView = view.findViewById(R.id.select_share_empty)
|
emptyTextView = view.findViewById(R.id.select_share_empty)
|
||||||
sharesListView!!.onItemClickListener = AdapterView.OnItemClickListener {
|
sharesListView!!.onItemClickListener =
|
||||||
parent, _, position, _ ->
|
AdapterView.OnItemClickListener { parent, _, position, _ ->
|
||||||
val share = parent.getItemAtPosition(position) as Share
|
val share = parent.getItemAtPosition(position) as Share
|
||||||
|
|
||||||
val action = SharesFragmentDirections.sharesToTrackCollection(
|
val action = SharesFragmentDirections.sharesToTrackCollection(
|
||||||
shareId = share.id,
|
shareId = share.id,
|
||||||
shareName = share.name
|
shareName = share.name
|
||||||
)
|
)
|
||||||
findNavController().navigate(action)
|
findNavController().navigate(action)
|
||||||
}
|
}
|
||||||
registerForContextMenu(sharesListView!!)
|
registerForContextMenu(sharesListView!!)
|
||||||
FragmentTitle.setTitle(this, R.string.button_bar_shares)
|
FragmentTitle.setTitle(this, R.string.button_bar_shares)
|
||||||
load(false)
|
load(false)
|
||||||
@ -273,19 +272,19 @@ class SharesFragment : Fragment() {
|
|||||||
Entry Count: ${share.getEntries().size}
|
Entry Count: ${share.getEntries().size}
|
||||||
Visit Count: ${share.visitCount}
|
Visit Count: ${share.visitCount}
|
||||||
""".trimIndent() +
|
""".trimIndent() +
|
||||||
(
|
(
|
||||||
if (share.created == null) "" else """
|
if (share.created == null) "" else """
|
||||||
|
|
||||||
Creation Date: ${share.created!!.replace('T', ' ')}
|
Creation Date: ${share.created!!.replace('T', ' ')}
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
) +
|
) +
|
||||||
(
|
(
|
||||||
if (share.lastVisited == null) "" else """
|
if (share.lastVisited == null) "" else """
|
||||||
|
|
||||||
Last Visited Date: ${share.lastVisited!!.replace('T', ' ')}
|
Last Visited Date: ${share.lastVisited!!.replace('T', ' ')}
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
) +
|
) +
|
||||||
if (share.expires == null) "" else """
|
if (share.expires == null) "" else """
|
||||||
|
|
||||||
Expiration Date: ${share.expires!!.replace('T', ' ')}
|
Expiration Date: ${share.expires!!.replace('T', ' ')}
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
@ -321,9 +320,9 @@ class SharesFragment : Fragment() {
|
|||||||
object : LoadingTask<Any?>(activity, refreshSharesListView, cancellationToken) {
|
object : LoadingTask<Any?>(activity, refreshSharesListView, cancellationToken) {
|
||||||
@Throws(Throwable::class)
|
@Throws(Throwable::class)
|
||||||
override fun doInBackground(): Any? {
|
override fun doInBackground(): Any? {
|
||||||
var millis = timeSpanPicker.timeSpan.totalMilliseconds
|
var millis = timeSpanPicker.getTimeSpan()
|
||||||
if (millis > 0) {
|
if (millis > 0) {
|
||||||
millis = TimeSpan.getCurrentTime().add(millis).totalMilliseconds
|
millis += System.currentTimeMillis()
|
||||||
}
|
}
|
||||||
val shareDescriptionText = shareDescription.text
|
val shareDescriptionText = shareDescription.text
|
||||||
val description = shareDescriptionText?.toString()
|
val description = shareDescriptionText?.toString()
|
||||||
@ -341,19 +340,22 @@ class SharesFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun error(error: Throwable) {
|
override fun error(error: Throwable) {
|
||||||
val msg: String
|
val msg: String =
|
||||||
msg = if (error is OfflineException || error is ApiNotSupportedException) {
|
if (error is OfflineException || error is ApiNotSupportedException) {
|
||||||
getErrorMessage(
|
getErrorMessage(
|
||||||
error
|
error
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
String.format(
|
String.format(
|
||||||
Locale.ROOT,
|
Locale.ROOT,
|
||||||
"%s %s",
|
"%s %s",
|
||||||
resources.getString(R.string.playlist_updated_info_error, share.name),
|
resources.getString(
|
||||||
getErrorMessage(error)
|
R.string.playlist_updated_info_error,
|
||||||
)
|
share.name
|
||||||
}
|
),
|
||||||
|
getErrorMessage(error)
|
||||||
|
)
|
||||||
|
}
|
||||||
Util.toast(context, msg, false)
|
Util.toast(context, msg, false)
|
||||||
}
|
}
|
||||||
}.execute()
|
}.execute()
|
||||||
|
@ -7,9 +7,11 @@
|
|||||||
|
|
||||||
package org.moire.ultrasonic.imageloader
|
package org.moire.ultrasonic.imageloader
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.graphics.BitmapFactory
|
import android.graphics.BitmapFactory
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import androidx.media3.common.util.UnstableApi
|
||||||
import androidx.media3.session.BitmapLoader
|
import androidx.media3.session.BitmapLoader
|
||||||
import com.google.common.util.concurrent.ListenableFuture
|
import com.google.common.util.concurrent.ListenableFuture
|
||||||
import com.google.common.util.concurrent.ListeningExecutorService
|
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.KoinComponent
|
||||||
import org.koin.core.component.inject
|
import org.koin.core.component.inject
|
||||||
|
|
||||||
|
@SuppressLint("UnsafeOptInUsageError")
|
||||||
class ArtworkBitmapLoader : BitmapLoader, KoinComponent {
|
class ArtworkBitmapLoader : BitmapLoader, KoinComponent {
|
||||||
|
|
||||||
private val imageLoader: ImageLoader by inject()
|
private val imageLoader: ImageLoader by inject()
|
||||||
|
@ -7,11 +7,11 @@
|
|||||||
|
|
||||||
package org.moire.ultrasonic.playback
|
package org.moire.ultrasonic.playback
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
import androidx.media3.common.HeartRating
|
import androidx.media3.common.HeartRating
|
||||||
import androidx.media3.common.Player
|
import androidx.media3.common.Player
|
||||||
import androidx.media3.common.util.UnstableApi
|
|
||||||
import androidx.media3.session.CommandButton
|
import androidx.media3.session.CommandButton
|
||||||
import androidx.media3.session.DefaultMediaNotificationProvider
|
import androidx.media3.session.DefaultMediaNotificationProvider
|
||||||
import androidx.media3.session.MediaNotification
|
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.service.MediaPlayerController
|
||||||
import org.moire.ultrasonic.util.toTrack
|
import org.moire.ultrasonic.util.toTrack
|
||||||
|
|
||||||
@UnstableApi
|
@SuppressLint("UnsafeOptInUsageError")
|
||||||
class MediaNotificationProvider(context: Context) :
|
class MediaNotificationProvider(context: Context) :
|
||||||
DefaultMediaNotificationProvider(context, ArtworkBitmapLoader()), KoinComponent {
|
DefaultMediaNotificationProvider(context, ArtworkBitmapLoader()), KoinComponent {
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.moire.ultrasonic.playback
|
package org.moire.ultrasonic.playback
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import android.app.PendingIntent
|
import android.app.PendingIntent
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
@ -39,6 +40,7 @@ import org.moire.ultrasonic.util.Util
|
|||||||
import org.moire.ultrasonic.util.toTrack
|
import org.moire.ultrasonic.util.toTrack
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
|
@SuppressLint("UnsafeOptInUsageError")
|
||||||
class PlaybackService : MediaLibraryService(), KoinComponent {
|
class PlaybackService : MediaLibraryService(), KoinComponent {
|
||||||
private lateinit var player: ExoPlayer
|
private lateinit var player: ExoPlayer
|
||||||
private lateinit var mediaLibrarySession: MediaLibrarySession
|
private lateinit var mediaLibrarySession: MediaLibrarySession
|
||||||
@ -84,7 +86,12 @@ class PlaybackService : MediaLibraryService(), KoinComponent {
|
|||||||
mediaLibrarySession.release()
|
mediaLibrarySession.release()
|
||||||
rxBusSubscription.dispose()
|
rxBusSubscription.dispose()
|
||||||
isStarted = false
|
isStarted = false
|
||||||
stopForeground(true)
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
|
stopForeground(STOP_FOREGROUND_REMOVE)
|
||||||
|
} else {
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
stopForeground(true)
|
||||||
|
}
|
||||||
stopSelf()
|
stopSelf()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,7 +39,6 @@ import androidx.media3.common.Timeline
|
|||||||
import androidx.media3.common.TrackSelectionParameters
|
import androidx.media3.common.TrackSelectionParameters
|
||||||
import androidx.media3.common.VideoSize
|
import androidx.media3.common.VideoSize
|
||||||
import androidx.media3.common.text.CueGroup
|
import androidx.media3.common.text.CueGroup
|
||||||
import androidx.media3.common.util.Util
|
|
||||||
import androidx.media3.session.MediaSession
|
import androidx.media3.session.MediaSession
|
||||||
import com.google.common.collect.ImmutableList
|
import com.google.common.collect.ImmutableList
|
||||||
import com.google.common.util.concurrent.ListenableFuture
|
import com.google.common.util.concurrent.ListenableFuture
|
||||||
@ -76,6 +75,7 @@ private const val SEEK_START_AFTER_SECONDS = 5
|
|||||||
* TODO: Minimize status updates.
|
* TODO: Minimize status updates.
|
||||||
*/
|
*/
|
||||||
@Suppress("TooManyFunctions")
|
@Suppress("TooManyFunctions")
|
||||||
|
@SuppressLint("UnsafeOptInUsageError")
|
||||||
class JukeboxMediaPlayer : JukeboxUnimplementedFunctions(), Player {
|
class JukeboxMediaPlayer : JukeboxUnimplementedFunctions(), Player {
|
||||||
private val tasks = TaskQueue()
|
private val tasks = TaskQueue()
|
||||||
private val executorService = Executors.newSingleThreadScheduledExecutor()
|
private val executorService = Executors.newSingleThreadScheduledExecutor()
|
||||||
@ -119,7 +119,7 @@ class JukeboxMediaPlayer : JukeboxUnimplementedFunctions(), Player {
|
|||||||
JukeboxNotificationActionFactory()
|
JukeboxNotificationActionFactory()
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
if (Util.SDK_INT >= 29) {
|
if (Build.VERSION.SDK_INT >= 29) {
|
||||||
startForeground(
|
startForeground(
|
||||||
notification.notificationId,
|
notification.notificationId,
|
||||||
notification.notification,
|
notification.notification,
|
||||||
@ -140,7 +140,12 @@ class JukeboxMediaPlayer : JukeboxUnimplementedFunctions(), Player {
|
|||||||
|
|
||||||
val extras = intent.extras
|
val extras = intent.extras
|
||||||
if ((extras != null) && extras.containsKey(Intent.EXTRA_KEY_EVENT)) {
|
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) {
|
when (event?.keyCode) {
|
||||||
KEYCODE_MEDIA_PLAY -> play()
|
KEYCODE_MEDIA_PLAY -> play()
|
||||||
KEYCODE_MEDIA_PAUSE -> stop()
|
KEYCODE_MEDIA_PAUSE -> stop()
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
package org.moire.ultrasonic.service
|
package org.moire.ultrasonic.service
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import android.app.PendingIntent
|
import android.app.PendingIntent
|
||||||
import android.content.ComponentName
|
import android.content.ComponentName
|
||||||
import android.content.Intent
|
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
|
* This class creates Intents and Actions to be used with the Media Notification
|
||||||
* of the Jukebox Service
|
* of the Jukebox Service
|
||||||
*/
|
*/
|
||||||
|
@SuppressLint("UnsafeOptInUsageError")
|
||||||
class JukeboxNotificationActionFactory : MediaNotification.ActionFactory {
|
class JukeboxNotificationActionFactory : MediaNotification.ActionFactory {
|
||||||
override fun createMediaAction(
|
override fun createMediaAction(
|
||||||
mediaSession: MediaSession,
|
mediaSession: MediaSession,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* JukeboxUnimplemented.kt
|
* JukeboxUnimplementedFunctions.kt
|
||||||
* Copyright (C) 2009-2022 Ultrasonic developers
|
* Copyright (C) 2009-2022 Ultrasonic developers
|
||||||
*
|
*
|
||||||
* Distributed under terms of the GNU GPLv3 license.
|
* Distributed under terms of the GNU GPLv3 license.
|
||||||
@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
package org.moire.ultrasonic.service
|
package org.moire.ultrasonic.service
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import android.app.Service
|
import android.app.Service
|
||||||
import android.view.Surface
|
import android.view.Surface
|
||||||
import android.view.SurfaceHolder
|
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.
|
* of the crowded Player interface, so the JukeboxMediaPlayer class can be a bit clearer.
|
||||||
*/
|
*/
|
||||||
@Suppress("TooManyFunctions")
|
@Suppress("TooManyFunctions")
|
||||||
|
@SuppressLint("UnsafeOptInUsageError")
|
||||||
abstract class JukeboxUnimplementedFunctions : Service(), Player {
|
abstract class JukeboxUnimplementedFunctions : Service(), Player {
|
||||||
|
|
||||||
override fun setMediaItems(mediaItems: MutableList<MediaItem>) {
|
override fun setMediaItems(mediaItems: MutableList<MediaItem>) {
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
package org.moire.ultrasonic.service
|
package org.moire.ultrasonic.service
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import androidx.media3.common.C
|
import androidx.media3.common.C
|
||||||
import androidx.media3.common.MediaItem
|
import androidx.media3.common.MediaItem
|
||||||
import androidx.media3.common.Player
|
import androidx.media3.common.Player
|
||||||
@ -20,6 +21,7 @@ import java.util.Arrays
|
|||||||
* This class wraps a simple playlist provided as List<MediaItem>
|
* This class wraps a simple playlist provided as List<MediaItem>
|
||||||
* to be usable as a Media3 Timeline.
|
* to be usable as a Media3 Timeline.
|
||||||
*/
|
*/
|
||||||
|
@SuppressLint("UnsafeOptInUsageError")
|
||||||
class PlaylistTimeline @JvmOverloads constructor(
|
class PlaylistTimeline @JvmOverloads constructor(
|
||||||
mediaItems: List<MediaItem>,
|
mediaItems: List<MediaItem>,
|
||||||
shuffledIndices: IntArray = createUnshuffledIndices(
|
shuffledIndices: IntArray = createUnshuffledIndices(
|
||||||
|
@ -30,7 +30,6 @@ import org.moire.ultrasonic.util.CancellationToken
|
|||||||
import org.moire.ultrasonic.util.FragmentBackgroundTask
|
import org.moire.ultrasonic.util.FragmentBackgroundTask
|
||||||
import org.moire.ultrasonic.util.Settings
|
import org.moire.ultrasonic.util.Settings
|
||||||
import org.moire.ultrasonic.util.ShareDetails
|
import org.moire.ultrasonic.util.ShareDetails
|
||||||
import org.moire.ultrasonic.util.TimeSpan
|
|
||||||
import org.moire.ultrasonic.util.TimeSpanPicker
|
import org.moire.ultrasonic.util.TimeSpanPicker
|
||||||
import org.moire.ultrasonic.util.Util.ifNotNull
|
import org.moire.ultrasonic.util.Util.ifNotNull
|
||||||
|
|
||||||
@ -145,9 +144,8 @@ class ShareHandler(val context: Context) {
|
|||||||
showDialog(fragment, shareDetails, swipe, cancellationToken, additionalId)
|
showDialog(fragment, shareDetails, swipe, cancellationToken, additionalId)
|
||||||
} else {
|
} else {
|
||||||
shareDetails.Description = Settings.defaultShareDescription
|
shareDetails.Description = Settings.defaultShareDescription
|
||||||
shareDetails.Expiration = TimeSpan.getCurrentTime().add(
|
shareDetails.Expiration = System.currentTimeMillis() +
|
||||||
Settings.defaultShareExpirationInMillis
|
Settings.defaultShareExpirationInMillis
|
||||||
).totalMilliseconds
|
|
||||||
share(fragment, shareDetails, swipe, cancellationToken, additionalId)
|
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
|
shareDescription = layout.findViewById<View>(R.id.share_description) as EditText
|
||||||
hideDialogCheckBox = layout.findViewById<View>(R.id.hide_dialog) as CheckBox
|
hideDialogCheckBox = layout.findViewById<View>(R.id.hide_dialog) as CheckBox
|
||||||
shareOnServerCheckBox = layout.findViewById<View>(R.id.share_on_server) 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
|
saveAsDefaultsCheckBox = layout.findViewById<View>(R.id.save_as_defaults) as CheckBox
|
||||||
timeSpanPicker = layout.findViewById<View>(R.id.date_picker) as TimeSpanPicker
|
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
|
textViewComment = layout.findViewById<View>(R.id.textViewComment) as TextView
|
||||||
textViewExpiration = layout.findViewById<View>(R.id.textViewExpiration) 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) { _, _ ->
|
builder.setPositiveButton(R.string.menu_share) { _, _ ->
|
||||||
if (!noExpirationCheckBox!!.isChecked) {
|
if (!noExpirationCheckBox!!.isChecked) {
|
||||||
val timeSpan: TimeSpan = timeSpanPicker!!.timeSpan
|
val timeSpan: Long = timeSpanPicker!!.getTimeSpan()
|
||||||
val now = TimeSpan.getCurrentTime()
|
shareDetails.Expiration = System.currentTimeMillis() + timeSpan
|
||||||
shareDetails.Expiration = now.add(timeSpan).totalMilliseconds
|
|
||||||
}
|
}
|
||||||
|
|
||||||
shareDetails.Description = shareDescription!!.text.toString()
|
shareDetails.Description = shareDescription!!.text.toString()
|
||||||
@ -204,7 +201,7 @@ class ShareHandler(val context: Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (saveAsDefaultsCheckBox!!.isChecked) {
|
if (saveAsDefaultsCheckBox!!.isChecked) {
|
||||||
val timeSpanType: String = timeSpanPicker!!.timeSpanType
|
val timeSpanType: String = timeSpanPicker!!.timeSpanType!!
|
||||||
val timeSpanAmount: Int = timeSpanPicker!!.timeSpanAmount
|
val timeSpanAmount: Int = timeSpanPicker!!.timeSpanAmount
|
||||||
Settings.defaultShareExpiration =
|
Settings.defaultShareExpiration =
|
||||||
if (!noExpirationCheckBox!!.isChecked && timeSpanAmount > 0)
|
if (!noExpirationCheckBox!!.isChecked && timeSpanAmount > 0)
|
||||||
@ -240,7 +237,7 @@ class ShareHandler(val context: Context) {
|
|||||||
noExpirationCheckBox!!.isChecked = false
|
noExpirationCheckBox!!.isChecked = false
|
||||||
timeSpanPicker!!.isEnabled = true
|
timeSpanPicker!!.isEnabled = true
|
||||||
timeSpanPicker!!.setTimeSpanAmount(timeSpanAmount.toString())
|
timeSpanPicker!!.setTimeSpanAmount(timeSpanAmount.toString())
|
||||||
timeSpanPicker!!.setTimeSpanType(timeSpanType)
|
timeSpanPicker!!.timeSpanType = timeSpanType
|
||||||
} else {
|
} else {
|
||||||
noExpirationCheckBox!!.isChecked = true
|
noExpirationCheckBox!!.isChecked = true
|
||||||
timeSpanPicker!!.isEnabled = false
|
timeSpanPicker!!.isEnabled = false
|
||||||
|
@ -18,7 +18,7 @@ import org.moire.ultrasonic.app.UApp
|
|||||||
* Contains convenience functions for reading and writing preferences
|
* Contains convenience functions for reading and writing preferences
|
||||||
*/
|
*/
|
||||||
object Settings {
|
object Settings {
|
||||||
private val PATTERN = Pattern.compile(":")
|
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
var theme by StringSetting(
|
var theme by StringSetting(
|
||||||
@ -229,15 +229,12 @@ object Settings {
|
|||||||
|
|
||||||
val defaultShareExpirationInMillis: Long
|
val defaultShareExpirationInMillis: Long
|
||||||
get() {
|
get() {
|
||||||
val preference =
|
val preference = defaultShareExpiration
|
||||||
preferences.getString(getKey(R.string.setting_key_default_share_expiration), "0")!!
|
val split = COLON_PATTERN.split(preference)
|
||||||
val split = PATTERN.split(preference)
|
|
||||||
if (split.size == 2) {
|
if (split.size == 2) {
|
||||||
val timeSpanAmount = split[0].toInt()
|
val timeSpanAmount = split[0].toLong()
|
||||||
val timeSpanType = split[1]
|
val timeSpanType = split[1]
|
||||||
val timeSpan =
|
return TimeSpanPicker.calculateTimeSpan(appContext, timeSpanType, timeSpanAmount)
|
||||||
TimeSpanPicker.calculateTimeSpan(appContext, timeSpanType, timeSpanAmount)
|
|
||||||
return timeSpan.totalMilliseconds
|
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@ -292,4 +289,6 @@ object Settings {
|
|||||||
|
|
||||||
private val appContext: Context
|
private val appContext: Context
|
||||||
get() = UApp.applicationContext()
|
get() = UApp.applicationContext()
|
||||||
|
|
||||||
|
val COLON_PATTERN = Pattern.compile(":")
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* StorageFile.kt
|
* StorageFile.kt
|
||||||
* Copyright (C) 2009-2021 Ultrasonic developers
|
* Copyright (C) 2009-2022 Ultrasonic developers
|
||||||
*
|
*
|
||||||
* Distributed under terms of the GNU GPLv3 license.
|
* Distributed under terms of the GNU GPLv3 license.
|
||||||
*/
|
*/
|
||||||
@ -91,8 +91,10 @@ class StorageFile(
|
|||||||
uri,
|
uri,
|
||||||
mode
|
mode
|
||||||
)
|
)
|
||||||
return descriptor?.createOutputStream()
|
val stream = descriptor?.createOutputStream()
|
||||||
?: throw IOException("Couldn't retrieve OutputStream")
|
?: throw IOException("Couldn't retrieve OutputStream")
|
||||||
|
descriptor.close()
|
||||||
|
return stream
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getFileInputStream(): InputStream {
|
override fun getFileInputStream(): InputStream {
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
package org.moire.ultrasonic.util
|
package org.moire.ultrasonic.util
|
||||||
|
|
||||||
|
import android.Manifest.permission.POST_NOTIFICATIONS
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.app.PendingIntent
|
import android.app.PendingIntent
|
||||||
@ -32,7 +33,10 @@ import android.util.TypedValue
|
|||||||
import android.view.Gravity
|
import android.view.Gravity
|
||||||
import android.view.inputmethod.InputMethodManager
|
import android.view.inputmethod.InputMethodManager
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.annotation.AnyRes
|
import androidx.annotation.AnyRes
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.media3.common.C
|
import androidx.media3.common.C
|
||||||
import androidx.media3.common.MediaItem
|
import androidx.media3.common.MediaItem
|
||||||
import androidx.media3.common.Player
|
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
|
@JvmStatic
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
fun getVersionName(context: Context): String? {
|
fun getVersionName(context: Context): String? {
|
||||||
|
@ -1,11 +1,18 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?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"
|
<RelativeLayout xmlns:a="http://schemas.android.com/apk/res/android"
|
||||||
a:orientation="vertical"
|
a:orientation="vertical"
|
||||||
a:layout_width="fill_parent"
|
a:layout_width="fill_parent"
|
||||||
a:layout_height="wrap_content">
|
a:layout_height="wrap_content">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
a:id="@+id/equalizer.frequency"
|
a:id="@+id/equalizer_frequency"
|
||||||
a:textSize="12sp"
|
a:textSize="12sp"
|
||||||
a:textColor="#c0c0c0"
|
a:textColor="#c0c0c0"
|
||||||
a:layout_width="wrap_content"
|
a:layout_width="wrap_content"
|
||||||
@ -15,7 +22,7 @@
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
a:id="@+id/equalizer.level"
|
a:id="@+id/equalizer_level"
|
||||||
a:text="0 dB"
|
a:text="0 dB"
|
||||||
a:textSize="12sp"
|
a:textSize="12sp"
|
||||||
a:textColor="#c0c0c0"
|
a:textColor="#c0c0c0"
|
||||||
@ -24,14 +31,14 @@
|
|||||||
a:layout_height="wrap_content"
|
a:layout_height="wrap_content"
|
||||||
a:layout_marginTop="8dp"
|
a:layout_marginTop="8dp"
|
||||||
a:layout_alignParentRight="true"
|
a:layout_alignParentRight="true"
|
||||||
a:layout_toEndOf="@+id/equalizer.frequency"
|
a:layout_toEndOf="@+id/equalizer_frequency"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<SeekBar
|
<SeekBar
|
||||||
a:id="@+id/equalizer.bar"
|
a:id="@+id/equalizer_bar"
|
||||||
a:layout_width="fill_parent"
|
a:layout_width="fill_parent"
|
||||||
a:layout_height="wrap_content"
|
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="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">Smazaná sdílení %s</string>
|
||||||
<string name="menu_deleted_share_error">Chyba vymazání 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_minutes">Minut</string>
|
||||||
<string name="settings.share_hours">Hodin</string>
|
<string name="settings.share_hours">Hodin</string>
|
||||||
<string name="settings.share_days">Dní</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="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">Freigabe %s gelöscht</string>
|
||||||
<string name="menu_deleted_share_error">Löschen der Freigabe %s fehlgeschlagen</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_minutes">Minuten</string>
|
||||||
<string name="settings.share_hours">Stunden</string>
|
<string name="settings.share_hours">Stunden</string>
|
||||||
<string name="settings.share_days">Tage</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="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">Eliminar compartido %s</string>
|
||||||
<string name="menu_deleted_share_error">Fallo al 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_minutes">Minutos</string>
|
||||||
<string name="settings.share_hours">Horas</string>
|
<string name="settings.share_hours">Horas</string>
|
||||||
<string name="settings.share_days">Días</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="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">Partage supprimé %s</string>
|
||||||
<string name="menu_deleted_share_error">Échec de la suppression du partage %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_minutes">Minutes</string>
|
||||||
<string name="settings.share_hours">Heures</string>
|
<string name="settings.share_hours">Heures</string>
|
||||||
<string name="settings.share_days">Jours</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="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">%s megosztás törölve</string>
|
||||||
<string name="menu_deleted_share_error">%s megosztás törlése sikertelen!</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_minutes">Perc</string>
|
||||||
<string name="settings.share_hours">Óra</string>
|
<string name="settings.share_hours">Óra</string>
|
||||||
<string name="settings.share_days">Nap</string>
|
<string name="settings.share_days">Nap</string>
|
||||||
|
@ -265,8 +265,6 @@
|
|||||||
<string name="widget.sdcard_missing">Nessuna scheda SD</string>
|
<string name="widget.sdcard_missing">Nessuna scheda SD</string>
|
||||||
<string name="settings.share_description_default">Descrizione Predefinita di Condivisione</string>
|
<string name="settings.share_description_default">Descrizione Predefinita di Condivisione</string>
|
||||||
<string name="download.menu_clear_playlist">Pulisci Playlist</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_minutes">Minuti</string>
|
||||||
<string name="settings.share_hours">Ore</string>
|
<string name="settings.share_hours">Ore</string>
|
||||||
<string name="settings.share_days">Giorni</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="select_share.empty">Geen delingen beschikbaar op server</string>
|
||||||
<string name="menu_deleted_share">Deling %s verwijderd</string>
|
<string name="menu_deleted_share">Deling %s verwijderd</string>
|
||||||
<string name="menu_deleted_share_error">Kan deling %s niet verwijderen</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_minutes">Minuten</string>
|
||||||
<string name="settings.share_hours">Uur</string>
|
<string name="settings.share_hours">Uur</string>
|
||||||
<string name="settings.share_days">Dagen</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="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">Usunięto udostępnienie %s</string>
|
||||||
<string name="menu_deleted_share_error">Nieudane usunięcie udostępnienia %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_minutes">Minuty</string>
|
||||||
<string name="settings.share_hours">Godziny</string>
|
<string name="settings.share_hours">Godziny</string>
|
||||||
<string name="settings.share_days">Dni</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="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">Compartilhamento %s excluído</string>
|
||||||
<string name="menu_deleted_share_error">Falha ao excluir o compartilhamento %s</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_minutes">Minutos</string>
|
||||||
<string name="settings.share_hours">Horas</string>
|
<string name="settings.share_hours">Horas</string>
|
||||||
<string name="settings.share_days">Dias</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="select_share.empty">Nenhum compartilhamento disponível no servidor</string>
|
||||||
<string name="menu_deleted_share">Compartilhamento %s apagado</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="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_minutes">Minutos</string>
|
||||||
<string name="settings.share_hours">Horas</string>
|
<string name="settings.share_hours">Horas</string>
|
||||||
<string name="settings.share_days">Dias</string>
|
<string name="settings.share_days">Dias</string>
|
||||||
|
@ -318,8 +318,6 @@
|
|||||||
<string name="select_share.empty">Нет доступных ресурсов на сервере</string>
|
<string name="select_share.empty">Нет доступных ресурсов на сервере</string>
|
||||||
<string name="menu_deleted_share">Удалить общий ресурс %s</string>
|
<string name="menu_deleted_share">Удалить общий ресурс %s</string>
|
||||||
<string name="menu_deleted_share_error">Не удалось удалить общий ресурс %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_minutes">Минуты</string>
|
||||||
<string name="settings.share_hours">Часы</string>
|
<string name="settings.share_hours">Часы</string>
|
||||||
<string name="settings.share_days">Дни</string>
|
<string name="settings.share_days">Дни</string>
|
||||||
|
@ -318,8 +318,6 @@
|
|||||||
<string name="select_share.empty">服务器上没有可用的共享</string>
|
<string name="select_share.empty">服务器上没有可用的共享</string>
|
||||||
<string name="menu_deleted_share">删除分享 %s</string>
|
<string name="menu_deleted_share">删除分享 %s</string>
|
||||||
<string name="menu_deleted_share_error">删除分享失败 %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_minutes">分钟</string>
|
||||||
<string name="settings.share_hours">小时</string>
|
<string name="settings.share_hours">小时</string>
|
||||||
<string name="settings.share_days">天</string>
|
<string name="settings.share_days">天</string>
|
||||||
|
@ -218,8 +218,6 @@
|
|||||||
<item>@string/settings.search_500</item>
|
<item>@string/settings.search_500</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
<string-array name="shareExpirationNames" translatable="false">
|
<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_minutes</item>
|
||||||
<item>@string/settings.share_hours</item>
|
<item>@string/settings.share_hours</item>
|
||||||
<item>@string/settings.share_days</item>
|
<item>@string/settings.share_days</item>
|
||||||
|
@ -353,8 +353,6 @@
|
|||||||
<string name="select_share.empty">No shares available on server</string>
|
<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">Deleted share %s</string>
|
||||||
<string name="menu_deleted_share_error">Failed to delete 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_minutes">Minutes</string>
|
||||||
<string name="settings.share_hours">Hours</string>
|
<string name="settings.share_hours">Hours</string>
|
||||||
<string name="settings.share_days">Days</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_delete">Delete files</string>
|
||||||
<string name="settings.debug.log_deleted">Deleted log 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.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.label">Configured servers</string>
|
||||||
<string name="server_selector.delete_confirmation">Are you sure you want to delete the server?</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>
|
<string name="server_editor.label">Editing server</string>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user