Set ringtone / notification tone via contentprovider (from assets) - android

I'm trying to set the android default ringtone or notification tone via content provider from my assets folder.
Surprisingly, it works like this, but is it a legitimate way?
Uri audiouri = Uri.parse("content://"+BuildConfig.APPLICATION_ID+"/"+soundname+".mp3");
RingtoneManager.setActualDefaultRingtoneUri(a, TYPE_NOTIFICATION, audiouri );
Unfortunately, the sound name isn't shown in Android settings.
Strangely the sound name is actually shown when I go to 'Other sounds'
I also tried this:
Uri audiouri = Uri.parse("content://"+BuildConfig.APPLICATION_ID+"/"+soundname+".mp3");
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.MediaColumns.TITLE, soundname);
Uri ringtoneuri = a.getContentResolver().insert(audiouri, contentValues);
RingtoneManager.setActualDefaultRingtoneUri(a, TYPE_NOTIFICATION, ringtoneuri);
resulting in a null sound (no sound is set)
third option I tried is:
Uri audiouri = MediaStore.Audio.Media.getContentUriForPath("content://"+BuildConfig.APPLICATION_ID+"/"+soundname+".mp3");
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.MediaColumns.DATA, "content://"+BuildConfig.APPLICATION_ID+"/"+soundname+".mp3");
contentValues.put(MediaStore.MediaColumns.TITLE, soundname);
Uri ringtoneuri = a.getContentResolver().insert(audiouri, contentValues);
RingtoneManager.setActualDefaultRingtoneUri(a, TYPE_NOTIFICATION, ringtoneuri);
Now the sound name is shown correctly, but no sound is actually played.
I get error on logcat:
java.io.FileNotFoundException: Can't access /content:/com.mydomain.myapp/test.mp3
So it seems it's taking the value from MediaColumns.DATA which does not support Content provider paths but only real paths. Right?
Final question: How to set tone AND name in android settings? Preferably without copying the file to external storage.

So, unfortunately I did not find out how to set asset as ringtone directly,
but this is a nice workaround:
When copying asset to internal app storage or cache dir (no permissions needed for that!) I was able to set the ringtone without WRITE_EXTERNAL_STORAGE permisson.
static void settone(int type, Sound sound, Activity a)
{
lastsound = sound; //global remember sound and type (alarm/ringtone/notification)
lasttype = type; // if we have to get permissions first, then call this from onActivityResult
if (canwritesystem(a))
{
RingtoneManager.setActualDefaultRingtoneUri(a, type, getringtoneuri(sound, a));
Toast.makeText(a, a.getString(R.string.settonesuccess), Toast.LENGTH_LONG).show();
}
else a.startActivityForResult(new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS).setData(Uri.parse("package:" + a.getPackageName())),CONTEXT_SET_TONE);
}
static Uri getringtoneuri(Sound sound, Activity a)
{
File tonefile = new File(sound.getpath); // path could be like: /Android/data/com.company.yourapp
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.MediaColumns.DATA, tonefile.getAbsolutePath());
contentValues.put(MediaStore.MediaColumns.TITLE, sound.getDisplayName());
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "audio/mp3");
contentValues.put(MediaStore.MediaColumns.SIZE, tonefile.length());
contentValues.put(MediaStore.Audio.Media.IS_RINGTONE, true);
contentValues.put(MediaStore.Audio.Media.IS_NOTIFICATION, true);
contentValues.put(MediaStore.Audio.Media.IS_ALARM, true);
contentValues.put(MediaStore.Audio.Media.IS_MUSIC, false);
Uri generalaudiouri = MediaStore.Audio.Media.INTERNAL_CONTENT_URI;
a.getContentResolver().delete(generalaudiouri, MediaStore.MediaColumns.DATA + "='" + tonefile.getAbsolutePath() + "'", null);
return a.getContentResolver().insert(generalaudiouri, contentValues);
}

Related

Display apps folder in dallery and images/video inside it like other popular apps

Hi I am new to android development and have been trying to accomplish the above said functionality.
I am testing app on Android 9, API 28. I am able to save captured image to folder but not been able to display it in gallery (Like WhatsApp).
I have tried:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
OutputStream os;
String[] split = imagePathNew.split("\\.");
ContentResolver resolver = context.getContentResolver();
ContentValues values = new ContentValues();
values.put(MediaStore.MediaColumns.DISPLAY_NAME, split[0] + ".jpg");
values.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg");
values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + File.separator + "Test");
Uri imageUri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
os = (OutputStream) resolver.openOutputStream(Objects.requireNonNull(imageUri)); // imageLocalUri is the uri of captured image in folder
Bitmap bitmap = MediaStore.Images.Media.getBitmap(resolver, imageLocalUri);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, os);
Objects.requireNonNull(os);
} else {
Intent updateInGallery = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
updateInGallery.setData(imageLocalUri); // imageLocalUri is the uri of captured image in folder
context.sendBroadcast(updateInGallery);
}
Can someone please help me with what am I doing wrong here?
Nothing is wrong with the posted code.
In the addition use following class
https://developer.android.com/reference/android/media/MediaScannerConnection#scanFile(java.lang.String,%20java.lang.String)
From docs
provides a way for applications to pass a newly created or downloaded media file to the media scanner service. The media scanner service will read metadata from the file and add the file to the media content provider.

