mirror of
https://gitlab.com/ultrasonic/ultrasonic.git
synced 2025-04-15 17:00:36 +03:00
Rework UI
This commit is contained in:
parent
af98b19050
commit
6d93a98b22
@ -14,4 +14,8 @@ data class Playlist @JvmOverloads constructor(
|
||||
companion object {
|
||||
private const val serialVersionUID = -4160515427075433798L
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return name
|
||||
}
|
||||
}
|
||||
|
@ -12,4 +12,6 @@ data class PodcastsChannel(
|
||||
companion object {
|
||||
private const val serialVersionUID = -4160515427075433798L
|
||||
}
|
||||
|
||||
override fun toString(): String = name.toString()
|
||||
}
|
||||
|
@ -1,3 +1,10 @@
|
||||
/*
|
||||
* ApiVersionCheckWrapper.kt
|
||||
* Copyright (C) 2009-2022 Ultrasonic developers
|
||||
*
|
||||
* Distributed under terms of the GNU GPLv3 license.
|
||||
*/
|
||||
|
||||
package org.moire.ultrasonic.api.subsonic
|
||||
|
||||
import okhttp3.ResponseBody
|
||||
|
@ -129,13 +129,12 @@ class SubsonicAPIClient(
|
||||
this.addInterceptor(loggingInterceptor)
|
||||
}
|
||||
|
||||
@Suppress("CustomX509TrustManager", "TrustAllX509TrustManager")
|
||||
private fun OkHttpClient.Builder.allowSelfSignedCertificates() {
|
||||
val trustManager =
|
||||
@Suppress("CustomX509TrustManager")
|
||||
|
||||
object : X509TrustManager {
|
||||
@Suppress("TrustAllX509TrustManager")
|
||||
override fun checkClientTrusted(p0: Array<out X509Certificate>?, p1: String?) {}
|
||||
@Suppress("TrustAllX509TrustManager")
|
||||
override fun checkServerTrusted(p0: Array<out X509Certificate>?, p1: String?) {}
|
||||
override fun getAcceptedIssuers(): Array<X509Certificate> = emptyArray()
|
||||
}
|
||||
|
@ -1,3 +1,10 @@
|
||||
/*
|
||||
* SubsonicAPIDefinition.kt
|
||||
* Copyright (C) 2009-2022 Ultrasonic developers
|
||||
*
|
||||
* Distributed under terms of the GNU GPLv3 license.
|
||||
*/
|
||||
|
||||
package org.moire.ultrasonic.api.subsonic
|
||||
|
||||
import okhttp3.ResponseBody
|
||||
|
@ -1,3 +1,10 @@
|
||||
/*
|
||||
* AlbumListOrderType.kt
|
||||
* Copyright (C) 2009-2022 Ultrasonic developers
|
||||
*
|
||||
* Distributed under terms of the GNU GPLv3 license.
|
||||
*/
|
||||
|
||||
package org.moire.ultrasonic.api.subsonic.models
|
||||
|
||||
/**
|
||||
@ -16,8 +23,7 @@ enum class AlbumListType(val typeName: String) {
|
||||
SORTED_BY_ARTIST("alphabeticalByArtist"),
|
||||
STARRED("starred"),
|
||||
BY_YEAR("byYear"),
|
||||
BY_GENRE("byGenre"),
|
||||
BY_ARTIST("albumsByArtist");
|
||||
BY_GENRE("byGenre");
|
||||
|
||||
override fun toString(): String {
|
||||
return typeName
|
||||
@ -36,7 +42,6 @@ enum class AlbumListType(val typeName: String) {
|
||||
in STARRED.typeName -> STARRED
|
||||
in BY_YEAR.typeName -> BY_YEAR
|
||||
in BY_GENRE.typeName -> BY_GENRE
|
||||
in BY_ARTIST.typeName -> BY_ARTIST
|
||||
else -> throw IllegalArgumentException("Unknown type: $typeName")
|
||||
}
|
||||
|
||||
|
@ -36,6 +36,7 @@ class AlbumListTypeTest {
|
||||
|
||||
@Test
|
||||
fun `Should return type name for toString call`() {
|
||||
AlbumListType.STARRED.typeName `should be equal to` AlbumListType.STARRED.toString()
|
||||
AlbumListType.STARRED.typeName `should be equal to`
|
||||
AlbumListType.STARRED.toString()
|
||||
}
|
||||
}
|
||||
|
@ -9,12 +9,10 @@ ktlint = "0.43.2"
|
||||
ktlintGradle = "11.0.0"
|
||||
detekt = "1.21.0"
|
||||
preferences = "1.2.0"
|
||||
media = "1.6.0"
|
||||
media3 = "1.0.0-beta02"
|
||||
|
||||
androidSupport = "1.5.0"
|
||||
androidLegacySupport = "1.0.0"
|
||||
androidSupportDesign = "1.6.1"
|
||||
materialDesign = "1.6.1"
|
||||
constraintLayout = "2.1.4"
|
||||
multidex = "2.0.1"
|
||||
room = "2.4.3"
|
||||
@ -22,6 +20,7 @@ kotlin = "1.7.20"
|
||||
kotlinxCoroutines = "1.6.4-native-mt"
|
||||
kotlinxGuava = "1.6.4"
|
||||
viewModelKtx = "2.5.1"
|
||||
swipeRefresh = "1.1.0"
|
||||
|
||||
retrofit = "2.9.0"
|
||||
jackson = "2.13.4"
|
||||
@ -50,8 +49,7 @@ ktlintGradle = { module = "org.jlleitschuh.gradle:ktlint-gradle", ver
|
||||
detekt = { module = "io.gitlab.arturbosch.detekt:detekt-gradle-plugin", version.ref = "detekt" }
|
||||
|
||||
core = { module = "androidx.core:core-ktx", version.ref = "androidxcore" }
|
||||
support = { module = "androidx.legacy:legacy-support-v4", version.ref = "androidLegacySupport" }
|
||||
design = { module = "com.google.android.material:material", version.ref = "androidSupportDesign" }
|
||||
design = { module = "com.google.android.material:material", version.ref = "materialDesign" }
|
||||
annotations = { module = "androidx.annotation:annotation", version.ref = "androidSupport" }
|
||||
multidex = { module = "androidx.multidex:multidex", version.ref = "multidex" }
|
||||
constraintLayout = { module = "androidx.constraintlayout:constraintlayout", version.ref = "constraintLayout" }
|
||||
@ -66,11 +64,10 @@ navigationUiKtx = { module = "androidx.navigation:navigation-ui-ktx", ve
|
||||
navigationFeature = { module = "androidx.navigation:navigation-dynamic-features-fragment", version.ref = "navigation" }
|
||||
navigationSafeArgs = { module = "androidx.navigation:navigation-safe-args-gradle-plugin", version.ref = "navigation"}
|
||||
preferences = { module = "androidx.preference:preference", version.ref = "preferences" }
|
||||
media = { module = "androidx.media:media", version.ref = "media" }
|
||||
media3exoplayer = { module = "androidx.media3:media3-exoplayer", version.ref = "media3" }
|
||||
media3okhttp = { module = "androidx.media3:media3-datasource-okhttp", version.ref = "media3" }
|
||||
media3session = { module = "androidx.media3:media3-session", version.ref = "media3" }
|
||||
|
||||
swipeRefresh = { module = "androidx.swiperefreshlayout:swiperefreshlayout", version.ref = "swipeRefresh" }
|
||||
kotlinStdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" }
|
||||
kotlinReflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin" }
|
||||
kotlinxCoroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinxCoroutines" }
|
||||
|
@ -4,6 +4,7 @@
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply from: "${project.rootDir}/gradle_scripts/code_quality.gradle"
|
||||
apply plugin: 'kotlin-kapt'
|
||||
|
||||
android {
|
||||
compileSdkVersion versions.compileSdk
|
||||
|
@ -97,7 +97,6 @@ dependencies {
|
||||
}
|
||||
|
||||
implementation libs.core
|
||||
implementation libs.support
|
||||
implementation libs.design
|
||||
implementation libs.multidex
|
||||
implementation libs.roomRuntime
|
||||
@ -105,10 +104,10 @@ dependencies {
|
||||
implementation libs.viewModelKtx
|
||||
implementation libs.constraintLayout
|
||||
implementation libs.preferences
|
||||
implementation libs.media
|
||||
implementation libs.media3exoplayer
|
||||
implementation libs.media3session
|
||||
implementation libs.media3okhttp
|
||||
implementation libs.swipeRefresh
|
||||
|
||||
implementation libs.navigationFragment
|
||||
implementation libs.navigationUi
|
||||
|
@ -8,7 +8,7 @@
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/strings.xml"
|
||||
line="154"
|
||||
line="153"
|
||||
column="5"/>
|
||||
</issue>
|
||||
|
||||
@ -70,138 +70,6 @@
|
||||
column="10"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ObsoleteLayoutParam"
|
||||
message="Invalid layout param in a `LinearLayout`: `layout_above`"
|
||||
errorLine1=" android:layout_above="@+id/bottom">"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/time_span_dialog.xml"
|
||||
line="12"
|
||||
column="9"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ObsoleteLayoutParam"
|
||||
message="Invalid layout param in a `LinearLayout`: `layout_below`"
|
||||
errorLine1=" android:layout_below="@+id/top">"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/time_span_dialog.xml"
|
||||
line="28"
|
||||
column="9"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="UnusedResources"
|
||||
message="The resource `R.drawable.ic_baseline_close` appears to be unused"
|
||||
errorLine1="<vector xmlns:android="http://schemas.android.com/apk/res/android""
|
||||
errorLine2="^">
|
||||
<location
|
||||
file="src/main/res/drawable/ic_baseline_close.xml"
|
||||
line="1"
|
||||
column="1"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="UnusedResources"
|
||||
message="The resource `R.drawable.ic_create_new_folder` appears to be unused"
|
||||
errorLine1="<vector xmlns:android="http://schemas.android.com/apk/res/android""
|
||||
errorLine2="^">
|
||||
<location
|
||||
file="src/main/res/drawable/ic_create_new_folder.xml"
|
||||
line="1"
|
||||
column="1"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="UnusedResources"
|
||||
message="The resource `R.drawable.ic_drag_queue` appears to be unused"
|
||||
errorLine1="<vector xmlns:android="http://schemas.android.com/apk/res/android""
|
||||
errorLine2="^">
|
||||
<location
|
||||
file="src/main/res/drawable/ic_drag_queue.xml"
|
||||
line="1"
|
||||
column="1"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="UnusedResources"
|
||||
message="The resource `R.drawable.ic_folder` appears to be unused"
|
||||
errorLine1="<vector xmlns:android="http://schemas.android.com/apk/res/android""
|
||||
errorLine2="^">
|
||||
<location
|
||||
file="src/main/res/drawable/ic_folder.xml"
|
||||
line="1"
|
||||
column="1"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="UnusedResources"
|
||||
message="The resource `R.drawable.ic_menu_arrow` appears to be unused"
|
||||
errorLine1="<vector xmlns:android="http://schemas.android.com/apk/res/android""
|
||||
errorLine2="^">
|
||||
<location
|
||||
file="src/main/res/drawable/ic_menu_arrow.xml"
|
||||
line="1"
|
||||
column="1"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="UnusedResources"
|
||||
message="The resource `R.drawable.ic_menu_backward` appears to be unused"
|
||||
errorLine1="<vector xmlns:android="http://schemas.android.com/apk/res/android""
|
||||
errorLine2="^">
|
||||
<location
|
||||
file="src/main/res/drawable/ic_menu_backward.xml"
|
||||
line="1"
|
||||
column="1"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="UnusedResources"
|
||||
message="The resource `R.drawable.ic_menu_forward` appears to be unused"
|
||||
errorLine1="<vector xmlns:android="http://schemas.android.com/apk/res/android""
|
||||
errorLine2="^">
|
||||
<location
|
||||
file="src/main/res/drawable/ic_menu_forward.xml"
|
||||
line="1"
|
||||
column="1"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="UnusedResources"
|
||||
message="The resource `R.drawable.ic_menu_help` appears to be unused"
|
||||
errorLine1="<vector xmlns:android="http://schemas.android.com/apk/res/android""
|
||||
errorLine2="^">
|
||||
<location
|
||||
file="src/main/res/drawable/ic_menu_help.xml"
|
||||
line="1"
|
||||
column="1"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="UnusedResources"
|
||||
message="The resource `R.drawable.ic_sd_storage` appears to be unused"
|
||||
errorLine1="<vector xmlns:android="http://schemas.android.com/apk/res/android""
|
||||
errorLine2="^">
|
||||
<location
|
||||
file="src/main/res/drawable/ic_sd_storage.xml"
|
||||
line="1"
|
||||
column="1"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="UnusedResources"
|
||||
message="The resource `R.drawable.ic_subdirectory_up` appears to be unused"
|
||||
errorLine1="<vector xmlns:android="http://schemas.android.com/apk/res/android""
|
||||
errorLine2="^">
|
||||
<location
|
||||
file="src/main/res/drawable/ic_subdirectory_up.xml"
|
||||
line="1"
|
||||
column="1"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="UnusedResources"
|
||||
message="The resource `R.drawable.media3_notification_pause` appears to be unused"
|
||||
@ -297,54 +165,10 @@
|
||||
errorLine2=" ~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/time_span_dialog.xml"
|
||||
line="30"
|
||||
line="28"
|
||||
column="10"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="Autofill"
|
||||
message="Missing `autofillHints` attribute"
|
||||
errorLine1=" <EditText"
|
||||
errorLine2=" ~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/update_playlist.xml"
|
||||
line="18"
|
||||
column="4"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="Autofill"
|
||||
message="Missing `autofillHints` attribute"
|
||||
errorLine1=" <EditText"
|
||||
errorLine2=" ~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/update_playlist.xml"
|
||||
line="40"
|
||||
column="4"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ContentDescription"
|
||||
message="Missing `contentDescription` attribute on image"
|
||||
errorLine1=" <ImageView"
|
||||
errorLine2=" ~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/chat_item.xml"
|
||||
line="7"
|
||||
column="6"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ContentDescription"
|
||||
message="Missing `contentDescription` attribute on image"
|
||||
errorLine1=" <ImageView"
|
||||
errorLine2=" ~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/chat_item_reverse.xml"
|
||||
line="64"
|
||||
column="6"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="LabelFor"
|
||||
message="Missing accessibility label: provide either a view with an `android:labelFor` that references this view or provide an `android:hint`"
|
||||
@ -374,29 +198,7 @@
|
||||
errorLine2=" ~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/time_span_dialog.xml"
|
||||
line="30"
|
||||
column="10"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="HardcodedText"
|
||||
message="Hardcoded string "0 dB", should use `@string` resource"
|
||||
errorLine1=" a:text="0 dB""
|
||||
errorLine2=" ~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/equalizer_bar.xml"
|
||||
line="26"
|
||||
column="13"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="RelativeOverlap"
|
||||
message="`@id/current_playing_duration` can overlap `@id/current_playing_position` if @string/util.no_time, @string/util.no_time grow due to localized text expansion"
|
||||
errorLine1=" <TextView"
|
||||
errorLine2=" ~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/player_slider.xml"
|
||||
line="29"
|
||||
line="28"
|
||||
column="10"/>
|
||||
</issue>
|
||||
|
||||
|
@ -1,56 +0,0 @@
|
||||
package org.moire.ultrasonic.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.moire.ultrasonic.R;
|
||||
import org.moire.ultrasonic.domain.Playlist;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class PlaylistAdapter extends ArrayAdapter<Playlist>
|
||||
{
|
||||
|
||||
private final Context context;
|
||||
|
||||
public PlaylistAdapter(Context context, List<Playlist> Playlists)
|
||||
{
|
||||
super(context, R.layout.playlist_list_item, Playlists);
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent)
|
||||
{
|
||||
Playlist entry = getItem(position);
|
||||
PlaylistView view;
|
||||
|
||||
if (convertView instanceof PlaylistView)
|
||||
{
|
||||
PlaylistView currentView = (PlaylistView) convertView;
|
||||
|
||||
ViewHolder viewHolder = (ViewHolder) convertView.getTag();
|
||||
view = currentView;
|
||||
view.setViewHolder(viewHolder);
|
||||
}
|
||||
else
|
||||
{
|
||||
view = new PlaylistView(context);
|
||||
view.setLayout();
|
||||
}
|
||||
|
||||
view.setPlaylist(entry);
|
||||
return view;
|
||||
}
|
||||
|
||||
static class ViewHolder
|
||||
{
|
||||
TextView name;
|
||||
}
|
||||
}
|
@ -1,62 +0,0 @@
|
||||
/*
|
||||
This file is part of Subsonic.
|
||||
|
||||
Subsonic is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Subsonic is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Subsonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.moire.ultrasonic.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import org.moire.ultrasonic.R;
|
||||
import org.moire.ultrasonic.domain.Playlist;
|
||||
|
||||
/**
|
||||
* Used to display playlists in a {@code ListView}.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class PlaylistView extends LinearLayout
|
||||
{
|
||||
private final Context context;
|
||||
private PlaylistAdapter.ViewHolder viewHolder;
|
||||
|
||||
public PlaylistView(Context context)
|
||||
{
|
||||
super(context);
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public void setLayout()
|
||||
{
|
||||
LayoutInflater.from(context).inflate(R.layout.playlist_list_item, this, true);
|
||||
viewHolder = new PlaylistAdapter.ViewHolder();
|
||||
viewHolder.name = findViewById(R.id.playlist_name);
|
||||
setTag(viewHolder);
|
||||
}
|
||||
|
||||
public void setViewHolder(PlaylistAdapter.ViewHolder viewHolder)
|
||||
{
|
||||
this.viewHolder = viewHolder;
|
||||
setTag(this.viewHolder);
|
||||
}
|
||||
|
||||
public void setPlaylist(Playlist playlist)
|
||||
{
|
||||
viewHolder.name.setText(playlist.getName());
|
||||
}
|
||||
}
|
@ -1,62 +0,0 @@
|
||||
/*
|
||||
This file is part of Subsonic.
|
||||
|
||||
Subsonic is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Subsonic is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Subsonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.moire.ultrasonic.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import org.moire.ultrasonic.R;
|
||||
import org.moire.ultrasonic.domain.Playlist;
|
||||
|
||||
/**
|
||||
* Used to display playlists in a {@code ListView}.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class PodcastChannelItemView extends LinearLayout
|
||||
{
|
||||
private final Context context;
|
||||
private PlaylistAdapter.ViewHolder viewHolder;
|
||||
|
||||
public PodcastChannelItemView(Context context)
|
||||
{
|
||||
super(context);
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public void setLayout()
|
||||
{
|
||||
LayoutInflater.from(context).inflate(R.layout.playlist_list_item, this, true);
|
||||
viewHolder = new PlaylistAdapter.ViewHolder();
|
||||
viewHolder.name = findViewById(R.id.playlist_name);
|
||||
setTag(viewHolder);
|
||||
}
|
||||
|
||||
public void setViewHolder(PlaylistAdapter.ViewHolder viewHolder)
|
||||
{
|
||||
this.viewHolder = viewHolder;
|
||||
setTag(this.viewHolder);
|
||||
}
|
||||
|
||||
public void setPlaylist(Playlist playlist)
|
||||
{
|
||||
viewHolder.name.setText(playlist.getName());
|
||||
}
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
package org.moire.ultrasonic.view;
|
||||
|
||||
import android.content.Context;
|
||||
import androidx.annotation.NonNull;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.moire.ultrasonic.R;
|
||||
import org.moire.ultrasonic.domain.PodcastsChannel;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
public class PodcastsChannelsAdapter extends ArrayAdapter<PodcastsChannel> {
|
||||
private final LayoutInflater layoutInflater;
|
||||
|
||||
public PodcastsChannelsAdapter(Context context, List<PodcastsChannel> channels) {
|
||||
super(context, R.layout.podcasts_channel_item, channels);
|
||||
|
||||
layoutInflater = (LayoutInflater) context
|
||||
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public View getView(int position, View convertView, @NonNull ViewGroup parent) {
|
||||
PodcastsChannel entry = getItem(position);
|
||||
|
||||
TextView view;
|
||||
if (convertView instanceof PlaylistView) {
|
||||
view = (TextView) convertView;
|
||||
} else {
|
||||
view = (TextView) layoutInflater
|
||||
.inflate(R.layout.podcasts_channel_item, parent, false);
|
||||
}
|
||||
|
||||
view.setText(entry.getTitle());
|
||||
|
||||
return view;
|
||||
}
|
||||
}
|
@ -16,7 +16,6 @@ import java.util.List;
|
||||
*/
|
||||
public class ShareAdapter extends ArrayAdapter<Share>
|
||||
{
|
||||
|
||||
private final Context context;
|
||||
|
||||
public ShareAdapter(Context context, List<Share> Shares)
|
||||
|
@ -52,7 +52,6 @@ import org.moire.ultrasonic.R
|
||||
import org.moire.ultrasonic.app.UApp
|
||||
import org.moire.ultrasonic.data.ActiveServerProvider
|
||||
import org.moire.ultrasonic.data.ServerSettingDao
|
||||
import org.moire.ultrasonic.fragment.MainFragmentDirections
|
||||
import org.moire.ultrasonic.fragment.OnBackPressedHandler
|
||||
import org.moire.ultrasonic.model.ServerSettingsModel
|
||||
import org.moire.ultrasonic.provider.SearchSuggestionProvider
|
||||
@ -72,11 +71,12 @@ import timber.log.Timber
|
||||
|
||||
/**
|
||||
* The main (and only) Activity of Ultrasonic which loads all other screens as Fragments.
|
||||
* Because this is the only Activity we have to manage the apps lifecycle through tis activitys
|
||||
* Because this is the only Activity we have to manage the apps lifecycle through this activity
|
||||
* onCreate/onResume/onDestroy methods...
|
||||
*/
|
||||
@Suppress("TooManyFunctions")
|
||||
class NavigationActivity : AppCompatActivity() {
|
||||
private var videoMenuItem: MenuItem? = null
|
||||
private var chatMenuItem: MenuItem? = null
|
||||
private var bookmarksMenuItem: MenuItem? = null
|
||||
private var sharesMenuItem: MenuItem? = null
|
||||
@ -301,6 +301,13 @@ class NavigationActivity : AppCompatActivity() {
|
||||
R.id.bookmarksFragment -> {
|
||||
navController.navigate(NavigationGraphDirections.toBookmarks())
|
||||
}
|
||||
R.id.trackCollectionFragment -> {
|
||||
navController.navigate(
|
||||
NavigationGraphDirections.toTrackCollection(
|
||||
getVideos = true
|
||||
)
|
||||
)
|
||||
}
|
||||
R.id.menu_exit -> {
|
||||
setResult(Constants.RESULT_CLOSE_ALL)
|
||||
mediaPlayerController.onDestroy()
|
||||
@ -319,6 +326,7 @@ class NavigationActivity : AppCompatActivity() {
|
||||
podcastsMenuItem = navigationView?.menu?.findItem(R.id.podcastFragment)
|
||||
playlistsMenuItem = navigationView?.menu?.findItem(R.id.playlistsFragment)
|
||||
downloadsMenuItem = navigationView?.menu?.findItem(R.id.downloadsFragment)
|
||||
videoMenuItem = navigationView?.menu?.findItem(R.id.trackCollectionFragment)
|
||||
|
||||
selectServerButton =
|
||||
navigationView?.getHeaderView(0)?.findViewById(R.id.header_select_server)
|
||||
@ -348,7 +356,7 @@ class NavigationActivity : AppCompatActivity() {
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
val retValue = super.onCreateOptionsMenu(menu)
|
||||
if (navigationView == null) {
|
||||
menuInflater.inflate(R.menu.navigation, menu)
|
||||
menuInflater.inflate(R.menu.navigation_drawer, menu)
|
||||
return true
|
||||
}
|
||||
return retValue
|
||||
@ -390,7 +398,7 @@ class NavigationActivity : AppCompatActivity() {
|
||||
)
|
||||
suggestions.saveRecentQuery(query, null)
|
||||
|
||||
val action = MainFragmentDirections.toSearchFragment(query, autoPlay)
|
||||
val action = NavigationGraphDirections.toSearchFragment(query, autoPlay)
|
||||
findNavController(R.id.nav_host_fragment).navigate(action)
|
||||
}
|
||||
}
|
||||
@ -498,5 +506,6 @@ class NavigationActivity : AppCompatActivity() {
|
||||
podcastsMenuItem?.isVisible = activeServer.podcastSupport != false
|
||||
playlistsMenuItem?.isVisible = isOnline
|
||||
downloadsMenuItem?.isVisible = isOnline
|
||||
videoMenuItem?.isVisible = isOnline
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
package org.moire.ultrasonic.adapters
|
||||
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
@ -15,31 +16,31 @@ import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.drakeet.multitype.ItemViewBinder
|
||||
import com.drakeet.multitype.ItemViewDelegate
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.moire.ultrasonic.R
|
||||
import org.moire.ultrasonic.domain.Album
|
||||
import org.moire.ultrasonic.imageloader.ImageLoader
|
||||
import org.moire.ultrasonic.service.MusicServiceFactory.getMusicService
|
||||
import org.moire.ultrasonic.util.LayoutType
|
||||
import org.moire.ultrasonic.util.Settings.shouldUseId3Tags
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
* Creates a Row in a RecyclerView which contains the details of an Album
|
||||
*/
|
||||
class AlbumRowBinder(
|
||||
val onItemClick: (Album) -> Unit,
|
||||
val onContextMenuClick: (MenuItem, Album) -> Boolean,
|
||||
open class AlbumRowDelegate(
|
||||
open val onItemClick: (Album) -> Unit,
|
||||
open val onContextMenuClick: (MenuItem, Album) -> Boolean,
|
||||
private val imageLoader: ImageLoader
|
||||
) : ItemViewBinder<Album, AlbumRowBinder.ViewHolder>(), KoinComponent {
|
||||
) : ItemViewDelegate<Album, AlbumRowDelegate.ListViewHolder>(), KoinComponent {
|
||||
|
||||
private val starDrawable: Int = R.drawable.ic_star_full
|
||||
private val starHollowDrawable: Int = R.drawable.ic_star_hollow
|
||||
|
||||
// Set our layout files
|
||||
val layout = R.layout.list_item_album
|
||||
open var layoutType = LayoutType.LIST
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, item: Album) {
|
||||
override fun onBindViewHolder(holder: ListViewHolder, item: Album) {
|
||||
holder.album.text = item.title
|
||||
holder.artist.text = item.artist
|
||||
holder.details.setOnClickListener { onItemClick(item) }
|
||||
@ -66,15 +67,40 @@ class AlbumRowBinder(
|
||||
/**
|
||||
* Holds the view properties of an Item row
|
||||
*/
|
||||
class ViewHolder(
|
||||
open class ListViewHolder(
|
||||
view: View
|
||||
) : RecyclerView.ViewHolder(view) {
|
||||
var album: TextView = view.findViewById(R.id.album_title)
|
||||
var artist: TextView = view.findViewById(R.id.album_artist)
|
||||
var details: LinearLayout = view.findViewById(R.id.row_album_details)
|
||||
var coverArt: ImageView = view.findViewById(R.id.coverart)
|
||||
var star: ImageView = view.findViewById(R.id.album_star)
|
||||
|
||||
var album: TextView
|
||||
var artist: TextView
|
||||
var details: LinearLayout
|
||||
var coverArt: ImageView
|
||||
var star: ImageView
|
||||
var coverArtId: String? = null
|
||||
|
||||
constructor(parent: ViewGroup, inflater: LayoutInflater) : this(
|
||||
inflater.inflate(R.layout.list_item_album, parent, false)
|
||||
)
|
||||
|
||||
init {
|
||||
album = view.findViewById(R.id.album_title)
|
||||
artist = view.findViewById(R.id.album_artist)
|
||||
details = view.findViewById(R.id.row_album_details)
|
||||
coverArt = view.findViewById(R.id.cover_art)
|
||||
star = view.findViewById(R.id.album_star)
|
||||
coverArtId = null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds the view properties of an Item row
|
||||
*/
|
||||
class CoverViewHolder(
|
||||
view: View
|
||||
) : ListViewHolder(view) {
|
||||
constructor(parent: ViewGroup, inflater: LayoutInflater) : this(
|
||||
inflater.inflate(R.layout.grid_item_album, parent, false)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -106,7 +132,24 @@ class AlbumRowBinder(
|
||||
}.start()
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(inflater: LayoutInflater, parent: ViewGroup): ViewHolder {
|
||||
return ViewHolder(inflater.inflate(layout, parent, false))
|
||||
override fun onCreateViewHolder(context: Context, parent: ViewGroup): ListViewHolder {
|
||||
return when (layoutType) {
|
||||
LayoutType.LIST -> ListViewHolder(
|
||||
parent,
|
||||
LayoutInflater.from(context)
|
||||
)
|
||||
LayoutType.COVER -> CoverViewHolder(
|
||||
parent,
|
||||
LayoutInflater.from(context)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AlbumGridDelegate(
|
||||
onItemClick: (Album) -> Unit,
|
||||
onContextMenuClick: (MenuItem, Album) -> Boolean,
|
||||
imageLoader: ImageLoader
|
||||
) : AlbumRowDelegate(onItemClick, onContextMenuClick, imageLoader) {
|
||||
override var layoutType = LayoutType.COVER
|
||||
}
|
@ -123,7 +123,7 @@ class ArtistRowBinder(
|
||||
var section: TextView = itemView.findViewById(R.id.row_section)
|
||||
var textView: TextView = itemView.findViewById(R.id.row_artist_name)
|
||||
var layout: RelativeLayout = itemView.findViewById(R.id.containing_layout)
|
||||
var coverArt: ImageView = itemView.findViewById(R.id.coverart)
|
||||
var coverArt: ImageView = itemView.findViewById(R.id.cover_art)
|
||||
var coverArtId: String? = null
|
||||
}
|
||||
|
||||
|
@ -13,7 +13,6 @@ import org.koin.core.component.KoinComponent
|
||||
import org.moire.ultrasonic.R
|
||||
import org.moire.ultrasonic.domain.Identifiable
|
||||
import org.moire.ultrasonic.domain.Track
|
||||
import timber.log.Timber
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
class TrackViewBinder(
|
||||
@ -63,7 +62,7 @@ class TrackViewBinder(
|
||||
diffAdapter.isSelected(item.longId)
|
||||
)
|
||||
|
||||
Timber.v("Setting listeners")
|
||||
// Timber.v("Setting listeners")
|
||||
|
||||
holder.itemView.setOnLongClickListener {
|
||||
if (onContextMenuClick != null) {
|
||||
@ -118,7 +117,7 @@ class TrackViewBinder(
|
||||
if (newStatus != holder.check.isChecked) holder.check.isChecked = newStatus
|
||||
}
|
||||
|
||||
Timber.v("Setting listeners done")
|
||||
// Timber.v("Setting listeners done")
|
||||
}
|
||||
|
||||
override fun onViewRecycled(holder: TrackViewHolder) {
|
||||
|
@ -81,7 +81,7 @@ class TrackViewHolder(val view: View) :
|
||||
draggable: Boolean,
|
||||
isSelected: Boolean = false
|
||||
) {
|
||||
Timber.v("Setting song")
|
||||
// Timber.v("Setting song")
|
||||
val useFiveStarRating = Settings.useFiveStarRating
|
||||
entry = song
|
||||
|
||||
@ -139,7 +139,7 @@ class TrackViewHolder(val view: View) :
|
||||
updateStatus(it.state, it.progress)
|
||||
}
|
||||
|
||||
Timber.v("Setting song done")
|
||||
// Timber.v("Setting song done")
|
||||
}
|
||||
|
||||
// This is called when the Holder is recycled and receives a new Song
|
||||
|
@ -10,24 +10,41 @@
|
||||
package org.moire.ultrasonic.fragment
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.navigation.fragment.navArgs
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kotlinx.coroutines.launch
|
||||
import org.moire.ultrasonic.NavigationGraphDirections
|
||||
import org.moire.ultrasonic.R
|
||||
import org.moire.ultrasonic.adapters.AlbumRowBinder
|
||||
import org.moire.ultrasonic.adapters.AlbumGridDelegate
|
||||
import org.moire.ultrasonic.adapters.AlbumRowDelegate
|
||||
import org.moire.ultrasonic.api.subsonic.models.AlbumListType
|
||||
import org.moire.ultrasonic.data.ActiveServerProvider
|
||||
import org.moire.ultrasonic.domain.Album
|
||||
import org.moire.ultrasonic.model.AlbumListModel
|
||||
import org.moire.ultrasonic.util.LayoutType
|
||||
import org.moire.ultrasonic.util.Settings
|
||||
import org.moire.ultrasonic.view.FilterButtonBar
|
||||
import org.moire.ultrasonic.view.SortOrder
|
||||
import org.moire.ultrasonic.view.ViewCapabilities
|
||||
|
||||
/**
|
||||
* Displays a list of Albums from the media library
|
||||
*/
|
||||
class AlbumListFragment : EntryListFragment<Album>() {
|
||||
class AlbumListFragment(
|
||||
private var layoutType: LayoutType = LayoutType.LIST,
|
||||
private var orderType: SortOrder? = null
|
||||
) : FilterableFragment, EntryListFragment<Album>() {
|
||||
|
||||
private var filterButtonBar: FilterButtonBar? = null
|
||||
|
||||
/**
|
||||
* The ViewModel to use to get the data
|
||||
@ -50,7 +67,8 @@ class AlbumListFragment : EntryListFragment<Album>() {
|
||||
* The central function to pass a query to the model and return a LiveData object
|
||||
*/
|
||||
override fun getLiveData(
|
||||
refresh: Boolean
|
||||
refresh: Boolean,
|
||||
append: Boolean
|
||||
): LiveData<List<Album>> {
|
||||
fetchAlbums(refresh)
|
||||
|
||||
@ -63,7 +81,7 @@ class AlbumListFragment : EntryListFragment<Album>() {
|
||||
listModel.viewModelScope.launch(handler) {
|
||||
refreshListView?.isRefreshing = true
|
||||
|
||||
if (navArgs.type == AlbumListType.BY_ARTIST) {
|
||||
if (navArgs.byArtist) {
|
||||
listModel.getAlbumsOfArtist(
|
||||
refresh = navArgs.refresh,
|
||||
id = navArgs.id!!,
|
||||
@ -71,7 +89,7 @@ class AlbumListFragment : EntryListFragment<Album>() {
|
||||
)
|
||||
} else {
|
||||
listModel.getAlbums(
|
||||
albumListType = navArgs.type,
|
||||
albumListType = orderType?.mapToAlbumListType() ?: navArgs.type,
|
||||
size = navArgs.size,
|
||||
offset = navArgs.offset,
|
||||
append = append,
|
||||
@ -82,38 +100,145 @@ class AlbumListFragment : EntryListFragment<Album>() {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Make generic
|
||||
override fun setLayoutType(newType: LayoutType) {
|
||||
layoutType = newType
|
||||
viewManager = if (layoutType == LayoutType.LIST) {
|
||||
LinearLayoutManager(this.context)
|
||||
} else {
|
||||
GridLayoutManager(this.context, ROWS)
|
||||
}
|
||||
|
||||
listView!!.layoutManager = viewManager
|
||||
|
||||
// Attach our onScrollListener
|
||||
val scrollListener = object : EndlessScrollListener(viewManager) {
|
||||
override fun onLoadMore(page: Int, totalItemsCount: Int, view: RecyclerView?) {
|
||||
// Triggered only when new data needs to be appended to the list
|
||||
// Add whatever code is needed to append new items to the bottom of the list
|
||||
fetchAlbums(append = true)
|
||||
}
|
||||
}
|
||||
|
||||
listView!!.addOnScrollListener(scrollListener)
|
||||
}
|
||||
|
||||
override fun setOrderType(newOrder: SortOrder) {
|
||||
orderType = newOrder
|
||||
|
||||
// If we are on an Artist page we just need to reorder the list. Otherwise refetch
|
||||
if (navArgs.byArtist) {
|
||||
listModel.sortListByOrder(newOrder.mapToAlbumListType())
|
||||
} else {
|
||||
fetchAlbums(refresh = true, append = false)
|
||||
}
|
||||
}
|
||||
|
||||
override var viewCapabilities: ViewCapabilities = ViewCapabilities(
|
||||
supportsGrid = true,
|
||||
supportedSortOrders = getListOfSortOrders()
|
||||
)
|
||||
|
||||
private fun getListOfSortOrders(): List<SortOrder> {
|
||||
val useId3 = Settings.shouldUseId3Tags
|
||||
val useId3Offline = Settings.useId3TagsOffline
|
||||
val isOnline = !ActiveServerProvider.isOffline()
|
||||
|
||||
val supported = mutableListOf<SortOrder>()
|
||||
|
||||
if (isOnline || useId3Offline) {
|
||||
supported.add(SortOrder.NEWEST)
|
||||
}
|
||||
if (isOnline) {
|
||||
supported.add(SortOrder.RECENT)
|
||||
}
|
||||
if (isOnline) {
|
||||
supported.add(SortOrder.FREQUENT)
|
||||
}
|
||||
if (isOnline && !useId3) {
|
||||
supported.add(SortOrder.HIGHEST)
|
||||
}
|
||||
if (isOnline) {
|
||||
supported.add(SortOrder.RANDOM)
|
||||
}
|
||||
if (isOnline) {
|
||||
supported.add(SortOrder.STARRED)
|
||||
}
|
||||
if (isOnline || useId3Offline) {
|
||||
supported.add(SortOrder.BY_NAME)
|
||||
}
|
||||
if (isOnline || useId3Offline) {
|
||||
supported.add(SortOrder.BY_ARTIST)
|
||||
}
|
||||
|
||||
return supported
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
val layout = if (navArgs.byArtist) R.layout.list_layout_filterable else mainLayout
|
||||
return inflater.inflate(layout, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
setTitle(navArgs.title)
|
||||
|
||||
// Attach our onScrollListener
|
||||
listView = view.findViewById<RecyclerView>(recyclerViewId).apply {
|
||||
val scrollListener = object : EndlessScrollListener(viewManager) {
|
||||
override fun onLoadMore(page: Int, totalItemsCount: Int, view: RecyclerView?) {
|
||||
// Triggered only when new data needs to be appended to the list
|
||||
// Add whatever code is needed to append new items to the bottom of the list
|
||||
fetchAlbums(append = true)
|
||||
}
|
||||
}
|
||||
addOnScrollListener(scrollListener)
|
||||
// In most cases this fragment will be hosted by a ViewPager2 in the MainFragment,
|
||||
// which provides its own FilterBar.
|
||||
// But when we are looking at the Albums of a specific Artist this Fragment is standalone,
|
||||
// so we need to setup the FilterBar here..
|
||||
if (navArgs.byArtist) {
|
||||
setTitle(navArgs.title)
|
||||
setupFilterBar(view)
|
||||
}
|
||||
|
||||
viewAdapter.register(
|
||||
AlbumRowBinder(
|
||||
{ entry -> onItemClick(entry) },
|
||||
{ menuItem, entry -> onContextMenuItemSelected(menuItem, entry) },
|
||||
imageLoaderProvider.getImageLoader()
|
||||
)
|
||||
)
|
||||
// Get a reference to the listView
|
||||
listView = view.findViewById(recyclerViewId)
|
||||
|
||||
setLayoutType(layoutType)
|
||||
|
||||
val imageLoader = imageLoaderProvider.getImageLoader()
|
||||
|
||||
// Magic to switch between different view layouts:
|
||||
// We register two delegates, one which layouts grid items and one which layouts row items
|
||||
// Based on the current status of the ViewType, the right delegate is picked.
|
||||
viewAdapter.register(Album::class).to(
|
||||
AlbumRowDelegate(::onItemClick, ::onContextMenuItemSelected, imageLoader),
|
||||
AlbumGridDelegate(::onItemClick, ::onContextMenuItemSelected, imageLoader)
|
||||
).withKotlinClassLinker { _, _ ->
|
||||
when (layoutType) {
|
||||
LayoutType.COVER -> AlbumGridDelegate::class
|
||||
LayoutType.LIST -> AlbumRowDelegate::class
|
||||
}
|
||||
}
|
||||
|
||||
emptyTextView.setText(R.string.select_album_empty)
|
||||
}
|
||||
|
||||
private fun setupFilterBar(view: View) {
|
||||
// Load last layout from settings
|
||||
layoutType = LayoutType.from(Settings.lastViewType)
|
||||
filterButtonBar = view.findViewById(R.id.filter_button_bar)
|
||||
filterButtonBar!!.setOnLayoutTypeChangedListener(::setLayoutType)
|
||||
filterButtonBar!!.setOnOrderChangedListener(::setOrderType)
|
||||
filterButtonBar!!.configureWithCapabilities(
|
||||
ViewCapabilities(
|
||||
supportsGrid = true,
|
||||
supportedSortOrders = listOf(
|
||||
SortOrder.BY_NAME,
|
||||
SortOrder.BY_YEAR
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
// Set layout toggle Chip to correct state
|
||||
filterButtonBar!!.setLayoutType(layoutType)
|
||||
}
|
||||
|
||||
override fun onItemClick(item: Album) {
|
||||
val action = AlbumListFragmentDirections.albumListToTrackCollection(
|
||||
val action = NavigationGraphDirections.toTrackCollection(
|
||||
item.id,
|
||||
isAlbum = item.isDirectory,
|
||||
name = item.title,
|
||||
@ -121,4 +246,20 @@ class AlbumListFragment : EntryListFragment<Album>() {
|
||||
)
|
||||
findNavController().navigate(action)
|
||||
}
|
||||
|
||||
private fun SortOrder.mapToAlbumListType(): AlbumListType = when (this) {
|
||||
SortOrder.RANDOM -> AlbumListType.RANDOM
|
||||
SortOrder.NEWEST -> AlbumListType.NEWEST
|
||||
SortOrder.HIGHEST -> AlbumListType.HIGHEST
|
||||
SortOrder.FREQUENT -> AlbumListType.FREQUENT
|
||||
SortOrder.RECENT -> AlbumListType.RECENT
|
||||
SortOrder.BY_NAME -> AlbumListType.SORTED_BY_NAME
|
||||
SortOrder.BY_ARTIST -> AlbumListType.SORTED_BY_ARTIST
|
||||
SortOrder.STARRED -> AlbumListType.STARRED
|
||||
SortOrder.BY_YEAR -> AlbumListType.BY_YEAR
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val ROWS = 3
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.navigation.fragment.navArgs
|
||||
import org.moire.ultrasonic.NavigationGraphDirections
|
||||
import org.moire.ultrasonic.R
|
||||
import org.moire.ultrasonic.adapters.ArtistRowBinder
|
||||
import org.moire.ultrasonic.api.subsonic.models.AlbumListType
|
||||
@ -41,7 +42,7 @@ class ArtistListFragment : EntryListFragment<ArtistOrIndex>() {
|
||||
/**
|
||||
* The central function to pass a query to the model and return a LiveData object
|
||||
*/
|
||||
override fun getLiveData(refresh: Boolean): LiveData<List<ArtistOrIndex>> {
|
||||
override fun getLiveData(refresh: Boolean, append: Boolean): LiveData<List<ArtistOrIndex>> {
|
||||
return listModel.getItems(navArgs.refresh || refresh, refreshListView!!)
|
||||
}
|
||||
|
||||
@ -66,15 +67,16 @@ class ArtistListFragment : EntryListFragment<ArtistOrIndex>() {
|
||||
override fun onItemClick(item: ArtistOrIndex) {
|
||||
// Check type
|
||||
val action = if (item is Index) {
|
||||
ArtistListFragmentDirections.artistsListToTrackCollection(
|
||||
NavigationGraphDirections.toTrackCollection(
|
||||
id = item.id,
|
||||
name = item.name,
|
||||
parentId = item.id,
|
||||
isArtist = (item is Artist)
|
||||
)
|
||||
} else {
|
||||
ArtistListFragmentDirections.artistsListToAlbumsList(
|
||||
type = AlbumListType.BY_ARTIST,
|
||||
NavigationGraphDirections.toAlbumList(
|
||||
type = AlbumListType.SORTED_BY_NAME,
|
||||
byArtist = true,
|
||||
id = item.id,
|
||||
title = item.name,
|
||||
size = 1000,
|
||||
|
@ -37,7 +37,8 @@ class BookmarksFragment : TrackCollectionFragment() {
|
||||
}
|
||||
|
||||
override fun getLiveData(
|
||||
refresh: Boolean
|
||||
refresh: Boolean,
|
||||
append: Boolean
|
||||
): LiveData<List<MusicDirectory.Child>> {
|
||||
listModel.viewModelScope.launch(handler) {
|
||||
refreshListView?.isRefreshing = true
|
||||
|
@ -40,7 +40,7 @@ class DownloadsFragment : MultiListFragment<Track>() {
|
||||
/**
|
||||
* The central function to pass a query to the model and return a LiveData object
|
||||
*/
|
||||
override fun getLiveData(refresh: Boolean): LiveData<List<Track>> {
|
||||
override fun getLiveData(refresh: Boolean, append: Boolean): LiveData<List<Track>> {
|
||||
return listModel.getList()
|
||||
}
|
||||
|
||||
|
@ -89,6 +89,7 @@ abstract class EndlessScrollListener : RecyclerView.OnScrollListener {
|
||||
loading = true
|
||||
}
|
||||
}
|
||||
|
||||
// If it’s still loading, we check to see if the dataset count has
|
||||
// changed, if so we conclude it has finished loading and update the current page
|
||||
// number and total item count.
|
||||
|
@ -2,6 +2,7 @@ package org.moire.ultrasonic.fragment
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.navigation.fragment.NavHostFragment
|
||||
|
||||
/**
|
||||
* Contains utility functions related to Fragment title handling
|
||||
@ -9,11 +10,17 @@ import androidx.fragment.app.Fragment
|
||||
class FragmentTitle {
|
||||
companion object {
|
||||
fun setTitle(fragment: Fragment, title: CharSequence?) {
|
||||
(fragment.activity as AppCompatActivity).supportActionBar?.title = title
|
||||
// Only set the title if our fragment is a direct child of the NavHostFragment...
|
||||
if (fragment.parentFragment is NavHostFragment) {
|
||||
(fragment.activity as AppCompatActivity).supportActionBar?.title = title
|
||||
}
|
||||
}
|
||||
|
||||
fun setTitle(fragment: Fragment, id: Int) {
|
||||
(fragment.activity as AppCompatActivity).supportActionBar?.setTitle(id)
|
||||
// Only set the title if our fragment is a direct child of the NavHostFragment...
|
||||
if (fragment.parentFragment is NavHostFragment) {
|
||||
(fragment.activity as AppCompatActivity).supportActionBar?.setTitle(id)
|
||||
}
|
||||
}
|
||||
|
||||
fun getTitle(fragment: Fragment): CharSequence? {
|
||||
|
@ -7,251 +7,217 @@
|
||||
|
||||
package org.moire.ultrasonic.fragment
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||
import androidx.viewpager2.widget.ViewPager2
|
||||
import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import com.google.android.material.tabs.TabLayoutMediator
|
||||
import java.lang.ref.SoftReference
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.moire.ultrasonic.NavigationGraphDirections
|
||||
import org.moire.ultrasonic.R
|
||||
import org.moire.ultrasonic.api.subsonic.models.AlbumListType
|
||||
import org.moire.ultrasonic.data.ActiveServerProvider.Companion.isOffline
|
||||
import org.moire.ultrasonic.databinding.MainBinding
|
||||
import org.moire.ultrasonic.data.ActiveServerProvider
|
||||
import org.moire.ultrasonic.fragment.legacy.PlaylistsFragment
|
||||
import org.moire.ultrasonic.fragment.legacy.SelectGenreFragment
|
||||
import org.moire.ultrasonic.util.LayoutType
|
||||
import org.moire.ultrasonic.util.Settings
|
||||
import org.moire.ultrasonic.util.Util
|
||||
import org.moire.ultrasonic.view.EMPTY_CAPABILITIES
|
||||
import org.moire.ultrasonic.view.FilterButtonBar
|
||||
import org.moire.ultrasonic.view.SortOrder
|
||||
import org.moire.ultrasonic.view.ViewCapabilities
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
* Displays the Main screen of Ultrasonic, where the music library can be browsed
|
||||
*/
|
||||
class MainFragment : Fragment(), KoinComponent {
|
||||
|
||||
private lateinit var musicTitle: TextView
|
||||
private lateinit var artistsButton: TextView
|
||||
private lateinit var albumsButton: TextView
|
||||
private lateinit var genresButton: TextView
|
||||
private lateinit var videosTitle: TextView
|
||||
private lateinit var songsTitle: TextView
|
||||
private lateinit var randomSongsButton: TextView
|
||||
private lateinit var songsStarredButton: TextView
|
||||
private lateinit var albumsTitle: TextView
|
||||
private lateinit var albumsNewestButton: TextView
|
||||
private lateinit var albumsRandomButton: TextView
|
||||
private lateinit var albumsHighestButton: TextView
|
||||
private lateinit var albumsStarredButton: TextView
|
||||
private lateinit var albumsRecentButton: TextView
|
||||
private lateinit var albumsFrequentButton: TextView
|
||||
private lateinit var albumsAlphaByNameButton: TextView
|
||||
private lateinit var albumsAlphaByArtistButton: TextView
|
||||
private lateinit var videosButton: TextView
|
||||
private var filterButtonBar: FilterButtonBar? = null
|
||||
private var layoutType: LayoutType = LayoutType.COVER
|
||||
private var binding: View? = null
|
||||
|
||||
private var binding: MainBinding? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
Util.applyTheme(this.context)
|
||||
super.onCreate(savedInstanceState)
|
||||
}
|
||||
private lateinit var musicCollectionAdapter: MusicCollectionAdapter
|
||||
private lateinit var viewPager: ViewPager2
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
binding = MainBinding.inflate(inflater, container, false)
|
||||
return binding!!.root
|
||||
Timber.i("onCreate")
|
||||
binding = inflater.inflate(R.layout.primary, container, false)
|
||||
return binding!!
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
setupButtons()
|
||||
setupClickListener()
|
||||
setupItemVisibility()
|
||||
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
}
|
||||
FragmentTitle.setTitle(this, R.string.music_library_label)
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
var shouldRelayout = false
|
||||
val currentId3Setting = Settings.shouldUseId3Tags
|
||||
// Load last layout from settings
|
||||
layoutType = LayoutType.from(Settings.lastViewType)
|
||||
|
||||
// If setting has changed...
|
||||
if (currentId3Setting != useId3) {
|
||||
useId3 = currentId3Setting
|
||||
shouldRelayout = true
|
||||
// Init ViewPager2
|
||||
musicCollectionAdapter = MusicCollectionAdapter(this, layoutType)
|
||||
viewPager = binding!!.findViewById(R.id.pager)
|
||||
viewPager.adapter = musicCollectionAdapter
|
||||
|
||||
filterButtonBar = binding!!.findViewById(R.id.filter_button_bar)
|
||||
musicCollectionAdapter.filterButtonBar = filterButtonBar
|
||||
|
||||
filterButtonBar!!.setOnLayoutTypeChangedListener {
|
||||
updateLayoutTypeOnCurrentFragment(it)
|
||||
}
|
||||
|
||||
// then setup the list anew.
|
||||
if (shouldRelayout) {
|
||||
setupItemVisibility()
|
||||
filterButtonBar!!.setOnOrderChangedListener {
|
||||
updateSortOrderOnCurrentFragment(it)
|
||||
}
|
||||
|
||||
// Set layout toggle Chip to correct state
|
||||
filterButtonBar!!.setLayoutType(layoutType)
|
||||
|
||||
// Listen to changes in the current page (=fragment)
|
||||
viewPager.registerOnPageChangeCallback(object : OnPageChangeCallback() {
|
||||
override fun onPageSelected(position: Int) {
|
||||
super.onPageSelected(position)
|
||||
|
||||
Timber.i("On Page changed $position")
|
||||
|
||||
// This is a bit tricky. We need to configure the FilterButtonBar based on the
|
||||
// fragments capabilities. But this function can be called before the fragment has
|
||||
// been created, and the ViewPager might create the fragments in arbitrary order.
|
||||
// Therefore we store a flag in the Adapter, to signal that the next created
|
||||
// fragment of the given position should propagate its capabilities
|
||||
val frag = findFragmentAtPosition(childFragmentManager, position)
|
||||
if (frag != null) {
|
||||
filterButtonBar!!.configureWithCapabilitiesFromFragment(frag)
|
||||
} else {
|
||||
musicCollectionAdapter.propagateCapabilitiesMatcher = position
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// The TabLayoutMediator manages the names of the Tabs (Albums, Artists, etc)
|
||||
val tabLayout: TabLayout = binding!!.findViewById(R.id.tab_layout)
|
||||
TabLayoutMediator(tabLayout, viewPager) { tab, position ->
|
||||
tab.text = musicCollectionAdapter.getTitleForFragment(position, requireContext())
|
||||
}.attach()
|
||||
}
|
||||
|
||||
private fun updateLayoutTypeOnCurrentFragment(it: LayoutType) {
|
||||
val curFrag = findCurrentFragment()
|
||||
|
||||
if (curFrag is FilterableFragment) {
|
||||
curFrag.setLayoutType(it)
|
||||
}
|
||||
|
||||
Settings.lastViewType = layoutType.value
|
||||
}
|
||||
|
||||
private fun updateSortOrderOnCurrentFragment(it: SortOrder) {
|
||||
val curFrag = findCurrentFragment()
|
||||
|
||||
if (curFrag is FilterableFragment) {
|
||||
curFrag.setOrderType(it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
binding = null
|
||||
private fun findCurrentFragment(): Fragment? {
|
||||
return findFragmentAtPosition(childFragmentManager, viewPager.currentItem)
|
||||
}
|
||||
|
||||
private fun setupButtons() {
|
||||
musicTitle = binding!!.mainMusic
|
||||
artistsButton = binding!!.mainArtistsButton
|
||||
albumsButton = binding!!.mainAlbumsButton
|
||||
genresButton = binding!!.mainGenresButton
|
||||
videosTitle = binding!!.mainVideosTitle
|
||||
songsTitle = binding!!.mainSongs
|
||||
randomSongsButton = binding!!.mainSongsButton
|
||||
songsStarredButton = binding!!.mainSongsStarred
|
||||
albumsTitle = binding!!.mainAlbums
|
||||
albumsNewestButton = binding!!.mainAlbumsNewest
|
||||
albumsRandomButton = binding!!.mainAlbumsRandom
|
||||
albumsHighestButton = binding!!.mainAlbumsHighest
|
||||
albumsStarredButton = binding!!.mainAlbumsStarred
|
||||
albumsRecentButton = binding!!.mainAlbumsRecent
|
||||
albumsFrequentButton = binding!!.mainAlbumsFrequent
|
||||
albumsAlphaByNameButton = binding!!.mainAlbumsAlphaByName
|
||||
albumsAlphaByArtistButton = binding!!.mainAlbumsAlphaByArtist
|
||||
videosButton = binding!!.mainVideos
|
||||
}
|
||||
|
||||
private fun setupItemVisibility() {
|
||||
// Cache some values
|
||||
useId3 = Settings.shouldUseId3Tags
|
||||
useId3Offline = Settings.useId3TagsOffline
|
||||
|
||||
val isOnline = !isOffline()
|
||||
|
||||
// Music
|
||||
musicTitle.isVisible = true
|
||||
artistsButton.isVisible = true
|
||||
albumsButton.isVisible = isOnline || useId3Offline
|
||||
genresButton.isVisible = isOnline
|
||||
|
||||
// Songs
|
||||
songsTitle.isVisible = true
|
||||
randomSongsButton.isVisible = true
|
||||
songsStarredButton.isVisible = isOnline
|
||||
|
||||
// Albums
|
||||
albumsTitle.isVisible = isOnline || useId3Offline
|
||||
albumsNewestButton.isVisible = isOnline || useId3Offline
|
||||
albumsRecentButton.isVisible = isOnline
|
||||
albumsFrequentButton.isVisible = isOnline
|
||||
albumsHighestButton.isVisible = isOnline && !useId3
|
||||
albumsRandomButton.isVisible = isOnline
|
||||
albumsStarredButton.isVisible = isOnline
|
||||
albumsAlphaByNameButton.isVisible = isOnline || useId3Offline
|
||||
albumsAlphaByArtistButton.isVisible = isOnline || useId3Offline
|
||||
|
||||
// Videos
|
||||
videosTitle.isVisible = isOnline
|
||||
videosButton.isVisible = isOnline
|
||||
}
|
||||
|
||||
private fun setupClickListener() {
|
||||
albumsNewestButton.setOnClickListener {
|
||||
showAlbumList(AlbumListType.NEWEST, R.string.main_albums_newest)
|
||||
}
|
||||
|
||||
albumsRandomButton.setOnClickListener {
|
||||
showAlbumList(AlbumListType.RANDOM, R.string.main_albums_random)
|
||||
}
|
||||
|
||||
albumsHighestButton.setOnClickListener {
|
||||
showAlbumList(AlbumListType.HIGHEST, R.string.main_albums_highest)
|
||||
}
|
||||
|
||||
albumsRecentButton.setOnClickListener {
|
||||
showAlbumList(AlbumListType.RECENT, R.string.main_albums_recent)
|
||||
}
|
||||
|
||||
albumsFrequentButton.setOnClickListener {
|
||||
showAlbumList(AlbumListType.FREQUENT, R.string.main_albums_frequent)
|
||||
}
|
||||
|
||||
albumsStarredButton.setOnClickListener {
|
||||
showAlbumList(AlbumListType.STARRED, R.string.main_albums_starred)
|
||||
}
|
||||
|
||||
albumsAlphaByNameButton.setOnClickListener {
|
||||
showAlbumList(AlbumListType.SORTED_BY_NAME, R.string.main_albums_alphaByName)
|
||||
}
|
||||
|
||||
albumsAlphaByArtistButton.setOnClickListener {
|
||||
showAlbumList(AlbumListType.SORTED_BY_ARTIST, R.string.main_albums_alphaByArtist)
|
||||
}
|
||||
|
||||
songsStarredButton.setOnClickListener {
|
||||
showStarredSongs()
|
||||
}
|
||||
|
||||
artistsButton.setOnClickListener {
|
||||
showArtists()
|
||||
}
|
||||
|
||||
albumsButton.setOnClickListener {
|
||||
showAlbumList(AlbumListType.SORTED_BY_NAME, R.string.main_albums_title)
|
||||
}
|
||||
|
||||
randomSongsButton.setOnClickListener {
|
||||
showRandomSongs()
|
||||
}
|
||||
|
||||
genresButton.setOnClickListener {
|
||||
showGenres()
|
||||
}
|
||||
|
||||
videosButton.setOnClickListener {
|
||||
showVideos()
|
||||
}
|
||||
}
|
||||
|
||||
private fun showStarredSongs() {
|
||||
val action = MainFragmentDirections.mainToTrackCollection(
|
||||
getStarred = true,
|
||||
)
|
||||
findNavController().navigate(action)
|
||||
}
|
||||
|
||||
private fun showRandomSongs() {
|
||||
val action = MainFragmentDirections.mainToTrackCollection(
|
||||
getRandom = true,
|
||||
size = Settings.maxSongs
|
||||
)
|
||||
findNavController().navigate(action)
|
||||
}
|
||||
|
||||
private fun showArtists() {
|
||||
val action = MainFragmentDirections.mainToArtistList(
|
||||
title = requireContext().resources.getString(R.string.main_artists_title)
|
||||
)
|
||||
findNavController().navigate(action)
|
||||
}
|
||||
|
||||
private fun showAlbumList(type: AlbumListType, titleIndex: Int) {
|
||||
val title = requireContext().resources.getString(titleIndex, "")
|
||||
val action = MainFragmentDirections.mainToAlbumList(
|
||||
type = type,
|
||||
title = title,
|
||||
size = Settings.maxAlbums,
|
||||
offset = 0
|
||||
)
|
||||
findNavController().navigate(action)
|
||||
}
|
||||
|
||||
private fun showGenres() {
|
||||
findNavController().navigate(R.id.mainToSelectGenre)
|
||||
}
|
||||
|
||||
private fun showVideos() {
|
||||
val action = MainFragmentDirections.mainToTrackCollection(
|
||||
getVideos = true,
|
||||
)
|
||||
findNavController().navigate(action)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private var useId3 = false
|
||||
private var useId3Offline = false
|
||||
private fun findFragmentAtPosition(
|
||||
fragmentManager: FragmentManager,
|
||||
position: Int
|
||||
): Fragment? {
|
||||
// If a fragment was recently created and never shown the fragment manager might not
|
||||
// hold a reference to it. Fallback on the WeakMap instead.
|
||||
return fragmentManager.findFragmentByTag("f$position")
|
||||
?: musicCollectionAdapter.fragmentMap[position]?.get()
|
||||
}
|
||||
}
|
||||
|
||||
private fun FilterButtonBar.configureWithCapabilitiesFromFragment(frag: Fragment?) {
|
||||
if (frag is FilterableFragment) {
|
||||
Timber.w("Setting kapas: ${frag.viewCapabilities}")
|
||||
this.configureWithCapabilities(frag.viewCapabilities)
|
||||
} else {
|
||||
Timber.w("Setting kapas: $EMPTY_CAPABILITIES")
|
||||
this.configureWithCapabilities(EMPTY_CAPABILITIES)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
class MusicCollectionAdapter(fragment: Fragment, initialType: LayoutType = LayoutType.LIST) :
|
||||
FragmentStateAdapter(fragment) {
|
||||
|
||||
var filterButtonBar: FilterButtonBar? = null
|
||||
private var layoutType: LayoutType = initialType
|
||||
|
||||
var propagateCapabilitiesMatcher: Int? = null
|
||||
|
||||
// viewPager.findFragmentAtPosition(childFragmentManager, position) is sometimes delayed..
|
||||
var fragmentMap: HashMap<Int, SoftReference<Fragment>> = hashMapOf()
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
// Hide Genre tab when offline
|
||||
return if (ActiveServerProvider.isOffline()) 4 else 5
|
||||
}
|
||||
|
||||
override fun createFragment(position: Int): Fragment {
|
||||
|
||||
Timber.i("Creating new fragment at position: $position")
|
||||
|
||||
val action = when (position) {
|
||||
0 -> NavigationGraphDirections.toArtistList()
|
||||
1 -> NavigationGraphDirections.toAlbumList(
|
||||
AlbumListType.NEWEST,
|
||||
size = Settings.maxAlbums
|
||||
)
|
||||
2 -> NavigationGraphDirections.toPlaylistFragment()
|
||||
3 -> NavigationGraphDirections.toTrackCollection()
|
||||
else -> NavigationGraphDirections.toGenreList()
|
||||
}
|
||||
|
||||
val fragment = when (position) {
|
||||
0 -> ArtistListFragment()
|
||||
1 -> AlbumListFragment(layoutType)
|
||||
2 -> PlaylistsFragment()
|
||||
3 -> TrackCollectionFragment(SortOrder.RANDOM)
|
||||
else -> SelectGenreFragment()
|
||||
}
|
||||
|
||||
fragmentMap[position] = SoftReference(fragment)
|
||||
fragment.arguments = action.arguments
|
||||
|
||||
// See comment in onPageSelected
|
||||
if (propagateCapabilitiesMatcher == position) {
|
||||
Timber.w("Setting capacities while creating, $position")
|
||||
propagateCapabilitiesMatcher = null
|
||||
filterButtonBar!!.configureWithCapabilitiesFromFragment(fragment)
|
||||
}
|
||||
|
||||
return fragment
|
||||
}
|
||||
|
||||
fun getTitleForFragment(pos: Int, context: Context): String {
|
||||
return when (pos) {
|
||||
0 -> context.getString(R.string.main_artists_title)
|
||||
1 -> context.getString(R.string.main_albums_title)
|
||||
2 -> context.getString(R.string.playlist_label)
|
||||
3 -> context.getString(R.string.main_songs_title)
|
||||
4 -> context.getString(R.string.main_genres_title)
|
||||
else -> "Unknown"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface FilterableFragment {
|
||||
fun setLayoutType(newType: LayoutType) {}
|
||||
fun setOrderType(newOrder: SortOrder)
|
||||
var viewCapabilities: ViewCapabilities
|
||||
}
|
||||
|
@ -75,7 +75,7 @@ abstract class MultiListFragment<T : Identifiable> : Fragment() {
|
||||
/**
|
||||
* The central function to pass a query to the model and return a LiveData object
|
||||
*/
|
||||
open fun getLiveData(refresh: Boolean = false): LiveData<List<T>> {
|
||||
open fun getLiveData(refresh: Boolean = false, append: Boolean = false): LiveData<List<T>> {
|
||||
return MutableLiveData()
|
||||
}
|
||||
|
||||
|
@ -578,7 +578,8 @@ class PlayerFragment :
|
||||
|
||||
if (Settings.shouldUseId3Tags) {
|
||||
val action = PlayerFragmentDirections.playerToAlbumsList(
|
||||
type = AlbumListType.BY_ARTIST,
|
||||
type = AlbumListType.SORTED_BY_NAME,
|
||||
byArtist = true,
|
||||
id = track.artistId,
|
||||
title = track.artist,
|
||||
offset = 0,
|
||||
|
@ -25,7 +25,7 @@ import kotlinx.coroutines.launch
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
import org.moire.ultrasonic.R
|
||||
import org.moire.ultrasonic.adapters.AlbumRowBinder
|
||||
import org.moire.ultrasonic.adapters.AlbumRowDelegate
|
||||
import org.moire.ultrasonic.adapters.ArtistRowBinder
|
||||
import org.moire.ultrasonic.adapters.DividerBinder
|
||||
import org.moire.ultrasonic.adapters.MoreButtonBinder
|
||||
@ -109,7 +109,7 @@ class SearchFragment : MultiListFragment<Identifiable>(), KoinComponent {
|
||||
)
|
||||
|
||||
viewAdapter.register(
|
||||
AlbumRowBinder(
|
||||
AlbumRowDelegate(
|
||||
onItemClick = ::onItemClick,
|
||||
onContextMenuClick = ::onContextMenuItemSelected,
|
||||
imageLoader = imageLoaderProvider.getImageLoader()
|
||||
@ -280,7 +280,8 @@ class SearchFragment : MultiListFragment<Identifiable>(), KoinComponent {
|
||||
)
|
||||
} else {
|
||||
SearchFragmentDirections.searchToAlbumsList(
|
||||
type = AlbumListType.BY_ARTIST,
|
||||
type = AlbumListType.SORTED_BY_NAME,
|
||||
byArtist = true,
|
||||
id = item.id,
|
||||
title = item.name,
|
||||
size = 1000,
|
||||
|
@ -27,9 +27,10 @@ import java.util.Collections
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koin.android.ext.android.inject
|
||||
import org.moire.ultrasonic.NavigationGraphDirections
|
||||
import org.moire.ultrasonic.R
|
||||
import org.moire.ultrasonic.adapters.AlbumHeader
|
||||
import org.moire.ultrasonic.adapters.AlbumRowBinder
|
||||
import org.moire.ultrasonic.adapters.AlbumRowDelegate
|
||||
import org.moire.ultrasonic.adapters.HeaderViewBinder
|
||||
import org.moire.ultrasonic.adapters.TrackViewBinder
|
||||
import org.moire.ultrasonic.data.ActiveServerProvider
|
||||
@ -50,6 +51,8 @@ import org.moire.ultrasonic.util.ConfirmationDialog
|
||||
import org.moire.ultrasonic.util.EntryByDiscAndTrackComparator
|
||||
import org.moire.ultrasonic.util.Settings
|
||||
import org.moire.ultrasonic.util.Util
|
||||
import org.moire.ultrasonic.view.SortOrder
|
||||
import org.moire.ultrasonic.view.ViewCapabilities
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
@ -59,10 +62,11 @@ import timber.log.Timber
|
||||
* where the list can contain Albums as well. This happens especially when having ID3 tags disabled,
|
||||
* or using Offline mode, both in which Indexes instead of Artists are being used.
|
||||
*
|
||||
* TODO: Remove more button and introduce endless scrolling
|
||||
*/
|
||||
@Suppress("TooManyFunctions")
|
||||
open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
|
||||
open class TrackCollectionFragment(
|
||||
initialOrder: SortOrder? = null
|
||||
) : MultiListFragment<MusicDirectory.Child>(), FilterableFragment {
|
||||
|
||||
private var albumButtons: View? = null
|
||||
private var selectButton: MaterialButton? = null
|
||||
@ -73,7 +77,6 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
|
||||
private var unpinButton: MaterialButton? = null
|
||||
private var downloadButton: MaterialButton? = null
|
||||
private var deleteButton: MaterialButton? = null
|
||||
private var moreButton: MaterialButton? = null
|
||||
private var playAllButtonVisible = false
|
||||
private var shareButtonVisible = false
|
||||
private var playAllButton: MenuItem? = null
|
||||
@ -87,6 +90,9 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
|
||||
override val listModel: TrackCollectionModel by viewModels()
|
||||
private val rxBusSubscription: CompositeDisposable = CompositeDisposable()
|
||||
|
||||
private var sortOrder = initialOrder
|
||||
private var offset: Int? = null
|
||||
|
||||
/**
|
||||
* The id of the main layout
|
||||
*/
|
||||
@ -138,7 +144,7 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
|
||||
)
|
||||
|
||||
viewAdapter.register(
|
||||
AlbumRowBinder(
|
||||
AlbumRowDelegate(
|
||||
{ entry -> onItemClick(entry) },
|
||||
{ menuItem, entry -> onContextMenuItemSelected(menuItem, entry) },
|
||||
imageLoaderProvider.getImageLoader()
|
||||
@ -161,6 +167,25 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
|
||||
) {
|
||||
triggerButtonUpdate()
|
||||
}
|
||||
|
||||
// Attach our onScrollListener
|
||||
val scrollListener = object : EndlessScrollListener(viewManager) {
|
||||
override fun onLoadMore(page: Int, totalItemsCount: Int, view: RecyclerView?) {
|
||||
Timber.w("LOAD MORE")
|
||||
// Triggered only when new data needs to be appended to the list
|
||||
// Add whatever code is needed to append new items to the bottom of the list
|
||||
loadMoreTracks()
|
||||
}
|
||||
}
|
||||
|
||||
listView!!.addOnScrollListener(scrollListener)
|
||||
}
|
||||
|
||||
private fun loadMoreTracks() {
|
||||
if (displayRandom() || navArgs.genreName != null) {
|
||||
offset = navArgs.offset + navArgs.size
|
||||
getLiveData(refresh = true, append = true)
|
||||
}
|
||||
}
|
||||
|
||||
internal open fun handleRefresh() {
|
||||
@ -176,7 +201,6 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
|
||||
unpinButton = view.findViewById(R.id.select_album_unpin)
|
||||
downloadButton = view.findViewById(R.id.select_album_download)
|
||||
deleteButton = view.findViewById(R.id.select_album_delete)
|
||||
moreButton = view.findViewById(R.id.select_album_more)
|
||||
|
||||
selectButton?.setOnClickListener {
|
||||
selectAllOrNone()
|
||||
@ -469,24 +493,9 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
|
||||
}
|
||||
}
|
||||
|
||||
val listSize = navArgs.size
|
||||
|
||||
// Hide select button for video lists and singular selection lists
|
||||
selectButton!!.isVisible = !allVideos && viewAdapter.hasMultipleSelection() && songCount > 0
|
||||
|
||||
if (songCount > 0) {
|
||||
if (listSize == 0 || songCount < listSize) {
|
||||
moreButton!!.visibility = View.GONE
|
||||
} else {
|
||||
moreButton!!.visibility = View.VISIBLE
|
||||
if (navArgs.getRandom) {
|
||||
moreRandomTracks()
|
||||
} else if (navArgs.genreName != null) {
|
||||
moreSongsForGenre()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Show a text if we have no entries
|
||||
emptyView.isVisible = entryList.isEmpty()
|
||||
|
||||
@ -524,33 +533,6 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
|
||||
Timber.i("Processed list")
|
||||
}
|
||||
|
||||
private fun moreSongsForGenre() {
|
||||
moreButton!!.setOnClickListener {
|
||||
val action = TrackCollectionFragmentDirections.loadMoreTracks(
|
||||
genreName = navArgs.genreName,
|
||||
size = navArgs.size,
|
||||
offset = navArgs.offset + navArgs.size
|
||||
)
|
||||
findNavController().navigate(action)
|
||||
}
|
||||
}
|
||||
|
||||
private fun moreRandomTracks() {
|
||||
|
||||
val listSize = navArgs.size
|
||||
|
||||
moreButton!!.setOnClickListener {
|
||||
val offset = navArgs.offset + listSize
|
||||
|
||||
val action = TrackCollectionFragmentDirections.loadMoreTracks(
|
||||
getRandom = true,
|
||||
size = listSize,
|
||||
offset = offset
|
||||
)
|
||||
findNavController().navigate(action)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun getSelectedSongs(): List<Track> {
|
||||
// Walk through selected set and get the Entries based on the saved ids.
|
||||
return viewAdapter.getCurrentList().mapNotNull {
|
||||
@ -571,7 +553,8 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
|
||||
|
||||
@Suppress("LongMethod")
|
||||
override fun getLiveData(
|
||||
refresh: Boolean
|
||||
refresh: Boolean,
|
||||
append: Boolean
|
||||
): LiveData<List<MusicDirectory.Child>> {
|
||||
Timber.i("Starting gathering track collection data...")
|
||||
val id = navArgs.id
|
||||
@ -584,11 +567,11 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
|
||||
val shareName = navArgs.shareName
|
||||
val genreName = navArgs.genreName
|
||||
|
||||
val getStarredTracks = navArgs.getStarred
|
||||
val getStarredTracks = displayStarred()
|
||||
val getVideos = navArgs.getVideos
|
||||
val getRandomTracks = navArgs.getRandom
|
||||
val albumListSize = navArgs.size
|
||||
val albumListOffset = navArgs.offset
|
||||
val getRandomTracks = displayRandom()
|
||||
val size = if (navArgs.size < 0) Settings.maxSongs else navArgs.size
|
||||
val offset = offset ?: navArgs.offset
|
||||
val refresh2 = navArgs.refresh || refresh
|
||||
|
||||
listModel.viewModelScope.launch(handler) {
|
||||
@ -605,7 +588,7 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
|
||||
listModel.getShare(shareId)
|
||||
} else if (genreName != null) {
|
||||
setTitle(genreName)
|
||||
listModel.getSongsForGenre(genreName, albumListSize, albumListOffset)
|
||||
listModel.getSongsForGenre(genreName, size, offset, append)
|
||||
} else if (getStarredTracks) {
|
||||
setTitle(getString(R.string.main_songs_starred))
|
||||
listModel.getStarred()
|
||||
@ -614,7 +597,7 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
|
||||
listModel.getVideos(refresh2)
|
||||
} else if (getRandomTracks) {
|
||||
setTitle(R.string.main_songs_random)
|
||||
listModel.getRandom(albumListSize)
|
||||
listModel.getRandom(size, append)
|
||||
} else {
|
||||
setTitle(name)
|
||||
if (ActiveServerProvider.isID3Enabled()) {
|
||||
@ -633,6 +616,10 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
|
||||
return listModel.currentList
|
||||
}
|
||||
|
||||
private fun displayStarred() = (sortOrder == SortOrder.STARRED) || navArgs.getStarred
|
||||
|
||||
private fun displayRandom() = (sortOrder == SortOrder.RANDOM) || navArgs.getRandom
|
||||
|
||||
@Suppress("LongMethod")
|
||||
override fun onContextMenuItemSelected(
|
||||
menuItem: MenuItem,
|
||||
@ -703,7 +690,7 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
|
||||
override fun onItemClick(item: MusicDirectory.Child) {
|
||||
when {
|
||||
item.isDirectory -> {
|
||||
val action = TrackCollectionFragmentDirections.loadMoreTracks(
|
||||
val action = NavigationGraphDirections.toTrackCollection(
|
||||
id = item.id,
|
||||
isAlbum = true,
|
||||
name = item.title,
|
||||
@ -719,4 +706,24 @@ open class TrackCollectionFragment : MultiListFragment<MusicDirectory.Child>() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun setOrderType(newOrder: SortOrder) {
|
||||
sortOrder = newOrder
|
||||
getLiveData(true)
|
||||
}
|
||||
|
||||
override var viewCapabilities: ViewCapabilities = ViewCapabilities(
|
||||
supportsGrid = false,
|
||||
supportedSortOrders = getListOfSortOrders()
|
||||
)
|
||||
|
||||
private fun getListOfSortOrders(): List<SortOrder> {
|
||||
val isOnline = !isOffline()
|
||||
val supported = mutableListOf(SortOrder.RANDOM)
|
||||
|
||||
if (isOnline) {
|
||||
supported.add(SortOrder.STARRED)
|
||||
}
|
||||
return supported
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.AdapterView.AdapterContextMenuInfo
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.CheckBox
|
||||
import android.widget.EditText
|
||||
import android.widget.ListView
|
||||
@ -30,6 +31,7 @@ import androidx.navigation.fragment.findNavController
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
import java.util.Locale
|
||||
import org.koin.java.KoinJavaComponent.inject
|
||||
import org.moire.ultrasonic.NavigationGraphDirections
|
||||
import org.moire.ultrasonic.R
|
||||
import org.moire.ultrasonic.api.subsonic.ApiNotSupportedException
|
||||
import org.moire.ultrasonic.data.ActiveServerProvider.Companion.isOffline
|
||||
@ -45,7 +47,6 @@ import org.moire.ultrasonic.util.FragmentBackgroundTask
|
||||
import org.moire.ultrasonic.util.LoadingTask
|
||||
import org.moire.ultrasonic.util.Util.applyTheme
|
||||
import org.moire.ultrasonic.util.Util.toast
|
||||
import org.moire.ultrasonic.view.PlaylistAdapter
|
||||
|
||||
/**
|
||||
* Displays the playlists stored on the server
|
||||
@ -56,11 +57,14 @@ class PlaylistsFragment : Fragment() {
|
||||
private var refreshPlaylistsListView: SwipeRefreshLayout? = null
|
||||
private var playlistsListView: ListView? = null
|
||||
private var emptyTextView: View? = null
|
||||
private var playlistAdapter: PlaylistAdapter? = null
|
||||
private var playlistAdapter: ArrayAdapter<Playlist>? = null
|
||||
|
||||
private val downloadHandler = inject<DownloadHandler>(
|
||||
DownloadHandler::class.java
|
||||
)
|
||||
|
||||
private var cancellationToken: CancellationToken? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
applyTheme(this.context)
|
||||
super.onCreate(savedInstanceState)
|
||||
@ -83,7 +87,7 @@ class PlaylistsFragment : Fragment() {
|
||||
playlistsListView!!.setOnItemClickListener { parent, _, position, _ ->
|
||||
val (id1, name) = parent.getItemAtPosition(position) as Playlist
|
||||
|
||||
val action = PlaylistsFragmentDirections.playlistsToTrackCollection(
|
||||
val action = NavigationGraphDirections.toTrackCollection(
|
||||
id = id1,
|
||||
playlistId = id1,
|
||||
name = name,
|
||||
@ -116,7 +120,7 @@ class PlaylistsFragment : Fragment() {
|
||||
|
||||
override fun done(result: List<Playlist>) {
|
||||
playlistsListView!!.adapter =
|
||||
PlaylistAdapter(context, result).also { playlistAdapter = it }
|
||||
ArrayAdapter(requireContext(), R.layout.list_item_generic, result)
|
||||
emptyTextView!!.visibility = if (result.isEmpty()) View.VISIBLE else View.GONE
|
||||
}
|
||||
}
|
||||
@ -183,7 +187,7 @@ class PlaylistsFragment : Fragment() {
|
||||
)
|
||||
}
|
||||
R.id.playlist_menu_play_now -> {
|
||||
val action = PlaylistsFragmentDirections.playlistsToTrackCollection(
|
||||
val action = NavigationGraphDirections.toTrackCollection(
|
||||
playlistId = playlist.id,
|
||||
playlistName = playlist.name,
|
||||
autoPlay = true
|
||||
@ -191,7 +195,7 @@ class PlaylistsFragment : Fragment() {
|
||||
findNavController().navigate(action)
|
||||
}
|
||||
R.id.playlist_menu_play_shuffled -> {
|
||||
val action = PlaylistsFragmentDirections.playlistsToTrackCollection(
|
||||
val action = NavigationGraphDirections.toTrackCollection(
|
||||
playlistId = playlist.id,
|
||||
playlistName = playlist.name,
|
||||
autoPlay = true,
|
||||
|
@ -7,15 +7,16 @@
|
||||
|
||||
package org.moire.ultrasonic.fragment.legacy
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.ListView
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
import org.moire.ultrasonic.NavigationGraphDirections
|
||||
import org.moire.ultrasonic.R
|
||||
import org.moire.ultrasonic.domain.PodcastsChannel
|
||||
import org.moire.ultrasonic.fragment.FragmentTitle.Companion.setTitle
|
||||
@ -24,18 +25,20 @@ import org.moire.ultrasonic.util.BackgroundTask
|
||||
import org.moire.ultrasonic.util.CancellationToken
|
||||
import org.moire.ultrasonic.util.FragmentBackgroundTask
|
||||
import org.moire.ultrasonic.util.Util.applyTheme
|
||||
import org.moire.ultrasonic.view.PodcastsChannelsAdapter
|
||||
|
||||
/**
|
||||
* Displays the podcasts available on the server
|
||||
*
|
||||
* TODO: This file has been converted from Java, but not modernized yet.
|
||||
* TODO: Use Coroutines
|
||||
*/
|
||||
class PodcastFragment : Fragment() {
|
||||
|
||||
private var emptyTextView: View? = null
|
||||
var channelItemsListView: ListView? = null
|
||||
private var cancellationToken: CancellationToken? = null
|
||||
private var swipeRefresh: SwipeRefreshLayout? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
applyTheme(this.context)
|
||||
super.onCreate(savedInstanceState)
|
||||
@ -53,19 +56,19 @@ class PodcastFragment : Fragment() {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
cancellationToken = CancellationToken()
|
||||
swipeRefresh = view.findViewById(R.id.podcasts_refresh)
|
||||
swipeRefresh!!.setOnRefreshListener { load(view.context, true) }
|
||||
swipeRefresh!!.setOnRefreshListener { load(true) }
|
||||
setTitle(this, R.string.podcasts_label)
|
||||
emptyTextView = view.findViewById(R.id.select_podcasts_empty)
|
||||
channelItemsListView = view.findViewById(R.id.podcasts_channels_items_list)
|
||||
channelItemsListView!!.setOnItemClickListener { parent, _, position, _ ->
|
||||
val (id) = parent.getItemAtPosition(position) as PodcastsChannel
|
||||
val action = PodcastFragmentDirections.podcastToTrackCollection(
|
||||
val action = NavigationGraphDirections.toTrackCollection(
|
||||
podcastChannelId = id
|
||||
)
|
||||
|
||||
findNavController().navigate(action)
|
||||
}
|
||||
load(view.context, false)
|
||||
load(false)
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
@ -73,7 +76,7 @@ class PodcastFragment : Fragment() {
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
private fun load(context: Context, refresh: Boolean) {
|
||||
private fun load(refresh: Boolean) {
|
||||
val task: BackgroundTask<List<PodcastsChannel>> =
|
||||
object : FragmentBackgroundTask<List<PodcastsChannel>>(
|
||||
activity, true, swipeRefresh, cancellationToken
|
||||
@ -85,7 +88,8 @@ class PodcastFragment : Fragment() {
|
||||
}
|
||||
|
||||
override fun done(result: List<PodcastsChannel>) {
|
||||
channelItemsListView!!.adapter = PodcastsChannelsAdapter(context, result)
|
||||
channelItemsListView!!.adapter =
|
||||
ArrayAdapter(requireContext(), R.layout.list_item_generic, result)
|
||||
emptyTextView!!.visibility = if (result.isEmpty()) View.VISIBLE else View.GONE
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
import org.moire.ultrasonic.NavigationGraphDirections
|
||||
import org.moire.ultrasonic.R
|
||||
import org.moire.ultrasonic.domain.Genre
|
||||
import org.moire.ultrasonic.fragment.FragmentTitle.Companion.setTitle
|
||||
@ -58,13 +59,14 @@ class SelectGenreFragment : Fragment() {
|
||||
refreshGenreListView = view.findViewById(R.id.select_genre_refresh)
|
||||
genreListView = view.findViewById(R.id.select_genre_list)
|
||||
refreshGenreListView!!.setOnRefreshListener { load(true) }
|
||||
|
||||
genreListView!!.setOnItemClickListener { parent: AdapterView<*>,
|
||||
_: View?,
|
||||
position: Int,
|
||||
_: Long ->
|
||||
val genre = parent.getItemAtPosition(position) as Genre
|
||||
|
||||
val action = SelectGenreFragmentDirections.selectGenreToTrackCollection(
|
||||
val action = NavigationGraphDirections.toTrackCollection(
|
||||
genreName = genre.name,
|
||||
size = maxSongs,
|
||||
offset = 0
|
||||
@ -82,6 +84,7 @@ class SelectGenreFragment : Fragment() {
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
// TODO: Migrate to Coroutines
|
||||
private fun load(refresh: Boolean) {
|
||||
val task: BackgroundTask<List<Genre>> = object : FragmentBackgroundTask<List<Genre>>(
|
||||
activity, true, refreshGenreListView, cancellationToken
|
||||
|
@ -29,6 +29,7 @@ import androidx.navigation.fragment.findNavController
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
import java.util.Locale
|
||||
import org.koin.java.KoinJavaComponent
|
||||
import org.moire.ultrasonic.NavigationGraphDirections
|
||||
import org.moire.ultrasonic.R
|
||||
import org.moire.ultrasonic.api.subsonic.ApiNotSupportedException
|
||||
import org.moire.ultrasonic.domain.Share
|
||||
@ -82,7 +83,7 @@ class SharesFragment : Fragment() {
|
||||
AdapterView.OnItemClickListener { parent, _, position, _ ->
|
||||
val share = parent.getItemAtPosition(position) as Share
|
||||
|
||||
val action = SharesFragmentDirections.sharesToTrackCollection(
|
||||
val action = NavigationGraphDirections.toTrackCollection(
|
||||
shareId = share.id,
|
||||
shareName = share.name
|
||||
)
|
||||
|
@ -96,6 +96,25 @@ class AlbumListModel(application: Application) : GenericListModel(application) {
|
||||
}
|
||||
}
|
||||
|
||||
fun sortListByOrder(order: AlbumListType) {
|
||||
val newList = when (order) {
|
||||
AlbumListType.BY_YEAR -> {
|
||||
list.value?.sortedBy {
|
||||
it.year
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
list.value?.sortedBy {
|
||||
it.name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
newList?.let {
|
||||
list.postValue(it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun showSelectFolderHeader(): Boolean {
|
||||
val isAlphabetical = (lastType == AlbumListType.SORTED_BY_NAME) ||
|
||||
(lastType == AlbumListType.SORTED_BY_ARTIST)
|
||||
|
@ -56,11 +56,11 @@ class TrackCollectionModel(application: Application) : GenericListModel(applicat
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getSongsForGenre(genre: String, count: Int, offset: Int) {
|
||||
suspend fun getSongsForGenre(genre: String, count: Int, offset: Int, append: Boolean) {
|
||||
withContext(Dispatchers.IO) {
|
||||
val service = MusicServiceFactory.getMusicService()
|
||||
val musicDirectory = service.getSongsByGenre(genre, count, offset)
|
||||
updateList(musicDirectory)
|
||||
updateList(musicDirectory, append)
|
||||
}
|
||||
}
|
||||
|
||||
@ -94,7 +94,7 @@ class TrackCollectionModel(application: Application) : GenericListModel(applicat
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getRandom(size: Int) {
|
||||
suspend fun getRandom(size: Int, append: Boolean) {
|
||||
|
||||
withContext(Dispatchers.IO) {
|
||||
val service = MusicServiceFactory.getMusicService()
|
||||
@ -102,7 +102,7 @@ class TrackCollectionModel(application: Application) : GenericListModel(applicat
|
||||
|
||||
currentListIsSortable = false
|
||||
|
||||
updateList(musicDirectory)
|
||||
updateList(musicDirectory, append)
|
||||
}
|
||||
}
|
||||
|
||||
@ -158,8 +158,14 @@ class TrackCollectionModel(application: Application) : GenericListModel(applicat
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateList(root: MusicDirectory) {
|
||||
currentList.postValue(root.getChildren())
|
||||
private fun updateList(root: MusicDirectory, append: Boolean = false) {
|
||||
val newList = if (append) {
|
||||
currentList.value!! + root.getChildren()
|
||||
} else {
|
||||
root.getChildren()
|
||||
}
|
||||
|
||||
currentList.postValue(newList)
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
|
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* ViewType.kt
|
||||
* Copyright (C) 2009-2022 Ultrasonic developers
|
||||
*
|
||||
* Distributed under terms of the GNU GPLv3 license.
|
||||
*/
|
||||
|
||||
package org.moire.ultrasonic.util
|
||||
|
||||
enum class LayoutType(val value: Int) {
|
||||
LIST(0),
|
||||
COVER(1);
|
||||
|
||||
companion object {
|
||||
private val map = values().associateBy { it.value }
|
||||
fun from(value: Int): LayoutType {
|
||||
// Default to list if unmappable
|
||||
return map[value] ?: LIST
|
||||
}
|
||||
}
|
||||
}
|
@ -10,7 +10,6 @@ package org.moire.ultrasonic.util
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import androidx.core.net.toUri
|
||||
import androidx.media.utils.MediaConstants
|
||||
import androidx.media3.common.HeartRating
|
||||
import androidx.media3.common.MediaItem
|
||||
import androidx.media3.common.MediaMetadata
|
||||
@ -19,9 +18,15 @@ import java.text.DateFormat
|
||||
import org.moire.ultrasonic.domain.Track
|
||||
import org.moire.ultrasonic.provider.AlbumArtContentProvider
|
||||
|
||||
// Copied from androidx.media.utils.MediaConstants in order to avoid importing a whole dependecy
|
||||
// for a single string value
|
||||
private const val DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE =
|
||||
"android.media.browse.CONTENT_STYLE_GROUP_TITLE_HINT"
|
||||
|
||||
object MediaItemConverter {
|
||||
private const val CACHE_SIZE = 250
|
||||
private const val CACHE_EXPIRY_MINUTES = 10L
|
||||
|
||||
val mediaItemCache: LRUCache<String, TimeLimitedCache<MediaItem>> = LRUCache(CACHE_SIZE)
|
||||
val trackCache: LRUCache<String, TimeLimitedCache<Track>> = LRUCache(CACHE_SIZE)
|
||||
|
||||
@ -233,7 +238,7 @@ fun buildMediaItem(
|
||||
metadataBuilder.setExtras(
|
||||
Bundle().apply {
|
||||
putString(
|
||||
MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE,
|
||||
DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE,
|
||||
group
|
||||
)
|
||||
}
|
||||
|
@ -19,6 +19,10 @@ import org.moire.ultrasonic.app.UApp
|
||||
*/
|
||||
object Settings {
|
||||
|
||||
@JvmStatic
|
||||
val preferences: SharedPreferences
|
||||
get() = PreferenceManager.getDefaultSharedPreferences(Util.appContext())
|
||||
|
||||
@JvmStatic
|
||||
var theme by StringSetting(
|
||||
getKey(R.string.setting_key_theme),
|
||||
@ -241,10 +245,6 @@ object Settings {
|
||||
@JvmStatic
|
||||
var debugLogToFile by BooleanSetting(getKey(R.string.setting_key_debug_log_to_file), false)
|
||||
|
||||
@JvmStatic
|
||||
val preferences: SharedPreferences
|
||||
get() = PreferenceManager.getDefaultSharedPreferences(Util.appContext())
|
||||
|
||||
@JvmStatic
|
||||
val overrideLanguage by StringSetting(getKey(R.string.setting_key_override_language), "")
|
||||
|
||||
@ -267,11 +267,11 @@ object Settings {
|
||||
false
|
||||
)
|
||||
|
||||
// TODO: Remove in December 2022
|
||||
fun migrateFeatureStorage() {
|
||||
val sp = appContext.getSharedPreferences("feature_flags", Context.MODE_PRIVATE)
|
||||
useFiveStarRating = sp.getBoolean("FIVE_STAR_RATING", false)
|
||||
}
|
||||
@JvmStatic
|
||||
var lastViewType by IntSetting(
|
||||
getKey(R.string.setting_key_last_view_type),
|
||||
0
|
||||
)
|
||||
|
||||
fun hasKey(key: String): Boolean {
|
||||
return preferences.contains(key)
|
||||
|
@ -0,0 +1,219 @@
|
||||
/*
|
||||
* FilterButtonBar.kt
|
||||
* Copyright (C) 2009-2022 Ultrasonic developers
|
||||
*
|
||||
* Distributed under terms of the GNU GPLv3 license.
|
||||
*/
|
||||
|
||||
package org.moire.ultrasonic.view
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.widget.ArrayAdapter
|
||||
import androidx.appcompat.content.res.AppCompatResources
|
||||
import androidx.appcompat.widget.AppCompatAutoCompleteTextView
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.core.view.isVisible
|
||||
import com.google.android.material.chip.Chip
|
||||
import com.google.android.material.textfield.TextInputLayout
|
||||
import org.moire.ultrasonic.R
|
||||
import org.moire.ultrasonic.util.LayoutType
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
* This Widget provides a FilterBar, which allow to set the layout options
|
||||
* or sort order of a linked fragment
|
||||
*/
|
||||
class FilterButtonBar : ConstraintLayout {
|
||||
private var adapter: ArrayAdapter<TranslatedSortOrder>? = null
|
||||
private var orderChangedListener: ((SortOrder) -> Unit)? = null
|
||||
private var layoutTypeChangedListener: ((LayoutType) -> Unit)? = null
|
||||
private var layoutType: LayoutType = LayoutType.LIST
|
||||
private var viewTypeToggle: Chip? = null
|
||||
private var sortOrderMenu: TextInputLayout? = null
|
||||
private var sortOrderOptions: AppCompatAutoCompleteTextView? = null
|
||||
|
||||
constructor(context: Context) : super(context) {
|
||||
setup()
|
||||
}
|
||||
|
||||
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
|
||||
val inflater = LayoutInflater.from(context)
|
||||
inflater.inflate(R.layout.filter_button_bar, this)
|
||||
setup()
|
||||
}
|
||||
|
||||
/**
|
||||
* Set which capabilities are supported by
|
||||
* the linked fragment and configure the UI accordingly
|
||||
*
|
||||
* @param caps
|
||||
*/
|
||||
fun configureWithCapabilities(caps: ViewCapabilities) {
|
||||
viewTypeToggle!!.isVisible = caps.supportsGrid
|
||||
sortOrderMenu!!.isVisible = caps.supportedSortOrders.isNotEmpty()
|
||||
|
||||
if (caps.supportedSortOrders.isNotEmpty()) {
|
||||
Timber.i("Calculating order")
|
||||
val translatedOrders = caps.supportedSortOrders.map {
|
||||
TranslatedSortOrder(
|
||||
sortOrder = it,
|
||||
string = context.getString(getStringForSortOrder(it))
|
||||
)
|
||||
}
|
||||
// Fill the visible with the first available option
|
||||
sortOrderOptions!!.setText(getStringForSortOrder(translatedOrders.first().sortOrder))
|
||||
adapter!!.clear()
|
||||
// Next line addresses a bug in Android components:
|
||||
// https://github.com/material-components/material-components-android/issues/1464
|
||||
adapter!!.filter.filter("")
|
||||
adapter!!.addAll(translatedOrders)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This listener is called when the user has changed the layout type.
|
||||
* Register a callback from the linked fragment here, to trigger a relayout
|
||||
*
|
||||
* @param callback
|
||||
* @receiver
|
||||
*/
|
||||
fun setOnLayoutTypeChangedListener(callback: (LayoutType) -> Unit) {
|
||||
layoutTypeChangedListener = callback
|
||||
}
|
||||
|
||||
/**
|
||||
* This listener is called when the user has changed the sort order.
|
||||
* Register a callback from the linked fragment here, to trigger a resort
|
||||
*
|
||||
* @param callback
|
||||
* @receiver
|
||||
*/
|
||||
fun setOnOrderChangedListener(callback: (SortOrder) -> Unit) {
|
||||
orderChangedListener = callback
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup the necessary bindings
|
||||
*/
|
||||
|
||||
fun setup() {
|
||||
// Link layout toggle Chip
|
||||
viewTypeToggle = findViewById(R.id.chip_view_toggle)
|
||||
sortOrderMenu = findViewById(R.id.sort_order_menu)
|
||||
sortOrderOptions = findViewById(R.id.sort_order_menu_options)
|
||||
|
||||
viewTypeToggle!!.setOnClickListener {
|
||||
val newType = setLayoutType()
|
||||
layoutTypeChangedListener?.let { it(newType) }
|
||||
}
|
||||
|
||||
@SuppressLint("PrivateResource")
|
||||
adapter = ArrayAdapter<TranslatedSortOrder>(
|
||||
context,
|
||||
com.google.android.material.R.layout.mtrl_auto_complete_simple_item,
|
||||
mutableListOf<TranslatedSortOrder>()
|
||||
)
|
||||
|
||||
sortOrderOptions!!.setAdapter(adapter)
|
||||
sortOrderOptions!!.setOnItemClickListener { _, _, position, _ ->
|
||||
val item = adapter!!.getItem(position)
|
||||
item?.let { setOrderType(it.sortOrder) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function can be called from externally to set the layout type programmatically
|
||||
*
|
||||
* @param newType
|
||||
*/
|
||||
fun setLayoutType(newType: LayoutType = toggleLayoutType()): LayoutType {
|
||||
layoutType = newType
|
||||
updateToggleChipState(newType)
|
||||
return newType
|
||||
}
|
||||
|
||||
private fun toggleLayoutType(): LayoutType {
|
||||
return when (layoutType) {
|
||||
LayoutType.LIST -> {
|
||||
LayoutType.COVER
|
||||
}
|
||||
LayoutType.COVER -> {
|
||||
LayoutType.LIST
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function can be called from externally to set the layout type programmatically
|
||||
*
|
||||
* @param order
|
||||
*/
|
||||
fun setOrderType(order: SortOrder) {
|
||||
orderChangedListener?.let { it(order) }
|
||||
}
|
||||
|
||||
private fun updateToggleChipState(layoutType: LayoutType) {
|
||||
when (layoutType) {
|
||||
LayoutType.COVER -> {
|
||||
viewTypeToggle!!.chipIcon = AppCompatResources.getDrawable(
|
||||
context,
|
||||
R.drawable.ic_baseline_view_grid
|
||||
)
|
||||
viewTypeToggle!!.text = context.getString(R.string.grid_view)
|
||||
}
|
||||
LayoutType.LIST -> {
|
||||
viewTypeToggle!!.chipIcon = AppCompatResources.getDrawable(
|
||||
context,
|
||||
R.drawable.ic_baseline_view_list
|
||||
)
|
||||
viewTypeToggle!!.text = context.getString(R.string.list_view)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getStringForSortOrder(sortOrder: SortOrder): Int {
|
||||
return when (sortOrder) {
|
||||
SortOrder.RANDOM -> R.string.main_albums_random
|
||||
SortOrder.NEWEST -> R.string.main_albums_newest
|
||||
SortOrder.HIGHEST -> R.string.main_albums_highest
|
||||
SortOrder.FREQUENT -> R.string.main_albums_frequent
|
||||
SortOrder.RECENT -> R.string.main_albums_recent
|
||||
SortOrder.BY_NAME -> R.string.main_albums_alphaByName
|
||||
SortOrder.BY_ARTIST -> R.string.main_albums_alphaByArtist
|
||||
SortOrder.STARRED -> R.string.main_albums_starred
|
||||
SortOrder.BY_YEAR -> R.string.main_albums_by_year
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper which contains one sort order and the translated UI string for it
|
||||
*
|
||||
* @property sortOrder
|
||||
* @property string
|
||||
*/
|
||||
data class TranslatedSortOrder(
|
||||
val sortOrder: SortOrder,
|
||||
val string: String
|
||||
) {
|
||||
override fun toString() = string
|
||||
}
|
||||
|
||||
/**
|
||||
* ViewCapabilities defines which layout and sort orders a view can support
|
||||
*
|
||||
* @property supportsGrid
|
||||
* @property supportedSortOrders
|
||||
*/
|
||||
data class ViewCapabilities(
|
||||
val supportsGrid: Boolean = false,
|
||||
val supportedSortOrders: List<SortOrder>
|
||||
)
|
||||
|
||||
val EMPTY_CAPABILITIES = ViewCapabilities(
|
||||
supportsGrid = false,
|
||||
supportedSortOrders = listOf()
|
||||
)
|
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* kt
|
||||
* Copyright (C) 2009-2022 Ultrasonic developers
|
||||
*
|
||||
* Distributed under terms of the GNU GPLv3 license.
|
||||
*/
|
||||
|
||||
package org.moire.ultrasonic.view
|
||||
|
||||
/*
|
||||
* This enum is very similar to AlbumListType, but not completely the same.
|
||||
*/
|
||||
enum class SortOrder(val typeName: String) {
|
||||
RANDOM("random"),
|
||||
NEWEST("newest"),
|
||||
HIGHEST("highest"),
|
||||
FREQUENT("frequent"),
|
||||
RECENT("recent"),
|
||||
BY_NAME("alphabeticalByName"),
|
||||
BY_ARTIST("alphabeticalByArtist"),
|
||||
STARRED("starred"),
|
||||
BY_YEAR("byYear");
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
|
||||
</vector>
|
11
ultrasonic/src/main/res/drawable/ic_baseline_videos.xml
Normal file
11
ultrasonic/src/main/res/drawable/ic_baseline_videos.xml
Normal file
@ -0,0 +1,11 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:autoMirrored="true"
|
||||
android:tint="?attr/colorControlNormal"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M17,10.5V7c0,-0.55 -0.45,-1 -1,-1H4c-0.55,0 -1,0.45 -1,1v10c0,0.55 0.45,1 1,1h12c0.55,0 1,-0.45 1,-1v-3.5l4,4v-11l-4,4z" />
|
||||
</vector>
|
11
ultrasonic/src/main/res/drawable/ic_baseline_view_grid.xml
Normal file
11
ultrasonic/src/main/res/drawable/ic_baseline_view_grid.xml
Normal file
@ -0,0 +1,11 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:autoMirrored="true"
|
||||
android:tint="?attr/colorControlNormal"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M4,11h5L9,5L4,5v6zM4,18h5v-6L4,12v6zM10,18h5v-6h-5v6zM16,18h5v-6h-5v6zM10,11h5L15,5h-5v6zM16,5v6h5L21,5h-5z" />
|
||||
</vector>
|
11
ultrasonic/src/main/res/drawable/ic_baseline_view_list.xml
Normal file
11
ultrasonic/src/main/res/drawable/ic_baseline_view_list.xml
Normal file
@ -0,0 +1,11 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:autoMirrored="true"
|
||||
android:tint="?attr/colorControlNormal"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M4,14h4v-4L4,10v4zM4,19h4v-4L4,15v4zM4,9h4L8,5L4,5v4zM9,14h12v-4L9,10v4zM9,19h12v-4L9,15v4zM9,5v4h12L21,5L9,5z" />
|
||||
</vector>
|
@ -1,10 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="#FFF"
|
||||
android:pathData="M20,6h-8l-2,-2L4,4c-1.11,0 -1.99,0.89 -1.99,2L2,18c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2L22,8c0,-1.11 -0.89,-2 -2,-2zM19,14h-3v3h-2v-3h-3v-2h3L14,9h2v3h3v2z"/>
|
||||
</vector>
|
@ -1,10 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="#FFF"
|
||||
android:pathData="M20,9H4v2h16V9zM4,15h16v-2H4V15z"/>
|
||||
</vector>
|
@ -1,10 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="#FFF"
|
||||
android:pathData="M10,4H4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V8c0,-1.1 -0.9,-2 -2,-2h-8l-2,-2z"/>
|
||||
</vector>
|
@ -1,11 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M12,12 L24,0v24z"
|
||||
android:strokeWidth="0.396875"
|
||||
android:fillColor="#000000"
|
||||
android:strokeColor="#00000000"/>
|
||||
</vector>
|
@ -1,10 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="#FFF"
|
||||
android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z"/>
|
||||
</vector>
|
@ -1,10 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="#FFF"
|
||||
android:pathData="M12,4l-1.41,1.41L16.17,11H4v2h12.17l-5.58,5.59L12,20l8,-8z"/>
|
||||
</vector>
|
@ -1,10 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="#FFF"
|
||||
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,19h-2v-2h2v2zM15.07,11.25l-0.9,0.92C13.45,12.9 13,13.5 13,15h-2v-0.5c0,-1.1 0.45,-2.1 1.17,-2.83l1.24,-1.26c0.37,-0.36 0.59,-0.86 0.59,-1.41 0,-1.1 -0.9,-2 -2,-2s-2,0.9 -2,2L8,9c0,-2.21 1.79,-4 4,-4s4,1.79 4,4c0,0.88 -0.36,1.68 -0.93,2.25z"/>
|
||||
</vector>
|
@ -1,10 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="#FFF"
|
||||
android:pathData="M12,1c-4.97,0 -9,4.03 -9,9v7c0,1.66 1.34,3 3,3h3v-8H5v-2c0,-3.87 3.13,-7 7,-7s7,3.13 7,7v2h-4v8h3c1.66,0 3,-1.34 3,-3v-7c0,-4.97 -4.03,-9 -9,-9z"/>
|
||||
</vector>
|
@ -1,10 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="#FFF"
|
||||
android:pathData="M18,2h-8L4.02,8 4,20c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,4c0,-1.1 -0.9,-2 -2,-2zM12,8h-2L10,4h2v4zM15,8h-2L13,4h2v4zM18,8h-2L16,4h2v4z"/>
|
||||
</vector>
|
@ -1,10 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="#FFF"
|
||||
android:pathData="M 16,11 14.58,12.42 11,8.83 V 18 h 10 v 2 H 9 V 8.83 L 5.42,12.42 4,11 10,5 Z"/>
|
||||
</vector>
|
@ -5,9 +5,9 @@
|
||||
android:viewportHeight="100">
|
||||
<path
|
||||
android:pathData="M0,0h100v100h-100z"
|
||||
android:fillColor="#1a1a1a"/>
|
||||
android:fillColor="?attr/colorSecondary"/>
|
||||
<path
|
||||
android:pathData="M42.415,84.787C49.463,84.53 55.166,77.789 54.75,70.704 55.614,58.35 56.479,45.979 57.342,33.633c0.513,-0.518 1.407,0.72 1.903,0.815 5.393,3.785 9.987,9.62 9.845,16.528 -0.003,5.402 -1.991,10.554 -4.162,15.413C71.552,59.26 74.281,48.374 70.666,39.149 68.858,33.816 64.197,30.278 61.795,25.279A26.452,26.452 0,0 1,58.5 14.397c-0.343,-0.816 -1.323,-0.945 -2.094,-0.999l-0.017,-0.001c-2.434,-0.17 -2.216,1.472 -2.331,3.117l-3.274,46.814c0,0 -1.255,-0.207 -2.188,-0.272 -7.098,-0.965 -15.202,3.666 -16.437,11.095 -1.246,5.877 4.638,11.171 10.257,10.635z"
|
||||
android:strokeWidth="0.85"
|
||||
android:fillColor="#ffffff"/>
|
||||
android:fillColor="?attr/colorOnSecondary"/>
|
||||
</vector>
|
||||
|
@ -1,53 +1,54 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:a="http://schemas.android.com/apk/res/android"
|
||||
a:layout_width="fill_parent"
|
||||
a:layout_height="wrap_content"
|
||||
a:orientation="horizontal">
|
||||
a:layout_width="fill_parent"
|
||||
a:layout_height="wrap_content"
|
||||
a:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
a:id="@+id/chat_avatar"
|
||||
a:layout_width="50dip"
|
||||
a:layout_height="50dip"
|
||||
a:id="@+id/chat_avatar"/>
|
||||
a:contentDescription="@string/chat.user_avatar" />
|
||||
|
||||
<LinearLayout
|
||||
a:layout_width="fill_parent"
|
||||
a:layout_height="wrap_content"
|
||||
a:orientation="vertical"
|
||||
a:layout_toEndOf="@id/chat_avatar"
|
||||
a:gravity="center_vertical">
|
||||
a:gravity="center_vertical"
|
||||
a:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
a:id="@+id/chat_username"
|
||||
a:layout_width="wrap_content"
|
||||
a:layout_height="wrap_content"
|
||||
a:layout_gravity="left"
|
||||
a:layout_marginStart="6dip"
|
||||
a:layout_marginEnd="6dip"
|
||||
a:ellipsize="marquee"
|
||||
a:gravity="center_vertical|left"
|
||||
a:singleLine="true"
|
||||
a:textIsSelectable="true"
|
||||
a:textAppearance="?android:attr/textAppearanceMedium"
|
||||
a:textStyle="bold"
|
||||
a:layout_gravity="left"
|
||||
a:gravity="center_vertical|left"/>
|
||||
a:textIsSelectable="true"
|
||||
a:textStyle="bold" />
|
||||
|
||||
<LinearLayout
|
||||
a:id="@+id/chat_message_layout"
|
||||
a:layout_width="wrap_content"
|
||||
a:layout_height="wrap_content"
|
||||
a:layout_gravity="left"
|
||||
a:layout_marginTop="2dip"
|
||||
a:orientation="horizontal"
|
||||
a:gravity="center_vertical|left"
|
||||
a:layout_gravity="left">
|
||||
a:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
a:id="@+id/chat_time"
|
||||
a:layout_width="wrap_content"
|
||||
a:layout_height="wrap_content"
|
||||
a:layout_marginStart="6dip"
|
||||
a:gravity="left"
|
||||
a:singleLine="true"
|
||||
a:textIsSelectable="true"
|
||||
a:textAppearance="?android:attr/textAppearanceMedium"
|
||||
a:gravity="left"/>
|
||||
a:textIsSelectable="true" />
|
||||
|
||||
<TextView
|
||||
a:id="@+id/chat_message"
|
||||
@ -55,12 +56,12 @@
|
||||
a:layout_height="wrap_content"
|
||||
a:layout_marginStart="6dip"
|
||||
a:layout_marginEnd="6dip"
|
||||
a:textIsSelectable="true"
|
||||
a:autoLink="all"
|
||||
a:gravity="left"
|
||||
a:linksClickable="true"
|
||||
a:singleLine="false"
|
||||
a:autoLink="all"
|
||||
a:textAppearance="?android:attr/textAppearanceMedium"
|
||||
a:gravity="left"/>
|
||||
a:textIsSelectable="true" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</RelativeLayout>
|
@ -1,71 +1,72 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:a="http://schemas.android.com/apk/res/android"
|
||||
a:layout_width="fill_parent"
|
||||
a:layout_height="wrap_content"
|
||||
a:orientation="horizontal">
|
||||
a:layout_width="fill_parent"
|
||||
a:layout_height="wrap_content"
|
||||
a:orientation="horizontal">
|
||||
|
||||
<LinearLayout
|
||||
a:layout_width="fill_parent"
|
||||
a:layout_height="wrap_content"
|
||||
a:orientation="vertical"
|
||||
a:layout_gravity="right"
|
||||
a:layout_alignParentEnd="false"
|
||||
a:layout_gravity="right"
|
||||
a:layout_toStartOf="@id/chat_avatar"
|
||||
a:gravity="center_vertical|right">
|
||||
a:gravity="center_vertical|right"
|
||||
a:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
a:id="@+id/chat_username"
|
||||
a:layout_width="wrap_content"
|
||||
a:layout_height="wrap_content"
|
||||
a:layout_marginEnd="6dip"
|
||||
a:gravity="center_vertical|right"
|
||||
a:layout_gravity="right"
|
||||
a:layout_marginEnd="6dip"
|
||||
a:ellipsize="marquee"
|
||||
a:gravity="center_vertical|right"
|
||||
a:singleLine="true"
|
||||
a:textIsSelectable="true"
|
||||
a:textAppearance="?android:attr/textAppearanceMedium"
|
||||
a:textStyle="bold"/>
|
||||
a:textIsSelectable="true"
|
||||
a:textStyle="bold" />
|
||||
|
||||
<LinearLayout
|
||||
a:layout_width="wrap_content"
|
||||
a:layout_height="wrap_content"
|
||||
a:layout_marginTop="2dip"
|
||||
a:orientation="horizontal"
|
||||
a:layout_gravity="right|end"
|
||||
a:gravity="center_vertical|right">
|
||||
a:layout_marginTop="2dip"
|
||||
a:gravity="center_vertical|right"
|
||||
a:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
a:id="@+id/chat_time"
|
||||
a:layout_width="wrap_content"
|
||||
a:layout_height="wrap_content"
|
||||
a:layout_gravity="right"
|
||||
a:layout_marginStart="6dip"
|
||||
a:singleLine="true"
|
||||
a:gravity="center_vertical|right"
|
||||
a:textIsSelectable="true"
|
||||
a:singleLine="true"
|
||||
a:textAppearance="?android:attr/textAppearanceMedium"
|
||||
a:layout_gravity="right"/>
|
||||
a:textIsSelectable="true" />
|
||||
|
||||
<TextView
|
||||
a:id="@+id/chat_message"
|
||||
a:layout_width="wrap_content"
|
||||
a:layout_height="wrap_content"
|
||||
a:layout_gravity="right"
|
||||
a:layout_marginStart="6dip"
|
||||
a:layout_marginEnd="6dip"
|
||||
a:autoLink="all"
|
||||
a:gravity="center_vertical|right"
|
||||
a:linksClickable="true"
|
||||
a:singleLine="false"
|
||||
a:autoLink="all"
|
||||
a:textIsSelectable="true"
|
||||
a:textAppearance="?android:attr/textAppearanceMedium"
|
||||
a:gravity="center_vertical|right"
|
||||
a:layout_gravity="right"/>
|
||||
a:textIsSelectable="true" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
a:id="@+id/chat_avatar"
|
||||
a:layout_width="50dip"
|
||||
a:layout_height="50dip"
|
||||
a:id="@+id/chat_avatar"
|
||||
a:layout_alignParentEnd="true"
|
||||
a:layout_toStartOf="@id/chat_avatar"/>
|
||||
a:layout_toStartOf="@id/chat_avatar"
|
||||
a:contentDescription="@string/chat.user_avatar" />
|
||||
|
||||
</RelativeLayout>
|
@ -1,46 +1,36 @@
|
||||
<?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">
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
a:layout_width="fill_parent"
|
||||
a:layout_height="wrap_content"
|
||||
a:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
a:id="@+id/equalizer_frequency"
|
||||
a:textSize="12sp"
|
||||
a:textColor="#c0c0c0"
|
||||
a:layout_width="wrap_content"
|
||||
a:layout_height="wrap_content"
|
||||
a:layout_marginTop="8dp"
|
||||
a:layout_alignParentLeft="true"
|
||||
/>
|
||||
a:id="@+id/equalizer_frequency"
|
||||
a:layout_width="wrap_content"
|
||||
a:layout_height="wrap_content"
|
||||
a:layout_alignParentLeft="true"
|
||||
a:layout_marginTop="8dp"
|
||||
a:textColor="#c0c0c0"
|
||||
a:textSize="12sp" />
|
||||
|
||||
<TextView
|
||||
a:id="@+id/equalizer_level"
|
||||
a:text="0 dB"
|
||||
a:textSize="12sp"
|
||||
a:textColor="#c0c0c0"
|
||||
a:gravity="right"
|
||||
a:layout_width="wrap_content"
|
||||
a:layout_height="wrap_content"
|
||||
a:layout_marginTop="8dp"
|
||||
a:layout_alignParentRight="true"
|
||||
a:layout_toEndOf="@+id/equalizer_frequency"
|
||||
/>
|
||||
a:id="@+id/equalizer_level"
|
||||
a:layout_width="wrap_content"
|
||||
a:layout_height="wrap_content"
|
||||
a:layout_alignParentRight="true"
|
||||
a:layout_marginTop="8dp"
|
||||
a:layout_toEndOf="@+id/equalizer_frequency"
|
||||
a:gravity="right"
|
||||
a:textColor="#c0c0c0"
|
||||
a:textSize="12sp"
|
||||
tools:text="0 dB" />
|
||||
|
||||
<SeekBar
|
||||
a:id="@+id/equalizer_bar"
|
||||
a:layout_width="fill_parent"
|
||||
a:layout_height="wrap_content"
|
||||
a:layout_below="@+id/equalizer_frequency"
|
||||
/>
|
||||
|
||||
a:id="@+id/equalizer_bar"
|
||||
a:layout_width="fill_parent"
|
||||
a:layout_height="wrap_content"
|
||||
a:layout_below="@+id/equalizer_frequency" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
|
70
ultrasonic/src/main/res/layout/filter_button_bar.xml
Normal file
70
ultrasonic/src/main/res/layout/filter_button_bar.xml
Normal file
@ -0,0 +1,70 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:a="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
a:id="@+id/filter_layout"
|
||||
a:layout_width="match_parent"
|
||||
a:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toTopOf="@+id/sort_order_menu"
|
||||
app:layout_constraintTop_toTopOf="@+id/sort_order_menu"
|
||||
tools:layout_height="match_parent">
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
a:id="@+id/chip_view_toggle"
|
||||
a:layout_width="wrap_content"
|
||||
a:layout_height="wrap_content"
|
||||
a:layout_marginStart="8dp"
|
||||
a:text="@string/list_view"
|
||||
app:chipIcon="@drawable/ic_baseline_view_list"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/sort_order_menu"
|
||||
app:layout_constraintHorizontal_bias="1.0"
|
||||
app:layout_constraintHorizontal_chainStyle="spread_inside"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
a:id="@+id/sort_order_menu"
|
||||
style="@style/Widget.ChipDropdown"
|
||||
a:layout_width="wrap_content"
|
||||
a:layout_height="48dp"
|
||||
a:layout_marginStart="8dp"
|
||||
a:layout_marginTop="4dp"
|
||||
a:layout_marginEnd="8dp"
|
||||
a:background="@null"
|
||||
a:baselineAligned="false"
|
||||
a:ellipsize="marquee"
|
||||
a:text="@string/main.albums_newest"
|
||||
a:textAppearance="@style/TextAppearance.Material3.LabelLarge"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="1"
|
||||
app:layout_constraintHorizontal_chainStyle="spread_inside"
|
||||
app:layout_constraintStart_toEndOf="@+id/chip_view_toggle"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintWidth_default="wrap">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatAutoCompleteTextView
|
||||
a:id="@+id/sort_order_menu_options"
|
||||
a:layout_width="wrap_content"
|
||||
a:layout_height="32dp"
|
||||
a:ellipsize="end"
|
||||
a:focusable="false"
|
||||
a:inputType="none"
|
||||
a:paddingStart="16dp"
|
||||
a:paddingTop="0dp"
|
||||
a:paddingEnd="16dp"
|
||||
a:paddingBottom="0dp"
|
||||
a:singleLine="true"
|
||||
a:text="@string/main.albums_newest"
|
||||
a:textAppearance="@style/TextAppearance.Material3.LabelLarge"
|
||||
a:textColor="?attr/colorOnSurfaceVariant"
|
||||
tools:ignore="LabelFor,TouchTargetSizeCheck,TouchTargetSizeCheck" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
75
ultrasonic/src/main/res/layout/grid_item_album.xml
Normal file
75
ultrasonic/src/main/res/layout/grid_item_album.xml
Normal file
@ -0,0 +1,75 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.google.android.material.card.MaterialCardView xmlns:a="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
a:id="@+id/card"
|
||||
a:layout_width="match_parent"
|
||||
a:layout_height="wrap_content"
|
||||
a:layout_margin="8dp"
|
||||
style="?attr/materialCardViewFilledStyle">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
a:id="@+id/containing_layout"
|
||||
a:layout_width="match_parent"
|
||||
a:layout_height="wrap_content"
|
||||
a:background="?android:attr/selectableItemBackground"
|
||||
a:clickable="true"
|
||||
a:focusable="true">
|
||||
|
||||
<ImageView
|
||||
a:id="@+id/cover_art"
|
||||
a:layout_width="match_parent"
|
||||
a:layout_height="0dp"
|
||||
a:layout_gravity="center_horizontal|center_vertical"
|
||||
a:adjustViewBounds="true"
|
||||
a:scaleType="fitCenter"
|
||||
a:src="@drawable/unknown_album"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
a:contentDescription="@string/albumArt" />
|
||||
|
||||
<LinearLayout
|
||||
a:id="@+id/row_album_details"
|
||||
a:layout_width="match_parent"
|
||||
a:layout_height="wrap_content"
|
||||
a:gravity="center_vertical"
|
||||
a:orientation="vertical"
|
||||
a:padding="11dp"
|
||||
a:paddingTop="0dp"
|
||||
app:layout_constraintEnd_toEndOf="@+id/cover_art"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/cover_art">
|
||||
|
||||
<TextView
|
||||
a:id="@+id/album_title"
|
||||
a:layout_width="match_parent"
|
||||
a:layout_height="wrap_content"
|
||||
a:ellipsize="marquee"
|
||||
a:singleLine="true"
|
||||
a:textAppearance="@style/TextAppearance.Material3.LabelLarge"
|
||||
tools:text="Title" />
|
||||
|
||||
<TextView
|
||||
a:id="@+id/album_artist"
|
||||
a:layout_width="match_parent"
|
||||
a:layout_height="wrap_content"
|
||||
a:singleLine="true"
|
||||
a:textAppearance="@style/TextAppearance.Material3.LabelSmall"
|
||||
tools:text="Artist" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
a:id="@+id/album_star"
|
||||
a:layout_width="38dp"
|
||||
a:layout_height="38dp"
|
||||
a:background="@android:color/transparent"
|
||||
a:contentDescription="@string/download.menu_star"
|
||||
a:gravity="center_horizontal"
|
||||
a:padding="4dp"
|
||||
a:src="@drawable/ic_star_hollow"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/cover_art"
|
||||
app:layout_constraintEnd_toEndOf="@+id/cover_art" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
@ -10,7 +10,7 @@
|
||||
a:focusable="true">
|
||||
|
||||
<com.google.android.material.imageview.ShapeableImageView
|
||||
a:id="@+id/coverart"
|
||||
a:id="@+id/cover_art"
|
||||
a:layout_width="64dp"
|
||||
a:layout_height="64dp"
|
||||
a:layout_gravity="center_horizontal|center_vertical"
|
||||
@ -20,7 +20,7 @@
|
||||
a:src="@drawable/unknown_album"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:shapeAppearanceOverlay="@style/largeRoundedImageView" />
|
||||
app:shapeAppearanceOverlay="@style/ShapeAppearance.Material3.SmallComponent" />
|
||||
|
||||
<LinearLayout
|
||||
a:id="@+id/row_album_details"
|
||||
@ -35,8 +35,8 @@
|
||||
a:paddingEnd="3dip"
|
||||
a:textAppearance="?android:attr/textAppearanceMedium"
|
||||
app:layout_constraintEnd_toStartOf="@+id/album_star"
|
||||
app:layout_constraintLeft_toRightOf="@+id/coverart"
|
||||
app:layout_constraintStart_toEndOf="@+id/coverart"
|
||||
app:layout_constraintLeft_toRightOf="@+id/cover_art"
|
||||
app:layout_constraintStart_toEndOf="@+id/cover_art"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<TextView
|
||||
@ -61,6 +61,7 @@
|
||||
<ImageView
|
||||
a:id="@+id/album_star"
|
||||
a:layout_width="38dp"
|
||||
a:padding="4dp"
|
||||
a:layout_height="38dp"
|
||||
a:layout_marginStart="16dp"
|
||||
a:layout_marginTop="16dp"
|
||||
|
@ -23,7 +23,7 @@
|
||||
tools:text="A" />
|
||||
|
||||
<com.google.android.material.imageview.ShapeableImageView
|
||||
a:id="@+id/coverart"
|
||||
a:id="@+id/cover_art"
|
||||
a:layout_width="40dp"
|
||||
a:layout_height="40dp"
|
||||
a:layout_gravity="center_horizontal|center_vertical"
|
||||
@ -33,14 +33,14 @@
|
||||
a:layout_toEndOf="@+id/row_section"
|
||||
a:scaleType="fitCenter"
|
||||
a:src="@drawable/ic_contact_picture"
|
||||
app:shapeAppearanceOverlay="@style/roundedImageView" />
|
||||
app:shapeAppearanceOverlay="@style/ShapeAppearance.Material3.MediumComponent" />
|
||||
|
||||
<TextView
|
||||
a:id="@+id/row_artist_name"
|
||||
a:layout_width="fill_parent"
|
||||
a:layout_height="wrap_content"
|
||||
a:layout_marginEnd="12dp"
|
||||
a:layout_toEndOf="@+id/coverart"
|
||||
a:layout_toEndOf="@+id/cover_art"
|
||||
a:drawablePadding="6dip"
|
||||
a:gravity="center_vertical"
|
||||
a:minHeight="56dip"
|
||||
|
@ -1,11 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TextView xmlns:a="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
a:id="@android:id/text1"
|
||||
a:drawablePadding="6dip"
|
||||
a:layout_width="fill_parent"
|
||||
a:layout_height="wrap_content"
|
||||
a:textAppearance="?android:attr/textAppearanceMedium"
|
||||
a:layout_height="56dp"
|
||||
a:drawablePadding="6dip"
|
||||
a:gravity="center_vertical"
|
||||
a:paddingStart="3dip"
|
||||
a:paddingEnd="3dip"
|
||||
a:minHeight="50dip"/>
|
||||
a:paddingStart="16dip"
|
||||
a:paddingTop="8dp"
|
||||
a:paddingEnd="24dip"
|
||||
a:singleLine="true"
|
||||
a:ellipsize="marquee"
|
||||
a:paddingBottom="8dp"
|
||||
a:textAppearance="@style/TextAppearance.Material3.BodyLarge"
|
||||
tools:text="Item text" />
|
15
ultrasonic/src/main/res/layout/list_layout_filterable.xml
Normal file
15
ultrasonic/src/main/res/layout/list_layout_filterable.xml
Normal file
@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:a="http://schemas.android.com/apk/res/android"
|
||||
a:layout_width="fill_parent"
|
||||
a:layout_height="fill_parent"
|
||||
a:orientation="vertical">
|
||||
|
||||
<org.moire.ultrasonic.view.FilterButtonBar
|
||||
a:id="@+id/filter_button_bar"
|
||||
a:layout_width="match_parent"
|
||||
a:layout_height="wrap_content" />
|
||||
|
||||
<include layout="@layout/list_parts_empty_view" />
|
||||
<include layout="@layout/list_parts_recycler" />
|
||||
|
||||
</LinearLayout>
|
@ -1,205 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:id="@+id/main_list">
|
||||
<LinearLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/main_music"
|
||||
android:theme="@style/Ultrasonic.AllCapsLabel"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="6dp"
|
||||
android:text="@string/main.music"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/main_artists_button"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:minHeight="40dip"
|
||||
android:paddingStart="6dip"
|
||||
android:paddingEnd="6dip"
|
||||
android:text="@string/main.artists_title"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/main_albums_button"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:minHeight="40dip"
|
||||
android:paddingStart="6dip"
|
||||
android:paddingEnd="6dip"
|
||||
android:text="@string/main.albums_title"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/main_genres_button"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:minHeight="40dip"
|
||||
android:paddingStart="6dip"
|
||||
android:paddingEnd="6dip"
|
||||
android:text="@string/main.genres_title"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/main_songs"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="6dp"
|
||||
android:text="@string/main.songs_title"
|
||||
android:theme="@style/Ultrasonic.AllCapsLabel" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/main_songs_button"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:minHeight="40dip"
|
||||
android:paddingStart="6dip"
|
||||
android:paddingEnd="6dip"
|
||||
android:text="@string/main.songs_random"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/main_songs_starred"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:minHeight="40dip"
|
||||
android:paddingStart="6dip"
|
||||
android:paddingEnd="6dip"
|
||||
android:text="@string/main.songs_starred"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/main_albums"
|
||||
android:theme="@style/Ultrasonic.AllCapsLabel"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="6dp"
|
||||
android:text="@string/main.albums_title" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/main_albums_newest"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:minHeight="40dip"
|
||||
android:paddingStart="6dip"
|
||||
android:paddingEnd="6dip"
|
||||
android:text="@string/main.albums_newest"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/main_albums_recent"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:minHeight="40dip"
|
||||
android:paddingStart="6dip"
|
||||
android:paddingEnd="6dip"
|
||||
android:text="@string/main.albums_recent"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/main_albums_frequent"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:minHeight="40dip"
|
||||
android:paddingStart="6dip"
|
||||
android:paddingEnd="6dip"
|
||||
android:text="@string/main.albums_frequent"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/main_albums_highest"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:minHeight="40dip"
|
||||
android:paddingStart="6dip"
|
||||
android:paddingEnd="6dip"
|
||||
android:text="@string/main.albums_highest"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/main_albums_random"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:minHeight="40dip"
|
||||
android:paddingStart="6dip"
|
||||
android:paddingEnd="6dip"
|
||||
android:text="@string/main.albums_random"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/main_albums_starred"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:minHeight="40dip"
|
||||
android:paddingStart="6dip"
|
||||
android:paddingEnd="6dip"
|
||||
android:text="@string/main.albums_starred"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/main_albums_alphaByName"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:minHeight="40dip"
|
||||
android:paddingStart="6dip"
|
||||
android:paddingEnd="6dip"
|
||||
android:text="@string/main.albums_alphaByName"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/main_albums_alphaByArtist"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:minHeight="40dip"
|
||||
android:paddingStart="6dip"
|
||||
android:paddingEnd="6dip"
|
||||
android:text="@string/main.albums_alphaByArtist"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/main_videos_title"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="6dp"
|
||||
android:text="@string/main.videos"
|
||||
android:theme="@style/Ultrasonic.AllCapsLabel" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/main_videos"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:minHeight="40dip"
|
||||
android:paddingStart="6dip"
|
||||
android:paddingEnd="6dip"
|
||||
android:text="@string/main.videos"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
</layout>
|
@ -46,5 +46,5 @@
|
||||
a:layout_gravity="start"
|
||||
a:fitsSystemWindows="true"
|
||||
app:headerLayout="@layout/navigation_header"
|
||||
app:menu="@menu/navigation"/>
|
||||
app:menu="@menu/navigation_drawer"/>
|
||||
</androidx.drawerlayout.widget.DrawerLayout>
|
@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:a="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
a:layout_width="match_parent"
|
||||
a:layout_height="wrap_content"
|
||||
a:orientation="vertical"
|
||||
@ -33,7 +34,8 @@
|
||||
a:layout_alignParentRight="true"
|
||||
a:layout_marginEnd="12dip"
|
||||
a:text="@string/util.no_time"
|
||||
a:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
a:textAppearance="?android:attr/textAppearanceSmall"
|
||||
tools:ignore="RelativeOverlap" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
|
@ -1,18 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/playlist_name"
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:gravity="left|center_vertical"
|
||||
android:paddingStart="6dip"
|
||||
android:paddingEnd="6dip"
|
||||
android:minHeight="50dip"/>
|
||||
|
||||
</LinearLayout>
|
@ -1,9 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<TextView xmlns:a="http://schemas.android.com/apk/res/android"
|
||||
a:id="@+id/podcast_channel_item"
|
||||
a:layout_width="fill_parent"
|
||||
a:layout_height="wrap_content"
|
||||
a:padding="10dip"
|
||||
a:visibility="visible" />
|
||||
|
25
ultrasonic/src/main/res/layout/primary.xml
Normal file
25
ultrasonic/src/main/res/layout/primary.xml
Normal file
@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:a="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
a:layout_width="match_parent"
|
||||
a:layout_height="match_parent"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
a:orientation="vertical">
|
||||
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
a:id="@+id/tab_layout"
|
||||
app:tabMode="scrollable"
|
||||
a:layout_width="match_parent"
|
||||
a:layout_height="wrap_content" />
|
||||
|
||||
<org.moire.ultrasonic.view.FilterButtonBar
|
||||
a:id="@+id/filter_button_bar"
|
||||
a:layout_width="match_parent"
|
||||
a:layout_height="wrap_content" />
|
||||
|
||||
<androidx.viewpager2.widget.ViewPager2
|
||||
a:id="@+id/pager"
|
||||
a:layout_width="match_parent"
|
||||
a:layout_height="match_parent" />
|
||||
|
||||
</LinearLayout>
|
@ -1,19 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:a="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
a:layout_width="fill_parent"
|
||||
a:layout_height="fill_parent"
|
||||
a:orientation="vertical" >
|
||||
a:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
a:id="@+id/select_genre_empty"
|
||||
a:layout_width="fill_parent"
|
||||
a:layout_height="wrap_content"
|
||||
a:padding="10dip"
|
||||
a:padding="16dip"
|
||||
a:text="@string/select_genre.empty"
|
||||
a:visibility="gone" />
|
||||
a:textAppearance="@style/TextAppearance.Material3.BodyLarge"
|
||||
a:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/select_genre_refresh"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="0dip"
|
||||
|
@ -1,19 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:a="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
a:layout_width="fill_parent"
|
||||
a:layout_height="fill_parent"
|
||||
a:orientation="vertical" >
|
||||
a:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
a:id="@+id/select_playlist_empty"
|
||||
a:layout_width="fill_parent"
|
||||
a:layout_height="wrap_content"
|
||||
a:padding="10dip"
|
||||
a:padding="16dip"
|
||||
a:text="@string/select_playlist.empty"
|
||||
a:visibility="gone" />
|
||||
a:textAppearance="@style/TextAppearance.Material3.BodyLarge"
|
||||
a:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/select_playlist_refresh"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="0dip"
|
||||
|
@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:a="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
a:layout_width="fill_parent"
|
||||
a:layout_height="fill_parent"
|
||||
a:orientation="vertical">
|
||||
@ -8,13 +9,14 @@
|
||||
a:id="@+id/select_share_empty"
|
||||
a:layout_width="fill_parent"
|
||||
a:layout_height="wrap_content"
|
||||
a:padding="10dip"
|
||||
a:padding="16dip"
|
||||
a:text="@string/select_share.empty"
|
||||
a:visibility="gone" />
|
||||
a:textAppearance="@style/TextAppearance.Material3.BodyLarge"
|
||||
a:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/select_share_refresh"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="0dip"
|
||||
|
@ -1,13 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:a="http://schemas.android.com/apk/res/android"
|
||||
a:layout_width="0dip"
|
||||
a:layout_height="wrap_content"
|
||||
a:layout_gravity="center_vertical"
|
||||
a:layout_weight="1"
|
||||
a:paddingStart="6dip"
|
||||
a:paddingEnd="6dip"
|
||||
a:minHeight="44dip"
|
||||
a:orientation="vertical">
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
a:layout_width="0dp"
|
||||
a:layout_height="56dp"
|
||||
a:layout_gravity="center_vertical"
|
||||
a:layout_weight="1"
|
||||
a:minHeight="44dip"
|
||||
a:orientation="vertical"
|
||||
a:paddingStart="16dip"
|
||||
a:paddingEnd="16dip"
|
||||
tools:layout_width="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
a:layout_width="fill_parent"
|
||||
@ -20,7 +22,8 @@
|
||||
a:layout_width="wrap_content"
|
||||
a:layout_height="wrap_content"
|
||||
a:layout_gravity="left|center_vertical"
|
||||
a:textAppearance="?android:attr/textAppearanceMedium"/>
|
||||
a:textAppearance="?android:attr/textAppearanceMedium"
|
||||
tools:text="Url" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@ -38,9 +41,9 @@
|
||||
a:layout_gravity="left|center_vertical"
|
||||
a:layout_weight="1"
|
||||
a:ellipsize="middle"
|
||||
a:paddingStart="4dip"
|
||||
a:singleLine="true"
|
||||
a:textAppearance="?android:attr/textAppearanceSmall"/>
|
||||
a:textAppearance="?android:attr/textAppearanceSmall"
|
||||
tools:text="Description" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
@ -1,50 +1,48 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content"
|
||||
android:orientation="vertical">
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/top"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_above="@+id/bottom">
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/timeSpanDisableCheckBox"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/timeSpanDisableCheckBox"
|
||||
android:text="@string/time_span_disable"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentStart="true"/>
|
||||
android:layout_alignParentTop="true"
|
||||
android:text="@string/time_span_disable" />
|
||||
</RelativeLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/bottom"
|
||||
android:layout_below="@+id/top">
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/timeSpanEditText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="numberSigned"
|
||||
android:id="@+id/timeSpanEditText"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:gravity="left"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_alignParentEnd="false"
|
||||
android:ems="5"
|
||||
android:layout_alignParentEnd="false"/>
|
||||
android:gravity="left"
|
||||
android:inputType="numberSigned" />
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/timeSpanSpinner"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/timeSpanSpinner"
|
||||
android:layout_alignBottom="@+id/timeSpanEditText"
|
||||
android:layout_toEndOf="@+id/timeSpanEditText"/>
|
||||
android:layout_toEndOf="@+id/timeSpanEditText" />
|
||||
</RelativeLayout>
|
||||
|
||||
</LinearLayout>
|
@ -1,70 +1,76 @@
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/get_playlist_name_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:textSize="20sp"
|
||||
android:text="@string/common.name" />
|
||||
<EditText
|
||||
android:id="@+id/get_playlist_name"
|
||||
android:inputType="text"
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginStart="4dp"
|
||||
android:hint="@string/common.name" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content">
|
||||
<LinearLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/get_playlist_comment_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:textSize="20sp"
|
||||
android:text="@string/common.comment" />
|
||||
<EditText
|
||||
android:id="@+id/get_playlist_comment"
|
||||
android:inputType="text"
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginStart="4dp"
|
||||
android:hint="@string/common.comment" />
|
||||
</LinearLayout>
|
||||
<TextView
|
||||
android:id="@+id/get_playlist_name_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:labelFor="@+id/get_playlist_name"
|
||||
android:text="@string/common.name"
|
||||
android:textSize="20sp" />
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content">
|
||||
<EditText
|
||||
android:id="@+id/get_playlist_name"
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_weight="1"
|
||||
android:importantForAutofill="no"
|
||||
android:inputType="text" />
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/get_playlist_public_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:textSize="20sp"
|
||||
android:text="@string/common.public" />
|
||||
<CheckBox
|
||||
android:id="@+id/get_playlist_public"
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginStart="4dp"
|
||||
android:checked="false"/>
|
||||
</LinearLayout>
|
||||
<LinearLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/get_playlist_comment_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:labelFor="@+id/get_playlist_comment"
|
||||
android:text="@string/common.comment"
|
||||
android:textSize="20sp" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/get_playlist_comment"
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_weight="1"
|
||||
android:importantForAutofill="no"
|
||||
android:inputType="text" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/get_playlist_public_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:labelFor="@+id/get_playlist_public"
|
||||
android:text="@string/common.public"
|
||||
android:textSize="20sp" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/get_playlist_public"
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_weight="1"
|
||||
android:checked="false" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
@ -4,14 +4,10 @@
|
||||
a:checkableBehavior="none"
|
||||
a:enabled="true"
|
||||
a:visible="true">
|
||||
|
||||
<item
|
||||
a:id="@+id/mainFragment"
|
||||
a:checkable="true"
|
||||
a:icon="@drawable/ic_menu_home"
|
||||
a:title="@string/button_bar.home" />
|
||||
<item
|
||||
a:id="@+id/mediaLibraryFragment"
|
||||
a:checkable="true"
|
||||
a:icon="@drawable/ic_menu_browse"
|
||||
a:title="@string/button_bar.browse" />
|
||||
<item
|
||||
@ -19,11 +15,6 @@
|
||||
a:checkable="true"
|
||||
a:icon="@drawable/ic_menu_search"
|
||||
a:title="@string/button_bar.search" />
|
||||
<item
|
||||
a:id="@+id/playlistsFragment"
|
||||
a:checkable="true"
|
||||
a:icon="@drawable/ic_menu_playlists"
|
||||
a:title="@string/button_bar.playlists" />
|
||||
<item
|
||||
a:id="@+id/downloadsFragment"
|
||||
a:checkable="true"
|
||||
@ -44,16 +35,21 @@
|
||||
a:checkable="true"
|
||||
a:icon="@drawable/ic_menu_chat"
|
||||
a:title="@string/button_bar.chat" />
|
||||
<item
|
||||
a:id="@+id/playerFragment"
|
||||
a:checkable="true"
|
||||
a:icon="@drawable/media_start"
|
||||
a:title="@string/button_bar.now_playing" />
|
||||
<item
|
||||
a:id="@+id/podcastFragment"
|
||||
a:checkable="true"
|
||||
a:icon="@drawable/ic_menu_podcasts"
|
||||
a:title="@string/button_bar.podcasts" />
|
||||
<item
|
||||
a:id="@+id/trackCollectionFragment"
|
||||
a:checkable="true"
|
||||
a:icon="@drawable/ic_baseline_videos"
|
||||
a:title="@string/main.videos" />
|
||||
<item
|
||||
a:id="@+id/playerFragment"
|
||||
a:checkable="true"
|
||||
a:icon="@drawable/media_start"
|
||||
a:title="@string/button_bar.now_playing" />
|
||||
</group>
|
||||
<item a:title="@string/menu.common">
|
||||
<menu>
|
@ -1,90 +1,77 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/navigation_graph"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/navigation_graph"
|
||||
app:startDestination="@id/mainFragment">
|
||||
|
||||
<action android:id="@+id/toTrackCollection"
|
||||
app:destination="@id/trackCollectionFragment" />
|
||||
<action android:id="@+id/toBookmarks"
|
||||
app:destination="@id/bookmarksFragment" />
|
||||
<action android:id="@+id/toMediaLibrary"
|
||||
app:destination="@id/mediaLibraryFragment" />
|
||||
<action
|
||||
android:id="@+id/toTrackCollection"
|
||||
app:destination="@id/trackCollectionFragment" />
|
||||
<action
|
||||
android:id="@+id/toBookmarks"
|
||||
app:destination="@id/bookmarksFragment" />
|
||||
<action
|
||||
android:id="@+id/toMediaLibrary"
|
||||
app:destination="@id/mediaLibraryFragment" />
|
||||
<action
|
||||
android:id="@+id/toAlbumList"
|
||||
app:destination="@id/albumListFragment" />
|
||||
<action
|
||||
android:id="@+id/toArtistList"
|
||||
app:destination="@id/artistListFragment" />
|
||||
<action
|
||||
android:id="@+id/toGenreList"
|
||||
app:destination="@id/selectGenreFragment" />
|
||||
<action
|
||||
android:id="@+id/toSearchFragment"
|
||||
app:destination="@id/searchFragment" />
|
||||
<action
|
||||
android:id="@+id/toPlaylistFragment"
|
||||
app:destination="@id/playlistsFragment" />
|
||||
|
||||
<fragment
|
||||
android:id="@+id/mainFragment"
|
||||
android:name="org.moire.ultrasonic.fragment.MainFragment"
|
||||
android:label="@string/common.appname" >
|
||||
<action
|
||||
android:id="@+id/mainToTrackCollection"
|
||||
app:destination="@id/trackCollectionFragment" />
|
||||
<action
|
||||
android:id="@+id/mainToAlbumList"
|
||||
app:destination="@id/albumListFragment" />
|
||||
<action
|
||||
android:id="@+id/mainToArtistList"
|
||||
app:destination="@id/artistListFragment" />
|
||||
<action
|
||||
android:id="@+id/mainToSelectGenre"
|
||||
app:destination="@id/selectGenreFragment" />
|
||||
<action
|
||||
android:id="@+id/toTrackCollection"
|
||||
app:destination="@id/trackCollectionFragment" />
|
||||
android:label="@string/common.appname">
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
android:id="@+id/mediaLibraryFragment"
|
||||
android:name="org.moire.ultrasonic.fragment.ArtistListFragment"
|
||||
android:label="@string/music_library.label" >
|
||||
<action
|
||||
android:id="@+id/artistsListToAlbumsList"
|
||||
app:destination="@id/albumListFragment" />
|
||||
<action
|
||||
android:id="@+id/artistsListToTrackCollection"
|
||||
app:destination="@id/trackCollectionFragment" />
|
||||
</fragment>
|
||||
android:label="@string/music_library.label" />
|
||||
<fragment
|
||||
android:id="@+id/nowPlayingFragment"
|
||||
android:name="org.moire.ultrasonic.fragment.NowPlayingFragment"
|
||||
android:label="@string/button_bar.now_playing">
|
||||
<action
|
||||
android:id="@+id/toTrackCollection"
|
||||
app:destination="@id/trackCollectionFragment" />
|
||||
</fragment>
|
||||
android:label="@string/button_bar.now_playing" />
|
||||
<fragment
|
||||
android:id="@+id/artistListFragment"
|
||||
android:name="org.moire.ultrasonic.fragment.ArtistListFragment" >
|
||||
<action
|
||||
android:id="@+id/artistsListToAlbumsList"
|
||||
app:destination="@id/albumListFragment" />
|
||||
<action
|
||||
android:id="@+id/artistsListToTrackCollection"
|
||||
app:destination="@id/trackCollectionFragment" />
|
||||
android:name="org.moire.ultrasonic.fragment.ArtistListFragment">
|
||||
<argument
|
||||
android:name="refresh"
|
||||
app:argType="boolean"
|
||||
android:defaultValue="false" />
|
||||
android:defaultValue="false"
|
||||
app:argType="boolean" />
|
||||
<argument
|
||||
android:name="title"
|
||||
app:argType="string"
|
||||
app:nullable="true"
|
||||
android:defaultValue="@null"
|
||||
/>
|
||||
app:argType="string"
|
||||
app:nullable="true" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/trackCollectionFragment"
|
||||
android:name="org.moire.ultrasonic.fragment.TrackCollectionFragment" >
|
||||
android:name="org.moire.ultrasonic.fragment.TrackCollectionFragment">
|
||||
<argument
|
||||
android:name="id"
|
||||
app:nullable="true"
|
||||
android:defaultValue="@null"
|
||||
app:argType="string" />
|
||||
app:argType="string"
|
||||
app:nullable="true" />
|
||||
<argument
|
||||
android:name="isAlbum"
|
||||
app:argType="boolean"
|
||||
android:defaultValue="false"/>
|
||||
android:defaultValue="false"
|
||||
app:argType="boolean" />
|
||||
<argument
|
||||
android:name="isArtist"
|
||||
app:argType="boolean"
|
||||
android:defaultValue="false"/>
|
||||
android:defaultValue="false"
|
||||
app:argType="boolean" />
|
||||
<argument
|
||||
android:name="getRandom"
|
||||
android:defaultValue="false"
|
||||
@ -109,76 +96,83 @@
|
||||
android:name="refresh"
|
||||
android:defaultValue="true"
|
||||
app:argType="boolean" />
|
||||
<argument android:name="name"
|
||||
app:argType="string"
|
||||
<argument
|
||||
android:name="name"
|
||||
android:defaultValue="@null"
|
||||
app:nullable="true"/>
|
||||
<argument android:name="parentId"
|
||||
app:argType="string"
|
||||
app:nullable="true" />
|
||||
<argument
|
||||
android:name="parentId"
|
||||
android:defaultValue="@null"
|
||||
app:nullable="true"/>
|
||||
<argument android:name="genreName"
|
||||
app:argType="string"
|
||||
app:nullable="true" />
|
||||
<argument
|
||||
android:name="genreName"
|
||||
android:defaultValue="@null"
|
||||
app:nullable="true"/>
|
||||
<argument android:name="shareId"
|
||||
app:argType="string"
|
||||
app:nullable="true" />
|
||||
<argument
|
||||
android:name="shareId"
|
||||
android:defaultValue="@null"
|
||||
app:nullable="true"/>
|
||||
<argument android:name="playlistId"
|
||||
app:argType="string"
|
||||
app:nullable="true" />
|
||||
<argument
|
||||
android:name="playlistId"
|
||||
android:defaultValue="@null"
|
||||
app:nullable="true"/>
|
||||
<argument android:name="playlistName"
|
||||
app:argType="string"
|
||||
app:nullable="true" />
|
||||
<argument
|
||||
android:name="playlistName"
|
||||
android:defaultValue="@null"
|
||||
app:nullable="true"/>
|
||||
<argument android:name="shareName"
|
||||
app:argType="string"
|
||||
app:nullable="true" />
|
||||
<argument
|
||||
android:name="shareName"
|
||||
android:defaultValue="@null"
|
||||
app:nullable="true"/>
|
||||
<argument android:name="podcastChannelId"
|
||||
app:argType="string"
|
||||
app:nullable="true" />
|
||||
<argument
|
||||
android:name="podcastChannelId"
|
||||
android:defaultValue="@null"
|
||||
app:nullable="true"/>
|
||||
app:argType="string"
|
||||
app:nullable="true" />
|
||||
<argument
|
||||
android:name="albumListType"
|
||||
app:argType="string"
|
||||
app:nullable="true"
|
||||
android:defaultValue="@null"/>
|
||||
<argument
|
||||
android:name="size"
|
||||
app:argType="integer"
|
||||
android:defaultValue="0"
|
||||
/>
|
||||
<argument
|
||||
android:name="offset"
|
||||
app:argType="integer"
|
||||
android:defaultValue="0"
|
||||
/>
|
||||
<action
|
||||
android:id="@+id/loadMoreTracks"
|
||||
app:destination="@id/trackCollectionFragment" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/albumListFragment"
|
||||
android:name="org.moire.ultrasonic.fragment.AlbumListFragment" >
|
||||
<argument
|
||||
android:name="type"
|
||||
app:argType="org.moire.ultrasonic.api.subsonic.models.AlbumListType"
|
||||
/>
|
||||
<argument
|
||||
android:name="title"
|
||||
android:defaultValue="@null"
|
||||
app:argType="string"
|
||||
app:nullable="true" />
|
||||
<argument
|
||||
android:name="size"
|
||||
app:argType="integer"
|
||||
/>
|
||||
android:defaultValue="-1"
|
||||
app:argType="integer" />
|
||||
<argument
|
||||
android:name="offset"
|
||||
app:argType="integer"
|
||||
/>
|
||||
android:defaultValue="0"
|
||||
app:argType="integer" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/albumListFragment"
|
||||
android:name="org.moire.ultrasonic.fragment.AlbumListFragment">
|
||||
<argument
|
||||
android:name="type"
|
||||
app:argType="org.moire.ultrasonic.api.subsonic.models.AlbumListType" />
|
||||
<argument
|
||||
android:name="byArtist"
|
||||
android:defaultValue="false"
|
||||
app:argType="boolean" />
|
||||
<argument
|
||||
android:name="title"
|
||||
app:argType="string"
|
||||
android:defaultValue="@null"
|
||||
app:nullable="true" />
|
||||
<argument
|
||||
android:name="size"
|
||||
android:defaultValue="20"
|
||||
app:argType="integer" />
|
||||
<argument
|
||||
android:name="offset"
|
||||
android:defaultValue="0"
|
||||
app:argType="integer" />
|
||||
<argument
|
||||
android:name="append"
|
||||
android:defaultValue="false"
|
||||
@ -190,22 +184,22 @@
|
||||
<argument
|
||||
android:name="id"
|
||||
android:defaultValue="@null"
|
||||
app:nullable="true"
|
||||
app:argType="string" />
|
||||
app:argType="string"
|
||||
app:nullable="true" />
|
||||
<action
|
||||
android:id="@+id/albumListToTrackCollection"
|
||||
app:destination="@id/trackCollectionFragment" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/entryListFragment"
|
||||
android:name="org.moire.ultrasonic.fragment.EntryListFragment" >
|
||||
android:name="org.moire.ultrasonic.fragment.EntryListFragment">
|
||||
<action
|
||||
android:id="@+id/entryListToTrackCollection"
|
||||
app:destination="@id/trackCollectionFragment" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/searchFragment"
|
||||
android:name="org.moire.ultrasonic.fragment.SearchFragment" >
|
||||
android:name="org.moire.ultrasonic.fragment.SearchFragment">
|
||||
<action
|
||||
android:id="@+id/searchToTrackCollection"
|
||||
app:destination="@id/trackCollectionFragment" />
|
||||
@ -214,30 +208,24 @@
|
||||
app:destination="@id/albumListFragment" />
|
||||
<argument
|
||||
android:name="query"
|
||||
android:defaultValue="@null"
|
||||
app:argType="string"
|
||||
app:nullable="true"
|
||||
android:defaultValue="@null" />
|
||||
app:nullable="true" />
|
||||
<argument
|
||||
android:name="autoplay"
|
||||
app:argType="boolean"
|
||||
android:defaultValue="false" />
|
||||
android:defaultValue="false"
|
||||
app:argType="boolean" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/playlistsFragment"
|
||||
android:name="org.moire.ultrasonic.fragment.legacy.PlaylistsFragment" >
|
||||
<action
|
||||
android:id="@+id/playlistsToTrackCollection"
|
||||
app:destination="@id/trackCollectionFragment" />
|
||||
android:name="org.moire.ultrasonic.fragment.legacy.PlaylistsFragment">
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/downloadsFragment"
|
||||
android:name="org.moire.ultrasonic.fragment.DownloadsFragment" />
|
||||
<fragment
|
||||
android:id="@+id/sharesFragment"
|
||||
android:name="org.moire.ultrasonic.fragment.legacy.SharesFragment" >
|
||||
<action
|
||||
android:id="@+id/sharesToTrackCollection"
|
||||
app:destination="@id/trackCollectionFragment" />
|
||||
android:name="org.moire.ultrasonic.fragment.legacy.SharesFragment">
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/bookmarksFragment"
|
||||
@ -247,28 +235,21 @@
|
||||
android:name="org.moire.ultrasonic.fragment.ChatFragment" />
|
||||
<fragment
|
||||
android:id="@+id/podcastFragment"
|
||||
android:name="org.moire.ultrasonic.fragment.legacy.PodcastFragment" >
|
||||
<action
|
||||
android:id="@+id/podcastToTrackCollection"
|
||||
app:destination="@id/trackCollectionFragment" />
|
||||
android:name="org.moire.ultrasonic.fragment.legacy.PodcastFragment">
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/settingsFragment"
|
||||
android:name="org.moire.ultrasonic.fragment.SettingsFragment" >
|
||||
</fragment>
|
||||
android:name="org.moire.ultrasonic.fragment.SettingsFragment" />
|
||||
<fragment
|
||||
android:id="@+id/aboutFragment"
|
||||
android:name="org.moire.ultrasonic.fragment.AboutFragment" />
|
||||
<fragment
|
||||
android:id="@+id/selectGenreFragment"
|
||||
android:name="org.moire.ultrasonic.fragment.legacy.SelectGenreFragment">
|
||||
<action
|
||||
android:id="@+id/selectGenreToTrackCollection"
|
||||
app:destination="@id/trackCollectionFragment" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/playerFragment"
|
||||
android:name="org.moire.ultrasonic.fragment.PlayerFragment" >
|
||||
android:name="org.moire.ultrasonic.fragment.PlayerFragment">
|
||||
<action
|
||||
android:id="@+id/playerToSelectAlbum"
|
||||
app:destination="@id/trackCollectionFragment" />
|
||||
@ -284,7 +265,7 @@
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/lyricsFragment"
|
||||
android:name="org.moire.ultrasonic.fragment.legacy.LyricsFragment" >
|
||||
android:name="org.moire.ultrasonic.fragment.legacy.LyricsFragment">
|
||||
<argument
|
||||
android:name="artist"
|
||||
app:argType="string" />
|
||||
@ -297,20 +278,17 @@
|
||||
android:name="org.moire.ultrasonic.fragment.EqualizerFragment" />
|
||||
<fragment
|
||||
android:id="@+id/serverSelectorFragment"
|
||||
android:name="org.moire.ultrasonic.fragment.ServerSelectorFragment" >
|
||||
android:name="org.moire.ultrasonic.fragment.ServerSelectorFragment">
|
||||
<action
|
||||
android:id="@+id/toEditServer"
|
||||
app:destination="@id/editServerFragment" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/editServerFragment"
|
||||
android:name="org.moire.ultrasonic.fragment.EditServerFragment" >
|
||||
android:name="org.moire.ultrasonic.fragment.EditServerFragment">
|
||||
<argument
|
||||
android:name="index"
|
||||
app:argType="integer"
|
||||
android:defaultValue="-1" />
|
||||
android:defaultValue="-1"
|
||||
app:argType="integer" />
|
||||
</fragment>
|
||||
<action
|
||||
android:id="@+id/toSearchFragment"
|
||||
app:destination="@id/searchFragment" />
|
||||
</navigation>
|
@ -13,13 +13,11 @@
|
||||
<string name="button_bar.bookmarks">Záložky</string>
|
||||
<string name="button_bar.browse">Knihovna médií</string>
|
||||
<string name="button_bar.chat">Chat</string>
|
||||
<string name="button_bar.home">Ultrasonic Menu</string>
|
||||
<string name="button_bar.now_playing">Právě hraje</string>
|
||||
<string name="buttons.shuffle">Náhodně</string>
|
||||
<string name="podcasts.label">Podcasty</string>
|
||||
<string name="podcasts_channels.empty">Není registrován žádný podcast kanál</string>
|
||||
<string name="button_bar.podcasts">Podcasty</string>
|
||||
<string name="button_bar.playlists">Playlisty</string>
|
||||
<string name="button_bar.search">Hledat</string>
|
||||
<string name="chat.send_a_message">Poslat zprávu</string>
|
||||
<string name="common.appname">Ultrasonic</string>
|
||||
@ -87,7 +85,6 @@
|
||||
<string name="main.albums_title">Alba</string>
|
||||
<string name="main.artists_title">Umělci</string>
|
||||
<string name="main.genres_title">Žánry</string>
|
||||
<string name="main.music">Hudba</string>
|
||||
<string name="main.offline">Bez připojení</string>
|
||||
<string name="main.songs_random">Náhodné</string>
|
||||
<string name="main.songs_starred">Označené hvězdičkou</string>
|
||||
@ -307,7 +304,6 @@
|
||||
<string name="share_via">Sdílet skladby přes</string>
|
||||
<string name="menu.share">Sdílení</string>
|
||||
<string name="download.menu_show_artist">Zobrazit umělce</string>
|
||||
<string name="albumArt">albumArt</string>
|
||||
<string name="common_multiple_years">Vícenásobné roky</string>
|
||||
<string name="settings.debug.title">Možnosti ladění aplikace</string>
|
||||
<string name="settings.debug.log_to_file">Zapisovat logy ladění do souboru</string>
|
||||
|
@ -13,7 +13,6 @@
|
||||
<string name="button_bar.bookmarks">Lesezeichen</string>
|
||||
<string name="button_bar.browse">Medienbibliothek</string>
|
||||
<string name="button_bar.chat">Chat</string>
|
||||
<string name="button_bar.home">Ultrasonic Hauptseite</string>
|
||||
<string name="button_bar.now_playing">Aktuelle Wiedergabe</string>
|
||||
<string name="buttons.play">Abspielen</string>
|
||||
<string name="buttons.pause">Pause</string>
|
||||
@ -25,7 +24,6 @@
|
||||
<string name="podcasts.label">Podcast</string>
|
||||
<string name="podcasts_channels.empty">Keine Podcast Kanäle registriert</string>
|
||||
<string name="button_bar.podcasts">Podcast</string>
|
||||
<string name="button_bar.playlists">Wiedergabeliste</string>
|
||||
<string name="button_bar.search">Suche</string>
|
||||
<string name="chat.send_a_message">Nachricht senden</string>
|
||||
<string name="chat.send_button">Senden</string>
|
||||
@ -119,7 +117,6 @@
|
||||
<string name="main.albums_title">Alben</string>
|
||||
<string name="main.artists_title">Künstler*innen</string>
|
||||
<string name="main.genres_title">Genres</string>
|
||||
<string name="main.music">Musik</string>
|
||||
<string name="main.offline">Offline</string>
|
||||
<string name="main.setup_server">%s - Server einrichten</string>
|
||||
<string name="main.songs_random">Zufällig</string>
|
||||
|
@ -13,7 +13,6 @@
|
||||
<string name="button_bar.bookmarks">Marcadores</string>
|
||||
<string name="button_bar.browse">Biblioteca</string>
|
||||
<string name="button_bar.chat">Chat</string>
|
||||
<string name="button_bar.home">Inicio de Ultrasonic</string>
|
||||
<string name="button_bar.now_playing">Reproduciendo ahora</string>
|
||||
<string name="buttons.play">Reproducir</string>
|
||||
<string name="buttons.pause">Pausar</string>
|
||||
@ -25,7 +24,6 @@
|
||||
<string name="podcasts.label">Podcast</string>
|
||||
<string name="podcasts_channels.empty">No hay canales de Podcasts registrados</string>
|
||||
<string name="button_bar.podcasts">Podcast</string>
|
||||
<string name="button_bar.playlists">Listas de reproducción</string>
|
||||
<string name="button_bar.search">Buscar</string>
|
||||
<string name="chat.send_a_message">Enviar un mensaje</string>
|
||||
<string name="chat.send_button">Enviar</string>
|
||||
@ -119,7 +117,6 @@
|
||||
<string name="main.albums_title">Álbumes</string>
|
||||
<string name="main.artists_title">Artistas</string>
|
||||
<string name="main.genres_title">Géneros</string>
|
||||
<string name="main.music">Música</string>
|
||||
<string name="main.offline">Sin conexión</string>
|
||||
<string name="main.setup_server">%s - Configurar servidor</string>
|
||||
<string name="main.songs_random">Aleatorio</string>
|
||||
|
@ -13,7 +13,6 @@
|
||||
<string name="button_bar.bookmarks">Signets</string>
|
||||
<string name="button_bar.browse">Bibliothèque musicale</string>
|
||||
<string name="button_bar.chat">Salon de discussion</string>
|
||||
<string name="button_bar.home">Accueil Ultrasonic</string>
|
||||
<string name="button_bar.now_playing">Lecture en cours</string>
|
||||
<string name="buttons.play">Lecture</string>
|
||||
<string name="buttons.pause">Pause</string>
|
||||
@ -25,7 +24,6 @@
|
||||
<string name="podcasts.label">Podcast</string>
|
||||
<string name="podcasts_channels.empty">Aucune chaîne de podcast enregistrée</string>
|
||||
<string name="button_bar.podcasts">Podcast</string>
|
||||
<string name="button_bar.playlists">Playlists</string>
|
||||
<string name="button_bar.search">Recherche</string>
|
||||
<string name="chat.send_a_message">Envoyer un message</string>
|
||||
<string name="chat.send_button">Envoyer</string>
|
||||
@ -117,7 +115,6 @@
|
||||
<string name="main.albums_title">Albums</string>
|
||||
<string name="main.artists_title">Artistes</string>
|
||||
<string name="main.genres_title">Genres</string>
|
||||
<string name="main.music">Musique</string>
|
||||
<string name="main.offline">Hors-ligne</string>
|
||||
<string name="main.setup_server">%s - Configurer le serveur</string>
|
||||
<string name="main.songs_random">Aléatoire</string>
|
||||
|
@ -13,7 +13,6 @@
|
||||
<string name="button_bar.bookmarks">Könyvjelzők</string>
|
||||
<string name="button_bar.browse">Médiakönyvtár</string>
|
||||
<string name="button_bar.chat">Csevegés (Chat)</string>
|
||||
<string name="button_bar.home">Ultrasonic főoldal</string>
|
||||
<string name="button_bar.now_playing">Lejátszó</string>
|
||||
<string name="buttons.play">Lejátszás</string>
|
||||
<string name="buttons.pause">Szünet</string>
|
||||
@ -25,7 +24,6 @@
|
||||
<string name="podcasts.label">Podcast</string>
|
||||
<string name="podcasts_channels.empty">Nincsenek podcast-csatornák regisztrálva</string>
|
||||
<string name="button_bar.podcasts">Podcast</string>
|
||||
<string name="button_bar.playlists">Lejátszási listák</string>
|
||||
<string name="button_bar.search">Keresés</string>
|
||||
<string name="chat.send_a_message">Üzenet küldése</string>
|
||||
<string name="common.appname">Ultrasonic</string>
|
||||
@ -93,7 +91,6 @@
|
||||
<string name="main.albums_title">Albumok</string>
|
||||
<string name="main.artists_title">Előadók</string>
|
||||
<string name="main.genres_title">Műfajok</string>
|
||||
<string name="main.music">Zenék</string>
|
||||
<string name="main.offline">Kapcsolat nélküli</string>
|
||||
<string name="main.songs_random">Véletlenszerű</string>
|
||||
<string name="main.songs_starred">Csillaggal megjelölt</string>
|
||||
|
@ -17,7 +17,6 @@
|
||||
<string name="podcasts.label">Podcast</string>
|
||||
<string name="podcasts_channels.empty">Nessun canale podcast registrato</string>
|
||||
<string name="button_bar.podcasts">Podcast</string>
|
||||
<string name="button_bar.playlists">Playlists</string>
|
||||
<string name="button_bar.search">Cerca</string>
|
||||
<string name="chat.send_a_message">Spedisci un messaggio</string>
|
||||
<string name="common.cancel">Annulla</string>
|
||||
@ -84,7 +83,6 @@
|
||||
<string name="main.albums_title">Album</string>
|
||||
<string name="main.artists_title">Artisti</string>
|
||||
<string name="main.genres_title">Generi</string>
|
||||
<string name="main.music">Musica</string>
|
||||
<string name="main.offline">Disconnesso</string>
|
||||
<string name="main.songs_random">Casuale</string>
|
||||
<string name="main.songs_starred">Preferiti</string>
|
||||
|
@ -13,7 +13,6 @@
|
||||
<string name="button_bar.bookmarks">Bladwijzers</string>
|
||||
<string name="button_bar.browse">Mediabibliotheek</string>
|
||||
<string name="button_bar.chat">Chat</string>
|
||||
<string name="button_bar.home">Ultrasonic Main</string>
|
||||
<string name="button_bar.now_playing">Nu aan het afspelen</string>
|
||||
<string name="buttons.play">Afspelen</string>
|
||||
<string name="buttons.pause">Pauzeren</string>
|
||||
@ -25,7 +24,6 @@
|
||||
<string name="podcasts.label">Podcast</string>
|
||||
<string name="podcasts_channels.empty">Geen podcastkanalen opgegeven</string>
|
||||
<string name="button_bar.podcasts">Podcast</string>
|
||||
<string name="button_bar.playlists">Afspeellijsten</string>
|
||||
<string name="button_bar.search">Zoeken</string>
|
||||
<string name="chat.send_a_message">Bericht versturen</string>
|
||||
<string name="chat.send_button">Versturen</string>
|
||||
@ -119,7 +117,6 @@
|
||||
<string name="main.albums_title">Albums</string>
|
||||
<string name="main.artists_title">Artiesten</string>
|
||||
<string name="main.genres_title">Genres</string>
|
||||
<string name="main.music">Muziek</string>
|
||||
<string name="main.offline">Offline</string>
|
||||
<string name="main.setup_server">%s - Server instellen</string>
|
||||
<string name="main.songs_random">Willekeurig</string>
|
||||
|
@ -13,13 +13,11 @@
|
||||
<string name="button_bar.bookmarks">Zakładki</string>
|
||||
<string name="button_bar.browse">Biblioteka</string>
|
||||
<string name="button_bar.chat">Czat</string>
|
||||
<string name="button_bar.home">Ultrasonic</string>
|
||||
<string name="button_bar.now_playing">Teraz gra</string>
|
||||
<string name="buttons.shuffle">Wymieszaj</string>
|
||||
<string name="podcasts.label">Podcasty</string>
|
||||
<string name="podcasts_channels.empty">Brak kanałów</string>
|
||||
<string name="button_bar.podcasts">Podcast</string>
|
||||
<string name="button_bar.playlists">Playlisty</string>
|
||||
<string name="button_bar.search">Szukaj</string>
|
||||
<string name="chat.send_a_message">Wyślij wiadomość</string>
|
||||
<string name="common.appname">Ultrasonic</string>
|
||||
@ -87,7 +85,6 @@
|
||||
<string name="main.albums_title">Albumy</string>
|
||||
<string name="main.artists_title">Artyści</string>
|
||||
<string name="main.genres_title">Gatunki</string>
|
||||
<string name="main.music">Muzyka</string>
|
||||
<string name="main.offline">Offline</string>
|
||||
<string name="main.songs_random">Losowe</string>
|
||||
<string name="main.songs_starred">Ulubione</string>
|
||||
|
@ -13,7 +13,6 @@
|
||||
<string name="button_bar.bookmarks">Favoritos</string>
|
||||
<string name="button_bar.browse">Biblioteca de Mídia</string>
|
||||
<string name="button_bar.chat">Chat</string>
|
||||
<string name="button_bar.home">Menu Principal</string>
|
||||
<string name="button_bar.now_playing">Tocando Agora</string>
|
||||
<string name="buttons.play">Tocar</string>
|
||||
<string name="buttons.pause">Pausar</string>
|
||||
@ -25,7 +24,6 @@
|
||||
<string name="podcasts.label">Podcasts</string>
|
||||
<string name="podcasts_channels.empty">Nenhum canal de podcasts registrado</string>
|
||||
<string name="button_bar.podcasts">Podcasts</string>
|
||||
<string name="button_bar.playlists">Playlists</string>
|
||||
<string name="button_bar.search">Pesquisa</string>
|
||||
<string name="chat.send_a_message">Enviar uma mensagem</string>
|
||||
<string name="common.album">Álbum</string>
|
||||
@ -113,7 +111,6 @@
|
||||
<string name="main.albums_title">Álbuns</string>
|
||||
<string name="main.artists_title">Artistas</string>
|
||||
<string name="main.genres_title">Gêneros</string>
|
||||
<string name="main.music">Música</string>
|
||||
<string name="main.offline">Offline</string>
|
||||
<string name="main.setup_server">%s - Configurar Servidor</string>
|
||||
<string name="main.songs_random">Aleatórias</string>
|
||||
|
@ -13,13 +13,11 @@
|
||||
<string name="button_bar.bookmarks">Favoritos</string>
|
||||
<string name="button_bar.browse">Biblioteca de Mídia</string>
|
||||
<string name="button_bar.chat">Chat</string>
|
||||
<string name="button_bar.home">Menu Principal</string>
|
||||
<string name="button_bar.now_playing">Tocando Agora</string>
|
||||
<string name="buttons.shuffle">Misturar</string>
|
||||
<string name="podcasts.label">Podcasts</string>
|
||||
<string name="podcasts_channels.empty">Nenhum canal de podcasts registrado</string>
|
||||
<string name="button_bar.podcasts">Podcast</string>
|
||||
<string name="button_bar.playlists">Playlists</string>
|
||||
<string name="button_bar.search">Pesquisa</string>
|
||||
<string name="chat.send_a_message">Enviar uma mensagem</string>
|
||||
<string name="common.appname">Ultrasonic</string>
|
||||
@ -87,7 +85,6 @@
|
||||
<string name="main.albums_title">Álbuns</string>
|
||||
<string name="main.artists_title">Artistas</string>
|
||||
<string name="main.genres_title">Gêneros</string>
|
||||
<string name="main.music">Música</string>
|
||||
<string name="main.offline">Offline</string>
|
||||
<string name="main.songs_random">Aleatórias</string>
|
||||
<string name="main.songs_starred">Favoritas</string>
|
||||
|
@ -13,7 +13,6 @@
|
||||
<string name="button_bar.bookmarks">Закладки</string>
|
||||
<string name="button_bar.browse">Медиа библиотека</string>
|
||||
<string name="button_bar.chat">Чат</string>
|
||||
<string name="button_bar.home">UltraSonic Главная</string>
|
||||
<string name="button_bar.now_playing">Сейчас играет</string>
|
||||
<string name="buttons.play">Воспроизведение</string>
|
||||
<string name="buttons.pause">Пауза</string>
|
||||
@ -25,7 +24,6 @@
|
||||
<string name="podcasts.label">Подкаст</string>
|
||||
<string name="podcasts_channels.empty">Подкасты не зарегистрированы</string>
|
||||
<string name="button_bar.podcasts">Подкаст</string>
|
||||
<string name="button_bar.playlists">Плейлист</string>
|
||||
<string name="button_bar.search">Поиск</string>
|
||||
<string name="chat.send_a_message">Отправить сообщение</string>
|
||||
<string name="common.album">Альбом</string>
|
||||
@ -108,7 +106,6 @@
|
||||
<string name="main.albums_title">Альбомы</string>
|
||||
<string name="main.artists_title">Исполнители</string>
|
||||
<string name="main.genres_title">Жанры</string>
|
||||
<string name="main.music">Музыка</string>
|
||||
<string name="main.offline">Не в сети</string>
|
||||
<string name="main.songs_random">Случайный</string>
|
||||
<string name="main.songs_starred">Отмеченные</string>
|
||||
|
@ -13,7 +13,6 @@
|
||||
<string name="button_bar.bookmarks">书签</string>
|
||||
<string name="button_bar.browse">媒体库</string>
|
||||
<string name="button_bar.chat">聊天</string>
|
||||
<string name="button_bar.home">Ultrasonic 主页</string>
|
||||
<string name="button_bar.now_playing">正在播放</string>
|
||||
<string name="buttons.play">播放</string>
|
||||
<string name="buttons.pause">暂停</string>
|
||||
@ -25,7 +24,6 @@
|
||||
<string name="podcasts.label">播客</string>
|
||||
<string name="podcasts_channels.empty">没有已注册的播客频道</string>
|
||||
<string name="button_bar.podcasts">播客</string>
|
||||
<string name="button_bar.playlists">播放列表</string>
|
||||
<string name="button_bar.search">搜索</string>
|
||||
<string name="chat.send_a_message">发送消息</string>
|
||||
<string name="chat.send_button">发送</string>
|
||||
@ -104,7 +102,6 @@
|
||||
<string name="main.albums_title">专辑</string>
|
||||
<string name="main.artists_title">艺术家</string>
|
||||
<string name="main.genres_title">流派</string>
|
||||
<string name="main.music">音乐</string>
|
||||
<string name="main.offline">离线</string>
|
||||
<string name="main.setup_server">%s - 已设置服务器</string>
|
||||
<string name="main.songs_random">随机</string>
|
||||
|
@ -5,7 +5,6 @@
|
||||
<string name="button_bar.bookmarks">書籤</string>
|
||||
<string name="button_bar.browse">媒體庫</string>
|
||||
<string name="button_bar.now_playing">正在播放</string>
|
||||
<string name="button_bar.playlists">播放清單</string>
|
||||
<string name="button_bar.search">搜尋</string>
|
||||
<string name="common.cancel">取消</string>
|
||||
<string name="common.comment">註記</string>
|
||||
|
@ -55,4 +55,5 @@
|
||||
<string name="setting_key.override_language" translatable="false">overrideLanguage</string>
|
||||
<string name="setting_key.first_installed_version" translatable="false">firstInstalledVersion</string>
|
||||
<string name="setting_key.show_confirmation_dialog" translatable="false">showConfirmationDialog</string>
|
||||
<string name="setting_key.last_view_type" translatable="false">lastViewType</string>
|
||||
</resources>
|
@ -13,7 +13,6 @@
|
||||
<string name="button_bar.bookmarks">Bookmarks</string>
|
||||
<string name="button_bar.browse">Media Library</string>
|
||||
<string name="button_bar.chat">Chat</string>
|
||||
<string name="button_bar.home">Ultrasonic Main</string>
|
||||
<string name="button_bar.now_playing">Now Playing</string>
|
||||
<string name="buttons.play">Play</string>
|
||||
<string name="buttons.pause">Pause</string>
|
||||
@ -25,10 +24,10 @@
|
||||
<string name="podcasts.label">Podcast</string>
|
||||
<string name="podcasts_channels.empty">No podcasts channels registered</string>
|
||||
<string name="button_bar.podcasts">Podcast</string>
|
||||
<string name="button_bar.playlists">Playlists</string>
|
||||
<string name="button_bar.search">Search</string>
|
||||
<string name="chat.send_a_message">Send a message</string>
|
||||
<string name="chat.send_button">Send</string>
|
||||
<string name="chat.user_avatar">Avatar image</string>
|
||||
<string name="common.album">Album</string>
|
||||
<string name="common.appname">Ultrasonic</string>
|
||||
<string name="common.artist">Artist</string>
|
||||
@ -116,10 +115,10 @@
|
||||
<string name="main.albums_random">Random</string>
|
||||
<string name="main.albums_recent">Recently Played</string>
|
||||
<string name="main.albums_starred">Starred</string>
|
||||
<string name="main.albums_by_year">Chronological</string>
|
||||
<string name="main.albums_title">Albums</string>
|
||||
<string name="main.artists_title">Artists</string>
|
||||
<string name="main.genres_title">Genres</string>
|
||||
<string name="main.music">Music</string>
|
||||
<string name="main.offline">Offline</string>
|
||||
<string name="main.setup_server">%s - Set up Server</string>
|
||||
<string name="main.songs_random">Random</string>
|
||||
@ -370,7 +369,7 @@
|
||||
<string name="share_via">Share songs via</string>
|
||||
<string name="menu.share">Share</string>
|
||||
<string name="download.menu_show_artist">Show Artist</string>
|
||||
<string name="albumArt">albumArt</string>
|
||||
<string name="albumArt">Album artwork</string>
|
||||
<string name="common_multiple_years">Multiple Years</string>
|
||||
<string name="settings.show_confirmation_dialog">Show confirmation dialog</string>
|
||||
<string name="settings.show_confirmation_dialog_summary">Displays a confirmation dialog before deleting or unpinning songs</string>
|
||||
@ -451,5 +450,8 @@
|
||||
|
||||
<string name="settings.use_hw_offload_title">Use hardware playback (experimental)</string>
|
||||
<string name="settings.use_hw_offload_description">Try to play the media using the media decoder chip on your phone. This can improve battery usage.</string>
|
||||
<string name="list_view">List</string>
|
||||
<string name="grid_view">Cover</string>
|
||||
|
||||
|
||||
</resources>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<style name="Ultrasonic.AllCapsLabel" parent="">
|
||||
<item name="android:textStyle">bold</item>
|
||||
@ -15,16 +15,6 @@
|
||||
<item name="android:paddingStart">16dp</item>
|
||||
</style>
|
||||
|
||||
<style name="roundedImageView" parent="">
|
||||
<item name="cornerFamily">rounded</item>
|
||||
<item name="cornerSize">8dp</item>
|
||||
</style>
|
||||
|
||||
<style name="largeRoundedImageView" parent="">
|
||||
<item name="cornerFamily">rounded</item>
|
||||
<item name="cornerSize">2dp</item>
|
||||
</style>
|
||||
|
||||
<style name="Widget.AppWidget.AppWidget.Container" parent="android:Widget">
|
||||
<item name="android:id">@android:id/background</item>
|
||||
<item name="android:padding">?attr/appWidgetPadding</item>
|
||||
@ -60,6 +50,11 @@
|
||||
<item name="android:adjustViewBounds">true</item>
|
||||
</style>
|
||||
|
||||
<style tools:ignore="PrivateResource" name="Widget.ChipDropdown" parent="Widget.Material3.TextInputLayout.OutlinedBox.Dense.ExposedDropdownMenu">
|
||||
<item name="shapeAppearance">?attr/shapeAppearanceSmallComponent</item>
|
||||
<item name="boxStrokeColor">@color/m3_textfield_stroke_color</item>
|
||||
</style>
|
||||
|
||||
<style name="Theme.AppWidget.AppWidgetContainerCropped" parent="Theme.AppWidget.AppWidgetContainer">
|
||||
<!-- Apply padding to avoid the content of the widget colliding with the rounded corners -->
|
||||
<item name="appWidgetPadding">4dp</item>
|
||||
|
Loading…
x
Reference in New Issue
Block a user