Revised the gestures of the server adapter

This commit is contained in:
CappielloAntonio 2021-08-09 17:55:30 +02:00
parent 642c41f0c0
commit d88094b0cf
11 changed files with 195 additions and 21 deletions

View file

@ -1,6 +1,7 @@
package com.cappielloantonio.play.adapter; package com.cappielloantonio.play.adapter;
import android.content.Context; import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@ -8,6 +9,7 @@ import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
import androidx.navigation.Navigation;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import com.cappielloantonio.play.App; import com.cappielloantonio.play.App;
@ -17,6 +19,7 @@ import com.cappielloantonio.play.model.Server;
import com.cappielloantonio.play.model.Song; import com.cappielloantonio.play.model.Song;
import com.cappielloantonio.play.repository.SystemRepository; import com.cappielloantonio.play.repository.SystemRepository;
import com.cappielloantonio.play.ui.activity.MainActivity; import com.cappielloantonio.play.ui.activity.MainActivity;
import com.cappielloantonio.play.ui.fragment.dialog.ServerSignupDialog;
import com.cappielloantonio.play.util.PreferenceUtil; import com.cappielloantonio.play.util.PreferenceUtil;
import java.util.ArrayList; import java.util.ArrayList;

View file

@ -15,7 +15,7 @@ import com.cappielloantonio.play.model.Queue;
import com.cappielloantonio.play.model.RecentSearch; import com.cappielloantonio.play.model.RecentSearch;
import com.cappielloantonio.play.model.Server; import com.cappielloantonio.play.model.Server;
@Database(entities = {Queue.class, Server.class, RecentSearch.class, Download.class}, version = 12, exportSchema = false) @Database(entities = {Queue.class, Server.class, RecentSearch.class, Download.class}, version = 13, 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";

View file

@ -20,6 +20,12 @@ public interface ServerDao {
@Insert(onConflict = OnConflictStrategy.REPLACE) @Insert(onConflict = OnConflictStrategy.REPLACE)
void insert(Server server); void insert(Server server);
@Insert(onConflict = OnConflictStrategy.REPLACE)
void insertAll(List<Server> servers);
@Delete @Delete
void delete(Server server); void delete(Server server);
@Query("DELETE FROM server")
void deleteAll();
} }

View file

@ -1,12 +1,15 @@
package com.cappielloantonio.play.model; package com.cappielloantonio.play.model;
import android.os.Parcel;
import android.os.Parcelable;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.room.ColumnInfo; import androidx.room.ColumnInfo;
import androidx.room.Entity; import androidx.room.Entity;
import androidx.room.PrimaryKey; import androidx.room.PrimaryKey;
@Entity(tableName = "server") @Entity(tableName = "server")
public class Server { public class Server implements Parcelable {
@NonNull @NonNull
@PrimaryKey @PrimaryKey
@ColumnInfo(name = "id") @ColumnInfo(name = "id")
@ -96,4 +99,30 @@ public class Server {
public void setTimestamp(long timestamp) { public void setTimestamp(long timestamp) {
this.timestamp = timestamp; this.timestamp = timestamp;
} }
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(serverId);
dest.writeString(serverName);
dest.writeString(username);
dest.writeString(address);
dest.writeString(token);
dest.writeString(salt);
dest.writeLong(timestamp);
}
protected Server(Parcel in) {
this.serverId = in.readString();
this.serverName = in.readString();
this.username = in.readString();
this.address = in.readString();
this.token = in.readString();
this.salt = in.readString();
this.timestamp = in.readLong();
}
} }

View file

