mirror of
https://github.com/antebudimir/tempus.git
synced 2025-12-31 09:33:33 +00:00
193 lines
8.4 KiB
Java
193 lines
8.4 KiB
Java
package com.cappielloantonio.tempo.util;
|
|
|
|
import android.app.NotificationManager;
|
|
import android.app.PendingIntent;
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
import android.net.Uri;
|
|
import android.provider.Settings;
|
|
import android.webkit.MimeTypeMap;
|
|
|
|
import androidx.core.app.NotificationCompat;
|
|
import androidx.documentfile.provider.DocumentFile;
|
|
import androidx.media3.common.MediaItem;
|
|
|
|
import com.cappielloantonio.tempo.util.Preferences;
|
|
import com.cappielloantonio.tempo.util.ExternalAudioReader;
|
|
|
|
import java.io.InputStream;
|
|
import java.io.OutputStream;
|
|
import java.net.HttpURLConnection;
|
|
import java.net.URL;
|
|
import java.text.Normalizer;
|
|
import java.util.Locale;
|
|
|
|
public class ExternalAudioWriter {
|
|
|
|
private static String sanitizeFileName(String name) {
|
|
String sanitized = name.replaceAll("[\\/:*?\\\"<>|]", "_");
|
|
sanitized = sanitized.replaceAll("\\s+", " ").trim();
|
|
return sanitized;
|
|
}
|
|
|
|
private static String normalizeForComparison(String name) {
|
|
String s = sanitizeFileName(name);
|
|
s = Normalizer.normalize(s, Normalizer.Form.NFKD);
|
|
s = s.replaceAll("\\p{InCombiningDiacriticalMarks}+", "");
|
|
return s.toLowerCase(Locale.ROOT);
|
|
}
|
|
|
|
private static DocumentFile findFile(DocumentFile dir, String fileName) {
|
|
String normalized = normalizeForComparison(fileName);
|
|
for (DocumentFile file : dir.listFiles()) {
|
|
String existing = file.getName();
|
|
if (existing != null && normalizeForComparison(existing).equals(normalized)) {
|
|
return file;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public static void downloadToUserDirectory(Context context, MediaItem mediaItem, String fallbackName) {
|
|
new Thread(() -> {
|
|
String uriString = Preferences.getDownloadDirectoryUri();
|
|
|
|
if (uriString == null) {
|
|
notifyUnavailable(context);
|
|
return;
|
|
}
|
|
|
|
Uri treeUri = Uri.parse(uriString);
|
|
DocumentFile directory = DocumentFile.fromTreeUri(context, treeUri);
|
|
if (directory == null || !directory.canWrite()) {
|
|
notifyFailure(context, "Cannot write to folder.");
|
|
return;
|
|
}
|
|
|
|
try {
|
|
Uri mediaUri = mediaItem.requestMetadata.mediaUri;
|
|
if (mediaUri == null) {
|
|
notifyFailure(context, "Invalid media URI.");
|
|
return;
|
|
}
|
|
|
|
String scheme = mediaUri.getScheme();
|
|
if (scheme == null || (!scheme.equals("http") && !scheme.equals("https"))) {
|
|
notifyExists(context, fallbackName);
|
|
return;
|
|
}
|
|
|
|
HttpURLConnection connection = (HttpURLConnection) new URL(mediaUri.toString()).openConnection();
|
|
connection.connect();
|
|
|
|
String mimeType = connection.getContentType();
|
|
if (mimeType == null) mimeType = "application/octet-stream";
|
|
|
|
String extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType);
|
|
if (extension == null) extension = "bin";
|
|
|
|
String artist = mediaItem.mediaMetadata.artist != null ? mediaItem.mediaMetadata.artist.toString() : "";
|
|
String title = mediaItem.mediaMetadata.title != null ? mediaItem.mediaMetadata.title.toString() : fallbackName;
|
|
String album = mediaItem.mediaMetadata.albumTitle != null ? mediaItem.mediaMetadata.albumTitle.toString() : "";
|
|
String name = artist.isEmpty() ? title : artist + " - " + title;
|
|
if (!album.isEmpty()) name += " (" + album + ")";
|
|
|
|
String sanitized = sanitizeFileName(name);
|
|
String fullName = sanitized + "." + extension;
|
|
|
|
DocumentFile existingFile = findFile(directory, fullName);
|
|
long remoteLength = connection.getContentLengthLong();
|
|
if (existingFile != null && existingFile.exists()) {
|
|
long localLength = existingFile.length();
|
|
if (remoteLength > 0 && localLength == remoteLength) {
|
|
notifyExists(context, fullName);
|
|
return;
|
|
} else {
|
|
existingFile.delete();
|
|
}
|
|
}
|
|
|
|
DocumentFile targetFile = directory.createFile(mimeType, fullName);
|
|
if (targetFile == null) {
|
|
notifyFailure(context, "Failed to create file.");
|
|
return;
|
|
}
|
|
|
|
try (InputStream in = connection.getInputStream();
|
|
OutputStream out = context.getContentResolver().openOutputStream(targetFile.getUri())) {
|
|
if (out == null) {
|
|
notifyFailure(context, "Cannot open output stream.");
|
|
return;
|
|
}
|
|
|
|
byte[] buffer = new byte[8192];
|
|
int len;
|
|
long total = 0;
|
|
while ((len = in.read(buffer)) != -1) {
|
|
out.write(buffer, 0, len);
|
|
total += len;
|
|
}
|
|
|
|
if (remoteLength > 0 && total != remoteLength) {
|
|
targetFile.delete();
|
|
notifyFailure(context, "Incomplete download.");
|
|
} else {
|
|
notifySuccess(context, fullName);
|
|
ExternalAudioReader.refreshCache();
|
|
}
|
|
}
|
|
} catch (Exception e) {
|
|
notifyFailure(context, e.getMessage());
|
|
}
|
|
}).start();
|
|
}
|
|
|
|
private static void notifyUnavailable(Context context) {
|
|
NotificationManager manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
|
Intent settingsIntent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
|
|
Uri.fromParts("package", context.getPackageName(), null));
|
|
PendingIntent openSettings = PendingIntent.getActivity(context, 0, settingsIntent,
|
|
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
|
|
|
|
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, DownloadUtil.DOWNLOAD_NOTIFICATION_CHANNEL_ID)
|
|
.setContentTitle("No download folder set")
|
|
.setContentText("Tap to set one in settings")
|
|
.setSmallIcon(android.R.drawable.stat_notify_error)
|
|
.setPriority(NotificationCompat.PRIORITY_LOW)
|
|
.setSilent(true)
|
|
.setContentIntent(openSettings)
|
|
.setAutoCancel(true);
|
|
|
|
manager.notify(1011, builder.build());
|
|
}
|
|
|
|
private static void notifyFailure(Context context, String message) {
|
|
NotificationManager manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
|
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, DownloadUtil.DOWNLOAD_NOTIFICATION_CHANNEL_ID)
|
|
.setContentTitle("Download failed")
|
|
.setContentText(message)
|
|
.setSmallIcon(android.R.drawable.stat_notify_error)
|
|
.setAutoCancel(true);
|
|
manager.notify((int) System.currentTimeMillis(), builder.build());
|
|
}
|
|
|
|
private static void notifySuccess(Context context, String name) {
|
|
NotificationManager manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
|
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, DownloadUtil.DOWNLOAD_NOTIFICATION_CHANNEL_ID)
|
|
.setContentTitle("Download complete")
|
|
.setContentText(name)
|
|
.setSmallIcon(android.R.drawable.stat_sys_download_done)
|
|
.setAutoCancel(true);
|
|
manager.notify((int) System.currentTimeMillis(), builder.build());
|
|
}
|
|
|
|
private static void notifyExists(Context context, String name) {
|
|
NotificationManager manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
|
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, DownloadUtil.DOWNLOAD_NOTIFICATION_CHANNEL_ID)
|
|
.setContentTitle("Already downloaded")
|
|
.setContentText(name)
|
|
.setSmallIcon(android.R.drawable.stat_sys_warning)
|
|
.setAutoCancel(true);
|
|
manager.notify((int) System.currentTimeMillis(), builder.build());
|
|
}
|
|
}
|