Android Open Source - PlayMusicExporter Play Music Manager






From Project

Back to project page PlayMusicExporter.

License

The source code is released under:

Copyright (c) 2015 David Schulte Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Soft...

If you think the Android project PlayMusicExporter listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/*
 * Copyright (c) 2015 David Schulte// w w  w  .j  a v  a2s. c  o m
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

package de.arcus.playmusiclib;

import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.os.Environment;
import android.text.TextUtils;

import com.mpatric.mp3agic.ID3v1Genres;
import com.mpatric.mp3agic.ID3v1Tag;
import com.mpatric.mp3agic.ID3v2;
import com.mpatric.mp3agic.ID3v22Tag;
import com.mpatric.mp3agic.ID3v23Tag;
import com.mpatric.mp3agic.ID3v24Tag;
import com.mpatric.mp3agic.Mp3File;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import de.arcus.framework.logger.Logger;
import de.arcus.framework.superuser.SuperUser;
import de.arcus.framework.superuser.SuperUserTools;
import de.arcus.framework.utils.FileTools;
import de.arcus.framework.utils.MediaScanner;
import de.arcus.playmusiclib.enums.ID3v2Version;
import de.arcus.playmusiclib.exceptions.CouldNotOpenDatabaseException;
import de.arcus.playmusiclib.exceptions.NoSuperUserException;
import de.arcus.playmusiclib.exceptions.PlayMusicNotFoundException;
import de.arcus.playmusiclib.items.MusicTrack;


/**
 * Connects to the PlayMusic data
 */
public class PlayMusicManager {
    /**
     * PlayMusic package id
     */
    public static final String PLAYMUSIC_PACKAGE_ID = "com.google.android.music";

    /**
     * The last created instance
     */
    private static PlayMusicManager instance;

    /**
     * @return Gets the last created instance or returns null if there is no instance
     */
    public static PlayMusicManager getInstance() {
        return instance;
    }

    /**
     * Context of the app, needed to access to the package manager
     */
    private Context mContext;

    /**
     * @return Gets the app context
     */
    public Context getContext() {
        return mContext;
    }

    /**
     * Play Music database
     */
    private SQLiteDatabase mDatabase;

    /**
     * @return Gets the database
     */
    public SQLiteDatabase getDatabase() {
        return mDatabase;
    }

    /**
     * Path to the private app data
     * Eg.: /data/data/com.google.android.music/
     */
    private String mPathPrivateData;

    /**
     * Paths to all possible public app data
     * Eg.: /sdcard/Android/data/com.google.android.music/
     */
    private String[] mPathPublicData;

    /**
     * Application info from PlayMusic
     */
    private ApplicationInfo mPlayMusicApplicationInfo;

    /**
     * @return Gets the path to the database
     */
    private String getDatabasePath() {
        return mPathPrivateData + "/databases/music.db";
    }

    /**
     * The database will be copied to a temp folder to access from the app
     * @return Gets the temp path to the database
     */
    private String getTempDatabasePath() {
        return getTempPath() + "/music.db";
    }

    /**
     * @return Gets the temp path to the exported music
     */
    private String getTempPath() {
        return mContext.getCacheDir().getAbsolutePath();
    }

    /**
     * If this is set the data source will only load offline tracks
     */
    private boolean mOfflineOnly;

    /**
     * @return Returns whether the data source should only load offline tracks
     */
    public boolean getOfflineOnly() {
        return mOfflineOnly;
    }

    /**
     * @param offlineOnly Sets whether the data source should only load offline tracks
     */
    public void setOfflineOnly(boolean offlineOnly) {
        mOfflineOnly = offlineOnly;
    }

    /**
     * If this is set the exporter will add ID3 tag to the mp3 files
     */
    private boolean mID3Enable = true;

    /**
     * @return Gets whether the exporter adds ID3 tags to the mp3 files
     */
    public boolean getID3Enable() {
        return mID3Enable;
    }