Android set rintone to one existing in library

I want my app to set a ringtone. The user has selected one from the existing library entries before using the system picker and gets this extra back: RingtoneManager.EXTRA_RINGTONE_PICKED_URI
In this example I have picked "Andromeda" from the default ringtones and get this path: content://media/internal/audio/media/103
When I try to set it at a given time I run this code:
Uri ur = Uri.parse(ringtoneFile.getAbsolutePath()); RingtoneManager.setActualDefaultRingtoneUri(context, ringtoneType, uri);
I have also tried this version:
Uri ur = Uri.parse(ringtoneFile.getAbsolutePath()); android.provider.Settings.System.putString(context.getContentResolver(), android.provider.Settings.System.RINGTONE, uri.toString());
Neither works. The system's sound settings will look like this:
Only 103 is shown, not "Andromeda" as I would expect. When I have the emulator called it just makes a ding sound, so it probably can't play the desired file and uses some fallback one.
There are plenty of examples here where people pick a custom file from the filesystem and add that to the library anew using "ContentValues". But I do not want to add anything myself, I just want to set one from the default ringtones.
As an alternative I have tried to code as well. It does add an additional entry to the library. Unfortunately old ones are not deleted, but pile up. Also I get the same ding sound when calling the emulator, not the one I selected.
private boolean applyRingTone(File ringtoneFile, int ringtoneType, Context context)
{
Miscellaneous.logEvent("i", "Profile", "Request to set ringtone to " + ringtoneFile.getAbsolutePath(), 3);
ContentValues values = new ContentValues();
values.put(MediaStore.MediaColumns.DATA, ringtoneFile.getAbsolutePath());
values.put(MediaStore.MediaColumns.TITLE, context.getResources().getString(R.string.app_name) + " ringtone");
values.put(MediaStore.MediaColumns.MIME_TYPE, "audio/*");
values.put(MediaStore.MediaColumns.SIZE, ringtoneFile.length());
values.put(MediaStore.Audio.Media.ARTIST, R.string.app_name);
values.put(MediaStore.Audio.Media.IS_RINGTONE, ringtoneType == RingtoneManager.TYPE_RINGTONE);
values.put(MediaStore.Audio.Media.IS_NOTIFICATION, ringtoneType == RingtoneManager.TYPE_NOTIFICATION);
values.put(MediaStore.Audio.Media.IS_ALARM, false);
values.put(MediaStore.Audio.Media.IS_MUSIC, false);
Uri ur = MediaStore.Audio.Media.getContentUriForPath(ringtoneFile.getAbsolutePath());
context.getContentResolver().delete(ur, MediaStore.MediaColumns.DATA + "=\"" + ringtoneFile.getAbsolutePath() + "\"", null);
Uri uri = context.getContentResolver().insert(ur, values);
try
{
RingtoneManager.setActualDefaultRingtoneUri(context, ringtoneType, uri);
Miscellaneous.logEvent("i", "Profile", "Ringtone set to: " + uri.toString(), 1);
return true;
}
catch (Throwable t)
{
String message = "Error setting ringtone: " + Log.getStackTraceString(t);
Miscellaneous.logEvent("e", "Profile", message, 1);
}
return false;
}
I haven't quite solved my problem, yet, but at least spotted the culprit.
The way to set the file as ringtone does work. However the path of the source file is incorrect. That is pretty weird because it is determined by using a filepicker:
Intent fileIntent = new Intent(Intent.ACTION_GET_CONTENT);
fileIntent.setType("audio/*");
startActivityForResult(Intent.createChooser(fileIntent, "Select a ringtone"), intentCodeRingtonePickerCallsFile);
This returns as path:
content://com.android.externalstorage.documents/document/primary%3AMusic%2Fmusicfile.mp3
After converting it to a File object it'll become
/document/primary%3AMusic%2F04.%20Elevator%20Girl.mp3
When just hard-coding the path into the sourcecode (in a format a regular Linux user would assume it needs to have) it is actually simply:
/sdcard/Music/musicfile.mp3
And that works flawlessly. I'll have to figure out how to correctly determine the path, but the method to set a file as ringtone is functional.
UPDATE: I have an answer to my path problem: Get Real Path For Uri Android

How do insert file with content resolver in Android?

