mirror of
https://github.com/antebudimir/tempus.git
synced 2026-01-02 10:23:33 +00:00
Multi server/user implementation
This commit is contained in:
parent
4e269a7446
commit
78a4006ed6
30 changed files with 959 additions and 192 deletions
10
.idea/misc.xml
generated
10
.idea/misc.xml
generated
|
|
@ -3,20 +3,30 @@
|
||||||
<component name="DesignSurface">
|
<component name="DesignSurface">
|
||||||
<option name="filePathToZoomLevelMap">
|
<option name="filePathToZoomLevelMap">
|
||||||
<map>
|
<map>
|
||||||
|
<entry key="app/src/main/res/drawable-v24/ic_add.xml" value="0.27685185185185185" />
|
||||||
<entry key="app/src/main/res/drawable-v24/ic_launcher_foreground.xml" value="0.2814814814814815" />
|
<entry key="app/src/main/res/drawable-v24/ic_launcher_foreground.xml" value="0.2814814814814815" />
|
||||||
<entry key="app/src/main/res/drawable/ic_downloading.xml" value="0.2722222222222222" />
|
<entry key="app/src/main/res/drawable/ic_downloading.xml" value="0.2722222222222222" />
|
||||||
<entry key="app/src/main/res/drawable/ic_file_download.xml" value="0.2722222222222222" />
|
<entry key="app/src/main/res/drawable/ic_file_download.xml" value="0.2722222222222222" />
|
||||||
<entry key="app/src/main/res/drawable/ic_launcher_background.xml" value="0.2814814814814815" />
|
<entry key="app/src/main/res/drawable/ic_launcher_background.xml" value="0.2814814814814815" />
|
||||||
|
<entry key="app/src/main/res/drawable/ic_play.xml" value="0.27685185185185185" />
|
||||||
<entry key="app/src/main/res/drawable/ic_refresh.xml" value="0.18958333333333333" />
|
<entry key="app/src/main/res/drawable/ic_refresh.xml" value="0.18958333333333333" />
|
||||||
|
<entry key="app/src/main/res/drawable/ic_search.xml" value="0.27685185185185185" />
|
||||||
<entry key="app/src/main/res/drawable/ic_shuffle.xml" value="0.2722222222222222" />
|
<entry key="app/src/main/res/drawable/ic_shuffle.xml" value="0.2722222222222222" />
|
||||||
<entry key="app/src/main/res/drawable/ic_star.xml" value="0.2722222222222222" />
|
<entry key="app/src/main/res/drawable/ic_star.xml" value="0.2722222222222222" />
|
||||||
<entry key="app/src/main/res/layout/bottom_sheet_song_dialog.xml" value="0.21666666666666667" />
|
<entry key="app/src/main/res/layout/bottom_sheet_song_dialog.xml" value="0.21666666666666667" />
|
||||||
<entry key="app/src/main/res/layout/dialog_rating.xml" value="0.3229166666666667" />
|
<entry key="app/src/main/res/layout/dialog_rating.xml" value="0.3229166666666667" />
|
||||||
|
<entry key="app/src/main/res/layout/dialog_server_signup.xml" value="0.3229166666666667" />
|
||||||
|
<entry key="app/src/main/res/layout/fragment_album_catalogue.xml" value="0.3229166666666667" />
|
||||||
<entry key="app/src/main/res/layout/fragment_album_page.xml" value="0.1" />
|
<entry key="app/src/main/res/layout/fragment_album_page.xml" value="0.1" />
|
||||||
<entry key="app/src/main/res/layout/fragment_artist_page.xml" value="0.1" />
|
<entry key="app/src/main/res/layout/fragment_artist_page.xml" value="0.1" />
|
||||||
|
<entry key="app/src/main/res/layout/fragment_login.xml" value="0.3166496424923391" />
|
||||||
<entry key="app/src/main/res/layout/item_horizontal_album.xml" value="0.3166496424923391" />
|
<entry key="app/src/main/res/layout/item_horizontal_album.xml" value="0.3166496424923391" />
|
||||||
<entry key="app/src/main/res/layout/item_horizontal_track.xml" value="0.1" />
|
<entry key="app/src/main/res/layout/item_horizontal_track.xml" value="0.1" />
|
||||||
|
<entry key="app/src/main/res/layout/item_login_server.xml" value="0.25" />
|
||||||
|
<entry key="app/src/main/res/layout/item_player_now_playing_song.xml" value="0.3229166666666667" />
|
||||||
<entry key="app/src/main/res/layout/item_player_queue_song.xml" value="0.3229166666666667" />
|
<entry key="app/src/main/res/layout/item_player_queue_song.xml" value="0.3229166666666667" />
|
||||||
|
<entry key="app/src/main/res/menu/login_page_menu.xml" value="0.3229166666666667" />
|
||||||
|
<entry key="app/src/main/res/menu/toolbar_menu.xml" value="0.3229166666666667" />
|
||||||
</map>
|
</map>
|
||||||
</option>
|
</option>
|
||||||
</component>
|
</component>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,127 @@
|
||||||
|
package com.cappielloantonio.play.adapter;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.fragment.app.FragmentManager;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import com.cappielloantonio.play.App;
|
||||||
|
import com.cappielloantonio.play.R;
|
||||||
|
import com.cappielloantonio.play.interfaces.SystemCallback;
|
||||||
|
import com.cappielloantonio.play.model.Server;
|
||||||
|
import com.cappielloantonio.play.model.Song;
|
||||||
|
import com.cappielloantonio.play.repository.SystemRepository;
|
||||||
|
import com.cappielloantonio.play.ui.activity.MainActivity;
|
||||||
|
import com.cappielloantonio.play.util.PreferenceUtil;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class ServerAdapter extends RecyclerView.Adapter<ServerAdapter.ViewHolder> {
|
||||||
|
private static final String TAG = "ServerAdapter";
|
||||||
|
|
||||||
|
private List<Server> servers;
|
||||||
|
private final LayoutInflater mInflater;
|
||||||
|
private MainActivity mainActivity;
|
||||||
|
private Context context;
|
||||||
|
|
||||||
|
public ServerAdapter(MainActivity mainActivity, Context context) {
|
||||||
|
this.mInflater = LayoutInflater.from(context);
|
||||||
|
this.servers = new ArrayList<>();
|
||||||
|
this.mainActivity = mainActivity;
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||||
|
View view = mInflater.inflate(R.layout.item_login_server, parent, false);
|
||||||
|
return new ViewHolder(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(ViewHolder holder, int position) {
|
||||||
|
Server server = servers.get(position);
|
||||||
|
|
||||||
|
holder.serverName.setText(server.getServerName());
|
||||||
|
holder.serverAddress.setText(server.getAddress());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return servers.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Server> getItems() {
|
||||||
|
return this.servers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setItems(List<Server> servers) {
|
||||||
|
this.servers = servers;
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Server getItem(int id) {
|
||||||
|
return servers.get(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
|
||||||
|
TextView serverName;
|
||||||
|
TextView serverAddress;
|
||||||
|
|
||||||
|
ViewHolder(View itemView) {
|
||||||
|
super(itemView);
|
||||||
|
|
||||||
|
serverName = itemView.findViewById(R.id.server_name_text_view);
|
||||||
|
serverAddress = itemView.findViewById(R.id.server_address_text_view);
|
||||||
|
|
||||||
|
itemView.setOnClickListener(this);
|
||||||
|
|
||||||
|
serverName.setSelected(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
Server server = servers.get(getBindingAdapterPosition());
|
||||||
|
saveServerPreference(server.getAddress(), server.getUsername(), server.getToken(), server.getSalt());
|
||||||
|
|
||||||
|
SystemRepository systemRepository = new SystemRepository(App.getInstance());
|
||||||
|
systemRepository.checkUserCredential(new SystemCallback() {
|
||||||
|
@Override
|
||||||
|
public void onError(Exception exception) {
|
||||||
|
Toast.makeText(context, exception.getMessage(), Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSuccess(String token, String salt) {
|
||||||
|
saveServerPreference(null, null, token, salt);
|
||||||
|
enter();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void enter() {
|
||||||
|
mainActivity.goFromLogin();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveServerPreference(String server, String user, String token, String salt) {
|
||||||
|
if (user != null) PreferenceUtil.getInstance(context).setUser(user);
|
||||||
|
if (server != null) PreferenceUtil.getInstance(context).setServer(server);
|
||||||
|
|
||||||
|
if (token != null && salt != null) {
|
||||||
|
PreferenceUtil.getInstance(context).setToken(token);
|
||||||
|
PreferenceUtil.getInstance(context).setSalt(salt);
|
||||||
|
PreferenceUtil.getInstance(context).setServerId(servers.get(getBindingAdapterPosition()).getServerId());
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
App.getSubsonicClientInstance(context, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -9,11 +9,13 @@ import androidx.room.RoomDatabase;
|
||||||
import com.cappielloantonio.play.database.dao.DownloadDao;
|
import com.cappielloantonio.play.database.dao.DownloadDao;
|
||||||
import com.cappielloantonio.play.database.dao.QueueDao;
|
import com.cappielloantonio.play.database.dao.QueueDao;
|
||||||
import com.cappielloantonio.play.database.dao.RecentSearchDao;
|
import com.cappielloantonio.play.database.dao.RecentSearchDao;
|
||||||
|
import com.cappielloantonio.play.database.dao.ServerDao;
|
||||||
import com.cappielloantonio.play.model.Download;
|
import com.cappielloantonio.play.model.Download;
|
||||||
import com.cappielloantonio.play.model.Queue;
|
import com.cappielloantonio.play.model.Queue;
|
||||||
import com.cappielloantonio.play.model.RecentSearch;
|
import com.cappielloantonio.play.model.RecentSearch;
|
||||||
|
import com.cappielloantonio.play.model.Server;
|
||||||
|
|
||||||
@Database(entities = {Queue.class, RecentSearch.class, Download.class}, version = 5, exportSchema = false)
|
@Database(entities = {Queue.class, Server.class, RecentSearch.class, Download.class}, version = 9, exportSchema = false)
|
||||||
public abstract class AppDatabase extends RoomDatabase {
|
public abstract class AppDatabase extends RoomDatabase {
|
||||||
private static final String TAG = "AppDatabase";
|
private static final String TAG = "AppDatabase";
|
||||||
|
|
||||||
|
|
@ -31,6 +33,8 @@ public abstract class AppDatabase extends RoomDatabase {
|
||||||
|
|
||||||
public abstract QueueDao queueDao();
|
public abstract QueueDao queueDao();
|
||||||
|
|
||||||
|
public abstract ServerDao serverDao();
|
||||||
|
|
||||||
public abstract RecentSearchDao recentSearchDao();
|
public abstract RecentSearchDao recentSearchDao();
|
||||||
|
|
||||||
public abstract DownloadDao downloadDao();
|
public abstract DownloadDao downloadDao();
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,8 @@ public interface DownloadDao {
|
||||||
@Query("SELECT * FROM download")
|
@Query("SELECT * FROM download")
|
||||||
List<Download> getAll();
|
List<Download> getAll();
|
||||||
|
|
||||||
@Query("SELECT * FROM download LIMIT :size")
|
@Query("SELECT * FROM download WHERE server=:server LIMIT :size")
|
||||||
LiveData<List<Download>> getSample(int size);
|
LiveData<List<Download>> getSample(int size, String server);
|
||||||
|
|
||||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
void insert(Download download);
|
void insert(Download download);
|
||||||
|
|
@ -27,6 +27,6 @@ public interface DownloadDao {
|
||||||
@Query("DELETE FROM download WHERE id = :id")
|
@Query("DELETE FROM download WHERE id = :id")
|
||||||
void delete(String id);
|
void delete(String id);
|
||||||
|
|
||||||
@Query("DELETE FROM download")
|
@Query("DELETE FROM download WHERE server=:server")
|
||||||
void deleteAll();
|
void deleteAll(String server);
|
||||||
}
|
}
|
||||||
|
|
@ -12,20 +12,20 @@ import java.util.List;
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
public interface QueueDao {
|
public interface QueueDao {
|
||||||
@Query("SELECT * FROM queue")
|
@Query("SELECT * FROM queue WHERE server = :server")
|
||||||
LiveData<List<Queue>> getAll();
|
LiveData<List<Queue>> getAll(String server);
|
||||||
|
|
||||||
@Query("SELECT * FROM queue")
|
@Query("SELECT * FROM queue WHERE server = :server")
|
||||||
List<Queue> getAllSimple();
|
List<Queue> getAllSimple(String server);
|
||||||
|
|
||||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
void insertAll(List<Queue> songQueueObject);
|
void insertAll(List<Queue> songQueueObject);
|
||||||
|
|
||||||
@Query("DELETE FROM queue WHERE queue.track_order = :position")
|
@Query("DELETE FROM queue WHERE queue.track_order = :position AND server = :server")
|
||||||
void deleteByPosition(int position);
|
void deleteByPosition(int position, String server);
|
||||||
|
|
||||||
@Query("DELETE FROM queue")
|
@Query("DELETE FROM queue WHERE server = :server")
|
||||||
void deleteAll();
|
void deleteAll(String server);
|
||||||
|
|
||||||
@Query("SELECT COUNT(*) FROM queue;")
|
@Query("SELECT COUNT(*) FROM queue;")
|
||||||
int count();
|
int count();
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,4 @@ public interface RecentSearchDao {
|
||||||
|
|
||||||
@Delete
|
@Delete
|
||||||
void delete(RecentSearch search);
|
void delete(RecentSearch search);
|
||||||
|
|
||||||
@Query("DELETE FROM recent_search")
|
|
||||||
void deleteAll();
|
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
package com.cappielloantonio.play.database.dao;
|
||||||
|
|
||||||
|
import androidx.lifecycle.LiveData;
|
||||||
|
import androidx.room.Dao;
|
||||||
|
import androidx.room.Delete;
|
||||||
|
import androidx.room.Insert;
|
||||||
|
import androidx.room.OnConflictStrategy;
|
||||||
|
import androidx.room.Query;
|
||||||
|
|
||||||
|
import com.cappielloantonio.play.model.Queue;
|
||||||
|
import com.cappielloantonio.play.model.Server;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
public interface ServerDao {
|
||||||
|
@Query("SELECT * FROM server")
|
||||||
|
LiveData<List<Server>> getAll();
|
||||||
|
|
||||||
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
|
void insert(Server server);
|
||||||
|
|
||||||
|
@Delete
|
||||||
|
void delete(Server server);
|
||||||
|
}
|
||||||
|
|
@ -34,7 +34,7 @@ public class Album implements Parcelable {
|
||||||
public Album(AlbumID3 albumID3) {
|
public Album(AlbumID3 albumID3) {
|
||||||
this.id = albumID3.getId();
|
this.id = albumID3.getId();
|
||||||
this.title = albumID3.getName();
|
this.title = albumID3.getName();
|
||||||
this.year = albumID3.getYear();
|
this.year = albumID3.getYear() != null ? albumID3.getYear() : 0;
|
||||||
this.artistId = albumID3.getArtistId();
|
this.artistId = albumID3.getArtistId();
|
||||||
this.artistName = albumID3.getArtist();
|
this.artistName = albumID3.getArtist();
|
||||||
this.primary = albumID3.getCoverArtId();
|
this.primary = albumID3.getCoverArtId();
|
||||||
|
|
@ -44,7 +44,7 @@ public class Album implements Parcelable {
|
||||||
public Album(AlbumWithSongsID3 albumWithSongsID3) {
|
public Album(AlbumWithSongsID3 albumWithSongsID3) {
|
||||||
this.id = albumWithSongsID3.getId();
|
this.id = albumWithSongsID3.getId();
|
||||||
this.title = albumWithSongsID3.getName();
|
this.title = albumWithSongsID3.getName();
|
||||||
this.year = albumWithSongsID3.getYear();
|
this.year = albumWithSongsID3.getYear() != null ? albumWithSongsID3.getYear() : 0;
|
||||||
this.artistId = albumWithSongsID3.getArtistId();
|
this.artistId = albumWithSongsID3.getArtistId();
|
||||||
this.artistName = albumWithSongsID3.getArtist();
|
this.artistName = albumWithSongsID3.getArtist();
|
||||||
this.primary = albumWithSongsID3.getCoverArtId();
|
this.primary = albumWithSongsID3.getCoverArtId();
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,10 @@ public class Download {
|
||||||
@ColumnInfo(name = "duration")
|
@ColumnInfo(name = "duration")
|
||||||
private long duration;
|
private long duration;
|
||||||
|
|
||||||
public Download(String songID, String title, String albumId, String albumName, String artistId, String artistName, String primary, long duration) {
|
@ColumnInfo(name = "server")
|
||||||
|
private String server;
|
||||||
|
|
||||||
|
public Download(String songID, String title, String albumId, String albumName, String artistId, String artistName, String primary, long duration, String server) {
|
||||||
this.songID = songID;
|
this.songID = songID;
|
||||||
this.title = title;
|
this.title = title;
|
||||||
this.albumId = albumId;
|
this.albumId = albumId;
|
||||||
|
|
@ -42,6 +45,7 @@ public class Download {
|
||||||
this.artistName = artistName;
|
this.artistName = artistName;
|
||||||
this.primary = primary;
|
this.primary = primary;
|
||||||
this.duration = duration;
|
this.duration = duration;
|
||||||
|
this.server = server;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Download(Song song) {
|
public Download(Song song) {
|
||||||
|
|
@ -118,4 +122,12 @@ public class Download {
|
||||||
public void setDuration(long duration) {
|
public void setDuration(long duration) {
|
||||||
this.duration = duration;
|
this.duration = duration;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getServer() {
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setServer(String server) {
|
||||||
|
this.server = server;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,10 @@ public class Queue {
|
||||||
@ColumnInfo(name = "duration")
|
@ColumnInfo(name = "duration")
|
||||||
private long duration;
|
private long duration;
|
||||||
|
|
||||||
public Queue(int trackOrder, String songID, String title, String albumId, String albumName, String artistId, String artistName, String primary, long duration) {
|
@ColumnInfo(name = "server")
|
||||||
|
private String server;
|
||||||
|
|
||||||
|
public Queue(int trackOrder, String songID, String title, String albumId, String albumName, String artistId, String artistName, String primary, long duration, String server) {
|
||||||
this.trackOrder = trackOrder;
|
this.trackOrder = trackOrder;
|
||||||
this.songID = songID;
|
this.songID = songID;
|
||||||
this.title = title;
|
this.title = title;
|
||||||
|
|
@ -46,6 +49,7 @@ public class Queue {
|
||||||
this.artistName = artistName;
|
this.artistName = artistName;
|
||||||
this.primary = primary;
|
this.primary = primary;
|
||||||
this.duration = duration;
|
this.duration = duration;
|
||||||
|
this.server = server;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getTrackOrder() {
|
public int getTrackOrder() {
|
||||||
|
|
@ -119,4 +123,12 @@ public class Queue {
|
||||||
public void setDuration(long duration) {
|
public void setDuration(long duration) {
|
||||||
this.duration = duration;
|
this.duration = duration;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getServer() {
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setServer(String server) {
|
||||||
|
this.server = server;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ public class RecentSearch {
|
||||||
@ColumnInfo(name = "search")
|
@ColumnInfo(name = "search")
|
||||||
private String search;
|
private String search;
|
||||||
|
|
||||||
public RecentSearch(String search) {
|
public RecentSearch(@NonNull String search) {
|
||||||
this.search = search;
|
this.search = search;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -21,7 +21,7 @@ public class RecentSearch {
|
||||||
return search;
|
return search;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSearch(String search) {
|
public void setSearch(@NonNull String search) {
|
||||||
this.search = search;
|
this.search = search;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,99 @@
|
||||||
|
package com.cappielloantonio.play.model;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.room.ColumnInfo;
|
||||||
|
import androidx.room.Entity;
|
||||||
|
import androidx.room.PrimaryKey;
|
||||||
|
|
||||||
|
@Entity(tableName = "server")
|
||||||
|
public class Server {
|
||||||
|
@NonNull
|
||||||
|
@PrimaryKey
|
||||||
|
@ColumnInfo(name = "id")
|
||||||
|
private String serverId;
|
||||||
|
|
||||||
|
@ColumnInfo(name = "server_name")
|
||||||
|
private String serverName;
|
||||||
|
|
||||||
|
@ColumnInfo(name = "username")
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
@ColumnInfo(name = "address")
|
||||||
|
private String address;
|
||||||
|
|
||||||
|
@ColumnInfo(name = "token")
|
||||||
|
private String token;
|
||||||
|
|
||||||
|
@ColumnInfo(name = "salt")
|
||||||
|
private String salt;
|
||||||
|
|
||||||
|
@ColumnInfo(name = "timestamp")
|
||||||
|
private long timestamp;
|
||||||
|
|
||||||
|
public Server(@NonNull String serverId, String serverName, String username, String address, String token, String salt, long timestamp) {
|
||||||
|
this.serverId = serverId;
|
||||||
|
this.serverName = serverName;
|
||||||
|
this.username = username;
|
||||||
|
this.address = address;
|
||||||
|
this.token = token;
|
||||||
|
this.salt = salt;
|
||||||
|
this.timestamp = timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public String getServerId() {
|
||||||
|
return serverId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setServerId(@NonNull String serverId) {
|
||||||
|
this.serverId = serverId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getServerName() {
|
||||||
|
return serverName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setServerName(String serverName) {
|
||||||
|
this.serverName = serverName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUsername() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUsername(String username) {
|
||||||
|
this.username = username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAddress() {
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAddress(String address) {
|
||||||
|
this.address = address;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getToken() {
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setToken(String token) {
|
||||||
|
this.token = token;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSalt() {
|
||||||
|
return salt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSalt(String salt) {
|
||||||
|
this.salt = salt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getTimestamp() {
|
||||||
|
return timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTimestamp(long timestamp) {
|
||||||
|
this.timestamp = timestamp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -275,6 +275,7 @@ public class AlbumRepository {
|
||||||
|
|
||||||
getFirstAlbum(first -> {
|
getFirstAlbum(first -> {
|
||||||
getLastAlbum(last -> {
|
getLastAlbum(last -> {
|
||||||
|
if(first != -1 && last != -1) {
|
||||||
List<Integer> decadeList = new ArrayList();
|
List<Integer> decadeList = new ArrayList();
|
||||||
|
|
||||||
int startDecade = first - (first % 10);
|
int startDecade = first - (first % 10);
|
||||||
|
|
@ -286,6 +287,7 @@ public class AlbumRepository {
|
||||||
}
|
}
|
||||||
|
|
||||||
decades.setValue(decadeList);
|
decades.setValue(decadeList);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -295,7 +297,7 @@ public class AlbumRepository {
|
||||||
private void getFirstAlbum(DecadesCallback callback) {
|
private void getFirstAlbum(DecadesCallback callback) {
|
||||||
App.getSubsonicClientInstance(application, false)
|
App.getSubsonicClientInstance(application, false)
|
||||||
.getAlbumSongListClient()
|
.getAlbumSongListClient()
|
||||||
.getAlbumList2("byYear", 1, 0, 0, Calendar.getInstance().get(Calendar.YEAR))
|
.getAlbumList2("byYear", 1, 0, 1900, Calendar.getInstance().get(Calendar.YEAR))
|
||||||
.enqueue(new Callback<SubsonicResponse>() {
|
.enqueue(new Callback<SubsonicResponse>() {
|
||||||
@Override
|
@Override
|
||||||
public void onResponse(Call<SubsonicResponse> call, Response<SubsonicResponse> response) {
|
public void onResponse(Call<SubsonicResponse> call, Response<SubsonicResponse> response) {
|
||||||
|
|
@ -308,7 +310,7 @@ public class AlbumRepository {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(Call<SubsonicResponse> call, Throwable t) {
|
public void onFailure(Call<SubsonicResponse> call, Throwable t) {
|
||||||
|
callback.onLoadYear(-1);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -316,20 +318,23 @@ public class AlbumRepository {
|
||||||
private void getLastAlbum(DecadesCallback callback) {
|
private void getLastAlbum(DecadesCallback callback) {
|
||||||
App.getSubsonicClientInstance(application, false)
|
App.getSubsonicClientInstance(application, false)
|
||||||
.getAlbumSongListClient()
|
.getAlbumSongListClient()
|
||||||
.getAlbumList2("byYear", 1, 0, Calendar.getInstance().get(Calendar.YEAR), 0)
|
.getAlbumList2("byYear", 1, 0, Calendar.getInstance().get(Calendar.YEAR), 1900)
|
||||||
.enqueue(new Callback<SubsonicResponse>() {
|
.enqueue(new Callback<SubsonicResponse>() {
|
||||||
@Override
|
@Override
|
||||||
public void onResponse(Call<SubsonicResponse> call, Response<SubsonicResponse> response) {
|
public void onResponse(Call<SubsonicResponse> call, Response<SubsonicResponse> response) {
|
||||||
if (response.body().getStatus().getValue().equals(ResponseStatus.OK)) {
|
if (response.body().getStatus().getValue().equals(ResponseStatus.OK)) {
|
||||||
if(response.body().getAlbumList2().getAlbums().get(0) != null){
|
if(response.body().getAlbumList2().getAlbums().size() > 0 && response.body().getAlbumList2().getAlbums().get(0) != null){
|
||||||
callback.onLoadYear(response.body().getAlbumList2().getAlbums().get(0).getYear());
|
callback.onLoadYear(response.body().getAlbumList2().getAlbums().get(0).getYear());
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
callback.onLoadYear(-1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(Call<SubsonicResponse> call, Throwable t) {
|
public void onFailure(Call<SubsonicResponse> call, Throwable t) {
|
||||||
|
callback.onLoadYear(-1);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,11 @@ import android.app.Application;
|
||||||
import androidx.lifecycle.LiveData;
|
import androidx.lifecycle.LiveData;
|
||||||
import androidx.lifecycle.MutableLiveData;
|
import androidx.lifecycle.MutableLiveData;
|
||||||
|
|
||||||
|
import com.cappielloantonio.play.App;
|
||||||
import com.cappielloantonio.play.database.AppDatabase;
|
import com.cappielloantonio.play.database.AppDatabase;
|
||||||
import com.cappielloantonio.play.database.dao.DownloadDao;
|
import com.cappielloantonio.play.database.dao.DownloadDao;
|
||||||
import com.cappielloantonio.play.model.Download;
|
import com.cappielloantonio.play.model.Download;
|
||||||
|
import com.cappielloantonio.play.util.PreferenceUtil;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
@ -60,7 +62,7 @@ public class DownloadRepository {
|
||||||
}
|
}
|
||||||
|
|
||||||
public LiveData<List<Download>> getLiveDownloadSample(int size) {
|
public LiveData<List<Download>> getLiveDownloadSample(int size) {
|
||||||
listLiveDownloadSample = downloadDao.getSample(size);
|
listLiveDownloadSample = downloadDao.getSample(size, PreferenceUtil.getInstance(App.getInstance()).getServerId());
|
||||||
return listLiveDownloadSample;
|
return listLiveDownloadSample;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -121,7 +123,7 @@ public class DownloadRepository {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
downloadDao.deleteAll();
|
downloadDao.deleteAll(PreferenceUtil.getInstance(App.getInstance()).getServerId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,13 @@ import android.app.Application;
|
||||||
|
|
||||||
import androidx.lifecycle.LiveData;
|
import androidx.lifecycle.LiveData;
|
||||||
|
|
||||||
|
import com.cappielloantonio.play.App;
|
||||||
import com.cappielloantonio.play.database.AppDatabase;
|
import com.cappielloantonio.play.database.AppDatabase;
|
||||||
import com.cappielloantonio.play.database.dao.QueueDao;
|
import com.cappielloantonio.play.database.dao.QueueDao;
|
||||||
import com.cappielloantonio.play.model.Queue;
|
import com.cappielloantonio.play.model.Queue;
|
||||||
import com.cappielloantonio.play.model.Song;
|
import com.cappielloantonio.play.model.Song;
|
||||||
import com.cappielloantonio.play.util.MappingUtil;
|
import com.cappielloantonio.play.util.MappingUtil;
|
||||||
|
import com.cappielloantonio.play.util.PreferenceUtil;
|
||||||
import com.cappielloantonio.play.util.QueueUtil;
|
import com.cappielloantonio.play.util.QueueUtil;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
@ -26,7 +28,7 @@ public class QueueRepository {
|
||||||
}
|
}
|
||||||
|
|
||||||
public LiveData<List<Queue>> getLiveQueue() {
|
public LiveData<List<Queue>> getLiveQueue() {
|
||||||
listLiveQueue = queueDao.getAll();
|
listLiveQueue = queueDao.getAll(PreferenceUtil.getInstance(App.getInstance()).getServerId());
|
||||||
return listLiveQueue;
|
return listLiveQueue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -94,7 +96,7 @@ public class QueueRepository {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
songs = MappingUtil.mapQueue(queueDao.getAllSimple());
|
songs = MappingUtil.mapQueue(queueDao.getAllSimple(PreferenceUtil.getInstance(App.getInstance()).getServerId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Song> getSongs() {
|
public List<Song> getSongs() {
|
||||||
|
|
@ -113,7 +115,7 @@ public class QueueRepository {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
queueDao.insertAll(QueueUtil.getQueueElementsFromSongs(songs));
|
queueDao.insertAll(QueueUtil.getQueueElementsFromSongs(songs, PreferenceUtil.getInstance(App.getInstance()).getServerId()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -128,7 +130,7 @@ public class QueueRepository {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
queueDao.deleteByPosition(position);
|
queueDao.deleteByPosition(position, PreferenceUtil.getInstance(App.getInstance()).getServerId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -141,7 +143,7 @@ public class QueueRepository {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
queueDao.deleteAll();
|
queueDao.deleteAll(PreferenceUtil.getInstance(App.getInstance()).getServerId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,70 @@
|
||||||
|
package com.cappielloantonio.play.repository;
|
||||||
|
|
||||||
|
import android.app.Application;
|
||||||
|
|
||||||
|
import androidx.lifecycle.LiveData;
|
||||||
|
|
||||||
|
import com.cappielloantonio.play.database.AppDatabase;
|
||||||
|
import com.cappielloantonio.play.database.dao.ServerDao;
|
||||||
|
import com.cappielloantonio.play.model.Server;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ServerRepository {
|
||||||
|
private static final String TAG = "QueueRepository";
|
||||||
|
|
||||||
|
private ServerDao serverDao;
|
||||||
|
private LiveData<List<Server>> listLiveServer;
|
||||||
|
|
||||||
|
public ServerRepository(Application application) {
|
||||||
|
AppDatabase database = AppDatabase.getInstance(application);
|
||||||
|
serverDao = database.serverDao();
|
||||||
|
}
|
||||||
|
|
||||||
|
public LiveData<List<Server>> getLiveServer() {
|
||||||
|
listLiveServer = serverDao.getAll();
|
||||||
|
return listLiveServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void insert(Server server) {
|
||||||
|
InsertThreadSafe insert = new InsertThreadSafe(serverDao, server);
|
||||||
|
Thread thread = new Thread(insert);
|
||||||
|
thread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void delete(Server server) {
|
||||||
|
DeleteThreadSafe delete = new DeleteThreadSafe(serverDao, server);
|
||||||
|
Thread thread = new Thread(delete);
|
||||||
|
thread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class InsertThreadSafe implements Runnable {
|
||||||
|
private ServerDao serverDao;
|
||||||
|
private Server server;
|
||||||
|
|
||||||
|
public InsertThreadSafe(ServerDao serverDao, Server server) {
|
||||||
|
this.serverDao = serverDao;
|
||||||
|
this.server = server;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
serverDao.insert(server);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class DeleteThreadSafe implements Runnable {
|
||||||
|
private ServerDao serverDao;
|
||||||
|
private Server server;
|
||||||
|
|
||||||
|
public DeleteThreadSafe(ServerDao serverDao, Server server) {
|
||||||
|
this.serverDao = serverDao;
|
||||||
|
this.server = server;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
serverDao.delete(server);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -179,8 +179,11 @@ public class MainActivity extends BaseActivity {
|
||||||
|
|
||||||
// NAVIGATION
|
// NAVIGATION
|
||||||
public void goToLogin() {
|
public void goToLogin() {
|
||||||
if (Objects.requireNonNull(navController.getCurrentDestination()).getId() == R.id.landingFragment)
|
if (Objects.requireNonNull(navController.getCurrentDestination()).getId() == R.id.landingFragment) {
|
||||||
navController.navigate(R.id.action_landingFragment_to_loginFragment);
|
navController.navigate(R.id.action_landingFragment_to_loginFragment);
|
||||||
|
} else if(Objects.requireNonNull(navController.getCurrentDestination()).getId() == R.id.settingsFragment) {
|
||||||
|
navController.navigate(R.id.action_settingsFragment_to_loginFragment);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void goToHome() {
|
public void goToHome() {
|
||||||
|
|
|
||||||
|
|
@ -1,44 +1,64 @@
|
||||||
package com.cappielloantonio.play.ui.fragment;
|
package com.cappielloantonio.play.ui.fragment;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.core.view.ViewCompat;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
|
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
import com.cappielloantonio.play.App;
|
import com.cappielloantonio.play.R;
|
||||||
|
import com.cappielloantonio.play.adapter.ServerAdapter;
|
||||||
import com.cappielloantonio.play.databinding.FragmentLoginBinding;
|
import com.cappielloantonio.play.databinding.FragmentLoginBinding;
|
||||||
import com.cappielloantonio.play.interfaces.SystemCallback;
|
import com.cappielloantonio.play.service.MusicPlayerRemote;
|
||||||
import com.cappielloantonio.play.repository.SystemRepository;
|
|
||||||
import com.cappielloantonio.play.ui.activity.MainActivity;
|
import com.cappielloantonio.play.ui.activity.MainActivity;
|
||||||
import com.cappielloantonio.play.util.PreferenceUtil;
|
import com.cappielloantonio.play.ui.fragment.dialog.ServerSignupDialog;
|
||||||
|
import com.cappielloantonio.play.viewmodel.LoginViewModel;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
public class LoginFragment extends Fragment {
|
public class LoginFragment extends Fragment {
|
||||||
private static final String TAG = "LoginFragment";
|
private static final String TAG = "LoginFragment";
|
||||||
|
|
||||||
private FragmentLoginBinding bind;
|
private FragmentLoginBinding bind;
|
||||||
private MainActivity activity;
|
private MainActivity activity;
|
||||||
|
private LoginViewModel loginViewModel;
|
||||||
|
|
||||||
private String username;
|
private ServerAdapter serverAdapter;
|
||||||
private String password;
|
|
||||||
private String server;
|
|
||||||
|
|
||||||
private SystemRepository systemRepository;
|
@Override
|
||||||
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setHasOptionsMenu(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||||
|
super.onCreateOptionsMenu(menu, inflater);
|
||||||
|
inflater.inflate(R.menu.login_page_menu, menu);
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||||
activity = (MainActivity) getActivity();
|
activity = (MainActivity) getActivity();
|
||||||
|
|
||||||
|
loginViewModel = new ViewModelProvider(requireActivity()).get(LoginViewModel.class);
|
||||||
bind = FragmentLoginBinding.inflate(inflater, container, false);
|
bind = FragmentLoginBinding.inflate(inflater, container, false);
|
||||||
View view = bind.getRoot();
|
View view = bind.getRoot();
|
||||||
init();
|
|
||||||
|
initAppBar();
|
||||||
|
initServerListView();
|
||||||
|
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
@ -49,67 +69,75 @@ public class LoginFragment extends Fragment {
|
||||||
bind = null;
|
bind = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void init() {
|
private void initAppBar() {
|
||||||
systemRepository = new SystemRepository(App.getInstance());
|
activity.setSupportActionBar(bind.toolbar);
|
||||||
|
|
||||||
bind.loginButton.setOnClickListener(v -> {
|
bind.appBarLayout.addOnOffsetChangedListener((appBarLayout, verticalOffset) -> {
|
||||||
if (validateInput()) {
|
if ((bind.serverInfoSector.getHeight() + verticalOffset) < (2 * ViewCompat.getMinimumHeight(bind.toolbar))) {
|
||||||
saveServerPreference(server, username, password, null, null);
|
bind.toolbar.setTitle("Subsonic servers");
|
||||||
authenticate();
|
} else {
|
||||||
|
bind.toolbar.setTitle("");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean validateInput() {
|
private void initServerListView() {
|
||||||
username = bind.usernameTextView.getText().toString().trim();
|
bind.serverListRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
|
||||||
password = bind.passwordTextView.getText().toString();
|
bind.serverListRecyclerView.setHasFixedSize(true);
|
||||||
server = bind.serverTextView.getText().toString().trim();
|
|
||||||
|
serverAdapter = new ServerAdapter(activity, requireContext());
|
||||||
|
bind.serverListRecyclerView.setAdapter(serverAdapter);
|
||||||
|
loginViewModel.getServerList().observe(requireActivity(), servers -> {
|
||||||
|
if(servers.size() > 0) {
|
||||||
|
if (bind != null) bind.noServerAddedTextView.setVisibility(View.GONE);
|
||||||
|
if (bind != null) bind.serverListRecyclerView.setVisibility(View.VISIBLE);
|
||||||
|
serverAdapter.setItems(servers);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (bind != null) bind.noServerAddedTextView.setVisibility(View.VISIBLE);
|
||||||
|
if (bind != null) bind.serverListRecyclerView.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN, ItemTouchHelper.LEFT) {
|
||||||
|
int originalPosition = -1;
|
||||||
|
int fromPosition = -1;
|
||||||
|
int toPosition = -1;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
|
||||||
|
if (originalPosition == -1)
|
||||||
|
originalPosition = viewHolder.getBindingAdapterPosition();
|
||||||
|
|
||||||
|
fromPosition = viewHolder.getBindingAdapterPosition();
|
||||||
|
toPosition = target.getBindingAdapterPosition();
|
||||||
|
|
||||||
|
Collections.swap(serverAdapter.getItems(), fromPosition, toPosition);
|
||||||
|
recyclerView.getAdapter().notifyItemMoved(fromPosition, toPosition);
|
||||||
|
|
||||||
if (TextUtils.isEmpty(username)) {
|
|
||||||
Toast.makeText(requireContext(), "Empty username", Toast.LENGTH_SHORT).show();
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (TextUtils.isEmpty(server)) {
|
@Override
|
||||||
Toast.makeText(requireContext(), "Empty server url", Toast.LENGTH_SHORT).show();
|
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
|
||||||
return false;
|
loginViewModel.deleteServer(serverAdapter.getItem(viewHolder.getBindingAdapterPosition()));
|
||||||
|
bind.serverListRecyclerView.getAdapter().notifyItemRemoved(viewHolder.getBindingAdapterPosition());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
).attachToRecyclerView(bind.serverListRecyclerView);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||||
|
switch (item.getItemId()) {
|
||||||
|
case R.id.action_add:
|
||||||
|
ServerSignupDialog dialog = new ServerSignupDialog();
|
||||||
|
dialog.show(activity.getSupportFragmentManager(), null);
|
||||||
return true;
|
return true;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void authenticate() {
|
return false;
|
||||||
systemRepository.checkUserCredential(new SystemCallback() {
|
|
||||||
@Override
|
|
||||||
public void onError(Exception exception) {
|
|
||||||
Log.e(TAG, exception.getMessage());
|
|
||||||
Toast.makeText(requireContext(), exception.getMessage(), Toast.LENGTH_LONG).show();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSuccess(String token, String salt) {
|
|
||||||
saveServerPreference(null, null, null, token, salt);
|
|
||||||
enter();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void enter() {
|
|
||||||
activity.goFromLogin();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void saveServerPreference(String server, String user, String password, String token, String salt) {
|
|
||||||
if (user != null) PreferenceUtil.getInstance(requireContext()).setUser(user);
|
|
||||||
if (server != null) PreferenceUtil.getInstance(requireContext()).setServer(server);
|
|
||||||
if (password != null) PreferenceUtil.getInstance(requireContext()).setPassword(password);
|
|
||||||
|
|
||||||
if (token != null && salt != null) {
|
|
||||||
PreferenceUtil.getInstance(requireContext()).setPassword(null);
|
|
||||||
PreferenceUtil.getInstance(requireContext()).setToken(token);
|
|
||||||
PreferenceUtil.getInstance(requireContext()).setSalt(salt);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
App.getSubsonicClientInstance(requireContext(), true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,13 @@ import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
import androidx.preference.ListPreference;
|
import androidx.preference.ListPreference;
|
||||||
|
import androidx.preference.Preference;
|
||||||
import androidx.preference.PreferenceFragmentCompat;
|
import androidx.preference.PreferenceFragmentCompat;
|
||||||
|
|
||||||
import com.cappielloantonio.play.R;
|
import com.cappielloantonio.play.R;
|
||||||
import com.cappielloantonio.play.helper.ThemeHelper;
|
import com.cappielloantonio.play.helper.ThemeHelper;
|
||||||
import com.cappielloantonio.play.ui.activity.MainActivity;
|
import com.cappielloantonio.play.ui.activity.MainActivity;
|
||||||
|
import com.cappielloantonio.play.util.PreferenceUtil;
|
||||||
|
|
||||||
public class SettingsFragment extends PreferenceFragmentCompat {
|
public class SettingsFragment extends PreferenceFragmentCompat {
|
||||||
private static final String TAG = "SettingsFragment";
|
private static final String TAG = "SettingsFragment";
|
||||||
|
|
@ -36,6 +38,24 @@ public class SettingsFragment extends PreferenceFragmentCompat {
|
||||||
activity.setBottomNavigationBarVisibility(false);
|
activity.setBottomNavigationBarVisibility(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
|
||||||
|
findPreference("logout").setOnPreferenceClickListener(preference -> {
|
||||||
|
PreferenceUtil.getInstance(requireContext()).setUser(null);
|
||||||
|
PreferenceUtil.getInstance(requireContext()).setServer(null);
|
||||||
|
PreferenceUtil.getInstance(requireContext()).setPassword(null);
|
||||||
|
PreferenceUtil.getInstance(requireContext()).setToken(null);
|
||||||
|
PreferenceUtil.getInstance(requireContext()).setSalt(null);
|
||||||
|
PreferenceUtil.getInstance(requireContext()).setServerId(null);
|
||||||
|
|
||||||
|
activity.goToLogin();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||||
setPreferencesFromResource(R.xml.global_preferences, rootKey);
|
setPreferencesFromResource(R.xml.global_preferences, rootKey);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,142 @@
|
||||||
|
package com.cappielloantonio.play.ui.fragment.dialog;
|
||||||
|
|
||||||
|
import android.app.AlertDialog;
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.fragment.app.DialogFragment;
|
||||||
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
|
|
||||||
|
import com.cappielloantonio.play.App;
|
||||||
|
import com.cappielloantonio.play.R;
|
||||||
|
import com.cappielloantonio.play.databinding.DialogServerSignupBinding;
|
||||||
|
import com.cappielloantonio.play.interfaces.SystemCallback;
|
||||||
|
import com.cappielloantonio.play.model.Server;
|
||||||
|
import com.cappielloantonio.play.repository.SystemRepository;
|
||||||
|
import com.cappielloantonio.play.ui.activity.MainActivity;
|
||||||
|
import com.cappielloantonio.play.util.PreferenceUtil;
|
||||||
|
import com.cappielloantonio.play.viewmodel.LoginViewModel;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class ServerSignupDialog extends DialogFragment {
|
||||||
|
private static final String TAG = "ServerSignupDialog";
|
||||||
|
|
||||||
|
private DialogServerSignupBinding bind;
|
||||||
|
private MainActivity activity;
|
||||||
|
private Context context;
|
||||||
|
private LoginViewModel loginViewModel;
|
||||||
|
|
||||||
|
private SystemRepository systemRepository;
|
||||||
|
|
||||||
|
private String serverName;
|
||||||
|
private String username;
|
||||||
|
private String password;
|
||||||
|
private String server;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||||
|
activity = (MainActivity) getActivity();
|
||||||
|
context = requireContext();
|
||||||
|
|
||||||
|
loginViewModel = new ViewModelProvider(requireActivity()).get(LoginViewModel.class);
|
||||||
|
systemRepository = new SystemRepository(App.getInstance());
|
||||||
|
|
||||||
|
bind = DialogServerSignupBinding.inflate(LayoutInflater.from(requireContext()));
|
||||||
|
|
||||||
|
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||||
|
|
||||||
|
builder.setView(bind.getRoot())
|
||||||
|
.setTitle("Add server")
|
||||||
|
.setPositiveButton("Enter", (dialog, id) -> { })
|
||||||
|
.setNegativeButton("Cancel", (dialog, id) -> dialog.cancel());
|
||||||
|
|
||||||
|
return builder.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStart() {
|
||||||
|
super.onStart();
|
||||||
|
((AlertDialog) Objects.requireNonNull(getDialog())).getButton(AlertDialog.BUTTON_POSITIVE).setTextColor(getResources().getColor(R.color.colorAccent, null));
|
||||||
|
((AlertDialog) Objects.requireNonNull(getDialog())).getButton(AlertDialog.BUTTON_NEGATIVE).setTextColor(getResources().getColor(R.color.colorAccent, null));
|
||||||
|
|
||||||
|
((AlertDialog) Objects.requireNonNull(getDialog())).getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(v -> {
|
||||||
|
if (validateInput()) {
|
||||||
|
saveServerPreference(server, username, password, null, null);
|
||||||
|
authenticate();
|
||||||
|
((AlertDialog) Objects.requireNonNull(getDialog())).dismiss();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean validateInput() {
|
||||||
|
serverName = bind.serverNameTextView.getText().toString().trim();
|
||||||
|
username = bind.usernameTextView.getText().toString().trim();
|
||||||
|
password = bind.passwordTextView.getText().toString();
|
||||||
|
server = bind.serverTextView.getText().toString().trim();
|
||||||
|
|
||||||
|
if (TextUtils.isEmpty(serverName)) {
|
||||||
|
bind.serverNameTextView.setError("Required");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TextUtils.isEmpty(username)) {
|
||||||
|
bind.usernameTextView.setError("Required");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TextUtils.isEmpty(server)) {
|
||||||
|
bind.serverTextView.setError("Required");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void authenticate() {
|
||||||
|
systemRepository.checkUserCredential(new SystemCallback() {
|
||||||
|
@Override
|
||||||
|
public void onError(Exception exception) {
|
||||||
|
Log.e(TAG, exception.getMessage());
|
||||||
|
Toast.makeText(requireContext(), exception.getMessage(), Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSuccess(String token, String salt) {
|
||||||
|
saveServerPreference(null, null, null, token, salt);
|
||||||
|
enter();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void enter() {
|
||||||
|
activity.goFromLogin();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveServerPreference(String server, String user, String password, String token, String salt) {
|
||||||
|
if (user != null) PreferenceUtil.getInstance(context).setUser(user);
|
||||||
|
if (server != null) PreferenceUtil.getInstance(context).setServer(server);
|
||||||
|
if (password != null) PreferenceUtil.getInstance(context).setPassword(password);
|
||||||
|
|
||||||
|
if (token != null && salt != null) {
|
||||||
|
String serverID = UUID.randomUUID().toString();
|
||||||
|
|
||||||
|
PreferenceUtil.getInstance(context).setPassword(null);
|
||||||
|
PreferenceUtil.getInstance(context).setToken(token);
|
||||||
|
PreferenceUtil.getInstance(context).setSalt(salt);
|
||||||
|
PreferenceUtil.getInstance(context).setServerId(serverID);
|
||||||
|
|
||||||
|
loginViewModel.addServer(new Server(serverID, this.serverName, this.username, this.server, token, salt, System.currentTimeMillis()));
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
App.getSubsonicClientInstance(requireContext(), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -15,6 +15,7 @@ public class PreferenceUtil {
|
||||||
public static final String PASSWORD = "password";
|
public static final String PASSWORD = "password";
|
||||||
public static final String TOKEN = "token";
|
public static final String TOKEN = "token";
|
||||||
public static final String SALT = "salt";
|
public static final String SALT = "salt";
|
||||||
|
public static final String SERVER_ID = "server_id";
|
||||||
public static final String POSITION = "position";
|
public static final String POSITION = "position";
|
||||||
public static final String PROGRESS = "progress";
|
public static final String PROGRESS = "progress";
|
||||||
public static final String IMAGE_CACHE_SIZE = "image_cache_size";
|
public static final String IMAGE_CACHE_SIZE = "image_cache_size";
|
||||||
|
|
@ -89,6 +90,16 @@ public class PreferenceUtil {
|
||||||
editor.apply();
|
editor.apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getServerId() {
|
||||||
|
return mPreferences.getString(SERVER_ID, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setServerId(String serverId) {
|
||||||
|
final SharedPreferences.Editor editor = mPreferences.edit();
|
||||||
|
editor.putString(SERVER_ID, serverId);
|
||||||
|
editor.apply();
|
||||||
|
}
|
||||||
|
|
||||||
public int getPosition() {
|
public int getPosition() {
|
||||||
return mPreferences.getInt(POSITION, -1);
|
return mPreferences.getInt(POSITION, -1);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,12 +9,12 @@ import java.util.List;
|
||||||
public class QueueUtil {
|
public class QueueUtil {
|
||||||
private static final String TAG = "QueueUtil";
|
private static final String TAG = "QueueUtil";
|
||||||
|
|
||||||
public static List<Queue> getQueueElementsFromSongs(List<Song> songs) {
|
public static List<Queue> getQueueElementsFromSongs(List<Song> songs, String serverID) {
|
||||||
int counter = 0;
|
int counter = 0;
|
||||||
List<Queue> queue = new ArrayList<>();
|
List<Queue> queue = new ArrayList<>();
|
||||||
|
|
||||||
for (Song song : songs) {
|
for (Song song : songs) {
|
||||||
queue.add(new Queue(counter, song.getId(), song.getTitle(), song.getAlbumId(), song.getAlbumName(), song.getArtistId(), song.getArtistName(), song.getPrimary(), song.getDuration()));
|
queue.add(new Queue(counter, song.getId(), song.getTitle(), song.getAlbumId(), song.getAlbumName(), song.getArtistId(), song.getArtistName(), song.getPrimary(), song.getDuration(), serverID));
|
||||||
counter++;
|
counter++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
package com.cappielloantonio.play.viewmodel;
|
||||||
|
|
||||||
|
import android.app.Application;
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.lifecycle.AndroidViewModel;
|
||||||
|
import androidx.lifecycle.LiveData;
|
||||||
|
import androidx.lifecycle.MutableLiveData;
|
||||||
|
|
||||||
|
import com.cappielloantonio.play.App;
|
||||||
|
import com.cappielloantonio.play.interfaces.MediaCallback;
|
||||||
|
import com.cappielloantonio.play.model.Album;
|
||||||
|
import com.cappielloantonio.play.model.Server;
|
||||||
|
import com.cappielloantonio.play.repository.AlbumRepository;
|
||||||
|
import com.cappielloantonio.play.repository.ServerRepository;
|
||||||
|
import com.cappielloantonio.play.subsonic.models.AlbumID3;
|
||||||
|
import com.cappielloantonio.play.subsonic.models.ResponseStatus;
|
||||||
|
import com.cappielloantonio.play.subsonic.models.SubsonicResponse;
|
||||||
|
import com.cappielloantonio.play.util.MappingUtil;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import retrofit2.Call;
|
||||||
|
import retrofit2.Callback;
|
||||||
|
|
||||||
|
public class LoginViewModel extends AndroidViewModel {
|
||||||
|
private ServerRepository serverRepository;
|
||||||
|
|
||||||
|
public LoginViewModel(@NonNull Application application) {
|
||||||
|
super(application);
|
||||||
|
|
||||||
|
serverRepository = new ServerRepository(application);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LiveData<List<Server>> getServerList() {
|
||||||
|
return serverRepository.getLiveServer();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addServer(Server server) {
|
||||||
|
serverRepository.insert(server);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteServer(Server server) {
|
||||||
|
serverRepository.delete(server);
|
||||||
|
}
|
||||||
|
}
|
||||||
9
app/src/main/res/drawable-v24/ic_add.xml
Normal file
9
app/src/main/res/drawable-v24/ic_add.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
<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="@color/titleTextColor"
|
||||||
|
android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
|
||||||
|
</vector>
|
||||||
97
app/src/main/res/layout/dialog_server_signup.xml
Normal file
97
app/src/main/res/layout/dialog_server_signup.xml
Normal file
|
|
@ -0,0 +1,97 @@
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="24dp"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:layout_marginEnd="24dp"
|
||||||
|
android:textColorHint="?android:textColorHint"
|
||||||
|
app:endIconMode="clear_text"
|
||||||
|
app:endIconTint="?android:textColorSecondary"
|
||||||
|
app:errorEnabled="true"
|
||||||
|
app:hintTextColor="?android:textColorSecondary">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/server_name_text_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="Server Name"
|
||||||
|
android:inputType="textNoSuggestions"
|
||||||
|
android:text="Navidrome" />
|
||||||
|
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="24dp"
|
||||||
|
android:layout_marginEnd="24dp"
|
||||||
|
android:textColorHint="?android:textColorHint"
|
||||||
|
app:endIconMode="clear_text"
|
||||||
|
app:endIconTint="?android:textColorSecondary"
|
||||||
|
app:errorEnabled="true"
|
||||||
|
app:hintTextColor="?android:textColorSecondary">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/username_text_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="Username"
|
||||||
|
android:inputType="textShortMessage"
|
||||||
|
android:text="antonio" />
|
||||||
|
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="24dp"
|
||||||
|
android:layout_marginEnd="24dp"
|
||||||
|
android:textColorHint="?android:textColorHint"
|
||||||
|
app:endIconMode="password_toggle"
|
||||||
|
app:endIconTint="?android:textColorSecondary"
|
||||||
|
app:errorEnabled="true"
|
||||||
|
app:hintTextColor="?android:textColorSecondary">
|
||||||
|
|
||||||
|
<!-- Navidrome android:text="!$4EBXhSPUi4$E#oagvAisCA" -->
|
||||||
|
<!-- Gonic android:text="hYP3%yD!rx@GBf95B2wbRUk8" -->
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/password_text_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="Password"
|
||||||
|
android:inputType="textPassword"
|
||||||
|
android:text="!$4EBXhSPUi4$E#oagvAisCA" />
|
||||||
|
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="24dp"
|
||||||
|
android:layout_marginEnd="24dp"
|
||||||
|
android:textColorHint="?android:textColorHint"
|
||||||
|
app:endIconMode="clear_text"
|
||||||
|
app:endIconTint="?android:textColorSecondary"
|
||||||
|
app:errorEnabled="true"
|
||||||
|
app:hintTextColor="?android:textColorSecondary">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/server_text_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="Server URL"
|
||||||
|
android:inputType="textNoSuggestions"
|
||||||
|
android:text="http://192.168.1.81:4533" />
|
||||||
|
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
|
@ -1,104 +1,70 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<LinearLayout 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"
|
||||||
android:id="@+id/login_screen"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.Toolbar
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
app:layout_collapseMode="pin" />
|
||||||
|
|
||||||
|
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:paddingTop="8dp">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
|
android:id="@+id/app_bar_layout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:elevation="0dp">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:id="@+id/server_info_sector"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="172dp"
|
||||||
|
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
style="@style/HeadlineTextView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingStart="16dp"
|
||||||
|
android:paddingTop="16dp"
|
||||||
|
android:paddingEnd="16dp"
|
||||||
|
android:paddingBottom="24dp"
|
||||||
|
android:text="Subsonic servers"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toEndOf="parent" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/no_server_added_text_view"
|
||||||
|
style="@style/SubheadTextView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingStart="16dp"
|
||||||
|
android:paddingTop="16dp"
|
||||||
|
android:paddingEnd="16dp"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:orientation="vertical"
|
android:text="No server added"
|
||||||
android:paddingTop="20dp">
|
android:clipToPadding="false"
|
||||||
|
android:paddingBottom="@dimen/global_padding_bottom"
|
||||||
|
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||||
|
android:visibility="gone"/>
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
|
android:id="@+id/server_list_recycler_view"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="32dp"
|
android:clipToPadding="false"
|
||||||
android:layout_marginEnd="32dp"
|
android:paddingBottom="@dimen/global_padding_bottom"
|
||||||
android:textColorHint="?android:textColorHint"
|
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||||
app:boxCornerRadiusBottomEnd="24dp"
|
android:visibility="gone"/>
|
||||||
app:boxCornerRadiusBottomStart="24dp"
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
app:boxCornerRadiusTopEnd="24dp"
|
|
||||||
app:boxCornerRadiusTopStart="24dp"
|
|
||||||
app:boxStrokeColor="?android:textColorSecondary"
|
|
||||||
app:endIconMode="clear_text"
|
|
||||||
app:endIconTint="?android:textColorSecondary"
|
|
||||||
app:errorEnabled="true"
|
|
||||||
app:hintTextColor="?android:textColorSecondary">
|
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputEditText
|
|
||||||
android:id="@+id/username_text_view"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:hint="Username"
|
|
||||||
android:inputType="textShortMessage"
|
|
||||||
android:text="antonio" />
|
|
||||||
|
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
|
||||||
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="32dp"
|
|
||||||
android:layout_marginEnd="32dp"
|
|
||||||
android:textColorHint="?android:textColorHint"
|
|
||||||
app:boxCornerRadiusBottomEnd="24dp"
|
|
||||||
app:boxCornerRadiusBottomStart="24dp"
|
|
||||||
app:boxCornerRadiusTopEnd="24dp"
|
|
||||||
app:boxCornerRadiusTopStart="24dp"
|
|
||||||
app:boxStrokeColor="?android:textColorSecondary"
|
|
||||||
app:endIconMode="password_toggle"
|
|
||||||
app:endIconTint="?android:textColorSecondary"
|
|
||||||
app:errorEnabled="true"
|
|
||||||
app:hintTextColor="?android:textColorSecondary">
|
|
||||||
|
|
||||||
<!-- Navidrome android:text="!$4EBXhSPUi4$E#oagvAisCA" -->
|
|
||||||
<!-- Gonic android:text="hYP3%yD!rx@GBf95B2wbRUk8" -->
|
|
||||||
<com.google.android.material.textfield.TextInputEditText
|
|
||||||
android:id="@+id/password_text_view"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:hint="Password"
|
|
||||||
android:inputType="textPassword"
|
|
||||||
android:text="!$4EBXhSPUi4$E#oagvAisCA"/>
|
|
||||||
|
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
|
||||||
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="32dp"
|
|
||||||
android:layout_marginEnd="32dp"
|
|
||||||
android:textColorHint="?android:textColorHint"
|
|
||||||
app:boxCornerRadiusBottomEnd="24dp"
|
|
||||||
app:boxCornerRadiusBottomStart="24dp"
|
|
||||||
app:boxCornerRadiusTopEnd="24dp"
|
|
||||||
app:boxCornerRadiusTopStart="24dp"
|
|
||||||
app:boxStrokeColor="?android:textColorSecondary"
|
|
||||||
app:endIconMode="clear_text"
|
|
||||||
app:endIconTint="?android:textColorSecondary"
|
|
||||||
app:errorEnabled="true"
|
|
||||||
app:hintTextColor="?android:textColorSecondary">
|
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputEditText
|
|
||||||
android:id="@+id/server_text_view"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:hint="Server URL"
|
|
||||||
android:inputType="textNoSuggestions"
|
|
||||||
android:text="http://192.168.1.81:4533" />
|
|
||||||
|
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
|
||||||
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/login_button"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="64dp"
|
|
||||||
android:layout_margin="32dp"
|
|
||||||
android:backgroundTint="@color/colorAccent"
|
|
||||||
android:text="Login"
|
|
||||||
android:textAllCaps="false"
|
|
||||||
app:cornerRadius="24dp" />
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
59
app/src/main/res/layout/item_login_server.xml
Normal file
59
app/src/main/res/layout/item_login_server.xml
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@color/cardColor"
|
||||||
|
android:clipChildren="false"
|
||||||
|
android:foreground="?attr/selectableItemBackground">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/server_name_text_view"
|
||||||
|
style="@style/ItemTitleTextView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:ellipsize="marquee"
|
||||||
|
android:focusable="true"
|
||||||
|
android:focusableInTouchMode="true"
|
||||||
|
android:marqueeRepeatLimit="marquee_forever"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:paddingStart="20dp"
|
||||||
|
android:paddingTop="12dp"
|
||||||
|
android:paddingBottom="4dp"
|
||||||
|
android:paddingEnd="12dp"
|
||||||
|
android:scrollHorizontally="true"
|
||||||
|
android:text="@string/label_placeholder"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/server_holder_image"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/server_address_text_view"
|
||||||
|
style="@style/ItemSubtitleTextView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:ellipsize="marquee"
|
||||||
|
android:focusable="true"
|
||||||
|
android:focusableInTouchMode="true"
|
||||||
|
android:marqueeRepeatLimit="marquee_forever"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:paddingStart="20dp"
|
||||||
|
android:paddingBottom="12dp"
|
||||||
|
android:paddingEnd="12dp"
|
||||||
|
android:text="@string/label_placeholder"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/server_holder_image"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/server_name_text_view" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/server_holder_image"
|
||||||
|
android:layout_width="36dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:layout_marginEnd="20dp"
|
||||||
|
android:src="@drawable/ic_drag_handle"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
10
app/src/main/res/menu/login_page_menu.xml
Normal file
10
app/src/main/res/menu/login_page_menu.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_add"
|
||||||
|
android:icon="@drawable/ic_add"
|
||||||
|
android:iconTint="@color/titleTextColor"
|
||||||
|
android:title="Add"
|
||||||
|
app:showAsAction="always" />
|
||||||
|
</menu>
|
||||||
|
|
@ -93,6 +93,11 @@
|
||||||
android:name="com.cappielloantonio.play.ui.fragment.SettingsFragment"
|
android:name="com.cappielloantonio.play.ui.fragment.SettingsFragment"
|
||||||
android:label="SettingsFragment"
|
android:label="SettingsFragment"
|
||||||
tools:layout="@layout/fragment_settings">
|
tools:layout="@layout/fragment_settings">
|
||||||
|
<action
|
||||||
|
android:id="@+id/action_settingsFragment_to_loginFragment"
|
||||||
|
app:destination="@id/loginFragment"
|
||||||
|
app:popUpTo="@id/homeFragment"
|
||||||
|
app:popUpToInclusive="true" />
|
||||||
</fragment>
|
</fragment>
|
||||||
<fragment
|
<fragment
|
||||||
android:id="@+id/searchFragment"
|
android:id="@+id/searchFragment"
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,10 @@
|
||||||
app:title="@string/theme_selection"
|
app:title="@string/theme_selection"
|
||||||
app:useSimpleSummaryProvider="true" />
|
app:useSimpleSummaryProvider="true" />
|
||||||
|
|
||||||
|
<Preference
|
||||||
|
android:key="logout"
|
||||||
|
android:title="Log out" />
|
||||||
|
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
|
|
||||||
<PreferenceCategory app:title="@string/about_header">
|
<PreferenceCategory app:title="@string/about_header">
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue