How to get current playing song name in Android - android

According to this post, the song's artist, album, track can be retrieved from a BroadcastReceiver, but the song's information which are current playing in Spotify or Pandora cannot be retrieved.(Maybe because these apps don't send broadcast while playing songs).
My question is, how to get current playing song's information on system level, no matter whether the app is broadcasting.

Listening for com.android.music.* broadcasts is the official way to do this on Android.
Many apps like lyrics displayers rely on it to get info on the current track.
Sadly, if the app is not broadcasting the solution is either to have a look at their Dev documentation in case they provide another way to do this or directly ask them to implement it.
For Spotify, it looks like right now they use a 'com.spotify.music.metadatachanged' broadcast (and a settings in the app). You will have to track down separate cases like this one, nothing forces Android devs to implement the standard broadcast.

In android 4.4+, you can use the RemoteController class to get track information from the current playing song. This works with every app that implements a RemoteControlClient. It also works with video- and streamservices like chromecast.
http://forum.xda-developers.com/showthread.php?p=48702254#post48702254
This is a very handy guide if you want to use this RemoteController.
And the developers page:
https://developer.android.com/reference/android/media/RemoteController.html

definitely u know the path of the song where is running
Uri myUri1 = Uri.parse(path);
now u can call this method having the path then we can get all information either in the activity or from the service by binding
private void getTrackInfo(Uri audioFileUri) {
MediaMetadataRetriever metaRetriever= new MediaMetadataRetriever();
metaRetriever.setDataSource(getRealPathFromURI(audioFileUri));
String artist = metaRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ARTIST);
String title = metaRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_TITLE);
}
private String getRealPathFromURI(Uri uri) {
File myFile = new File(uri.getPath().toString());
String s = myFile.getAbsolutePath();
return s;
}

Related

API 29 Mediastore Access