I'm trying to insert audio file to shared storage in Android. I'm getting error on api 29(emulator).
Error :
java.lang.IllegalArgumentException: Primary directory (invalid) not allowed for content://media/external_primary/audio/media; allowed directories are [Alarms, Music, Notifications, Podcasts, Ringtones]
My Code is:
...
Uri collection = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
? MediaStore.Audio.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
: MediaStore.Audio.Media.getContentUri(MediaStore.VOLUME_EXTERNAL);
values = new ContentValues();
values.put(MediaStore.Audio.Media.DISPLAY_NAME, targetFileName);
values.put(MediaStore.Audio.Media.RELATIVE_PATH, targetFileDirPath);
values.put(MediaStore.Audio.Media.MIME_TYPE, "audio/mpeg");
values.put(MediaStore.Audio.Media.IS_PENDING, 1);
resolver = getContentResolver();
uri = resolver.insert(collection, values); // error throws from here
outputStream = uri != null ? resolver.openOutputStream(uri) : null;
...
What is the cause of this error and how can I solve this problem?
Apparently, MediaStore.Audio.Media.getContentUri() does not return a directly-usable Uri, at least on Android 10+. It points to an abstract location for "audio", but you cannot write content directly to that Uri. Instead, you need to use RELATIVE_PATH to specify one of the supported collections (Alarms, Music, Notifications, Podcasts, Ringtones), and then any path that you want inside of there.
Note, though, that RELATIVE_PATH itself is new to Android 10. For Android 9 and older devices, I recommend just writing to the filesystem directly.
{ Confirm usage of #CommnWare
values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS + "/" + FILE_DIR);
values.put(MediaStore.MediaColumns.IS_PENDING, 1);
values.put(MediaStore.MediaColumns.DISPLAY_NAME, FILE_NAME);
values.put(MediaStore.MediaColumns.MIME_TYPE, "text/plain");enter code here
ContentResolver resolver = _context.getContentResolver();
resolver.insert(MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY), values);
}

MediaStore - delete file after making folder hidden

I'm operating on the MediaStore directly and add/remove files from there whenever my app add/removes files. I want fast and instant MediaStore updates and the support of batch operations, that's why I do that. Here's an example of an add operation:
Uri uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
File f = new File(path);
ContentValues values = new ContentValues(7);
values.put(MediaStore.Images.Media.TITLE, fileName);
values.put(MediaStore.Images.Media.DISPLAY_NAME, fileName);
values.put(MediaStore.Images.Media.DATE_TAKEN, dateTaken);
values.put(MediaStore.Images.Media.DATE_MODIFIED, dateModified / 1000L);
values.put(MediaStore.Images.Media.MIME_TYPE, ExtensionUtil.getMimeType(fileName));
values.put(MediaStore.Images.Media.ORIENTATION, rotation);
values.put(MediaStore.Images.Media.DATA, filePath);
if (latitude != null || longitude != null)
{
values.put(MediaStore.Images.Media.LATITUDE, latitude);
values.put(MediaStore.Images.Media.LONGITUDE, longitude);
}
ContentProviderOperation operation = ContentProviderOperation.newInsert(uri)
.withValues(values)
.build()
Here's an delete operation:
String columnData = MediaStore.Images.Media.DATA;
Uri uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
ContentProviderOperation operation = ContentProviderOperation.newDelete(uri)
.withSelection(columnData + "=?", new String[]{path})
.build();
With this sort of operations I'm able to handle all cases and execute them in batches as I desire.
Problem
Hide a folder. If I hide a folder, I create a .nomedia file in it, afterwards I want to remove the entries of all medias in it from the MediaStore, BUT KEEP the files on the storage of course. Any ideas how I could create operations that do not delete the file as well?
I don't want to use the MediaScanner, I'm optimising speed and I need the operations so that I can call a lot of them in a batch if possible...

How to change album art using Jaudiotagger and MediaScanner in android?

I am using Jaudiotagger 2.2.5 for an android music tagging app. I am able to change metadata like album name, artist name, genre etc. But no matter what I try I can't get the album art part working. I have exhausted all suggestions that I could find online, but nothing seems to work. Jaudiotagger itself lacks documentation nor is the developer very helpful answering such issues.
for(Song s : songlist){ //for each song in the album
File file = new File(artUri);
if(file.exists()) {
Artwork cover = ArtworkFactory.createArtworkFromFile(file);
tag.deleteArtworkField();
tag.createField(cover);
tag.setField(cover);
af.commit();
}
Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
intent.setData(Uri.fromFile(f));
sendBroadcast(intent);
}
Additionally I am using another method to update the mediastore:
public void updateAlbumArtMediaStore(Context context, final long id, String art){
Uri uri = ContentUris.withAppendedId(Uri.parse("content://media/external/audio/albumart"), id);
context.getContentResolver().delete(uri,null, null);
ContentValues values = new ContentValues();
values.put("album_id", id);
values.put("_data", art);
Uri newuri = context.getContentResolver()
.insert(Uri.parse("content://media/external/audio/albumart"),
values);
if(newuri!=null){
Toast.makeText(AlbumTagEditorActivity.this, "UPDATED", Toast.LENGTH_LONG).show();
context.getContentResolver().notifyChange(uri, null);
}else{
Toast.makeText(AlbumTagEditorActivity.this, "FAILED", Toast.LENGTH_LONG).show();
}
}
But when I do this it only deletes the cover art.
Its been months since I am trying to get this right. Nothing I have tried ever worked.

Categories

Resources