Commit 9c36a835 authored by Matthieu Gallien's avatar Matthieu Gallien 🎵
Browse files

gets more accurate metadata for tracks and improve reliability

parent bd039c16
Pipeline #54793 passed with stage
in 9 minutes and 40 seconds
......@@ -66,7 +66,7 @@
</service>
</application>
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28"/>
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="30"/>
<supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
......
......@@ -15,91 +15,186 @@ import android.content.pm.PackageManager;
import android.database.Cursor;
import android.content.Intent;
import android.provider.MediaStore;
import java.util.ArrayList;
import java.util.HashMap;
public class ElisaActivity extends QtActivity
{
private static String[] tracksRequestedColumns = {
MediaStore.Audio.Media._ID,
MediaStore.Audio.Media.TITLE,
MediaStore.Audio.Media.TRACK,
MediaStore.Audio.Media.YEAR,
MediaStore.Audio.Media.DURATION,
MediaStore.Audio.Media.DATA,
MediaStore.Audio.Media.ARTIST,
MediaStore.Audio.Media.ARTIST_ID,
MediaStore.Audio.Media.ALBUM,
MediaStore.Audio.Media.ALBUM_ID,
MediaStore.Audio.Media.ALBUM_ARTIST,
MediaStore.Audio.Media.TRACK,
MediaStore.Audio.Media.DURATION,
MediaStore.Audio.Media.DATA,
MediaStore.Audio.Media.COMPOSER,
};
private static String[] albumsRequestedColumns = {
MediaStore.Audio.Albums._ID,
MediaStore.Audio.Albums.ALBUM,
MediaStore.Audio.Albums.ALBUM_ART,
MediaStore.Audio.Albums.ARTIST,
MediaStore.Audio.Albums.NUMBER_OF_SONGS,
MediaStore.Audio.Albums.ALBUM_ART,
};
public static void listAudioFiles(Context ctx)
public static ArrayList<TrackDataType> listAudioFiles(Context ctx)
{
androidMusicScanTracksStarting();
ArrayList<TrackDataType> allTracks = new ArrayList<TrackDataType>();
//Some audio may be explicitly marked as not being music
String tracksSelection = MediaStore.Audio.Media.IS_MUSIC + " != 0";
String tracksSortOrder = MediaStore.Audio.Media.DEFAULT_SORT_ORDER + " ASC";
HashMap<Integer, AlbumDataType> allAlbums = new HashMap<Integer, AlbumDataType>();
if (ContextCompat.checkSelfPermission(ctx, Manifest.permission.READ_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
androidMusicScanTracksFinishing();
return allTracks;
}
androidMusicScanAlbumsStarting();
Cursor albumsCursor = ctx.getContentResolver().query(MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI, albumsRequestedColumns, null, null, null);
androidMusicScanAlbumsFinishing();
albumsCursor.moveToFirst();
return;
if (albumsCursor == null) {
return allTracks;
}
if (!albumsCursor.moveToFirst()) {
return allTracks;
}
do {
allAlbums.put(albumsCursor.getInt(0), new AlbumDataType(albumsCursor.getString(1), albumsCursor.getString(2), albumsCursor.getString(3)));
} while(albumsCursor.moveToNext());
String tracksSelection = MediaStore.Audio.Media.IS_MUSIC + " != 0";
String tracksSortOrder = MediaStore.Audio.Media.DEFAULT_SORT_ORDER + " ASC";
Cursor tracksCursor = ctx.getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, tracksRequestedColumns, tracksSelection, null, tracksSortOrder);
tracksCursor.moveToFirst();
if (tracksCursor == null) {
return allTracks;
}
while(tracksCursor.moveToNext()) {
sendMusicFile(tracksCursor.getString(0) + "||" + tracksCursor.getString(1) + "||" +
tracksCursor.getString(2) + "||" + tracksCursor.getString(3) + "||" +
tracksCursor.getString(4) + "||" + tracksCursor.getString(5) + "||" +
tracksCursor.getString(6) + "||" + tracksCursor.getString(7) + "||" +
tracksCursor.getString(8) + "||" + tracksCursor.getString(9) + "||" +
tracksCursor.getString(10));
if (!tracksCursor.moveToFirst()) {
return allTracks;
}
androidMusicScanTracksFinishing();
do {
AlbumDataType currentAlbum = allAlbums.get(tracksCursor.getInt(5));
androidMusicScanAlbumsStarting();
int trackAndDiscNumber = tracksCursor.getInt(7);
//Some audio may be explicitly marked as not being music
/*String albumsSortOrder = MediaStore.Audio.Albums.DEFAULT_SORT_ORDER + " ASC";
allTracks.add(new TrackDataType(tracksCursor.getString(1), tracksCursor.getString(2),
tracksCursor.getString(4), tracksCursor.getString(6),
trackAndDiscNumber % 1000, trackAndDiscNumber / 1000,
tracksCursor.getLong(8), tracksCursor.getString(9),
currentAlbum.getAlbumCover(), "",
tracksCursor.getString(10)));
} while(tracksCursor.moveToNext());
Cursor albumsCursor = ctx.getContentResolver().query(MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI, albumsRequestedColumns, null, null, albumsSortOrder);
return allTracks;
}
}
albumsCursor.moveToFirst();
class TrackDataType
{
TrackDataType (String title, String artist, String albumName,
String albumArtist, int trackNumber, int discNumber,
long duration, String resourceURI, String albumCover,
String genre, String composer) {
this.mTitle = title;
this.mArtist = artist;
this.mAlbumName = albumName;
this.mAlbumArtist = albumArtist;
this.mTrackNumber = trackNumber;
this.mDiscNumber = discNumber;
this.mDuration = duration;
this.mResourceURI = resourceURI;
this.mAlbumCover = albumCover;
this.mGenre = genre;
this.mComposer = composer;
}
while(albumsCursor.moveToNext()) {
sendMusicAlbum(albumsCursor.getString(0) + "||" + albumsCursor.getString(1) + "||" +
albumsCursor.getString(2) + "||" + albumsCursor.getString(3) + "||" + albumsCursor.getString(4));
}*/
public String getTitle() {
return mTitle;
}
androidMusicScanAlbumsFinishing();
public String getArtist() {
return mArtist;
}
private static native void androidMusicScanTracksStarting();
public String getAlbumName() {
return mAlbumName;
}
public String getAlbumArtist() {
return mAlbumArtist;
}
public int getTrackNumber() {
return mTrackNumber;
}
public int getDiscNumber() {
return mDiscNumber;
}
public long getDuration() {
return mDuration;
}
public String getResourceURI() {
return mResourceURI;
}
public String getAlbumCover() {
return mAlbumCover;
}
public String getGenre() {
return mGenre;
}
public String getComposer() {
return mComposer;
}
private String mTitle;
private String mArtist;
private String mAlbumName;
private String mAlbumArtist;
private int mTrackNumber;
private int mDiscNumber;
private long mDuration;
private String mResourceURI;
private String mAlbumCover;
private String mGenre;
private String mComposer;
}
private static native void sendMusicFile(String musicFile);
class AlbumDataType
{
AlbumDataType (String albumName, String albumArtist, String albumCover) {
this.mAlbumName = albumName;
this.mAlbumArtist = albumArtist;
this.mAlbumCover = albumCover;
}
private static native void androidMusicScanTracksFinishing();
public String getAlbumName() {
return mAlbumName;
}
private static native void androidMusicScanAlbumsStarting();
public String getAlbumArtist() {
return mAlbumArtist;
}
private static native void sendMusicAlbum(String musicFile);
public String getAlbumCover() {
return mAlbumCover;
}
private static native void androidMusicScanAlbumsFinishing();
private String mAlbumName;
private String mAlbumArtist;
private String mAlbumCover;
}
......@@ -33,48 +33,6 @@
#include <algorithm>
#include <memory>
static void tracksAndroidScanStarted(JNIEnv */*env*/, jobject /*obj*/)
{
AndroidFileListing::currentInstance()->androidMusicTracksScanStarted();
}
static void sentMusicFile(JNIEnv */*env*/, jobject /*obj*/, jstring musicFile)
{
QAndroidJniObject musicFileJavaString(musicFile);
AndroidFileListing::currentInstance()->newMusicTrack(musicFileJavaString.toString());
}
static void tracksAndroidScanFinished(JNIEnv */*env*/, jobject /*obj*/)
{
AndroidFileListing::currentInstance()->androidMusicTracksScanFinished();
}
static void albumsAndroidScanStarted(JNIEnv */*env*/, jobject /*obj*/)
{
AndroidFileListing::currentInstance()->androidMusicAlbumsScanStarted();
}
static void sentMusicAlbum(JNIEnv */*env*/, jobject /*obj*/, jstring musicAlbum)
{
QAndroidJniObject musicAlbumJavaString(musicAlbum);
AndroidFileListing::currentInstance()->newMusicAlbum(musicAlbumJavaString.toString());
}
static void albumsAndroidScanFinished(JNIEnv */*env*/, jobject /*obj*/)
{
AndroidFileListing::currentInstance()->androidMusicAlbumsScanFinished();
}
static void readExternalStoragePermissionIsOk(JNIEnv */*env*/, jobject /*obj*/)
{
AndroidFileListing::currentInstance()->readExternalStoragePermissionIsOk();
}
static void readExternalStoragePermissionIsKo(JNIEnv */*env*/, jobject /*obj*/)
{
AndroidFileListing::currentInstance()->readExternalStoragePermissionIsKo();
}
class AndroidFileListingPrivate
{
public:
......@@ -114,105 +72,6 @@ AndroidFileListing *AndroidFileListing::currentInstance()
void AndroidFileListing::registerNativeMethods()
{
JNINativeMethod methods[] {{"androidMusicScanTracksStarting", "()V", reinterpret_cast<void *>(tracksAndroidScanStarted)},
{"sendMusicFile", "(Ljava/lang/String;)V", reinterpret_cast<void *>(sentMusicFile)},
{"androidMusicScanTracksFinishing", "()V", reinterpret_cast<void *>(tracksAndroidScanFinished)},
{"androidMusicScanAlbumsStarting", "()V", reinterpret_cast<void *>(albumsAndroidScanStarted)},
{"sendMusicAlbum", "(Ljava/lang/String;)V", reinterpret_cast<void *>(sentMusicAlbum)},
{"androidMusicScanAlbumsFinishing", "()V", reinterpret_cast<void *>(albumsAndroidScanFinished)},
};
QAndroidJniObject javaClass("org/kde/elisa/ElisaActivity");
QAndroidJniEnvironment env;
jclass objectClass = env->GetObjectClass(javaClass.object<jobject>());
env->RegisterNatives(objectClass,
methods,
sizeof(methods) / sizeof(methods[0]));
env->DeleteLocalRef(objectClass);
}
void AndroidFileListing::readExternalStoragePermissionIsOk()
{
}
void AndroidFileListing::readExternalStoragePermissionIsKo()
{
qCInfo(orgKdeElisaAndroid()) << "AndroidFileListing::readExternalStoragePermissionIsKo";
}
void AndroidFileListing::androidMusicTracksScanStarted()
{
qCInfo(orgKdeElisaAndroid()) << "AndroidFileListing::androidMusicTracksScanStarted";
}
void AndroidFileListing::newMusicTrack(const QString &trackDescription)
{
qCInfo(orgKdeElisaAndroid()) << "AndroidFileListing::newMusicTrack" << trackDescription;
auto trackData = trackDescription.split(QStringLiteral("||"));
auto newTrack = DataTypes::TrackDataType{};
newTrack[DataTypes::TitleRole] = trackData[1];
bool conversionOK = false;
if (trackData[2] != QLatin1String("null")) {
newTrack[DataTypes::TrackNumberRole] = trackData[2].toInt(&conversionOK);
if (!conversionOK) {
qInfo() << "newMusicTrack" << trackData[1] << trackData[2];
}
}
if (trackData[3] != QLatin1String("null")) {
newTrack[DataTypes::YearRole] = trackData[3].toInt(&conversionOK);
if (!conversionOK) {
qInfo() << "newMusicTrack" << trackData[1] << trackData[3];
}
}
if (trackData[4] != QLatin1String("null")) {
newTrack[DataTypes::DurationRole] = QTime::fromMSecsSinceStartOfDay(trackData[4].toInt());
} else {
newTrack[DataTypes::DurationRole] = QTime::fromMSecsSinceStartOfDay(1);
}
newTrack[DataTypes::ResourceRole] = QUrl::fromLocalFile(trackData[5]);
newTrack[DataTypes::ArtistRole] = trackData[6];
newTrack[DataTypes::AlbumRole] = trackData[8];
newTrack[DataTypes::ComposerRole] = trackData[10];
QFileInfo scanFileInfo(trackData[5]);
newTrack[DataTypes::FileModificationTime] = scanFileInfo.metadataChangeTime();
newTrack[DataTypes::RatingRole] = 0;
newTrack[DataTypes::ElementTypeRole] = ElisaUtils::Track;
qCInfo(orgKdeElisaAndroid()) << "AndroidFileListing::newMusicTrack" << newTrack;
d->mNewTracks.push_back(newTrack);
}
void AndroidFileListing::androidMusicTracksScanFinished()
{
qCInfo(orgKdeElisaAndroid()) << "AndroidFileListing::androidMusicTracksScanFinished";
}
void AndroidFileListing::androidMusicAlbumsScanStarted()
{
qCInfo(orgKdeElisaAndroid()) << "AndroidFileListing::androidMusicAlbumsScanStarted";
}
void AndroidFileListing::newMusicAlbum(const QString &albumDescription)
{
auto albumData = albumDescription.split(QStringLiteral("||"));
if (albumData[2] != QLatin1String("null)")) {
d->mCovers[albumData[1]] = QUrl::fromLocalFile(albumData[2]);
}
}
void AndroidFileListing::androidMusicAlbumsScanFinished()
{
qCInfo(orgKdeElisaAndroid()) << "AndroidFileListing::androidMusicAlbumsScanFinished";
Q_EMIT tracksList(d->mNewTracks, d->mCovers);
Q_EMIT indexingFinished();
}
void AndroidFileListing::executeInit(QHash<QUrl, QDateTime> allFiles)
......@@ -237,10 +96,44 @@ void AndroidFileListing::triggerRefreshOfContent()
AbstractFileListing::triggerRefreshOfContent();
QAndroidJniObject::callStaticMethod<void>("org/kde/elisa/ElisaActivity",
QAndroidJniObject musicList = QAndroidJniObject::callStaticObjectMethod("org/kde/elisa/ElisaActivity",
"listAudioFiles",
"(Landroid/content/Context;)V",
"(Landroid/content/Context;)Ljava/util/ArrayList;",
QtAndroid::androidContext().object());
auto nbTracks = musicList.callMethod<jint>("size");
for (int i = 0; i < nbTracks; ++i) {
auto newTrack = DataTypes::TrackDataType{};
auto track = musicList.callObjectMethod("get", "(I)Ljava/lang/Object;", i);
newTrack[DataTypes::TitleRole] = track.callObjectMethod("getTitle", "()Ljava/lang/String;").toString();
newTrack[DataTypes::ArtistRole] = track.callObjectMethod("getArtist", "()Ljava/lang/String;").toString();
newTrack[DataTypes::AlbumRole] = track.callObjectMethod("getAlbumName", "()Ljava/lang/String;").toString();
newTrack[DataTypes::AlbumArtistRole] = track.callObjectMethod("getAlbumArtist", "()Ljava/lang/String;").toString();
newTrack[DataTypes::TrackNumberRole] = track.callMethod<jint>("getTrackNumber");
newTrack[DataTypes::DiscNumberRole] = track.callMethod<jint>("getDiscNumber");
newTrack[DataTypes::DurationRole] = QTime::fromMSecsSinceStartOfDay(track.callMethod<jlong>("getDuration"));
newTrack[DataTypes::ResourceRole] = QUrl::fromLocalFile(track.callObjectMethod("getResourceURI", "()Ljava/lang/String;").toString());
newTrack[DataTypes::ImageUrlRole] = QUrl::fromLocalFile(track.callObjectMethod("getAlbumCover", "()Ljava/lang/String;").toString());
newTrack[DataTypes::GenreRole] = track.callObjectMethod("getGenre", "()Ljava/lang/String;").toString();
newTrack[DataTypes::ComposerRole] = track.callObjectMethod("getComposer", "()Ljava/lang/String;").toString();
QFileInfo scanFileInfo(newTrack.resourceURI().toLocalFile());
newTrack[DataTypes::FileModificationTime] = scanFileInfo.metadataChangeTime();
newTrack[DataTypes::RatingRole] = 0;
newTrack[DataTypes::ElementTypeRole] = ElisaUtils::Track;
qCInfo(orgKdeElisaAndroid()) << "AndroidFileListing::triggerRefreshOfContent" << newTrack;
d->mNewTracks.push_back(newTrack);
}
Q_EMIT tracksList(d->mNewTracks, d->mCovers);
Q_EMIT indexingFinished();
}
DataTypes::TrackDataType AndroidFileListing::scanOneFile(const QUrl &scanFile, const QFileInfo &scanFileInfo, FileSystemWatchingModes watchForFileSystemChanges)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment