ADD: SongEditorDialog

This commit is contained in:
Fawkes100 2025-01-18 12:53:50 +01:00
parent 06e5ce1301
commit 899f3a254d
21 changed files with 434 additions and 21 deletions

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>

View File

@ -4,7 +4,7 @@
<component name="FrameworkDetectionExcludesConfiguration">
<file type="web" url="file://$PROJECT_DIR$" />
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" project-jdk-name="21" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="21" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RunConfigurationProducerService">
<option name="ignoredProducers">
<set>
<option value="com.intellij.execution.junit.AbstractAllInDirectoryConfigurationProducer" />
<option value="com.intellij.execution.junit.AllInPackageConfigurationProducer" />
<option value="com.intellij.execution.junit.PatternConfigurationProducer" />
<option value="com.intellij.execution.junit.TestInClassConfigurationProducer" />
<option value="com.intellij.execution.junit.UniqueIdConfigurationProducer" />
<option value="com.intellij.execution.junit.testDiscovery.JUnitTestDiscoveryConfigurationProducer" />
<option value="org.jetbrains.kotlin.idea.junit.KotlinJUnitRunConfigurationProducer" />
<option value="org.jetbrains.kotlin.idea.junit.KotlinPatternConfigurationProducer" />
</set>
</option>
</component>
</project>

6
.idea/vcs.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

View File

