nextNoteVault #23
							
								
								
									
										23
									
								
								.idea/dataSources.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								.idea/dataSources.xml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,23 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<project version="4">
 | 
			
		||||
  <component name="DataSourceManagerImpl" format="xml" multifile-model="true">
 | 
			
		||||
    <data-source source="LOCAL" name="music_database" uuid="eb23f694-6586-450b-8f6f-a75731d36b96">
 | 
			
		||||
      <driver-ref>sqlite.xerial</driver-ref>
 | 
			
		||||
      <synchronize>true</synchronize>
 | 
			
		||||
      <jdbc-driver>org.sqlite.JDBC</jdbc-driver>
 | 
			
		||||
      <jdbc-url>jdbc:sqlite:$PROJECT_DIR$/music_database</jdbc-url>
 | 
			
		||||
      <jdbc-additional-properties>
 | 
			
		||||
        <property name="com.intellij.clouds.kubernetes.db.enabled" value="false" />
 | 
			
		||||
      </jdbc-additional-properties>
 | 
			
		||||
      <working-dir>$ProjectFileDir$</working-dir>
 | 
			
		||||
      <libraries>
 | 
			
		||||
        <library>
 | 
			
		||||
          <url>file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/Xerial SQLiteJDBC/3.45.1/org/xerial/sqlite-jdbc/3.45.1.0/sqlite-jdbc-3.45.1.0.jar</url>
 | 
			
		||||
        </library>
 | 
			
		||||
        <library>
 | 
			
		||||
          <url>file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/Xerial SQLiteJDBC/3.45.1/org/slf4j/slf4j-api/1.7.36/slf4j-api-1.7.36.jar</url>
 | 
			
		||||
        </library>
 | 
			
		||||
      </libraries>
 | 
			
		||||
    </data-source>
 | 
			
		||||
  </component>
 | 
			
		||||
</project>
 | 
			
		||||
							
								
								
									
										18
									
								
								.idea/deploymentTargetSelector.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								.idea/deploymentTargetSelector.xml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,18 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<project version="4">
 | 
			
		||||
  <component name="deploymentTargetSelector">
 | 
			
		||||
    <selectionStates>
 | 
			
		||||
      <SelectionState runConfigName="app">
 | 
			
		||||
        <option name="selectionMode" value="DROPDOWN" />
 | 
			
		||||
        <DropdownSelection timestamp="2025-04-10T18:07:32.446414465Z">
 | 
			
		||||
          <Target type="DEFAULT_BOOT">
 | 
			
		||||
            <handle>
 | 
			
		||||
              <DeviceId pluginId="PhysicalDevice" identifier="serial=R52N50NLGRT" />
 | 
			
		||||
            </handle>
 | 
			
		||||
          </Target>
 | 
			
		||||
        </DropdownSelection>
 | 
			
		||||
        <DialogSelection />
 | 
			
		||||
      </SelectionState>
 | 
			
		||||
    </selectionStates>
 | 
			
		||||
  </component>
 | 
			
		||||
</project>
 | 
			
		||||
@ -1,4 +1,3 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<project version="4">
 | 
			
		||||
  <component name="ExternalStorageConfigurationManager" enabled="true" />
 | 
			
		||||
  <component name="FrameworkDetectionExcludesConfiguration">
 | 
			
		||||
 | 
			
		||||