@ -5,8 +5,11 @@ import android.app.Application;
import androidx.lifecycle.LiveData; import androidx.lifecycle.LiveData;
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.ServerDao; import com.cappielloantonio.play.database.dao.ServerDao;
import com.cappielloantonio.play.model.Server; import com.cappielloantonio.play.model.Server;
import com.cappielloantonio.play.model.Song;
import com.cappielloantonio.play.util.QueueUtil;
import java.util.List; import java.util.List;
@ -38,6 +41,20 @@ public class ServerRepository {
thread.start(); thread.start();
} }
public void order(List<Server> servers) {
try {
final Thread delete = new Thread(new DeleteAllThreadSafe(serverDao));
final Thread insertAll = new Thread(new InsertAllThreadSafe(serverDao, servers));
delete.start();
delete.join();
insertAll.start();
insertAll.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private static class InsertThreadSafe implements Runnable { private static class InsertThreadSafe implements Runnable {
private ServerDao serverDao; private ServerDao serverDao;
private Server server; private Server server;
@ -67,4 +84,32 @@ public class ServerRepository {
serverDao.delete(server); serverDao.delete(server);
} }
} }
private static class InsertAllThreadSafe implements Runnable {
private ServerDao serverDao;
private List<Server> servers;
public InsertAllThreadSafe(ServerDao queueDao, List<Server> servers) {
this.serverDao = queueDao;
this.servers = servers;
}
@Override
public void run() {
serverDao.insertAll(servers);
}
}
private static class DeleteAllThreadSafe implements Runnable {
private ServerDao serverDao;
public DeleteAllThreadSafe(ServerDao serverDao) {
this.serverDao = serverDao;
}
@Override
public void run() {
serverDao.deleteAll();
}
}
} }

View file

@ -25,6 +25,7 @@ public class SystemRepository {
.enqueue(new Callback<SubsonicResponse>() { .enqueue(new Callback<SubsonicResponse>() {
@Override @Override
public void onResponse(Call<SubsonicResponse> call, retrofit2.Response<SubsonicResponse> response) { public void onResponse(Call<SubsonicResponse> call, retrofit2.Response<SubsonicResponse> response) {
if(response.body() != null) {
if (response.body().getStatus().getValue().equals(ResponseStatus.FAILED)) { if (response.body().getStatus().getValue().equals(ResponseStatus.FAILED)) {
callback.onError(new Exception(response.body().getError().getCode().getValue() + " - " + response.body().getError().getMessage())); callback.onError(new Exception(response.body().getError().getCode().getValue() + " - " + response.body().getError().getMessage()));
} else if (response.body().getStatus().getValue().equals(ResponseStatus.OK)) { } else if (response.body().getStatus().getValue().equals(ResponseStatus.OK)) {
@ -35,6 +36,10 @@ public class SystemRepository {
callback.onError(new Exception("Empty response")); callback.onError(new Exception("Empty response"));
} }
} }
else {
callback.onError(new Exception(String.valueOf(response.code())));
}
}
@Override @Override
public void onFailure(Call<SubsonicResponse> call, Throwable t) { public void onFailure(Call<SubsonicResponse> call, Throwable t) {

View file

@ -84,7 +84,9 @@ public class Subsonic {
} }
public String getUrl() { public String getUrl() {
return preferences.getServerUrl() + "/rest/"; String url = preferences.getServerUrl() + "/rest/";
return url.replace("//rest","/rest");
} }
public Map<String, String> getParams() { public Map<String, String> getParams() {

View file

@ -20,7 +20,6 @@ import androidx.recyclerview.widget.RecyclerView;
import com.cappielloantonio.play.R; import com.cappielloantonio.play.R;
import com.cappielloantonio.play.adapter.ServerAdapter; import com.cappielloantonio.play.adapter.ServerAdapter;
import com.cappielloantonio.play.databinding.FragmentLoginBinding; import com.cappielloantonio.play.databinding.FragmentLoginBinding;
import com.cappielloantonio.play.service.MusicPlayerRemote;
import com.cappielloantonio.play.ui.activity.MainActivity; import com.cappielloantonio.play.ui.activity.MainActivity;
import com.cappielloantonio.play.ui.fragment.dialog.ServerSignupDialog; import com.cappielloantonio.play.ui.fragment.dialog.ServerSignupDialog;
import com.cappielloantonio.play.viewmodel.LoginViewModel; import com.cappielloantonio.play.viewmodel.LoginViewModel;
@ -36,6 +35,8 @@ public class LoginFragment extends Fragment {
private ServerAdapter serverAdapter; private ServerAdapter serverAdapter;
private ItemTouchHelper itemTouchHelper;
@Override @Override
public void onCreate(@Nullable Bundle savedInstanceState) { public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
@ -99,7 +100,7 @@ public class LoginFragment extends Fragment {
} }
}); });
new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN, ItemTouchHelper.LEFT) { new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
int originalPosition = -1; int originalPosition = -1;
int fromPosition = -1; int fromPosition = -1;
int toPosition = -1; int toPosition = -1;
@ -118,10 +119,34 @@ public class LoginFragment extends Fragment {
return false; return false;
} }
@Override
public void clearView(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
super.clearView(recyclerView, viewHolder);
loginViewModel.orderServer(serverAdapter.getItems());
originalPosition = -1;
fromPosition = -1;
toPosition = -1;
}
@Override @Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) { public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
switch (direction) {
case ItemTouchHelper.LEFT:
loginViewModel.deleteServer(serverAdapter.getItem(viewHolder.getBindingAdapterPosition())); loginViewModel.deleteServer(serverAdapter.getItem(viewHolder.getBindingAdapterPosition()));
bind.serverListRecyclerView.getAdapter().notifyItemRemoved(viewHolder.getBindingAdapterPosition()); break;
case ItemTouchHelper.RIGHT:
Bundle bundle = new Bundle();
bundle.putParcelable("server_object", serverAdapter.getItem(viewHolder.getBindingAdapterPosition()));
ServerSignupDialog dialog = new ServerSignupDialog();
dialog.setArguments(bundle);
dialog.show(activity.getSupportFragmentManager(), null);
bind.serverListRecyclerView.getAdapter().notifyDataSetChanged();
break;
}
} }
} }
).attachToRecyclerView(bind.serverListRecyclerView); ).attachToRecyclerView(bind.serverListRecyclerView);

