1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-12-25 01:55:50 +01:00

Huawei: Added selecting for more than one item for music management

This commit is contained in:
Me7c7 2024-12-01 11:17:45 +02:00 committed by José Rebelo
parent 16a8efb7ee
commit 7edb1f5941
6 changed files with 242 additions and 55 deletions

View File

@ -31,6 +31,7 @@ import androidx.annotation.NonNull;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.appbar.MaterialToolbar;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
@ -80,6 +81,12 @@ public class MusicManagerActivity extends AbstractGBActivity {
private FloatingActionButton fabMusicUpload;
private FloatingActionButton fabMusicPlaylistAdd;
private MaterialToolbar bottomToolbar;
private TextView selectionInfo;
private ImageButton selectionAddPlaylist;
private ImageButton selectionDelete;
private int maxMusicCount = 0;
private int maxPlaylistCount = 0;
@ -134,6 +141,14 @@ public class MusicManagerActivity extends AbstractGBActivity {
hideActionButtons();
bottomToolbar = findViewById(R.id.bottom_toolbar);
ImageButton cancelSelection = findViewById(R.id.music_cancel_selection);
selectionInfo = findViewById(R.id.music_selection_info);
selectionAddPlaylist = findViewById(R.id.music_multiselect_add_to_playlist);
selectionDelete = findViewById(R.id.music_multiselect_delete);
RecyclerView musicListView = findViewById(R.id.music_songs_list);
loadingView = findViewById(R.id.music_loading);
@ -153,20 +168,56 @@ public class MusicManagerActivity extends AbstractGBActivity {
musicAdapter = new MusicListAdapter(
musicList,
new MusicListAdapter.onItemAction() {
new MusicListAdapter.onDataAction() {
@Override
public void onItemClick(View view, GBDeviceMusic music) {
openPopupMenu(view, music);
}
@Override
public boolean onItemLongClick(View view, GBDeviceMusic music) {
return false;
public void onMultiSelect(int count, boolean limit) {
multiSelect(count, limit);
}
}
);
musicListView.setAdapter(musicAdapter);
cancelSelection.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
musicAdapter.clearSelectedItems();
}
});
selectionAddPlaylist.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
List<GBDeviceMusic> musics = musicAdapter.getSelectedItems();
if(!musics.isEmpty()) {
ArrayList<Integer> list = new ArrayList<>();
for(GBDeviceMusic music: musics) {
list.add(music.getId());
}
addMusicSongToPlaylist(list);
}
}
});
selectionDelete.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
List<GBDeviceMusic> musics = musicAdapter.getSelectedItems();
if(!musics.isEmpty()) {
ArrayList<Integer> list = new ArrayList<>();
for(GBDeviceMusic music: musics) {
list.add(music.getId());
}
deleteMusicMultipleConfirm(list);
}
}
});
playlistSpinnerLayout = findViewById(R.id.music_playlists_layout);
playlistsSpinner = findViewById(R.id.music_playlists);
@ -194,6 +245,7 @@ public class MusicManagerActivity extends AbstractGBActivity {
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
GBDeviceMusicPlaylist item = (GBDeviceMusicPlaylist) adapterView.getItemAtPosition(i);
musicAdapter.clearSelectedItems();
if (item.getId() == 0) {
deletePlaylist.setVisibility(View.GONE);
renamePlaylist.setVisibility(View.GONE);
@ -218,6 +270,25 @@ public class MusicManagerActivity extends AbstractGBActivity {
playlistsSpinner.setAdapter(playlistAdapter);
}
private void multiSelect(int count, boolean limit) {
if(limit) {
GB.toast(this, R.string.music_multiselect_limit, Toast.LENGTH_LONG, GB.WARN);
}
if(count > 0) {
hideActionButtons();
bottomToolbar.setVisibility(View.VISIBLE);
selectionInfo.setText("" + count);
if(playlists.size() <= 1) {
selectionAddPlaylist.setVisibility(View.GONE);
} else {
selectionAddPlaylist.setVisibility(View.VISIBLE);
}
} else {
bottomToolbar.setVisibility(View.GONE);
showActionButtons();
}
}
private void hideActionButtons() {
fabMusicUpload.hide();
fabMusicPlaylistAdd.hide();
@ -246,6 +317,7 @@ public class MusicManagerActivity extends AbstractGBActivity {
private void stopLoading() {
loadingTimeout.removeCallbacks(loadingRunnable);
loadingView.setVisibility(View.GONE);
musicAdapter.clearSelectedItems();
showActionButtons();
}
@ -311,7 +383,6 @@ public class MusicManagerActivity extends AbstractGBActivity {
}
GBDeviceMusicPlaylist current = (GBDeviceMusicPlaylist) playlistsSpinner.getSelectedItem();
musicList.clear();
if (current.getId() == 0) {
menu.removeItem(R.id.musicmanager_delete_from_playlist);
} else {
@ -335,19 +406,36 @@ public class MusicManagerActivity extends AbstractGBActivity {
deleteMusicConfirm(music);
return true;
} else if (itemId == R.id.musicmanager_add_to_playlist) {
addMusicSongToPlaylist(music);
ArrayList<Integer> list = new ArrayList<>();
list.add(music.getId());
addMusicSongToPlaylist(list);
return true;
}
return false;
}
private void deleteMusicConfirm(final GBDeviceMusic music) {
ArrayList<Integer> list = new ArrayList<>();
list.add(music.getId());
new MaterialAlertDialogBuilder(this)
.setTitle(R.string.Delete)
.setMessage(this.getString(R.string.music_delete_confirm_description, music.getTitle()))
.setIcon(R.drawable.ic_warning)
.setPositiveButton(android.R.string.yes, (dialog, whichButton) -> {
deleteMusicFromDevice((GBDeviceMusicPlaylist) playlistsSpinner.getSelectedItem(), music);
deleteMusicFromDevice((GBDeviceMusicPlaylist) playlistsSpinner.getSelectedItem(), list);
})
.setNegativeButton(android.R.string.no, null)
.show();
}
private void deleteMusicMultipleConfirm(final ArrayList<Integer> list) {
new MaterialAlertDialogBuilder(this)
.setTitle(R.string.Delete)
.setMessage(this.getString(R.string.music_delete_multiple_confirm_description, list.size()))
.setIcon(R.drawable.ic_warning)
.setPositiveButton(android.R.string.yes, (dialog, whichButton) -> {
deleteMusicFromDevice((GBDeviceMusicPlaylist) playlistsSpinner.getSelectedItem(), list);
})
.setNegativeButton(android.R.string.no, null)
.show();
@ -368,17 +456,13 @@ public class MusicManagerActivity extends AbstractGBActivity {
GBApplication.deviceService(mGBDevice).onMusicOperation(2, playlist.getId(), newPlaylistName, null);
}
private void addMusicToDevicePlaylist(GBDeviceMusicPlaylist playlist, final GBDeviceMusic music) {
private void addMusicToDevicePlaylist(GBDeviceMusicPlaylist playlist, final ArrayList<Integer> list) {
startLoading();
ArrayList<Integer> list = new ArrayList<>();
list.add(music.getId());
GBApplication.deviceService(mGBDevice).onMusicOperation(3, playlist.getId(), null, list);
}
private void deleteMusicFromDevice(GBDeviceMusicPlaylist playlist, final GBDeviceMusic music) {
private void deleteMusicFromDevice(GBDeviceMusicPlaylist playlist, final ArrayList<Integer> list) {
startLoading();
ArrayList<Integer> list = new ArrayList<>();
list.add(music.getId());
GBApplication.deviceService(mGBDevice).onMusicOperation(4, playlist.getId(), null, list);
}
@ -387,7 +471,7 @@ public class MusicManagerActivity extends AbstractGBActivity {
input.setInputType(InputType.TYPE_CLASS_TEXT);
FrameLayout container = new FrameLayout(this);
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
params.leftMargin = getResources().getDimensionPixelSize(R.dimen.dialog_margin);
params.rightMargin = getResources().getDimensionPixelSize(R.dimen.dialog_margin);
input.setLayoutParams(params);
@ -398,7 +482,9 @@ public class MusicManagerActivity extends AbstractGBActivity {
.setView(container)
.setPositiveButton(android.R.string.ok, (dialog, whichButton) -> {
String playlistName = input.getText().toString();
addPlaylistToDevice(playlistName);
if(!playlistName.isEmpty()) {
addPlaylistToDevice(playlistName);
}
})
.setNegativeButton(android.R.string.cancel, null)
.show();
@ -423,7 +509,9 @@ public class MusicManagerActivity extends AbstractGBActivity {
.setView(container)
.setPositiveButton(android.R.string.ok, (dialog, whichButton) -> {
String playlistName = input.getText().toString();
renamePlaylistOnDevice(playlist, playlistName);
if(!playlistName.isEmpty()) {
renamePlaylistOnDevice(playlist, playlistName);
}
})
.setNegativeButton(android.R.string.cancel, null)
.show();
@ -443,7 +531,7 @@ public class MusicManagerActivity extends AbstractGBActivity {
.show();
}
private void addMusicSongToPlaylist(final GBDeviceMusic music) {
private void addMusicSongToPlaylist(final ArrayList<Integer> list) {
final Spinner dPlaylists = new Spinner(this);
List<GBDeviceMusicPlaylist> dialogPlaylists = new ArrayList<>();
@ -469,7 +557,7 @@ public class MusicManagerActivity extends AbstractGBActivity {
.setView(container)
.setPositiveButton(android.R.string.ok, (dialog, whichButton) -> {
GBDeviceMusicPlaylist playlist = (GBDeviceMusicPlaylist) dPlaylists.getSelectedItem();
addMusicToDevicePlaylist(playlist, music);
addMusicToDevicePlaylist(playlist, list);
})
.setNegativeButton(android.R.string.cancel, null)
.show();
@ -513,7 +601,7 @@ public class MusicManagerActivity extends AbstractGBActivity {
int playlistIndex = intent.getIntExtra("playlistIndex", -1);
String playlistName = intent.getStringExtra("playlistName");
if (playlistIndex != -1 && !TextUtils.isEmpty(playlistName)) {
if (playlistIndex != -1 && playlistName != null) {
playlists.add(new GBDeviceMusicPlaylist(playlistIndex, playlistName, new ArrayList<>()));
playlistAdapter.notifyDataSetChanged();
}
@ -531,7 +619,7 @@ public class MusicManagerActivity extends AbstractGBActivity {
} else if (operation == 2) {
int playlistIndex = intent.getIntExtra("playlistIndex", -1);
String playlistName = intent.getStringExtra("playlistName");
if (playlistIndex != -1 && !TextUtils.isEmpty(playlistName)) {
if (playlistIndex != -1 && playlistName != null) {
for (GBDeviceMusicPlaylist playlist : playlists) {
if (playlist.getId() == playlistIndex) {
playlist.setName(playlistName);

View File

@ -3,13 +3,11 @@ package nodomain.freeyourgadget.gadgetbridge.adapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.R;
@ -17,15 +15,18 @@ import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceMusic;
public class MusicListAdapter extends RecyclerView.Adapter<MusicListAdapter.MusicViewHolder> {
public interface onItemAction {
public static int MAX_MULTISELECT_COUNT = 50;
public interface onDataAction {
void onItemClick(View view, GBDeviceMusic music);
boolean onItemLongClick(View view, GBDeviceMusic music);
void onMultiSelect(int count, boolean limit);
}
private final List<GBDeviceMusic> musicList;
private final onItemAction callback;
private final onDataAction callback;
public MusicListAdapter(List<GBDeviceMusic> list, onItemAction callback) {
private final List<GBDeviceMusic> selectedItems = new ArrayList<>();
public MusicListAdapter(List<GBDeviceMusic> list, onDataAction callback) {
this.musicList = list;
this.callback = callback;
}
@ -46,6 +47,20 @@ public class MusicListAdapter extends RecyclerView.Adapter<MusicListAdapter.Musi
return new MusicViewHolder(view);
}
private void toggleSelection(GBDeviceMusic music) {
if(selectedItems.size() >= MAX_MULTISELECT_COUNT) {
callback.onMultiSelect(selectedItems.size(), true);
return;
}
if(selectedItems.contains(music)) {
selectedItems.remove(music);
} else {
selectedItems.add(music);
}
notifyItemChanged(musicList.indexOf(music));
callback.onMultiSelect(selectedItems.size(), false);
}
@Override
public void onBindViewHolder(final MusicListAdapter.MusicViewHolder holder, int position) {
final GBDeviceMusic music = musicList.get(position);
@ -53,32 +68,50 @@ public class MusicListAdapter extends RecyclerView.Adapter<MusicListAdapter.Musi
holder.musicTitle.setText(music.getTitle());
holder.musicArtist.setText(music.getArtist());
holder.icon.setSelected(selectedItems.contains(music));
if(callback != null) {
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
callback.onItemClick(view, music);
if(!selectedItems.isEmpty()) {
toggleSelection(music);
} else {
callback.onItemClick(view, music);
}
}
});
holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View view) {
return callback.onItemLongClick(view, music);
toggleSelection(music);
return true;
}
});
}
}
public void clearSelectedItems() {
selectedItems.clear();
notifyDataSetChanged();
callback.onMultiSelect(selectedItems.size(), false);
}
public List<GBDeviceMusic> getSelectedItems() {
return selectedItems;
}
public static class MusicViewHolder extends RecyclerView.ViewHolder {
final TextView musicArtist;
final TextView musicTitle;
final ImageView icon;
MusicViewHolder(View itemView) {
super(itemView);
musicArtist = itemView.findViewById(R.id.item_details);
musicTitle = itemView.findViewById(R.id.item_name);
icon = itemView.findViewById(R.id.item_image);
}
}

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true" android:drawable="@drawable/ic_edit" />
<item android:drawable="@drawable/ic_music" />
</selector>

View File

@ -21,27 +21,29 @@
android:layout_below="@id/music_device_info"
android:orientation="horizontal">
<Spinner
android:id="@+id/music_playlists"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:layout_weight="20" />
<ImageButton
android:id="@+id/music_playlist_rename"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_weight="1"
android:background="@null"
android:contentDescription="@string/music_rename_playlist"
app:srcCompat="@drawable/ic_edit"/>
<ImageButton
android:id="@+id/music_playlist_delete"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_weight="1"
android:background="@null"
android:contentDescription="@string/music_delete"
app:srcCompat="@drawable/ic_delete" />
<Spinner
android:id="@+id/music_playlists"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:layout_weight="20" />
<ImageButton
android:id="@+id/music_playlist_rename"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_weight="1"
android:background="@null"
android:contentDescription="@string/music_rename_playlist"
app:srcCompat="@drawable/ic_edit" />
<ImageButton
android:id="@+id/music_playlist_delete"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_weight="1"
android:background="@null"
android:contentDescription="@string/music_delete"
app:srcCompat="@drawable/ic_delete" />
</LinearLayout>
@ -53,6 +55,7 @@
android:layout_centerHorizontal="true"
android:divider="@null" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab_music_upload"
android:layout_width="wrap_content"
@ -61,7 +64,6 @@
android:layout_alignParentBottom="true"
android:layout_gravity="bottom|end"
android:layout_margin="16dp"
android:contentDescription="@string/music_new_playlist"
app:srcCompat="@android:drawable/stat_sys_upload" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
@ -73,8 +75,65 @@
android:layout_gravity="bottom|end"
android:layout_marginEnd="16dp"
android:layout_marginBottom="90dp"
app:srcCompat="@drawable/ic_add"
/>
android:contentDescription="@string/music_new_playlist"
app:srcCompat="@drawable/ic_add" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/bottom_toolbar"
android:layout_width="match_parent"
android:layout_height="54dp"
android:layout_gravity="bottom"
android:background="@color/primary_dark">
<ImageButton
android:id="@+id/music_cancel_selection"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_gravity="start"
android:layout_weight="1"
android:background="@null"
android:contentDescription="@string/music_rename_playlist"
app:srcCompat="@drawable/ic_action_notify_cancel" />
<TextView
android:id="@+id/music_selection_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:layout_weight="1"
android:text="" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end">
<ImageButton
android:id="@+id/music_multiselect_add_to_playlist"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_toStartOf="@id/music_multiselect_delete"
android:layout_weight="1"
android:background="@null"
android:contentDescription="@string/music_add_to_playlist"
app:srcCompat="@drawable/ic_checklist" />
<ImageButton
android:id="@+id/music_multiselect_delete"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_weight="1"
android:background="@null"
android:contentDescription="@string/music_delete"
app:srcCompat="@drawable/ic_delete" />
</LinearLayout>
</com.google.android.material.appbar.MaterialToolbar>
</LinearLayout>
<RelativeLayout
android:id="@+id/music_loading"

View File

@ -26,7 +26,7 @@
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:contentDescription="@string/candidate_item_device_image"
android:src="@drawable/ic_music"/>
android:src="@drawable/ic_music_item"/>
<LinearLayout
android:layout_width="match_parent"

View File

@ -3462,6 +3462,7 @@
<string name="pref_music_management_title">Manage Music</string>
<string name="pref_music_management_summary">Manage music on the watch</string>
<string name="music_delete_confirm_description">Are you sure you want to delete \'%1$s\'?</string>
<string name="music_delete_multiple_confirm_description">Are you sure you want to delete \'%1$d\' songs?</string>
<string name="music_add_to_playlist">Add to playlist</string>
<string name="music_delete_from_playlist">Delete from playlist</string>
<string name="music_delete">Delete song</string>
@ -3470,6 +3471,7 @@
<string name="music_rename_playlist">Rename playlist</string>
<string name="music_error">Error occurred</string>
<string name="music_huawei_device_info">Supported formats: %1$s\nWatch storage: %2$d MB</string>
<string name="music_multiselect_limit">Limit for selecting more than one item reached</string>
<!-- Welcome screens strings -->
<string name="first_start_welcome_title">Welcome</string>