@ -26,14 +26,14 @@ public class MainActivity extends AppCompatActivity {
setContentView(binding.getRoot());
setSupportActionBar(binding.appBarMain.toolbar);
binding.appBarMain.fab.setOnClickListener(new View.OnClickListener() {
/*binding.appBarMain.fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null)
.setAnchorView(R.id.fab).show();
}
});
});*/
DrawerLayout drawer = binding.drawerLayout;
NavigationView navigationView = binding.navView;
// Passing each menu ID as a set of Ids because each

View File

@ -6,11 +6,12 @@ import androidx.room.Room;
import androidx.room.RoomDatabase;
import androidx.room.TypeConverters;
import com.stormtales.notevault.data.entities.Song;
import com.stormtales.notevault.data.repositories.SongDao;
import com.stormtales.notevault.data.dao.SongDao;
import com.stormtales.notevault.data.sync.DateConverter;
import com.stormtales.notevault.data.sync.SyncStatusConverter;
@Database(entities = {Song.class}, version = 1, exportSchema = false)
@TypeConverters(SyncStatusConverter.class)
@TypeConverters({SyncStatusConverter.class, DateConverter.class})
public abstract class MusicDatabase extends RoomDatabase {
public abstract SongDao getSongTable();

View File

@ -0,0 +1,17 @@
package com.stormtales.notevault.data.dao;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.Query;
import com.stormtales.notevault.data.entities.Song;
import java.util.List;
@Dao
public interface SongDao {
@Insert
void insert(Song song);
@Query("SELECT * FROM song")
List<Song> getAllSongs();
}

View File

@ -1,6 +1,7 @@
package com.stormtales.notevault.data.entities;
import androidx.room.Entity;
import androidx.room.Ignore;
import androidx.room.PrimaryKey;
import com.stormtales.notevault.data.sync.SyncStatus;
@ -22,6 +23,14 @@ public class Song {
private String genre;
private int year;
@Ignore
public Song(String title, String composer, String genre, int year) {
this.title = title;
this.composer = composer;
this.genre = genre;
this.year = year;
}
public int getLocalID() {
return localID;
}

View File

@ -1,7 +0,0 @@
package com.stormtales.notevault.data.repositories;
import androidx.room.Dao;
@Dao
public interface SongDao {
}

View File

@ -0,0 +1,21 @@
package com.stormtales.notevault.data.repositories;
import android.app.Application;
import com.stormtales.notevault.data.MusicDatabase;
import com.stormtales.notevault.data.dao.SongDao;
import com.stormtales.notevault.data.entities.Song;
import java.util.concurrent.Executors;
public class SongRepository {
private SongDao songDao;
public SongRepository(Application application) {
MusicDatabase database = MusicDatabase.getDatabase(application);
songDao = database.getSongTable();
}
public void insert(Song song) {
Executors.newSingleThreadExecutor().execute(() -> {
songDao.insert(song);
});
}
}

View File

@ -0,0 +1,20 @@
package com.stormtales.notevault.data.sync;
import androidx.room.TypeConverter;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class DateConverter {
// Konvertiere LocalDateTime in String
@TypeConverter
public static String fromLocalDateTime(LocalDateTime localDateTime) {
return localDateTime == null ? null : localDateTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
}
// Konvertiere String zurück in LocalDateTime
@TypeConverter
public static LocalDateTime toLocalDateTime(String dateTimeString) {
return dateTimeString == null ? null : LocalDateTime.parse(dateTimeString, DateTimeFormatter.ISO_LOCAL_DATE_TIME);
}
}

View File

@ -1,18 +1,30 @@
package com.stormtales.notevault.ui.home;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;
import androidx.activity.result.ActivityResult;
import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.stormtales.notevault.R;
import com.stormtales.notevault.databinding.FragmentHomeBinding;
import com.stormtales.notevault.ui.songeditor.SongEditorDialog;
public class HomeFragment extends Fragment {
private FragmentHomeBinding binding;
private static final int PICK_FILE_REQUEST_CODE = 1;
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
@ -21,6 +33,42 @@ public class HomeFragment extends Fragment {
binding = FragmentHomeBinding.inflate(inflater, container, false);
View root = binding.getRoot();
ActivityResultLauncher<Intent> launcher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
activityResult -> {
getActivity();
if(activityResult.getResultCode() == Activity.RESULT_OK && activityResult.getData() != null) {
if(activityResult.getData().getClipData() != null) {
int fileNumber = activityResult.getData().getClipData().getItemCount();
Uri[] files = new Uri[fileNumber];
for(int i = 0; i < fileNumber; i++) {
files[i] = activityResult.getData().getClipData().getItemAt(i).getUri();
Toast.makeText(requireContext(), "Uri: " + files[i].toString(), Toast.LENGTH_SHORT).show();
}
handleSelectedNoteSheets(files);
} else {
Uri uri = activityResult.getData().getData();
Toast.makeText(requireContext(), "Uri: " + uri.toString(), Toast.LENGTH_SHORT).show();
handleSelectedNoteSheets(uri);
}
}
}
);
FloatingActionButton importBtn = root.findViewById(R.id.addSongBtn);
importBtn.setOnClickListener(v -> {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.setType("*/*"); // Alle Dateitypen
String[] mimeTypes = {"application/pdf", "image/png", "image/jpeg"};
intent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes);
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
intent.addCategory(Intent.CATEGORY_OPENABLE);
launcher.launch(intent);
});
final TextView textView = binding.textHome;
homeViewModel.getText().observe(getViewLifecycleOwner(), textView::setText);
@ -32,4 +80,13 @@ public class HomeFragment extends Fragment {
super.onDestroyView();
binding = null;
}
private void handleSelectedNoteSheets(Uri... files) {
SongEditorDialog songEditorDialog = new SongEditorDialog();
int width = getResources().getDisplayMetrics().widthPixels;
songEditorDialog.show(getParentFragmentManager(), "songEditorDialog");
}
}

View File

@ -3,10 +3,13 @@ package com.stormtales.notevault.ui.home;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import com.stormtales.notevault.data.entities.Song;
import com.stormtales.notevault.data.repositories.SongRepository;
public class HomeViewModel extends ViewModel {
private final MutableLiveData<String> mText;
private SongRepository songRepository;
public HomeViewModel() {
mText = new MutableLiveData<>();
@ -16,4 +19,8 @@ public class HomeViewModel extends ViewModel {
public LiveData<String> getText() {
return mText;
}
public void addSong(Song song) {
songRepository.insert(song);
}
}

View File

@ -0,0 +1,67 @@
package com.stormtales.notevault.ui.songeditor;
import android.app.AlertDialog;
import android.app.Dialog;
import android.os.Bundle;
import android.text.Layout;
import android.widget.Button;
import android.widget.EditText;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.lifecycle.ViewModelProvider;
import com.stormtales.notevault.R;
import com.stormtales.notevault.data.entities.Song;
import com.stormtales.notevault.ui.home.HomeViewModel;
import org.jetbrains.annotations.NotNull;
public class SongEditorDialog extends DialogFragment {
Dialog dialog;
public SongEditorDialog() {
// Required empty public constructor
}
@NonNull
@Override
public @NotNull Dialog onCreateDialog(@Nullable @org.jetbrains.annotations.Nullable Bundle savedInstanceState) {
LayoutInflater inflater = LayoutInflater.from(getContext());
View dialogView = inflater.inflate(R.layout.fragment_song_editor_dialog, null);
dialogView.findViewById(R.id.btnCancel).setOnClickListener(v-> onCancel());
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setCancelable(false);
builder.setView(dialogView);
dialog = builder.create();
dialog.setCanceledOnTouchOutside(false);
return dialog;
}
private void onCancel() {
dialog.dismiss();
}
private void onSave() {
String title = ((EditText) dialog.findViewById(R.id.etTitle)).getText().toString();
String composer = ((EditText) dialog.findViewById(R.id.etComposer)).getText().toString();
int releaseYear = Integer.parseInt(((EditText) dialog.findViewById(R.id.etYear)).getText().toString());
String genre = ((EditText) dialog.findViewById(R.id.etGenre)).getText().toString();
Song song = new Song(title, composer, genre, releaseYear);
HomeViewModel homeViewModel = new ViewModelProvider(requireActivity()).get(HomeViewModel.class);
homeViewModel.addSong(song);
dialog.dismiss();
}
}

View File

@ -0,0 +1,49 @@
package com.stormtales.notevault.utils;
import android.content.Context;
import android.media.ExifInterface;
import android.net.Uri;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
public class NoteSheetsUtil {
private static long getImageTimestamp(Context context, Uri imageUri) {
try {
InputStream inputStream = context.getContentResolver().openInputStream(imageUri);
ExifInterface exif = new ExifInterface(inputStream);
String dateTime = exif.getAttribute(ExifInterface.TAG_DATETIME);
if(dateTime != null) {
SimpleDateFormat format = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss", Locale.getDefault());
Date date = format.parse(dateTime);
return date != null ? date.getTime() : 0;
} else {
File file = new File(imageUri.getPath());
return file.lastModified();
}
} catch (IOException | ParseException e) {
throw new RuntimeException(e);
}
}
public static void sortNoteSheetsByTimestamp(Context context, Uri[] uris) {
ArrayList<UriTimestamp> uriTimestamps = new ArrayList<>();
for(Uri uri : uris) {
long timestamp = getImageTimestamp(context, uri);
uriTimestamps.add(new UriTimestamp(uri, timestamp));
}
Collections.sort(uriTimestamps, Comparator.comparingLong(UriTimestamp::getTimestamp));
for(int i=0; i<uriTimestamps.size(); i++) {
uris[i] = uriTimestamps.get(i).getUri();
}
}
}

View File

@ -0,0 +1,21 @@
package com.stormtales.notevault.utils;
import android.net.Uri;
public class UriTimestamp {
private final Uri uri;
private final long timestamp;
public UriTimestamp(Uri uri, long timestamp) {
this.uri = uri;
this.timestamp = timestamp;
}
public Uri getUri() {
return uri;
}
public long getTimestamp() {
return timestamp;
}
}

View File

@ -23,13 +23,4 @@
<include layout="@layout/content_main"/>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_marginEnd="@dimen/fab_margin"
android:layout_marginBottom="16dp"
app:srcCompat="@android:drawable/ic_dialog_email"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -7,6 +7,14 @@
android:layout_height="match_parent"
tools:context=".ui.home.HomeFragment">
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:src="@android:drawable/ic_input_add"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="true" android:id="@+id/addSongBtn"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginBottom="16dp" android:layout_marginEnd="16dp"
app:layout_constraintEnd_toEndOf="parent" android:accessibilityPaneTitle="Add a Song"/>
<TextView
android:id="@+id/text_home"
android:layout_width="match_parent"

View File

@ -0,0 +1,122 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="24dp"
android:background="?android:attr/windowBackground">
<!-- Titel des Songs -->
<TextView
android:id="@+id/tvTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Song Title"
android:textSize="16sp"
android:textColor="#000000"
android:paddingBottom="8dp"
android:fontFamily="sans-serif-medium"/>
<EditText
android:id="@+id/etTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Enter Song Title"
android:inputType="text"
android:padding="12dp"
android:layout_marginBottom="16dp"
android:backgroundTint="@color/light_blue_600"/>
<!-- Komponist -->
<TextView
android:id="@+id/tvComposer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Composer"
android:textSize="16sp"
android:textColor="#000000"
android:paddingBottom="8dp"
android:fontFamily="sans-serif-medium"/>
<EditText
android:id="@+id/etComposer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Enter Composer"
android:inputType="text"
android:padding="12dp"
android:layout_marginBottom="16dp"
android:backgroundTint="@color/light_blue_600"/>
<!-- Erscheinungsjahr -->
<TextView
android:id="@+id/tvYear"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Release Year"
android:textSize="16sp"
android:textColor="#000000"
android:paddingBottom="8dp"
android:fontFamily="sans-serif-medium"/>
<EditText
android:id="@+id/etYear"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Enter Release Year"
android:inputType="number"
android:padding="12dp"
android:layout_marginBottom="16dp"
android:backgroundTint="@color/light_blue_600"/>
<!-- Genre -->
<TextView
android:id="@+id/tvGenre"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Genre"
android:textSize="16sp"
android:textColor="#000000"
android:paddingBottom="8dp"
android:fontFamily="sans-serif-medium"/>
<EditText
android:id="@+id/etGenre"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Enter Genre"
android:inputType="text"
android:padding="12dp"
android:layout_marginBottom="16dp"
android:backgroundTint="@color/light_blue_600"/>
<!-- Buttons -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="end">
<Button
android:id="@+id/btnCancel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Cancel"
android:layout_marginEnd="8dp"
android:backgroundTint="@android:color/darker_gray"
android:textColor="@android:color/white"
android:paddingHorizontal="16dp"
android:paddingVertical="8dp" />
<Button
android:id="@+id/btnSave"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Save"
android:backgroundTint="@color/light_blue_600"
android:textColor="@android:color/white"
android:paddingHorizontal="16dp"
android:paddingVertical="8dp" />
</LinearLayout>
</LinearLayout>

View File

@ -7,4 +7,8 @@
<color name="teal_700">#FF018786</color>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
<color name="light_blue_400">#FF29B6F6</color>
<color name="light_blue_600">#FF039BE5</color>
<color name="gray_400">#FFBDBDBD</color>
<color name="gray_600">#FF757575</color>
</resources>

View File

@ -10,4 +10,6 @@
<string name="menu_home">Home</string>
<string name="menu_gallery">Gallery</string>
<string name="menu_slideshow">Slideshow</string>
<!-- TODO: Remove or change this placeholder text -->
<string name="hello_blank_fragment">Hello blank fragment</string>
</resources>