From 747fcdc4294c9111730283098165e7c80167b259 Mon Sep 17 00:00:00 2001 From: sebastian Date: Sat, 2 Nov 2024 09:37:07 +0100 Subject: [PATCH] Select songs for Concert --- .idea/deploymentTargetDropDown.xml | 15 +-- app/build.gradle.kts | 3 +- .../java/core/notevault/MainActivity.java | 20 ++- .../java/core/notevault/data/ConcertSong.java | 55 +++++++++ .../core/notevault/data/MusicDatabase.java | 2 +- .../java/core/notevault/data/MusicNote.java | 8 ++ .../core/notevault/data/MusicNoteDAO.java | 11 ++ .../gallery/detail/ConcertDetailFragment.java | 17 +++ .../gallery/detail/ConcertSongSelector.java | 116 ++++++++++++++++++ .../detail/ConcertSongSelectorAdapter.java | 85 +++++++++++++ .../core/notevault/ui/home/HomeFragment.java | 5 +- .../layout/dialog_concert_song_selector.xml | 29 +++++ .../res/layout/fragment_concert_detail.xml | 81 +++++++----- app/src/main/res/layout/fragment_home.xml | 4 +- app/src/main/res/layout/item_concert_song.xml | 19 +++ gradle/libs.versions.toml | 2 + 16 files changed, 414 insertions(+), 58 deletions(-) create mode 100644 app/src/main/java/core/notevault/data/ConcertSong.java create mode 100644 app/src/main/java/core/notevault/ui/gallery/detail/ConcertSongSelector.java create mode 100644 app/src/main/java/core/notevault/ui/gallery/detail/ConcertSongSelectorAdapter.java create mode 100644 app/src/main/res/layout/dialog_concert_song_selector.xml create mode 100644 app/src/main/res/layout/item_concert_song.xml diff --git a/.idea/deploymentTargetDropDown.xml b/.idea/deploymentTargetDropDown.xml index 341e9f7..612ad88 100644 --- a/.idea/deploymentTargetDropDown.xml +++ b/.idea/deploymentTargetDropDown.xml @@ -6,20 +6,7 @@ - - - - - - - - - - - - - - + diff --git a/app/build.gradle.kts b/app/build.gradle.kts index eec3c30..338d995 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -9,7 +9,7 @@ android { defaultConfig { applicationId = "core.notevault" - minSdk = 26 + minSdk = 28 targetSdk = 34 versionCode = 1 versionName = "1.0" @@ -44,6 +44,7 @@ dependencies { implementation(libs.room.common) implementation("androidx.recyclerview:recyclerview:1.3.2") implementation("com.github.chrisbanes:PhotoView:2.3.0") + implementation(libs.car.ui.lib) testImplementation(libs.junit) androidTestImplementation(libs.ext.junit) androidTestImplementation(libs.espresso.core) diff --git a/app/src/main/java/core/notevault/MainActivity.java b/app/src/main/java/core/notevault/MainActivity.java index aae38b7..a936fa2 100644 --- a/app/src/main/java/core/notevault/MainActivity.java +++ b/app/src/main/java/core/notevault/MainActivity.java @@ -17,12 +17,10 @@ import androidx.navigation.ui.AppBarConfiguration; import androidx.navigation.ui.NavigationUI; import androidx.drawerlayout.widget.DrawerLayout; import androidx.appcompat.app.AppCompatActivity; -import core.notevault.data.Concert; -import core.notevault.data.MusicDatabase; -import core.notevault.data.MusicNote; -import core.notevault.data.NoteSheet; +import core.notevault.data.*; import core.notevault.databinding.ActivityMainBinding; import core.notevault.ui.gallery.GalleryFragment; +import core.notevault.ui.gallery.detail.ConcertSongSelector; import core.notevault.ui.gallery.editor.ConcertEditorDialog; import core.notevault.ui.home.HomeFragment; import core.notevault.ui.metadatadialog.MetaDataDialog; @@ -30,9 +28,12 @@ import core.notevault.util.NoteSheetsUtil; import java.io.*; import java.nio.file.Files; +import java.util.ArrayList; import java.util.Date; +import java.util.List; -public class MainActivity extends AppCompatActivity implements MetaDataDialog.OnMetadataListener, ConcertEditorDialog.OnConcertEditorListener { +public class MainActivity extends AppCompatActivity implements MetaDataDialog.OnMetadataListener, + ConcertEditorDialog.OnConcertEditorListener, ConcertSongSelector.OnSongSelectedListener { private AppBarConfiguration mAppBarConfiguration; private ActivityMainBinding binding; @@ -164,4 +165,13 @@ public class MainActivity extends AppCompatActivity implements MetaDataDialog.On }).start(); } + + @Override + public void onSongsSelected(List songs, int concertID) { + new Thread(() -> { + for(MusicNote musicNote : songs) { + ConcertSong concertSong = new ConcertSong(musicNote.getMusicNoteId(), concertID); + } + }).start(); + } } \ No newline at end of file diff --git a/app/src/main/java/core/notevault/data/ConcertSong.java b/app/src/main/java/core/notevault/data/ConcertSong.java new file mode 100644 index 0000000..3411d14 --- /dev/null +++ b/app/src/main/java/core/notevault/data/ConcertSong.java @@ -0,0 +1,55 @@ +package core.notevault.data; + +import androidx.room.Entity; +import androidx.room.Ignore; +import androidx.room.PrimaryKey; + +@Entity(tableName = "concert_songs") +public class ConcertSong { + + @PrimaryKey(autoGenerate = true) + private int id; + + private long musicNoteID; + private int concertID; + + @Ignore + public ConcertSong(long musicNoteID, int concertID) { + this.musicNoteID = musicNoteID; + this.concertID = concertID; + } + + @Ignore + public ConcertSong(int id, long musicNoteID, int concertID) { + this.id = id; + this.musicNoteID = musicNoteID; + this.concertID = concertID; + } + + public ConcertSong() { + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public long getMusicNoteID() { + return musicNoteID; + } + + public void setMusicNoteID(long musicNoteID) { + this.musicNoteID = musicNoteID; + } + + public int getConcertID() { + return concertID; + } + + public void setConcertID(int concertID) { + this.concertID = concertID; + } +} diff --git a/app/src/main/java/core/notevault/data/MusicDatabase.java b/app/src/main/java/core/notevault/data/MusicDatabase.java index cba9f82..8ec1a8d 100644 --- a/app/src/main/java/core/notevault/data/MusicDatabase.java +++ b/app/src/main/java/core/notevault/data/MusicDatabase.java @@ -5,7 +5,7 @@ import androidx.room.Room; import androidx.room.RoomDatabase; import androidx.room.Database; -@Database(entities = {MusicNote.class, NoteSheet.class, Concert.class}, version = 2, exportSchema = false) +@Database(entities = {MusicNote.class, NoteSheet.class, Concert.class, ConcertSong.class}, version = 2, exportSchema = false) public abstract class MusicDatabase extends RoomDatabase { public abstract MusicNoteDAO musicNoteDao(); diff --git a/app/src/main/java/core/notevault/data/MusicNote.java b/app/src/main/java/core/notevault/data/MusicNote.java index 0f8212c..90135fb 100644 --- a/app/src/main/java/core/notevault/data/MusicNote.java +++ b/app/src/main/java/core/notevault/data/MusicNote.java @@ -22,6 +22,14 @@ public class MusicNote { this.genre = genre; } + public MusicNote(long musicNoteId, String title) { + this.musicNoteId = musicNoteId; + this.title = title; + } + + @Ignore + + public MusicNote() { } diff --git a/app/src/main/java/core/notevault/data/MusicNoteDAO.java b/app/src/main/java/core/notevault/data/MusicNoteDAO.java index 27cc46a..01d201e 100644 --- a/app/src/main/java/core/notevault/data/MusicNoteDAO.java +++ b/app/src/main/java/core/notevault/data/MusicNoteDAO.java @@ -33,4 +33,15 @@ public interface MusicNoteDAO { @Delete void deleteSong(MusicNote musicNote); + + @Insert + void insertConcertSong(ConcertSong concertSong); + + @Query("SELECT m.* FROM music_notes m JOIN concert_songs cs ON m.musicNoteId = cs.musicNoteID WHERE cs.id = :concertID") + List getAllMusicNotesOfConcert(long concertID); + + @Delete + void deleteConcertSong(ConcertSong concertSong); + + } diff --git a/app/src/main/java/core/notevault/ui/gallery/detail/ConcertDetailFragment.java b/app/src/main/java/core/notevault/ui/gallery/detail/ConcertDetailFragment.java index fe19fda..2bc4293 100644 --- a/app/src/main/java/core/notevault/ui/gallery/detail/ConcertDetailFragment.java +++ b/app/src/main/java/core/notevault/ui/gallery/detail/ConcertDetailFragment.java @@ -5,6 +5,7 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import androidx.fragment.app.Fragment; +import com.google.android.material.floatingactionbutton.FloatingActionButton; import core.notevault.data.Concert; import core.notevault.databinding.FragmentConcertDetailBinding; @@ -24,6 +25,22 @@ public class ConcertDetailFragment extends Fragment { binding.textConcertDetailsTitle.setText(concertTitle); binding.textConcertDetailsDate.setText(concertDate); + FloatingActionButton fab = binding.addSongConcert; + fab.setOnClickListener(view -> { + ConcertSongSelector concertSongSelector = new ConcertSongSelector(); + + Bundle bundle = new Bundle(); + bundle.putString("concertTitle", concertTitle); + bundle.putString("concertDate", concertDate); + bundle.putInt("concertID", concertId); + + concertSongSelector.setArguments(bundle); + + concertSongSelector.show(getParentFragmentManager(), ConcertSongSelector.TAG); + }); + return binding.getRoot(); } + + } diff --git a/app/src/main/java/core/notevault/ui/gallery/detail/ConcertSongSelector.java b/app/src/main/java/core/notevault/ui/gallery/detail/ConcertSongSelector.java new file mode 100644 index 0000000..c024799 --- /dev/null +++ b/app/src/main/java/core/notevault/ui/gallery/detail/ConcertSongSelector.java @@ -0,0 +1,116 @@ +package core.notevault.ui.gallery.detail; + +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.Context; +import android.os.AsyncTask; +import android.os.Bundle; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.SearchView; +import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.fragment.app.DialogFragment; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import core.notevault.R; +import core.notevault.data.ConcertSong; +import core.notevault.data.MusicDatabase; +import core.notevault.data.MusicNote; +import core.notevault.data.MusicNoteDAO; +import core.notevault.ui.metadatadialog.MetaDataDialog; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class ConcertSongSelector extends DialogFragment { + + private ConcertSongSelectorAdapter adapter; + private OnSongSelectedListener listener; + private int concertID; + + public interface OnSongSelectedListener { + void onSongsSelected(List songs, int concertID); + } + + @NonNull + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + LayoutInflater inflater = getActivity().getLayoutInflater(); + View dialogView = inflater.inflate(R.layout.dialog_concert_song_selector, null); + + String concertTitle = getArguments().getString("concertTitle"); + String concertDate = getArguments().getString("concertDate"); + concertID = getArguments().getInt("concertID"); + + TextView dialogTitle = dialogView.findViewById(R.id.dialog_title_concert_song_selector); + dialogTitle.setText("Wähle Stücke für das Konzert '" + concertTitle + "' am " + concertDate + " aus"); + + RecyclerView recyclerView = dialogView.findViewById(R.id.concert_song_selector_recycler); + recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); + + + adapter = new ConcertSongSelectorAdapter(new ArrayList<>()); + recyclerView.setAdapter(adapter); + + SearchView searchView = dialogView.findViewById(R.id.search_view); + searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { + @Override + public boolean onQueryTextSubmit(String s) { + return false; + } + + @Override + public boolean onQueryTextChange(String s) { + adapter.filter(s); + return true; + } + }); + + fetchAllSongs(); + + return new AlertDialog.Builder(requireContext()) + .setView(dialogView) + .setPositiveButton("Speichern", (dialog, which) -> { + List selectedSongs = adapter.getSelectedSongs(); + listener.onSongsSelected(selectedSongs, concertID); + }) + .setNegativeButton("Abbrechen", (dialog, which) -> {} ) + .create(); + } + + @Override + public void onAttach(@NonNull Context context) { + super.onAttach(context); + if (context instanceof ConcertSongSelector.OnSongSelectedListener) { + listener = (ConcertSongSelector.OnSongSelectedListener) context; + } else { + throw new RuntimeException(context.toString() + + " must implement OnMetadataListener"); + } + } + + private void fetchAllSongs() { + // Ersetze dies durch das Laden der Songs aus der Datenbank oder einer anderen Quelle + new LoadSongTitlesTask().execute(); + } + + private class LoadSongTitlesTask extends AsyncTask> { + + @Override + protected List doInBackground(Void... voids) { + MusicDatabase db = MusicDatabase.getDatabase(getContext()); + MusicNoteDAO musicNoteDAO = db.musicNoteDao(); + return musicNoteDAO.getAllNotes(); + } + + @Override + protected void onPostExecute(List songs) { + adapter.setSongs(songs); + } + } + + public static String TAG = "ConcertSongSelector"; +} diff --git a/app/src/main/java/core/notevault/ui/gallery/detail/ConcertSongSelectorAdapter.java b/app/src/main/java/core/notevault/ui/gallery/detail/ConcertSongSelectorAdapter.java new file mode 100644 index 0000000..a3185a1 --- /dev/null +++ b/app/src/main/java/core/notevault/ui/gallery/detail/ConcertSongSelectorAdapter.java @@ -0,0 +1,85 @@ +package core.notevault.ui.gallery.detail; + +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.CheckBox; +import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; +import core.notevault.R; +import core.notevault.data.MusicNote; + +import java.util.ArrayList; +import java.util.List; + +public class ConcertSongSelectorAdapter extends RecyclerView.Adapter { + private List songs; + private List selectedSongs = new ArrayList<>(); + private List filteredSongs; + + public ConcertSongSelectorAdapter(List songs) { + this.songs = songs; + this.filteredSongs = new ArrayList<>(songs); + } + + @NonNull + @Override + public ConcertSongSelectorViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View view = LayoutInflater.from(parent.getContext()) + .inflate(R.layout.item_concert_song, parent, false); + return new ConcertSongSelectorViewHolder(view); + } + + @Override + public void onBindViewHolder(@NonNull ConcertSongSelectorViewHolder holder, int position) { + MusicNote song = filteredSongs.get(position); + holder.bind(song); + } + + + @Override + public int getItemCount() { + return filteredSongs.size(); + } + + public List getSelectedSongs() { + return selectedSongs; + } + + public void setSongs(List musicNotes) { + songs = musicNotes; + filteredSongs = new ArrayList<>(musicNotes); + notifyDataSetChanged(); + } + + public void filter(String query) { + filteredSongs.clear(); + if (query.isEmpty()) { + filteredSongs.addAll(songs); + } else { + for (MusicNote song : songs) { + if (song.getTitle().toLowerCase().contains(query.toLowerCase())) { + filteredSongs.add(song); + } + } + } + notifyDataSetChanged(); + } + + class ConcertSongSelectorViewHolder extends RecyclerView.ViewHolder { + private final TextView songTitle; + private final CheckBox songCheckbox; + + ConcertSongSelectorViewHolder(View itemView) { + super(itemView); + songTitle = itemView.findViewById(R.id.song_title); + songCheckbox = itemView.findViewById(R.id.song_checkbox); + } + + void bind(MusicNote song) { + songTitle.setText(song.getTitle()); + songCheckbox.setChecked(selectedSongs.contains(song)); + } + } +} diff --git a/app/src/main/java/core/notevault/ui/home/HomeFragment.java b/app/src/main/java/core/notevault/ui/home/HomeFragment.java index a034ef6..c65696c 100644 --- a/app/src/main/java/core/notevault/ui/home/HomeFragment.java +++ b/app/src/main/java/core/notevault/ui/home/HomeFragment.java @@ -6,12 +6,10 @@ import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; import android.os.Looper; -import android.provider.OpenableColumns; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.TextView; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -29,7 +27,6 @@ import core.notevault.databinding.FragmentHomeBinding; import core.notevault.ui.metadatadialog.MetaDataDialog; import core.notevault.util.NoteSheetsUtil; -import java.util.ArrayList; import java.util.List; public class HomeFragment extends Fragment { @@ -48,7 +45,7 @@ public class HomeFragment extends Fragment { binding = FragmentHomeBinding.inflate(inflater, container, false); View root = binding.getRoot(); - FloatingActionButton importBtn = root.findViewById(R.id.importMusicNotesBtn); + FloatingActionButton importBtn = root.findViewById(R.id.add_song_concert); importBtn.setOnClickListener(v -> openFileChooser()); RecyclerView recyclerView = root.findViewById(R.id.note_recycler_view); diff --git a/app/src/main/res/layout/dialog_concert_song_selector.xml b/app/src/main/res/layout/dialog_concert_song_selector.xml new file mode 100644 index 0000000..f010800 --- /dev/null +++ b/app/src/main/res/layout/dialog_concert_song_selector.xml @@ -0,0 +1,29 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_concert_detail.xml b/app/src/main/res/layout/fragment_concert_detail.xml index a941fdb..be152a3 100644 --- a/app/src/main/res/layout/fragment_concert_detail.xml +++ b/app/src/main/res/layout/fragment_concert_detail.xml @@ -1,43 +1,62 @@ - + - + android:layout_height="match_parent" + android:orientation="vertical" android:layout_above="@+id/add_song_concert" + android:padding="16dp" android:id="@+id/linearLayout"> - + android:layout_marginBottom="16dp" + app:cardCornerRadius="8dp" + app:cardElevation="4dp"> - + android:orientation="horizontal" + android:padding="16dp"> - + - - + - + + - + + + + + + diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml index cf4735b..925a47e 100644 --- a/app/src/main/res/layout/fragment_home.xml +++ b/app/src/main/res/layout/fragment_home.xml @@ -11,14 +11,14 @@ android:id="@+id/note_recycler_view" android:layout_width="match_parent" android:layout_height="match_parent" - android:layout_above="@+id/importMusicNotesBtn"/> + android:layout_above="@+id/add_song_concert"/> + + + + + + \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 2626fc5..b10c347 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -11,6 +11,7 @@ lifecycleViewmodelKtx = "2.6.1" navigationFragment = "2.6.0" navigationUi = "2.6.0" roomCommon = "2.6.1" +carUiLib = "2.6.0" [libraries] junit = { group = "junit", name = "junit", version.ref = "junit" } @@ -24,6 +25,7 @@ lifecycle-viewmodel-ktx = { group = "androidx.lifecycle", name = "lifecycle-view navigation-fragment = { group = "androidx.navigation", name = "navigation-fragment", version.ref = "navigationFragment" } navigation-ui = { group = "androidx.navigation", name = "navigation-ui", version.ref = "navigationUi" } room-common = { group = "androidx.room", name = "room-common", version.ref = "roomCommon" } +car-ui-lib = { group = "com.android.car.ui", name = "car-ui-lib", version.ref = "carUiLib" } [plugins] android-application = { id = "com.android.application", version.ref = "agp" }