@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<configuration>
|
||||||
|
<packageSources>
|
||||||
|
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
|
||||||
|
</packageSources>
|
||||||
|
</configuration>
|
@ -0,0 +1 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="48dp" android:height="48dp" android:viewportWidth="24" android:viewportHeight="24" android:tint="?attr/colorControlNormal"><path android:fillColor="#FFFFFFFF" android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z" /></vector>
|
@ -0,0 +1 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="48dp" android:height="48dp" android:viewportWidth="24" android:viewportHeight="24" android:tint="?attr/colorControlNormal"><path android:fillColor="#FFFFFFFF" android:pathData="M19.14,12.94c0.04,-0.3 0.06,-0.61 0.06,-0.94c0,-0.32 -0.02,-0.64 -0.07,-0.94l2.03,-1.58c0.18,-0.14 0.23,-0.41 0.12,-0.61l-1.92,-3.32c-0.12,-0.22 -0.37,-0.29 -0.59,-0.22l-2.39,0.96c-0.5,-0.38 -1.03,-0.7 -1.62,-0.94L14.4,2.81c-0.04,-0.24 -0.24,-0.41 -0.48,-0.41h-3.84c-0.24,0 -0.43,0.17 -0.47,0.41L9.25,5.35C8.66,5.59 8.12,5.92 7.63,6.29L5.24,5.33c-0.22,-0.08 -0.47,0 -0.59,0.22L2.74,8.87C2.62,9.08 2.66,9.34 2.86,9.48l2.03,1.58C4.84,11.36 4.8,11.69 4.8,12s0.02,0.64 0.07,0.94l-2.03,1.58c-0.18,0.14 -0.23,0.41 -0.12,0.61l1.92,3.32c0.12,0.22 0.37,0.29 0.59,0.22l2.39,-0.96c0.5,0.38 1.03,0.7 1.62,0.94l0.36,2.54c0.05,0.24 0.24,0.41 0.48,0.41h3.84c0.24,0 0.44,-0.17 0.47,-0.41l0.36,-2.54c0.59,-0.24 1.13,-0.56 1.62,-0.94l2.39,0.96c0.22,0.08 0.47,0 0.59,-0.22l1.92,-3.32c0.12,-0.22 0.07,-0.47 -0.12,-0.61L19.14,12.94zM12,15.6c-1.98,0 -3.6,-1.62 -3.6,-3.6s1.62,-3.6 3.6,-3.6s3.6,1.62 3.6,3.6S13.98,15.6 12,15.6z" /></vector>
|
After Width: | Height: | Size: 482 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 368 B |
After Width: | Height: | Size: 859 B |
@ -0,0 +1 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="48dp" android:height="48dp" android:viewportWidth="24" android:viewportHeight="24" android:tint="?attr/colorControlNormal"><path android:fillColor="#FFFFFFFF" android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z" /></vector>
|
@ -0,0 +1 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="48dp" android:height="48dp" android:viewportWidth="24" android:viewportHeight="24" android:tint="?attr/colorControlNormal"><path android:fillColor="#FFFFFFFF" android:pathData="M19.14,12.94c0.04,-0.3 0.06,-0.61 0.06,-0.94c0,-0.32 -0.02,-0.64 -0.07,-0.94l2.03,-1.58c0.18,-0.14 0.23,-0.41 0.12,-0.61l-1.92,-3.32c-0.12,-0.22 -0.37,-0.29 -0.59,-0.22l-2.39,0.96c-0.5,-0.38 -1.03,-0.7 -1.62,-0.94L14.4,2.81c-0.04,-0.24 -0.24,-0.41 -0.48,-0.41h-3.84c-0.24,0 -0.43,0.17 -0.47,0.41L9.25,5.35C8.66,5.59 8.12,5.92 7.63,6.29L5.24,5.33c-0.22,-0.08 -0.47,0 -0.59,0.22L2.74,8.87C2.62,9.08 2.66,9.34 2.86,9.48l2.03,1.58C4.84,11.36 4.8,11.69 4.8,12s0.02,0.64 0.07,0.94l-2.03,1.58c-0.18,0.14 -0.23,0.41 -0.12,0.61l1.92,3.32c0.12,0.22 0.37,0.29 0.59,0.22l2.39,-0.96c0.5,0.38 1.03,0.7 1.62,0.94l0.36,2.54c0.05,0.24 0.24,0.41 0.48,0.41h3.84c0.24,0 0.44,-0.17 0.47,-0.41l0.36,-2.54c0.59,-0.24 1.13,-0.56 1.62,-0.94l2.39,0.96c0.22,0.08 0.47,0 0.59,-0.22l1.92,-3.32c0.12,-0.22 0.07,-0.47 -0.12,-0.61L19.14,12.94zM12,15.6c-1.98,0 -3.6,-1.62 -3.6,-3.6s1.62,-3.6 3.6,-3.6s3.6,1.62 3.6,3.6S13.98,15.6 12,15.6z" /></vector>
|
@ -0,0 +1 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="48dp" android:height="48dp" android:viewportWidth="24" android:viewportHeight="24" android:tint="?attr/colorControlNormal"><path android:fillColor="#FFFFFFFF" android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z" /></vector>
|
@ -0,0 +1 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="48dp" android:height="48dp" android:viewportWidth="24" android:viewportHeight="24" android:tint="?attr/colorControlNormal"><path android:fillColor="#FFFFFFFF" android:pathData="M19.14,12.94c0.04,-0.3 0.06,-0.61 0.06,-0.94c0,-0.32 -0.02,-0.64 -0.07,-0.94l2.03,-1.58c0.18,-0.14 0.23,-0.41 0.12,-0.61l-1.92,-3.32c-0.12,-0.22 -0.37,-0.29 -0.59,-0.22l-2.39,0.96c-0.5,-0.38 -1.03,-0.7 -1.62,-0.94L14.4,2.81c-0.04,-0.24 -0.24,-0.41 -0.48,-0.41h-3.84c-0.24,0 -0.43,0.17 -0.47,0.41L9.25,5.35C8.66,5.59 8.12,5.92 7.63,6.29L5.24,5.33c-0.22,-0.08 -0.47,0 -0.59,0.22L2.74,8.87C2.62,9.08 2.66,9.34 2.86,9.48l2.03,1.58C4.84,11.36 4.8,11.69 4.8,12s0.02,0.64 0.07,0.94l-2.03,1.58c-0.18,0.14 -0.23,0.41 -0.12,0.61l1.92,3.32c0.12,0.22 0.37,0.29 0.59,0.22l2.39,-0.96c0.5,0.38 1.03,0.7 1.62,0.94l0.36,2.54c0.05,0.24 0.24,0.41 0.48,0.41h3.84c0.24,0 0.44,-0.17 0.47,-0.41l0.36,-2.54c0.59,-0.24 1.13,-0.56 1.62,-0.94l2.39,0.96c0.22,0.08 0.47,0 0.59,-0.22l1.92,-3.32c0.12,-0.22 0.07,-0.47 -0.12,-0.61L19.14,12.94zM12,15.6c-1.98,0 -3.6,-1.62 -3.6,-3.6s1.62,-3.6 3.6,-3.6s3.6,1.62 3.6,3.6S13.98,15.6 12,15.6z" /></vector>
|
After Width: | Height: | Size: 640 B |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 954 B |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 3.1 KiB |
@ -0,0 +1 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="48dp" android:height="48dp" android:viewportWidth="24" android:viewportHeight="24" android:tint="?attr/colorControlNormal"><path android:fillColor="#FFFFFFFF" android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z" /></vector>
|
@ -0,0 +1 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="48dp" android:height="48dp" android:viewportWidth="24" android:viewportHeight="24" android:tint="?attr/colorControlNormal"><path android:fillColor="#FFFFFFFF" android:pathData="M19.14,12.94c0.04,-0.3 0.06,-0.61 0.06,-0.94c0,-0.32 -0.02,-0.64 -0.07,-0.94l2.03,-1.58c0.18,-0.14 0.23,-0.41 0.12,-0.61l-1.92,-3.32c-0.12,-0.22 -0.37,-0.29 -0.59,-0.22l-2.39,0.96c-0.5,-0.38 -1.03,-0.7 -1.62,-0.94L14.4,2.81c-0.04,-0.24 -0.24,-0.41 -0.48,-0.41h-3.84c-0.24,0 -0.43,0.17 -0.47,0.41L9.25,5.35C8.66,5.59 8.12,5.92 7.63,6.29L5.24,5.33c-0.22,-0.08 -0.47,0 -0.59,0.22L2.74,8.87C2.62,9.08 2.66,9.34 2.86,9.48l2.03,1.58C4.84,11.36 4.8,11.69 4.8,12s0.02,0.64 0.07,0.94l-2.03,1.58c-0.18,0.14 -0.23,0.41 -0.12,0.61l1.92,3.32c0.12,0.22 0.37,0.29 0.59,0.22l2.39,-0.96c0.5,0.38 1.03,0.7 1.62,0.94l0.36,2.54c0.05,0.24 0.24,0.41 0.48,0.41h3.84c0.24,0 0.44,-0.17 0.47,-0.41l0.36,-2.54c0.59,-0.24 1.13,-0.56 1.62,-0.94l2.39,0.96c0.22,0.08 0.47,0 0.59,-0.22l1.92,-3.32c0.12,-0.22 0.07,-0.47 -0.12,-0.61L19.14,12.94zM12,15.6c-1.98,0 -3.6,-1.62 -3.6,-3.6s1.62,-3.6 3.6,-3.6s3.6,1.62 3.6,3.6S13.98,15.6 12,15.6z" /></vector>
|
@ -1,32 +1,69 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_height="fill_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_width="fill_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
>
|
||||||
|
<include
|
||||||
|
layout="@layout/commonTopBar"
|
||||||
|
android:id="@+id/commonTopBar"
|
||||||
|
/>
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
<!-- The content itself -->
|
||||||
|
<android.support.design.widget.CoordinatorLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="fill_parent"
|
||||||
android:theme="@style/AppTheme.AppBarOverlay">
|
android:layout_below="@id/commonTopBar"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
<androidx.appcompat.widget.Toolbar
|
<android.support.v7.widget.RecyclerView
|
||||||
android:id="@+id/toolbar"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="?attr/actionBarSize"
|
android:layout_height="match_parent"
|
||||||
android:background="?attr/colorPrimary"
|
android:id="@+id/recyclerView1"
|
||||||
app:popupTheme="@style/AppTheme.PopupOverlay" />
|
/>
|
||||||
|
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
<RelativeLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
<include layout="@layout/content_main" />
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="bottom|end"
|
||||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
android:layout_marginLeft="5pt"
|
||||||
android:id="@+id/fab"
|
android:layout_marginRight="5pt"
|
||||||
android:layout_width="wrap_content"
|
android:layout_marginBottom="5pt"
|
||||||
android:layout_height="wrap_content"
|
android:id="@+id/currentSongBar"
|
||||||
android:layout_gravity="bottom|end"
|
>
|
||||||
android:layout_margin="@dimen/fab_margin"
|
<android.support.design.widget.FloatingActionButton
|
||||||
app:srcCompat="@android:drawable/ic_dialog_email" />
|
android:layout_width="@dimen/design_fab_size_mini"
|
||||||
|
android:layout_height="@dimen/design_fab_size_mini"
|
||||||
|
android:id="@+id/currentSongInteractButton"
|
||||||
|
android:layout_alignParentRight="true"
|
||||||
|
android:layout_alignParentBottom="true"
|
||||||
|
android:layout_margin="0pt"
|
||||||
|
app:elevation="0dp"
|
||||||
|
/>
|
||||||
|
<ProgressBar
|
||||||
|
style="?android:attr/progressBarStyleHorizontal"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/currentSongProgress"
|
||||||
|
android:layout_alignParentLeft="true"
|
||||||
|
android:layout_alignParentBottom="true"
|
||||||
|
android:layout_toLeftOf="@id/currentSongInteractButton"
|
||||||
|
/>
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/currentSong"
|
||||||
|
android:text="Hello World!"
|
||||||
|
android:layout_alignParentLeft="true"
|
||||||
|
android:textAlignment="center"
|
||||||
|
android:layout_toLeftOf="@id/currentSongInteractButton"
|
||||||
|
android:layout_above="@id/currentSongProgress"
|
||||||
|
/>
|
||||||
|
<!-- Alternative: SeekBar -->
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
</android.support.design.widget.CoordinatorLayout>
|
||||||
|
</LinearLayout>
|
@ -0,0 +1,37 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<android.support.design.widget.AppBarLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
>
|
||||||
|
<android.support.v7.widget.Toolbar
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
|
||||||
|
>
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:id="@+id/titleText">
|
||||||
|
<TextView
|
||||||
|
android:text="@string/app_name"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/appTitleText" />
|
||||||
|
<TextView
|
||||||
|
android:text="@string/player_name"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/playerNameText" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</android.support.v7.widget.Toolbar>
|
||||||
|
</android.support.design.widget.AppBarLayout>
|
@ -1,16 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
|
||||||
tools:showIn="@layout/activity_main">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_centerInParent="true"
|
|
||||||
android:text="Hello World!" />
|
|
||||||
|
|
||||||
</RelativeLayout>
|
|
@ -0,0 +1,31 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<FrameLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
<!-- I am going to assume that the match_parent width respects the two-column layout. -->
|
||||||
|
<!-- The CardView just adds some beauty -->
|
||||||
|
<android.support.v7.widget.CardView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:minHeight="100dp"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
app:cardElevation="4dp"
|
||||||
|
app:cardUseCompatPadding="true"
|
||||||
|
app:cardCornerRadius="5dp"
|
||||||
|
>
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="center"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:text="Song Name"
|
||||||
|
android:id="@+id/songName"
|
||||||
|
/>
|
||||||
|
</android.support.v7.widget.CardView>
|
||||||
|
|
||||||
|
</FrameLayout>
|
@ -0,0 +1 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="48dp" android:height="48dp" android:viewportWidth="24" android:viewportHeight="24" android:tint="?attr/colorControlNormal"><path android:fillColor="#FFFFFFFF" android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z" /></vector>
|
@ -0,0 +1 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="48dp" android:height="48dp" android:viewportWidth="24" android:viewportHeight="24" android:tint="?attr/colorControlNormal"><path android:fillColor="#FFFFFFFF" android:pathData="M19.14,12.94c0.04,-0.3 0.06,-0.61 0.06,-0.94c0,-0.32 -0.02,-0.64 -0.07,-0.94l2.03,-1.58c0.18,-0.14 0.23,-0.41 0.12,-0.61l-1.92,-3.32c-0.12,-0.22 -0.37,-0.29 -0.59,-0.22l-2.39,0.96c-0.5,-0.38 -1.03,-0.7 -1.62,-0.94L14.4,2.81c-0.04,-0.24 -0.24,-0.41 -0.48,-0.41h-3.84c-0.24,0 -0.43,0.17 -0.47,0.41L9.25,5.35C8.66,5.59 8.12,5.92 7.63,6.29L5.24,5.33c-0.22,-0.08 -0.47,0 -0.59,0.22L2.74,8.87C2.62,9.08 2.66,9.34 2.86,9.48l2.03,1.58C4.84,11.36 4.8,11.69 4.8,12s0.02,0.64 0.07,0.94l-2.03,1.58c-0.18,0.14 -0.23,0.41 -0.12,0.61l1.92,3.32c0.12,0.22 0.37,0.29 0.59,0.22l2.39,-0.96c0.5,0.38 1.03,0.7 1.62,0.94l0.36,2.54c0.05,0.24 0.24,0.41 0.48,0.41h3.84c0.24,0 0.44,-0.17 0.47,-0.41l0.36,-2.54c0.59,-0.24 1.13,-0.56 1.62,-0.94l2.39,0.96c0.22,0.08 0.47,0 0.59,-0.22l1.92,-3.32c0.12,-0.22 0.07,-0.47 -0.12,-0.61L19.14,12.94zM12,15.6c-1.98,0 -3.6,-1.62 -3.6,-3.6s1.62,-3.6 3.6,-3.6s3.6,1.62 3.6,3.6S13.98,15.6 12,15.6z" /></vector>
|
After Width: | Height: | Size: 482 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 368 B |
After Width: | Height: | Size: 859 B |
After Width: | Height: | Size: 640 B |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 954 B |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 3.1 KiB |
@ -1,4 +1,7 @@
|
|||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">QuickPlay</string>
|
<string name="app_name">QuickPlay</string>
|
||||||
|
<string name="player_name">Not Connected!</string>
|
||||||
|
|
||||||
<string name="action_settings">Settings</string>
|
<string name="action_settings">Settings</string>
|
||||||
|
<string name="action_edit">Edit</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -0,0 +1,117 @@
|
|||||||
|
QuickPlay
|
||||||
|
===
|
||||||
|
|
||||||
|
A simple Xamarin.Android app to allow fast playing of songs on remote players
|
||||||
|
like MPD. Supports all Android versions since KitKat (to repurpose an old tablet).
|
||||||
|
|
||||||
|
Setup of additional services
|
||||||
|
===
|
||||||
|
|
||||||
|
Since QuickPlay is only a frontend to MPD running somewhere, and the location
|
||||||
|
has to be specified in the player configuration (see below).
|
||||||
|
|
||||||
|
Currently, only unauthenticated MPD connections specified by IP address are supported.
|
||||||
|
|
||||||
|
Usage
|
||||||
|
===
|
||||||
|
|
||||||
|
First, make sure that you have the player configuration INI file. Put that on
|
||||||
|
the magic URL: <http://www.ms.mff.cuni.cz/~turinskp/znelky.ini>. Now run the
|
||||||
|
app, your songs will appear and you can play them.
|
||||||
|
|
||||||
|
An example commented INI file is provided: `playerconfig.ini`. This is what you
|
||||||
|
would see if you use that:
|
||||||
|
|
||||||
|
![UI example](./uiexample.png)
|
||||||
|
|
||||||
|
Architecture Overview
|
||||||
|
===
|
||||||
|
|
||||||
|
The app comprises of three main parts:
|
||||||
|
|
||||||
|
- Configuration
|
||||||
|
- Player -- the backend
|
||||||
|
- Layout -- the frontend
|
||||||
|
|
||||||
|
Configuration
|
||||||
|
---
|
||||||
|
|
||||||
|
The configuration is split into two parts: Applocation configuration and player
|
||||||
|
configuration.
|
||||||
|
|
||||||
|
Application configuration is local to the device and describes application
|
||||||
|
behaviour and where to obtain the player configuration. It
|
||||||
|
is just a XML-serializable class (for simplicity).
|
||||||
|
|
||||||
|
Player configuration is stored on an HTTP server or local filesystem. It is an
|
||||||
|
INI-like file designed to be written by hand. Since INI is not a well-specified
|
||||||
|
format, the specific format used in QuickPlay is described in `IniParser.cs`
|
||||||
|
file. (It was faster to implement own format than to find a usable one on
|
||||||
|
NuGet.) For use as a player configuration, the section `[general]` has to be
|
||||||
|
specified with connection details, other sections directly describe individual
|
||||||
|
songs.
|
||||||
|
|
||||||
|
The motivation for the split is to make player configuration easily shareable,
|
||||||
|
e.g. by a QR code pointing to the config.
|
||||||
|
|
||||||
|
Currently, there is no application configuration and the player configuration
|
||||||
|
is always downloaded from <http://www.ms.mff.cuni.cz/~turinskp/znelky.ini>,
|
||||||
|
|
||||||
|
Players
|
||||||
|
---
|
||||||
|
|
||||||
|
QuickPlay is designed to support other players, not just MPD. Therefore, a
|
||||||
|
`Player` is just an interface that can play Playables and export some trivial
|
||||||
|
state.
|
||||||
|
|
||||||
|
A `Playable` is an abstract object that can be played. Usually it is a song
|
||||||
|
(description thereof), but there is a possibility it could be for example a
|
||||||
|
playlist. (In the extreme case that someone would like to play chess instead of
|
||||||
|
songs, the individual chess pieces would be playables.)
|
||||||
|
|
||||||
|
In order to allow players to show current state, `MainActivity` has method
|
||||||
|
`OnPlayerUpdate`, which re-fetches player state and displays it.
|
||||||
|
|
||||||
|
It is expected that the players will maintain a service to monitor the remote
|
||||||
|
state. It is not required though, it is really implementation detail of the
|
||||||
|
client. (`MainActivity.OnPlayerUpdate` still has to be run on UI thread.)
|
||||||
|
|
||||||
|
### MpdPlayer details
|
||||||
|
|
||||||
|
The MpdPlayer is just a thin wrapper around [slightly
|
||||||
|
patched](https://github.com/LEdoian/mpcCore)
|
||||||
|
[mpcCore](https://github.com/svanelten/mpcCore) library. It currently only
|
||||||
|
sends commands to the MPD, but extension by service to monitor it is ready
|
||||||
|
(`MpdMonitorService.cs`).
|
||||||
|
|
||||||
|
|
||||||
|
Frontend
|
||||||
|
---
|
||||||
|
|
||||||
|
Frontend is just a bunch of boilerplate code to power the RecyclerView behind
|
||||||
|
it. The only "original" part are layout strategies -- objects that determine in
|
||||||
|
what order the playables will be shown, based on their metadata.
|
||||||
|
|
||||||
|
At the moment, the only layout strategy implemented is lexicographic sorting by
|
||||||
|
the shown name.
|
||||||
|
|
||||||
|
|
||||||
|
Planned Features
|
||||||
|
====
|
||||||
|
|
||||||
|
- [ ] Working application configuration
|
||||||
|
- [ ] Player configuration sharing via QR codes
|
||||||
|
- [ ] Showing current song progress
|
||||||
|
- [ ] Possibility to stop current song
|
||||||
|
- [ ] MPD player monitoring service
|
||||||
|
- [ ] Time-based layout strategy
|
||||||
|
|
||||||
|
Bugs
|
||||||
|
---
|
||||||
|
|
||||||
|
- [ ] The MpcCore client seems to disconnect after some time for no apparent reason.
|
||||||
|
|
||||||
|
The far future
|
||||||
|
---
|
||||||
|
- [ ] Editing the player config right on the device (with INI file export)
|
||||||
|
- [ ] Tests. (The code should be testable, but no tests has been written yet.)
|
@ -0,0 +1,43 @@
|
|||||||
|
# This is a player configuration example for QuickPlay.
|
||||||
|
# Everything after either ';' or '#' is ignored.
|
||||||
|
# Spaces around '=' and at both ends of each line are trimmed.
|
||||||
|
# Everything is case-sensitive and UTF-8.
|
||||||
|
# Multiple sections are forbidden, so are same keys in same section.
|
||||||
|
# (Really, this is just an encoding of a Dictionary<string, Dictionary<string, string>>)
|
||||||
|
|
||||||
|
# Section 'general' is special, it declares general config (well...) of the
|
||||||
|
# player
|
||||||
|
[general]
|
||||||
|
# Name is shown in the app to aid distinguishing between multiple possible players
|
||||||
|
name = Example client
|
||||||
|
# Type will determine what kind of player should be used. Currently it is not used.
|
||||||
|
# More generally, keys that are not used are ignored. They are still parsed,
|
||||||
|
# though, and should be considered reserved by QuickPlay (i.e. not used arbitrarily).
|
||||||
|
type = MPD
|
||||||
|
# Connection specifies where to connect. Usually it is just IP address, but if
|
||||||
|
# you want to specify port, use notation ip.ad.dr.ess@port (i.e. separate using '@').
|
||||||
|
connection = 198.51.100.3
|
||||||
|
|
||||||
|
# All other sections represent individual songs / playables.
|
||||||
|
# The section name is the name the song will be visible as.
|
||||||
|
[Breakfast]
|
||||||
|
# Expected time of playing. Currently unused, will be used for sorting.
|
||||||
|
# Allowed format: HH:MM.
|
||||||
|
time = 15:00
|
||||||
|
# Path, under which MPD can find the song. Can contain even strange characters,
|
||||||
|
# but cannot start with whitespace due to trimming. (Frankly, if your
|
||||||
|
# filenames start with whitespace, you have much bigger problems.)
|
||||||
|
path = Unsorted/Kevin_MacLeod/Polkas/Snare Bounce Polka.mp3
|
||||||
|
|
||||||
|
# Create as many playables as you wish. Remember, no repeating of section names.
|
||||||
|
[Lunch]
|
||||||
|
time = 16:00
|
||||||
|
path = Classical/MacLeod K. - Dance of the Sugar Plum Fairy (P.I.Tchaikovsky).mp3
|
||||||
|
|
||||||
|
# Even section names are UTF-8, so we can use non-plain-ASCII characters.
|
||||||
|
[Café]
|
||||||
|
time = 22:00
|
||||||
|
path = Irish/Fiddles McGinty.mp3
|
||||||
|
|
||||||
|
# All the mentioned songs are from Kevin MacLeod under CC BY 4.0 licence. Not
|
||||||
|
# that you can hear them, but the attribution has to be somewhere :-)
|
@ -0,0 +1,13 @@
|
|||||||
|
jako zápočtový program do C# (NPRG035) bych chtěl dělat aplikaci pro
|
||||||
|
Android na rychlé spouštění skladeb pomocí Music Player Daemonu (MPD).
|
||||||
|
Použili bychom to v rámci korespondenčního semináře M&M, kde tento
|
||||||
|
přehrávač používáme jako "znělky" ke svolání účastníků soustředění na
|
||||||
|
program.
|
||||||
|
|
||||||
|
Program by tedy sestával z tlačítek pro různé znělky, při jejichž
|
||||||
|
stisknutí by se daná znělka spustila. Tato tlačítka by byla popsána v
|
||||||
|
nějakém konfiguračním souboru (asi INI), který by si uživatelé mohli
|
||||||
|
stáhnout a nahrát.
|
||||||
|
|
||||||
|
Očekávám, že bych to psal v Xamarinu s použitím existujících bindingů
|
||||||
|
pro ovládáni MPD. Chtěl bych si vyzkoušet psaní mobilních aplikací.
|
After Width: | Height: | Size: 20 KiB |