    /**
     * @param id3Enable Sets whether the exporter adds ID3 tags to the mp3 files
     */
    public void setID3Enable(boolean id3Enable) {
        mID3Enable = id3Enable;
    }

    /**
     * If this is set the exporter will add the artwork to the ID2v2 tag
     */
    private boolean mID3EnableArtwork = true;

    /**
     * @return Gets whether the exporter adds the artwork image
     */
    public boolean getID3EnableArtwork() {
        return mID3EnableArtwork;
    }

    /**
     * @param id3EnableArtwork Sets whether the exporter adds the artwork image
     */
    public void setID3EnableArtwork(boolean id3EnableArtwork) {
        mID3EnableArtwork = id3EnableArtwork;
    }

    /**
     * If this is set the exporter will also adds ID3v1 tags
     */
    private boolean mID3EnableFallback = true;

    /**
     * @return Gets whether the exporter adds ID3v1 tags as fallback
     */
    public boolean getID3EnableFallback() {
        return mID3EnableFallback;
    }

    /**
     * @param id3EnableFallback Sets whether the exporter adds ID3v1 tags as fallback
     */
    public void setID3EnableFallback(boolean id3EnableFallback) {
        mID3EnableFallback = id3EnableFallback;
    }

    /**
     * The sub version of ID3v2
     * Use 2.3 for default to fix issues with the Windows Windows Media Player
     */
    private ID3v2Version mID3v2Version = ID3v2Version.ID3v23;

    /**
     * @return Gets the sub version of ID3v2
     */
    public ID3v2Version getID3v2Version() {
        return mID3v2Version;
    }

    /**
     * @param id3v2Version Sets the sub version of ID3v2
     */
    public void setID3v2Version(ID3v2Version id3v2Version) {
        mID3v2Version = id3v2Version;
    }

    /**
     * Creates a new PlayMusic manager
     * @param context App context
     */
    public PlayMusicManager(Context context) {
        mContext = context;
        instance = this;
    }


    /**
     * Loads all needed information and opens the database
     * @throws PlayMusicNotFoundException PlayMusic is not installed
     * @throws NoSuperUserException No super user permissions
     * @throws CouldNotOpenDatabaseException Could not open the database
     */
    public void startUp() throws PlayMusicNotFoundException, NoSuperUserException, CouldNotOpenDatabaseException {
        // Gets the package manager
        PackageManager packageManager = mContext.getPackageManager();

        try {
            // Loads the application info
            mPlayMusicApplicationInfo = packageManager.getApplicationInfo(PLAYMUSIC_PACKAGE_ID, 0);
        } catch (PackageManager.NameNotFoundException e) {
            // No PlayMusic
            throw new PlayMusicNotFoundException();
        }


        // Path to the private data
        mPathPrivateData = mPlayMusicApplicationInfo.dataDir;

        List<String> publicDataList = new ArrayList<>();
        // Search on all sdcards
        for (String storage : FileTools.getStorages()) {
            String publicData = storage + "/Android/data/com.google.android.music";

            // Directory exists
            if (FileTools.directoryExists(publicData))
                publicDataList.add(publicData);
        }
        // Convert to array
        mPathPublicData = publicDataList.toArray(new String[publicDataList.size()]);

        // Loads the database
        loadDatabase();
    }

    /**
     * Copies the database to a temp directory and opens it
     * @throws NoSuperUserException No super user permissions
     * @throws de.arcus.playmusiclib.exceptions.CouldNotOpenDatabaseException Could not open the database
     */
    private void loadDatabase() throws NoSuperUserException, CouldNotOpenDatabaseException {
        // Ask for super user
        if (!SuperUser.askForPermissions())
            throw new NoSuperUserException();

        // Close the database
        closeDatabase();

        // Copy the database to the temp folder
        if (!SuperUserTools.fileCopy(getDatabasePath(), getTempDatabasePath()))
            throw new CouldNotOpenDatabaseException();

        // Opens the database
        try {
            mDatabase = SQLiteDatabase.openDatabase(getTempDatabasePath(), null, SQLiteDatabase.OPEN_READONLY);
        } catch (SQLException e) {
            throw new CouldNotOpenDatabaseException();
        }
    }