@ -38,4 +38,8 @@ public interface SongSyncAPI {
 | 
			
		||||
 | 
			
		||||
    @GET("/sync/songs/get/notesheet/")
 | 
			
		||||
    Call<ResponseBody> downloadNotesheet(@Query("server_filename") String server_filename);
 | 
			
		||||
 | 
			
		||||
    @Multipart
 | 
			
		||||
    @POST("/ai/regognize/title")
 | 
			
		||||
    Call<AIRecognizedSong> recognizeTitle(@Part MultipartBody.Part image);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -215,6 +215,22 @@ public class SongSyncService {
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void recognizeTitle(String firstNoteSheet, RecognizedSongCallback callback) {
 | 
			
		||||
        RequestBody requestFile = RequestBody.create(MediaType.parse("image/jpeg"), new File(firstNoteSheet));
 | 
			
		||||
        MultipartBody.Part image = MultipartBody.Part.createFormData("image", firstNoteSheet, requestFile);
 | 
			
		||||
        songSyncAPI.recognizeTitle(image).enqueue(new Callback<AIRecognizedSong>() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void onResponse(Call<AIRecognizedSong> call, Response<AIRecognizedSong> response) {
 | 
			
		||||
                callback.onRecognizedSong(response.body());
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            @Override
 | 
			
		||||
            public void onFailure(Call<AIRecognizedSong> call, Throwable throwable) {
 | 
			
		||||
                Log.d("SongSyncService", "Recognition failed: " + throwable.getMessage(), throwable);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public interface SyncDeletedSongsCallback {
 | 
			
		||||
        void finishSongSyncing(List<String> remoteDeletedSongs, List<Integer> localDeletedSongs);
 | 
			
		||||
@ -223,4 +239,8 @@ public class SongSyncService {
 | 
			
		||||
    public interface UploadNoteSheetCallback {
 | 
			
		||||
        void finishUploadNoteSheets(List<UploadResponse> uploadResponses);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public interface RecognizedSongCallback {
 | 
			
		||||
        void onRecognizedSong(AIRecognizedSong aiRecognizedSong);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,40 @@
 | 
			
		||||
package com.stormtales.notevault.network.sync.models;
 | 
			
		||||
 | 
			
		||||
public class AIRecognizedSong {
 | 
			
		||||
    private String title;
 | 
			
		||||
    private int year;
 | 
			
		||||
    private String composer;
 | 
			
		||||
    private String description;
 | 
			
		||||
 | 
			
		||||
    public String getTitle() {
 | 
			
		||||
        return title;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setTitle(String title) {
 | 
			
		||||
        this.title = title;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public int getYear() {
 | 
			
		||||
        return year;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setYear(int year) {
 | 
			
		||||
        this.year = year;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String getComposer() {
 | 
			
		||||
        return composer;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setComposer(String composer) {
 | 
			
		||||
        this.composer = composer;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String getDescription() {
 | 
			
		||||
        return description;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setDescription(String description) {
 | 
			
		||||
        this.description = description;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -6,6 +6,7 @@ import android.content.Context;
 | 
			
		||||
import android.net.Uri;
 | 
			
		||||
import android.os.Bundle;
 | 
			
		||||
import android.text.Layout;
 | 
			
		||||
import android.util.Log;
 | 
			
		||||
import android.widget.Button;
 | 
			
		||||
import android.widget.EditText;
 | 
			
		||||
import androidx.annotation.NonNull;
 | 
			
		||||
@ -20,11 +21,17 @@ import com.stormtales.notevault.R;
 | 
			
		||||
import com.stormtales.notevault.data.entities.NoteSheet;
 | 
			
		||||
import com.stormtales.notevault.data.entities.Song;
 | 
			
		||||
import com.stormtales.notevault.data.sync.SyncStatus;
 | 
			
		||||
import com.stormtales.notevault.network.sync.SongSyncService;
 | 
			
		||||
import com.stormtales.notevault.network.sync.models.AIRecognizedSong;
 | 
			
		||||
import com.stormtales.notevault.ui.home.HomeViewModel;
 | 
			
		||||
import com.stormtales.notevault.utils.NoteSheetsUtil;
 | 
			
		||||
import com.stormtales.notevault.utils.Tupel;
 | 
			
		||||
import org.jetbrains.annotations.NotNull;
 | 
			
		||||
 | 
			
		||||
import java.io.File;
 | 
			
		||||
import java.io.FileOutputStream;
 | 
			
		||||
import java.io.InputStream;
 | 
			
		||||
import java.io.OutputStream;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
@ -36,9 +43,12 @@ public class SongEditorDialog extends DialogFragment {
 | 
			
		||||
    private HomeViewModel homeViewModel;
 | 
			
		||||
 | 
			
		||||
    private Song editedSong;
 | 
			
		||||
    private SongSyncService songSyncService;
 | 
			
		||||
 | 
			
		||||
    private View progressbar;
 | 
			
		||||
    public SongEditorDialog() {
 | 
			
		||||
        // Required empty public constructor
 | 
			
		||||
        this.songSyncService = new SongSyncService(this.getContext());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -49,21 +59,79 @@ public class SongEditorDialog extends DialogFragment {
 | 
			
		||||
        View dialogView = inflater.inflate(R.layout.fragment_song_editor_dialog, null);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        dialogView.findViewById(R.id.btnCancel).setOnClickListener(v-> onCancel());
 | 
			
		||||
        dialogView.findViewById(R.id.btnSave).setOnClickListener(v -> onSave());
 | 
			
		||||
 | 
			
		||||
        if(this.noteSheetFiles == null || noteSheetFiles.length == 0) {
 | 
			
		||||
            dialogView.findViewById(R.id.btnAutoDetect).setEnabled(false);
 | 
			
		||||
        } else {
 | 
			
		||||
            progressbar = dialogView.findViewById(R.id.spinnerAutoDetect);
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        dialogView.findViewById(R.id.btnAutoDetect).setOnClickListener(v -> {
 | 
			
		||||
            progressbar.setVisibility(ViewGroup.VISIBLE);
 | 
			
		||||
            extractTitleFromFirstPage();
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
 | 
			
		||||
        builder.setCancelable(false);
 | 
			
		||||
        builder.setView(dialogView);
 | 
			
		||||
 | 
			
		||||
        dialog = builder.create();
 | 
			
		||||
        dialog.setCanceledOnTouchOutside(false);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        return dialog;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private File createTemporaryFileForRecognition(Uri noteSheetUri) {
 | 
			
		||||
        try {
 | 
			
		||||
            // Hole den InputStream des Bildes
 | 
			
		||||
            InputStream inputStream = getContext().getContentResolver().openInputStream(noteSheetUri);
 | 
			
		||||
            if (inputStream != null) {
 | 
			
		||||
                // Erstelle eine temporäre Datei
 | 
			
		||||
                File tempFile = File.createTempFile("temp_note_sheet", ".jpg", getContext().getCacheDir());
 | 
			
		||||
 | 
			
		||||
                // Inhalt des InputStreams in die temporäre Datei schreiben
 | 
			
		||||
                try (OutputStream outputStream = new FileOutputStream(tempFile)) {
 | 
			
		||||
                    byte[] buffer = new byte[8192];
 | 
			
		||||
                    int bytesRead;
 | 
			
		||||
                    while ((bytesRead = inputStream.read(buffer)) != -1) {
 | 
			
		||||
                        outputStream.write(buffer, 0, bytesRead);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Schließe den InputStream
 | 
			
		||||
                inputStream.close();
 | 
			
		||||
 | 
			
		||||
                return tempFile;
 | 
			
		||||
            }
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            Log.e("SongEditorDialog", "Fehler beim Erstellen der temporären Datei: " + e.getMessage());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
    private void extractTitleFromFirstPage() {
 | 
			
		||||
        if(noteSheetFiles != null && noteSheetFiles.length > 0) {
 | 
			
		||||
            Uri firstPageUri = noteSheetFiles[0];
 | 
			
		||||
 | 
			
		||||
            // Temporäre Datei erstellen
 | 
			
		||||
            File tempFile = createTemporaryFileForRecognition(firstPageUri);
 | 
			
		||||
            if(tempFile != null) {
 | 
			
		||||
                songSyncService.recognizeTitle(tempFile.getAbsolutePath(), new SongSyncService.RecognizedSongCallback() {
 | 
			
		||||
                    @Override
 | 
			
		||||
                    public void onRecognizedSong(AIRecognizedSong aiRecognizedSong) {
 | 
			
		||||
                        progressbar.setVisibility(ViewGroup.GONE);
 | 
			
		||||
                        ((EditText) dialog.findViewById(R.id.etTitle)).setText(aiRecognizedSong.getTitle());
 | 
			
		||||
                        ((EditText) dialog.findViewById(R.id.etComposer)).setText(aiRecognizedSong.getComposer());
 | 
			
		||||
                        ((EditText) dialog.findViewById(R.id.etYear)).setText(String.valueOf(aiRecognizedSong.getYear()));
 | 
			
		||||
                        ((EditText) dialog.findViewById(R.id.etGenre)).setText(aiRecognizedSong.getDescription());
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onStart() {
 | 
			
		||||
        super.onStart();
 | 
			
		||||
@ -97,13 +165,12 @@ public class SongEditorDialog extends DialogFragment {
 | 
			
		||||
        } else {
 | 
			
		||||
            Song song = new Song(title, composer, genre, releaseYear);
 | 
			
		||||
 | 
			
		||||
            List<NoteSheet> noteSheetList = new ArrayList<>();
 | 
			
		||||
            Context context = this.getContext();
 | 
			
		||||
            List<NoteSheet> noteSheetList = new ArrayList<>();
 | 
			
		||||
            for(Uri uri : noteSheetFiles) {
 | 
			
		||||
                Tupel<String, String> result = NoteSheetsUtil.saveImageInternally(context.getContentResolver(), uri, context.getFilesDir());
 | 
			
		||||
                noteSheetList.add(new NoteSheet(result.getValue00(), result.getValue01()));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            homeViewModel.addSong(song, noteSheetList);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -96,6 +96,30 @@
 | 
			
		||||
            android:layout_height="wrap_content"
 | 
			
		||||
            android:orientation="horizontal"
 | 
			
		||||
            android:gravity="end">
 | 
			
		||||
        <!-- ProgressBar als Spinner -->
 | 
			
		||||
        <ProgressBar
 | 
			
		||||
                android:id="@+id/spinnerAutoDetect"
 | 
			
		||||
                android:layout_width="24dp"
 | 
			
		||||
                android:layout_height="24dp"
 | 
			
		||||
                android:layout_marginEnd="16dp"
 | 
			
		||||
                android:baselineAligned="false"
 | 
			
		||||
 | 
			
		||||
                android:visibility="gone"
 | 
			
		||||
                android:indeterminate="true" />
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        <TextView
 | 
			
		||||
                android:id="@+id/btnAutoDetect"
 | 
			
		||||
                android:layout_width="wrap_content"
 | 
			
		||||
                android:layout_height="wrap_content"
 | 
			
		||||
                android:text="Automatische Erkennung"
 | 
			
		||||
                android:textColor="@color/light_blue_600"
 | 
			
		||||
                android:paddingEnd="16dp"
 | 
			
		||||
                android:clickable="true"
 | 
			
		||||
                android:focusable="true"
 | 
			
		||||
                android:textSize="16sp"
 | 
			
		||||
                android:layout_gravity="center_vertical"
 | 
			
		||||
                android:fontFamily="sans-serif-medium" />
 | 
			
		||||
 | 
			
		||||
        <Button
 | 
			
		||||
                android:id="@+id/btnCancel"
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user