View file

@ -39,6 +39,7 @@ public class ServerSignupDialog extends DialogFragment {
private String username; private String username;
private String password; private String password;
private String server; private String server;
private boolean directAccess = false;
@Override @Override
public Dialog onCreateDialog(Bundle savedInstanceState) { public Dialog onCreateDialog(Bundle savedInstanceState) {
@ -54,7 +55,8 @@ public class ServerSignupDialog extends DialogFragment {
builder.setView(bind.getRoot()) builder.setView(bind.getRoot())
.setTitle("Add server") .setTitle("Add server")
.setPositiveButton("Enter", (dialog, id) -> { }) .setNeutralButton("Delete", (dialog, id) -> { })
.setPositiveButton("Save", (dialog, id) -> { })
.setNegativeButton("Cancel", (dialog, id) -> dialog.cancel()); .setNegativeButton("Cancel", (dialog, id) -> dialog.cancel());
return builder.create(); return builder.create();
@ -63,6 +65,29 @@ public class ServerSignupDialog extends DialogFragment {
@Override @Override
public void onStart() { public void onStart() {
super.onStart(); super.onStart();
setServerInfo();
setButtonAction();
}
private void setServerInfo() {
if (getArguments() != null) {
loginViewModel.setServerToEdit(getArguments().getParcelable("server_object"));
if (loginViewModel.getServerToEdit() != null) {
bind.serverNameTextView.setText(loginViewModel.getServerToEdit().getServerName());
bind.usernameTextView.setText(loginViewModel.getServerToEdit().getUsername());
bind.passwordTextView.setText("");
bind.serverTextView.setText(loginViewModel.getServerToEdit().getAddress());
bind.directAccessCheckbox.setChecked(false);
}
} else {
loginViewModel.setServerToEdit(null);
}
}
private void setButtonAction() {
((AlertDialog) Objects.requireNonNull(getDialog())).getButton(AlertDialog.BUTTON_NEUTRAL).setTextColor(getResources().getColor(R.color.colorAccent, null));
((AlertDialog) Objects.requireNonNull(getDialog())).getButton(AlertDialog.BUTTON_POSITIVE).setTextColor(getResources().getColor(R.color.colorAccent, null)); ((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_NEGATIVE).setTextColor(getResources().getColor(R.color.colorAccent, null));
@ -70,9 +95,14 @@ public class ServerSignupDialog extends DialogFragment {
if (validateInput()) { if (validateInput()) {
saveServerPreference(server, username, password, null, null); saveServerPreference(server, username, password, null, null);
authenticate(); authenticate();
((AlertDialog) Objects.requireNonNull(getDialog())).dismiss(); Objects.requireNonNull(getDialog()).dismiss();
} }
}); });
((AlertDialog) Objects.requireNonNull(getDialog())).getButton(AlertDialog.BUTTON_NEUTRAL).setOnClickListener(v -> {
loginViewModel.deleteServer(null);
Objects.requireNonNull(getDialog()).dismiss();
});
} }
private boolean validateInput() { private boolean validateInput() {
@ -80,6 +110,7 @@ public class ServerSignupDialog extends DialogFragment {
username = bind.usernameTextView.getText().toString().trim(); username = bind.usernameTextView.getText().toString().trim();
password = bind.passwordTextView.getText().toString(); password = bind.passwordTextView.getText().toString();
server = bind.serverTextView.getText().toString().trim(); server = bind.serverTextView.getText().toString().trim();
directAccess = bind.directAccessCheckbox.isChecked();
if (TextUtils.isEmpty(serverName)) { if (TextUtils.isEmpty(serverName)) {
bind.serverNameTextView.setError("Required"); bind.serverNameTextView.setError("Required");
@ -110,7 +141,7 @@ public class ServerSignupDialog extends DialogFragment {
@Override @Override
public void onSuccess(String token, String salt) { public void onSuccess(String token, String salt) {
saveServerPreference(null, null, null, token, salt); saveServerPreference(null, null, null, token, salt);
enter(); if (directAccess) enter();
} }
}); });
} }
@ -125,7 +156,7 @@ public class ServerSignupDialog extends DialogFragment {
if (password != null) PreferenceUtil.getInstance(context).setPassword(password); if (password != null) PreferenceUtil.getInstance(context).setPassword(password);
if (token != null && salt != null) { if (token != null && salt != null) {
String serverID = UUID.randomUUID().toString(); String serverID = loginViewModel.getServerToEdit() != null ? loginViewModel.getServerToEdit().getServerId() : UUID.randomUUID().toString();
PreferenceUtil.getInstance(context).setPassword(null); PreferenceUtil.getInstance(context).setPassword(null);
PreferenceUtil.getInstance(context).setToken(token); PreferenceUtil.getInstance(context).setToken(token);

View file

@ -12,6 +12,7 @@ import com.cappielloantonio.play.App;
import com.cappielloantonio.play.interfaces.MediaCallback; import com.cappielloantonio.play.interfaces.MediaCallback;
import com.cappielloantonio.play.model.Album; import com.cappielloantonio.play.model.Album;
import com.cappielloantonio.play.model.Server; import com.cappielloantonio.play.model.Server;
import com.cappielloantonio.play.model.Song;
import com.cappielloantonio.play.repository.AlbumRepository; import com.cappielloantonio.play.repository.AlbumRepository;
import com.cappielloantonio.play.repository.ServerRepository; import com.cappielloantonio.play.repository.ServerRepository;
import com.cappielloantonio.play.subsonic.models.AlbumID3; import com.cappielloantonio.play.subsonic.models.AlbumID3;
@ -27,6 +28,7 @@ import retrofit2.Callback;
public class LoginViewModel extends AndroidViewModel { public class LoginViewModel extends AndroidViewModel {
private ServerRepository serverRepository; private ServerRepository serverRepository;
private Server toEdit = null;
public LoginViewModel(@NonNull Application application) { public LoginViewModel(@NonNull Application application) {
super(application); super(application);
@ -43,6 +45,23 @@ public class LoginViewModel extends AndroidViewModel {
} }
public void deleteServer(Server server) { public void deleteServer(Server server) {
if(server != null) {
serverRepository.delete(server); serverRepository.delete(server);
} }
else if(toEdit != null) {
serverRepository.delete(toEdit);
}
}
public void orderServer(List<Server> servers) {
serverRepository.order(servers);
}
public void setServerToEdit(Server server) {
toEdit = server;
}
public Server getServerToEdit() {
return toEdit;
}
} }

View file

@ -100,6 +100,15 @@
android:inputType="textNoSuggestions" android:inputType="textNoSuggestions"
android:text="http://192.168.1.81:4533" android:text="http://192.168.1.81:4533"
android:textCursorDrawable="@null"/> android:textCursorDrawable="@null"/>
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
<CheckBox
android:id="@+id/direct_access_checkbox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginTop="12dp"
android:layout_marginEnd="24dp"
android:text="Direct access"
android:onClick="onCheckboxClicked"/>
</LinearLayout> </LinearLayout>