    /**
     * Reloads the database from PlayMusic
     * @throws NoSuperUserException No super user permissions
     * @throws CouldNotOpenDatabaseException Could not open the database
     */
    public void realoadDatabase() throws NoSuperUserException, CouldNotOpenDatabaseException {
        // Reload database
        loadDatabase();
    }

    /**
     * Closes the database if it's open
     */
    private void closeDatabase() {
        if (mDatabase == null) return;

        mDatabase.close();
    }

    /**
     * Debug function to get the database
     */
    public void copyDatabaseToSdCard() {
        FileTools.fileCopy(getTempDatabasePath(), Environment.getExternalStorageDirectory() + "/music.db");
    }

    /**
     * @return Gets the path to the private music
     */
    public String getPrivateMusicPath() {
        return mPathPrivateData + "/files/music";
    }

    /**
     * Gets the path to the music track
     * @param localCopyPath The local copy path
     * @return The path to the music file
     */
    public String getMusicFile(String localCopyPath) {
        // LocalCopyPath is empty
        if (TextUtils.isEmpty(localCopyPath)) return null;

        String path;

        // Search in the public data
        for (String publicData : mPathPublicData) {
            path = publicData + "/files/music/" + localCopyPath;

            if (FileTools.fileExists(path)) return path;
        }


        // Private music path
        path = getPrivateMusicPath() + "/" + localCopyPath;
        // Don't check if the file exists, this will freeze the UI thread
        //if (SuperUserTools.fileExists(path)) return path;

        return path;
    }

    /**
     * @return Gets the path to the private files
     */
    public String getPrivateFilesPath() {
        return mPathPrivateData + "/files";
    }

    /**
     * Gets the full path to the artwork
     * @param artworkPath The artwork path
     * @return The full path to the artwork
     */
    public String getArtworkPath(String artworkPath) {
        // Artwork path is empty
        if (TextUtils.isEmpty(artworkPath)) return null;

        String path;

        // Search in the public data
        for (String publicData : mPathPublicData) {
            path = publicData + "/files/" + artworkPath;

            if (FileTools.fileExists(path)) return path;
        }

        // Private artwork path
        path = getPrivateFilesPath() + "/" + artworkPath;

        // Don't check if the file exists, this will freeze the UI thread
        // if (SuperUserTools.fileExists(path)) return path;

        return path;
    }

    /**
     * Exports a track to the sd card
     * @param musicTrack The music track you want to export
     * @param dest The destination path
     * @return Returns whether the export was successful
     */
    public boolean exportMusicTrack(MusicTrack musicTrack, String dest) {
        // Check for null
        if (musicTrack == null) return false;

        String srcFile = musicTrack.getSourceFile();

        // Could not find the source file
        if (srcFile == null) return false;

        String fileTmp = getTempPath() + "/tmp.mp3";

        // Copy to temp path failed
        if (!SuperUserTools.fileCopy(srcFile, fileTmp))
            return false;

        // Encrypt the file
        if (musicTrack.isEncoded()) {
            String fileTmpCrypt = getTempPath() + "/crypt.mp3";

            // Encrypts the file
            if (trackEncrypt(musicTrack, fileTmp, fileTmpCrypt)) {
                // Remove the old tmp file
                FileTools.fileDelete(fileTmp);

                // New tmp file
                fileTmp = fileTmpCrypt;
            } else {
                Logger.getInstance().logWarning("ExportMusicTrack", "Encrypting failed! Continue with decrypted file.");
            }
        }

        // Creates the destination directory
        String directory = new File(dest).getParent();
        if (directory != null && !FileTools.directoryExists(directory))
            FileTools.directoryCreate(directory);

        // We want to export the ID3 tags
        if (mID3Enable) {
            // Adds the meta data
            if (!trackWriteID3(musicTrack, fileTmp, dest)) {
                Logger.getInstance().logWarning("ExportMusicTrack", "ID3 writer failed! Continue without ID3 tags.");

                // Failed, moving without meta data
                if (!FileTools.fileMove(fileTmp, dest)) {
                    Logger.getInstance().logError("ExportMusicTrack", "Moving the raw file failed!");

                    // Could not copy the file
                    return false;
                }
            }
        } else {
            // Moving the file
            if (!FileTools.fileMove(fileTmp, dest)) {
                Logger.getInstance().logError("ExportMusicTrack", "Moving the raw file failed!");

                // Could not copy the file
                return false;
            }
        }

        cleanUp();

        // Adds the file to the media system
        new MediaScanner(mContext, dest);

        // Done
        return true;
    }