My app creates playlists in the android mediastore. All is well for api's including 28 however, api 29 seems to require additional permissions.
Inserting a new playlist name and id works without issue. When it comes to inserting track id and play order, an access permission exception is thrown.
In verifying the Uri, i found that when resolver.insert for API 29 the exception error is:
java.lang.SecurityException: myapp_name has no access to content://media/external_primary/audio/media/146
The code:
Uri exturi = MediaStore.Audio.Playlists.Members.getContentUri("external", playlist_id);
// exturi : content://media/external/audio/playlists/227/members
// values : audio_id=146 play_order=0
values.put(MediaStore.Audio.Playlists.Members.PLAY_ORDER, play_order);
values.put(MediaStore.Audio.Playlists.Members.AUDIO_ID, audio_id);
try {
resolver.insert(exturi, values);
} catch (Exception e) {
e.printStackTrace();
}
Strange thing is that although inserting a new playlist into Mediastore works but adding tracks (track_id, play order) gives an access permission error
How to resolve this exception error for API 29?
Update Feb 2021:
a small step forward, I am pretty sure I need to get Documenturi for the original uri but still gives me the access error. So the issue does not lie with accessing the tracks but with the uri itself.
doc_uri = MediaStore.getDocumentUri(context,playlist_members_uri);
java.lang.SecurityException: com.flyingdutchman.newplaylistmanager has no access to content://media/external/audio/playlists/130/members
I think this is an Android 10 bug, so I've filed a report here: https://issuetracker.google.com/issues/147619577 (includes instructions for an emulator test case to reproduce it if that interests you). Please consider starring it to let the Android team know that it affects you.
From what I can tell, it only affects files on 'external' storage, like sdcards mounted on /storage/XXXX-XXXX
In the meantime, the only fix that some of my users were able to successfully apply is to move their music files to the internal storage (reboot and wait for the media scan to finish to be sure that MediaStore is up-to-date).
in my further research for the answer, I came across this;
All about the media database (Mediastore) with android 11
Create playlist with uri "MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI", and the date row in external.db for the playlist is:
_id
_display_name
volume_name
308
New playlist.m3u
external_primary
The playlist's volume name is "external_primary".
2.
Music file is under flash card
Music file's id in external.db is 278
The volume name of flash card is "1EDD-DDE0"
When add this music file to playlist, got below exception:
Exception message: java.lang.SecurityException: has no access to content://media/external_primary/audio/media/278
If I create playlist with uri MediaStore.Audio.Playlists.getContentUri("1edd-dde0"), then music can be successfully added to the playlist.
It seems that the reason is the mismatch of volume name between playlist and the music file to be added. Only when playlist's volume name is same to music file's, inserting operation can be complete.
Update for android 11.
Worth noting that the media database has moved from
/data/data/com.android.providers.media
to
/data/data/com.google.android.providers.media.module
also the structures have changes significantly
and
I came across the same issue. As the MediaProvider changes to Google's MediaProvider, the Scoped Storage feature is activated. When you try to modify a playlist file, but it's not created by your app (or it did be created by your app, but after OTA to new Android version, which changes to use Google's MediaProvider, it scans your playlist file and put a record to its database, but leaves the owner_package_name colume empty, it's a new colume, the old MediaProvider database has no owner_package_name colume, so no one could tell this playlist file was created by you), you will get a SecurityException says you have no access to this file.
You can check if the playlist file was owned by your app before doing the 'insert' operation:
Uri uri = MediaStore.Audio.Playlists.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
String[] projection = new String[] {
MediaStore.Audio.Playlists._ID,
MediaStore.Audio.Playlists.NAME,
MediaStore.Audio.Playlists.OWNER_PACKAGE_NAME,
};
String where = MediaStore.Audio.Playlists._ID + "=?";
String[] args = new String[] {playlistFileId};
Cursor cursor = resolver.query(uri, projection, where, args, null);
if (cursor != null) {
cursor.moveToFirst();
if (!cursor.isAfterLast()) {
String ownerPkg = cursor.getString(
cursor.getColumnIndex(MediaStore.Audio.Playlists.OWNER_PACKAGE_NAME));
// print ownerPkg here
}
}
If the owner package name of this playlist file is empty or other app's package name, that you probably have no access to write this playlist file due to the scoped storage feature limit.
According to this document, we can consider using MediaStore.createWriteRequest() method to prompt user to grant write permission to playlist file for our own app, but this request only available to certain kind of files, like images, audios, videos etc, but not for some other kinds like playlist files which ends in .m3u suffix.
Also, according to this, when you try to operate some image or audio files that's not created by your app in public storage, you will get a RecoverableSecurityException and you can use this exception to prompt user to get user consent to modify the file, but for playlist kind files, you will just get SecurityException instead of RecoverableSecurityException.
So the result is, you may never be able to access to that playlist file again, you can not modify it, and you can not delete it too. My solution is just create a new playlist file, so it's owned by my app, now I finally have full access to it. You may need to migrate your old playlist data to the new one.
AND FINALLY I FIND THIS
MediaStore.Audio.Playlists
This class was deprecated in API level 31.
Android playlists are now deprecated. We (Google) will keep the current functionality for compatibility resons, but we will no longer take feature request. We do not advise adding new usages of Android Playlists. M3U files can be used as an alternative.
In conclusion, no longer a relevant post
I have implemented the SAF so do not use scopedStorage and have access once the user accepts.
The fact that I can insert new playlist entries clearly shows access to MediaStore, I can also delete these. However trying to add tracks to these playlists does not work for api29. Inserting/deleting a new playlist does not involve any files located on internal or external sdcards as it is simply adding values.
the permissions for both internal and external sdcard:
2020-07-12 14:39:04.435 11858-11858/com.flyingdutchman.newplaylistmanager E/onCreate:: uriPermission: UriPermission {uri=content://com.android.externalstorage.documents/tree/17F5-240A%3A, modeFlags=3, persistedTime=1594551961263}
2020-07-12 14:39:04.435 11858-11858/com.flyingdutchman.newplaylistmanager E/onCreate:: uriPermission: UriPermission {uri=content://com.android.externalstorage.documents/tree/primary%3A, modeFlags=3, persistedTime=1594551926876}
The question now becomes
How do I ensure saf permissions are recognised by the resolver.insert method when inserting/modify tracks into the Media database
Update May 2020
Stepping through the resolver code with debug F7
Scenario 1 results in permission error (incorrect MediaStore.VOLUME_EXTERNAL).
playlist_uri = MediaStore.Audio.Playlists.getContentUri(MediaStore.VOLUME_EXTERNAL);
playlist_members_uri = MediaStore.Audio.Playlists.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
.buildUpon()
.appendEncodedPath(Long.toString(playlist_id))
.appendEncodedPath("members")
.build();
acquireProvider(mContext, auth); = media
Uri createdRow = provider.insert(mPackageName, mAttributionTag, url, values, extras); = null
mPackageName=com.flyingdutchman.newplaylistmanager
mAttributionTag=null
values[0] = 206
values[1]=69
values[2]=1
extras=null
DatabaseUtils.java
public static final void readExceptionFromParcel(Parcel reply) {
int code = reply.readExceptionCode();
if (code == 0) return;
String msg = reply.readString();
DatabaseUtils.readExceptionFromParcel(reply, msg, code);
}
msg = com.flyingdutchman.newplaylistmanager has no access to content://media/external_primary/audio/playlists/206
Scenario 2 results in NO permission error BUT no tracks added to audio_playlists_map table.
playlist_uri = MediaStore.Audio.Playlists.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
url=content://media/external_primary/audio/playlists/206/members

Fastest way to check if Video File have Following MetaData?

There are Number of ways to check video file Meta data , using FFmpegMediaMetadataRetriever(Slow but reliable) and using Native MediaMetadataRetriever(Slow and not reliable).
There are number of question answered in SO for same purpose to get MetaData using FFMPEG or Native Media api , like Q1 , Q2 , Q3 but they are not solving my problem.
My Problem:
Get following meta data from file(Video) of android directory:
Video have Sound/Audio or not?
Creation date and time
thumbnail of video file
Kindly let me know if you have any suggestion or code samples would be big help.
when i want check video has audio or not that time i created this method. method return True if Video has Audio otherwise False
just you pass Context and Uri of Your video file
private boolean setHasAudioOrNot(JoinVideoActivity activity, Uri uri) {
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
retriever.setDataSource(activity.getApplicationContext(),uri);
Log.e("Command","audiohas?? " +retriever.extractMetadata(16));
return retriever.extractMetadata(16) != null;
}

Android: Listen for the Audio file deleted

I'm using android MediaStore to get the list of audio files on my music app and these files can be added to playlist. If the file gets deleted from the device memory, the file information need to be deleted from playlist.
Is there any listener/receiver to listen when the audio files are deleted from the device internal/external memory?
I know we can use FileObserver for any file change. But this required a background service to always run and listen for the file changed. I want to avoid having a service for this. Is there any other way?
You can register a BroadcastReceiver for MediaScanner events, and refresh your list whenever you receive Intent.ACTION_MEDIA_SCANNER_FINISHED intent.
IntentFilter scanFileReceiverFilter= new IntentFilter();
scanFileReceiverFilter.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED);
scanFileReceiverFilter.addDataScheme("file");
BroadcastReceiver scanFileReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equals(Intent.ACTION_MEDIA_SCANNER_FINISHED)) {
Uri uri = intent.getData();
String path = null;
if (uri != null) {
path = uri.getPath();
}
//TODO:refresh list in path
}
}
};
getActivity().registerReceiver(scanFileReceiver, scanFileReceiverFilter);
Any app which is deleting local data is expected to run Media scanner after changes, to sync the media DB, so you should receive this.
But this works only while your application is running. For background deleting, you should register one in manifest.
If you are using DB to store playlists entries, then this will be efficient as you can check all files with path LIKE path received for refreshing.
Please note that sometimes this event might be received late due to huge amount of content present in device.
You can also use ContentObserver calling registerContentObserver with uri MediaStore.Audio.Media.getContentUri("external"). Since all Media Providers have Audio view, this should work efficiently.
Try registering ContentObserver with context.getContentResolver.registerContentObserver(args). I do this in onResume(). get current number of tracks in device memory. I do this in onCreate(). also get current number of tracks in the onchanged method of the ContentObserver in onResume(). If the figures don't match, refresh your current list. You can also register your ContentObserver in service for consistent results. Also don't forget to unregister ContentObserver in onPause() and onDestroy() using context.getContentResolver.unregisterContentObserver(args). works like charm. I will post the code once I retrieve it. Happy coding!