    /**
     * Copies the music file to a new path and adds the mp3 meta data
     * @param musicTrack Track information
     * @param src The source mp3 file
     * @param dest The destination path
     * return Return if the operation was successful
     */
    private boolean trackWriteID3(MusicTrack musicTrack, String src, String dest) {
        try {
            // Opens the mp3
            Mp3File mp3File = new Mp3File(src);

            // Removes all existing tags
            mp3File.removeId3v1Tag();
            mp3File.removeId3v2Tag();
            mp3File.removeCustomTag();

            // We want to add a fallback ID3v1 tag
            if (mID3EnableFallback) {
                // Create a new tag with ID3v1
                ID3v1Tag tagID3v1 = new ID3v1Tag();

                // Set all tag values
                tagID3v1.setTrack(musicTrack.getTitle());
                tagID3v1.setArtist(musicTrack.getArtist());
                tagID3v1.setAlbum(musicTrack.getAlbum());
                tagID3v1.setYear(musicTrack.getYear());

                // Search the genre
                for(int n=0; n<ID3v1Genres.GENRES.length; n++) {
                    // Genre found
                    if (ID3v1Genres.GENRES[n].equals(musicTrack.getGenre())) {
                        tagID3v1.setGenre(n);
                        break;
                    }
                }

                mp3File.setId3v1Tag(tagID3v1);
            }

            // It can't be null
            ID3v2 tagID3v2 = null;

            // Creates the requested version
            switch(mID3v2Version) {
                case ID3v22:
                    tagID3v2 = new ID3v22Tag();
                    break;
                case ID3v23:
                    tagID3v2 = new ID3v23Tag();
                    break;
                case ID3v24:
                    tagID3v2 = new ID3v24Tag();
                    break;
            }


            // Set all tag values
            tagID3v2.setTitle(musicTrack.getTitle());
            tagID3v2.setArtist(musicTrack.getArtist());
            tagID3v2.setAlbum(musicTrack.getAlbum());
            tagID3v2.setAlbumArtist(musicTrack.getAlbumArtist());
            tagID3v2.setTrack("" + musicTrack.getTrackNumber());
            tagID3v2.setPartOfSet("" + musicTrack.getDiscNumber());
            tagID3v2.setYear(musicTrack.getYear());

            if (!TextUtils.isEmpty(musicTrack.getGenre())) {
                try {
                    // Maybe the genre is not supported
                    tagID3v2.setGenreDescription(musicTrack.getGenre());
                } catch (IllegalArgumentException e) {
                    Logger.getInstance().logWarning("TrackWriteID3", e.getMessage());
                }
            }

            // Add the artwork to the meta data
            if (mID3EnableArtwork) {
                String artworkPath = musicTrack.getArtworkPath();

                if (artworkPath != null) {

                    // Reads the artwork with root permissions (maybe it is in /data)
                    byte[] artworkData = SuperUserTools.fileReadToByteArray(artworkPath);
                    if (artworkData != null) {
                        // The file extension is always .jpg even if the image is a PNG file,
                        // so we need to check the magic number

                        // JPEG is default
                        String mimeType = "image/jpeg";

                        // Check for other image formats
                        if (artworkData.length > 4) {
                            // PNG-Header
                            if (artworkData[0] == -119 && artworkData[1] == 80 && artworkData[2] == 78 && artworkData[3] == 71) {
                                mimeType = "image/png";
                            }
                        }

                        // Adds the artwork to the meta data
                        tagID3v2.setAlbumImage(artworkData, mimeType);
                    }
                }
            }

            mp3File.setId3v2Tag(tagID3v2);


            // Save the file
            mp3File.save(dest);

            // Done
            return true;
        } catch (Exception e) {
            Logger.getInstance().logError("TrackWriteId3", e.toString());
        }

        // Failed
        return false;
    }

    /**
     * Encrypts a track and save it to a new path
     * @param musicTrack The music track
     * @param src The source mp3 file
     * @param dest The destination path
     * @return Return if the operation was successful
     */
    private boolean trackEncrypt(MusicTrack musicTrack, String src, String dest) {

        try {
            AllAccessExporter allAccessExporter = new AllAccessExporter(src, musicTrack.getCpData());

            // Checks the magic number
            if (!allAccessExporter.hasValidMagicNumber()) {
                Logger.getInstance().logError("TrackEncrypt", "Invalid magic number! This is not an AllAccess file");
                return false;
            }

            // Saves the file
            return allAccessExporter.save(dest);
        } catch (Exception e) {
            Logger.getInstance().logError("TrackEncrypt", e.toString());
        }

        // Failed
        return false;

    }

    /**
     * Deletes all cache files
     */
    private void cleanUp() {
        FileTools.fileDelete(getTempPath() + "/tmp.mp3");
        FileTools.fileDelete(getTempPath() + "/crypt.mp3");
    }
}




Java Source Code List

de.arcus.framework.ApplicationTest.java
de.arcus.framework.crashhandler.CrashActivity.java
de.arcus.framework.crashhandler.CrashHandler.java
de.arcus.framework.logger.Logger.java
de.arcus.framework.settings.AppSettings.java
de.arcus.framework.superuser.SuperUserCommandCallback.java
de.arcus.framework.superuser.SuperUserCommand.java
de.arcus.framework.superuser.SuperUserTools.java
de.arcus.framework.superuser.SuperUser.java
de.arcus.framework.utils.FileTools.java
de.arcus.framework.utils.MediaScanner.java
de.arcus.playmusicexporter2.ApplicationTest.java
de.arcus.playmusicexporter2.activitys.MusicTrackDetailActivity.java
de.arcus.playmusicexporter2.activitys.MusicTrackListActivity.java
de.arcus.playmusicexporter2.adapter.MusicTrackAdapter.java
de.arcus.playmusicexporter2.adapter.MusicTrackListAdapter.java
de.arcus.playmusicexporter2.fragments.MusicTrackDetailFragment.java
de.arcus.playmusicexporter2.fragments.MusicTrackListFragment.java
de.arcus.playmusicexporter2.fragments.NavigationDrawerFragment.java
de.arcus.playmusicexporter2.utils.ImageViewLoader.java
de.arcus.playmusicexporter2.utils.MusicPathBuilder.java
de.arcus.playmusiclib.AllAccessExporter.java
de.arcus.playmusiclib.ApplicationTest.java
de.arcus.playmusiclib.PlayMusicManager.java
de.arcus.playmusiclib.datasources.AlbumDataSource.java
de.arcus.playmusiclib.datasources.ArtistDataSource.java
de.arcus.playmusiclib.datasources.DataSource.java
de.arcus.playmusiclib.datasources.MusicTrackDataSource.java
de.arcus.playmusiclib.datasources.PlaylistDataSource.java
de.arcus.playmusiclib.enums.ID3v2Version.java
de.arcus.playmusiclib.exceptions.CouldNotOpenDatabaseException.java
de.arcus.playmusiclib.exceptions.NoSuperUserException.java
de.arcus.playmusiclib.exceptions.PlayMusicNotFoundException.java
de.arcus.playmusiclib.items.Album.java
de.arcus.playmusiclib.items.Artist.java
de.arcus.playmusiclib.items.MusicTrackList.java
de.arcus.playmusiclib.items.MusicTrack.java
de.arcus.playmusiclib.items.Playlist.java