How can I get the location data of a video in Android?

I am trying to get the metadata for the video files stored on my app's user's phone. I can get the file name, id, date taken and so on. However, latitude and longitude data always returns as 0.0. I have been referring to this:
developer.android.com/reference/android/provider/MediaStore.Video.VideoColumns.html
Yes, I am already enabling use location in my settings. I have a very similar function to this for images which works fine.
public void getLocalVideoFiles(Context context) {
ContentResolver videoResolver = context.getContentResolver();
Uri videoUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
String test = getRealPathFromURI(context, videoUri);
Cursor videoCursor = videoResolver.query(videoUri, null, null, null, null);
if(videoCursor!=null && videoCursor.moveToFirst()){
//get columns
int latColumn = videoCursor.getColumnIndex
(MediaStore.Video.Media.LATITUDE);
int lonColumn = videoCursor.getColumnIndex
(MediaStore.Video.Media.LONGITUDE);
do {
String thisLat = Double.toString(videoCursor.getDouble(latColumn));
String thisLon = Double.toString(videoCursor.getDouble(lonColumn));
Log.d("video Latitude",thisLat);
Log.d("video Longitude",thisLon);
}
while (videoCursor.moveToNext());
}
return localClips;
}
The approach described here: Geotagging a captured video yields similar results (null value in the METADATA_KEY_LOCATION column).
So, my question is: does the built-in Android video tool record location data when creating videos? It seems like the answer is no, but I don't understand why there are columns for the location data if this is the case. If that is not the case, how can I access the video location data? I need the location of video files that have already been taken.
Thanks in advance!
Well i've just tested your assumption that google does not keep location data while recording video and it's incorrect.
For example: using my nexus 5 with version 5.1 i was able to get a geotag on a video i just took. you can try it by yourself and if your phone is rooted, just browse the MediaStore external DB (com.android.providers.media) using some SQLITE viewer
but let's say that google does not keep GeoTag. there are number of reasons why they would keep such a column:
To support other video libraries that do want to keep geo taggging
To allow users who are implementing Video recorder a way to save current location ( using FusedLocation or something similar). a way of doing it is just updating the relevant row in the DB for example:
getContentResolver().update(MediaStore.Video.Media.EXTERNAL_CONTENT_URI.buildUpon().appendPath("2152").build(), cv, null, null);
to support previous versions that did support tagging

Can one retrieve data from a MediaPlayer's stream?

If I were to stream some sort of media to a MediaPlayer, is there any way I could copy it before/as/after it is played? For instance, if I were to stream a YouTube clip, is it possible to save that clip as it is being played?
Edit:
(Ocelot's answer made me realise how localised this question is).
What I am looking to do is copy the stream of a MediaPlayer already in progress (be it youtube or music stream). I want to be able to be notified when a new stream starts and ends. So far the only thing I found (for the latter) that is even remotely close it the broadcast string ACTION_AUDIO_BECOMING_NOISY but that doesn't really do anything for what I need. I there any way to do this?
I haven't tested this, and it looks like quite a bit of work, but here is what I would try:
Create a subclass of Socket. In this class, you can handle all byte reads, and save the stream locally or do whatever you want with it
Create your own content provider, which you can use to pass URIs to your media player, in your own format. Example: mystream://youtube.com/watch?v=3Rhy37u
In your content provider, override the openFile method and in it, open your own socket, and create a ParcelFileDescriptor with it.
Now, simply passing the new format url to your mediaplayer should make all streams go through your Socket, where you can save your data.
one way is to first find out where the video is in the server for exmaple in youtube with simple regex like this :
Regex("(?<=&t=)[^&]*").Match(file).Value;
you could retrieve url to the video and then download it like
public static void Download(string videoID, string newFilePath)
{
WebClient wc = new WebClient();
string file = wc.DownloadString(string.Format("http://www.youtube.com/watch?v={0}", videoID));
string t = new Regex("(?<=&t=)[^&]*").Match(file).Value;
wc.DownloadFile(string.Format("http://www.youtube.com/get_video?t={0}=&video_id={1}",t,videoID), newFilePath);
}
it's c# code but you could easily convert it to java.
#zrgiu
I tried to go with this solution, but the MediaPlayer retrieves a FileDescriptor from the URI, so sadly no http URL can be passed like this.
I also found another solution, it suggests to create a local ProxyServer on your device to serve files from the internet, it should be possible to also save the files streamed via the proxy.

Categories

Resources