com.lunchareas.echomp.utils.MediaDataUtils.java Source code

Java tutorial

Introduction

Here is the source code for com.lunchareas.echomp.utils.MediaDataUtils.java

Source

/*
Echo Music Player
Copyright (C) 2019 David Zhang
    
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
    
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
    
You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.lunchareas.echomp.utils;

import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.media.MediaPlayer;
import android.net.Uri;
import android.provider.BaseColumns;
import android.provider.MediaStore;
import android.util.Log;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.lunchareas.echomp.R;
import com.lunchareas.echomp.databases.RecentlyPlayedPair;
import com.lunchareas.echomp.databases.RecentlyPlayedStore;
import com.lunchareas.echomp.databases.TopTracksPair;
import com.lunchareas.echomp.databases.TopTracksStore;
import com.lunchareas.echomp.dataloaders.SongLoader;
import com.lunchareas.echomp.models.Song;

import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class MediaDataUtils {

    private static final String TAG = MediaDataUtils.class.getName();

    private static final int NUM_OF_DAYS = 7;

    /*
    SONG UTILITIES:
     */
    public static void deleteSong(long id, String path, Context context) {

        // Delete the file
        File file = new File(path);
        if (!file.delete()) {
            Log.e(TAG, "Failed to delete song?");
        }

        // Delete song from db
        context.getContentResolver().delete(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, "_id=" + id, null);
    }

    public static void deleteSongList(List<Song> songList, Context context) {

        // Go through songs and delete individually
        for (Song song : songList) {

            // Delete the file
            File file = new File(song.getPath());
            if (!file.delete()) {
                Log.e(TAG, "Failed to delete song?");
            }

            // Delete song from db
            context.getContentResolver().delete(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, "_id=" + song.getId(),
                    null);
        }
    }

    public static void renameSong(long id, String name, Context context) {

        // Create values
        ContentValues values = new ContentValues();
        values.put(MediaStore.Audio.Media.TITLE, name);

        // Update database
        context.getContentResolver().update(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, values, "_id=" + id, null);
    }

    public static void changeSongAlbum(long id, String album, Context context) {

        // Create values
        ContentValues values = new ContentValues();
        values.put(MediaStore.Audio.Media.ALBUM, album);

        // Update database
        context.getContentResolver().update(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, values, "_id=" + id, null);
    }

    public static void changeSongArtist(long id, String artist, Context context) {

        // Create values
        ContentValues values = new ContentValues();
        values.put(MediaStore.Audio.Media.ARTIST, artist);

        // Update database
        context.getContentResolver().update(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, values, "_id=" + id, null);
    }

    public static void updateSongMetadata(final long songId, final long albumId, final String url,
            final String path, final Context context) {

        // Get youtube ID and JSON object
        final String videoId = MusicDownloadUtils.getYoutubeId(url);

        /*
        Update cover art from YT
         */
        Thread image = new Thread(new Runnable() {
            @Override
            public void run() {

                try {
                    // Connect to URL
                    Log.e(TAG, "Connecting to YT API for data.");
                    URL jsonUrl = new URL("https://www.googleapis.com/youtube/v3/videos?id=" + videoId + "&key="
                            + Constants.YT_API_KEY + "&part=snippet");
                    HttpURLConnection request = (HttpURLConnection) jsonUrl.openConnection();
                    request.connect();

                    // Convert to json object
                    JsonParser parser = new JsonParser();
                    JsonElement root = parser.parse(new InputStreamReader((InputStream) request.getContent()));
                    JsonObject object = root.getAsJsonObject();
                    JsonObject thumbnails = object.getAsJsonArray("items").get(0).getAsJsonObject()
                            .getAsJsonObject("snippet").getAsJsonObject("thumbnails");

                    // Change artist name
                    boolean standard = thumbnails.has("standard");
                    boolean maxres = thumbnails.has("maxres");

                    // Download highest quality image
                    if (maxres) {
                        String link = thumbnails.getAsJsonObject("maxres").get("url").toString();
                        link = link.substring(1, link.length() - 1);
                        ImageDownloadUtils.downloadSongArt(link, songId, albumId, context);
                        Log.e(TAG, "Downloading max res image.");
                    } else if (standard) {
                        String link = thumbnails.getAsJsonObject("standard").get("url").toString();
                        link = link.substring(1, link.length() - 1);
                        ImageDownloadUtils.downloadSongArt(link, songId, albumId, context);
                        Log.e(TAG, "Downloading standard res image.");
                    } else {
                        String link = thumbnails.getAsJsonObject("high").get("url").toString();
                        link = link.substring(1, link.length() - 1);
                        ImageDownloadUtils.downloadSongArt(link, songId, albumId, context);
                        Log.e(TAG, "Downloading low res image.");
                    }

                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });

        /*
        Get data from Youtube API, key in Constants.java
         */
        Thread metadata = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    // Connect to URL
                    URL jsonUrl = new URL("https://www.googleapis.com/youtube/v3/videos?id=" + videoId + "&key="
                            + Constants.YT_API_KEY + "&part=snippet");
                    HttpURLConnection request = (HttpURLConnection) jsonUrl.openConnection();
                    request.connect();

                    // Convert to json object
                    JsonParser parser = new JsonParser();
                    JsonElement root = parser.parse(new InputStreamReader((InputStream) request.getContent()));
                    JsonObject object = root.getAsJsonObject();
                    JsonObject snippet = object.getAsJsonArray("items").get(0).getAsJsonObject()
                            .getAsJsonObject("snippet");

                    /*
                    for (Map.Entry<String, JsonElement> entry: artistObject.entrySet()) {
                    Log.e(TAG, entry.getKey());
                    if (entry.getKey().equals("channelTitle")) {
                        MediaDataUtils.changeSongArtist(songId, entry.getValue().toString(), context);
                        Log.e(TAG, "Changed artist to " + entry.getValue().toString() + ".");
                    }
                    }
                    */

                    // Change artist name
                    String artist = snippet.get("channelTitle").toString();
                    artist = artist.substring(1, artist.length() - 1);
                    MediaDataUtils.changeSongArtist(songId, artist, context);

                    // TODO Find better album names?
                    String album = snippet.get("title").toString();
                    album = album.substring(1, album.length() - 1);
                    MediaDataUtils.changeSongAlbum(songId, album, context);

                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });

        // Wait for completions
        metadata.start();
        image.start();
        try {
            metadata.join();
            image.join();
        } catch (Exception e) {
            e.printStackTrace();
        }
        Log.d(TAG, "Finished waiting for image and metadata change.");
    }

    /*
    PLAYLIST UTILITIES:
     */
    public static long createPlaylist(String name, Context context) {

        // Create cursor
        ContentResolver resolver = context.getContentResolver();
        String[] projection = new String[] { MediaStore.Audio.PlaylistsColumns.NAME };
        String selection = MediaStore.Audio.PlaylistsColumns.NAME + " = '" + name + "'";
        Cursor cursor = resolver.query(MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI, projection, selection, null,
                null);

        // Create playlist in media store
        if (cursor != null) {
            if (cursor.getCount() <= 0) {
                ContentValues values = new ContentValues(1);
                values.put(MediaStore.Audio.PlaylistsColumns.NAME, name);
                Uri uri = resolver.insert(MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI, values);
                if (uri != null) {
                    cursor.close();
                    return Long.parseLong(uri.getLastPathSegment());
                }
            }
            cursor.close();
        }

        return 0;
    }

    public static void deletePlaylist(long id, Context context) {

        // Delete from db
        context.getContentResolver().delete(MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI, "_id=" + id, null);
    }

    public static void deletePlaylistSongs(long playlistId, List<Long> selected, Context context) {

        // Go through songs and delete
        Uri uri = MediaStore.Audio.Playlists.Members.getContentUri("external", playlistId);
        ContentResolver resolver = context.getContentResolver();
        for (Long id : selected) {
            String[] loc = { Long.toString(id) };
            resolver.delete(uri, MediaStore.Audio.Playlists.Members.AUDIO_ID + "=?", loc);
        }
    }

    public static void renamePlaylist(long id, String name, Context context) {

        // Create the data
        ContentValues values = new ContentValues();
        values.put(MediaStore.Audio.Playlists.NAME, name);

        // Insert data
        context.getContentResolver().update(MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI, values, "_id=" + id,
                null);
    }

    public static List<Song> getSongsFromPlaylist(long playlistId, Context context) {

        // Initial model
        List<Song> songList;

        // Handle diff cases
        if (playlistId == Constants.LAST_ADDED_ID)
            songList = getSongsFromLastAddedPlaylist();
        else if (playlistId == Constants.RECENTLY_PLAYED_ID)
            songList = getSongsFromRecentlyPlayedPlaylist(context);
        else if (playlistId == Constants.TOP_TRACKS_ID)
            songList = getSongsFromTopTracksPlaylist(context);
        else
            songList = getSongsFromDefaultPlaylist(playlistId, context);

        return songList;
    }

    private static List<Song> getSongsFromLastAddedPlaylist() {

        // Get songs first
        List<Song> songList = new ArrayList<>();
        for (Song song : SongLoader.songList) {
            if (song.getDate() > (System.currentTimeMillis() / 1000 - NUM_OF_DAYS * 3600 * 24))
                songList.add(song);
        }

        return songList;
    }

    private static List<Song> getSongsFromRecentlyPlayedPlaylist(Context context) {

        // Get songs from store
        RecentlyPlayedStore store = new RecentlyPlayedStore(context);
        List<RecentlyPlayedPair> pairList = store.getPairList();
        Collections.sort(pairList, new Comparator<RecentlyPlayedPair>() {
            public int compare(RecentlyPlayedPair pair1, RecentlyPlayedPair pair2) {
                return pair2.getVal() - pair1.getVal();
            }
        });

        // Create song list
        List<Song> songList = new ArrayList<>();
        for (int i = 0; i < pairList.size(); i++) {
            songList.add(SongLoader.findSongById(pairList.get(i).getKey()));
        }

        return songList;
    }

    private static List<Song> getSongsFromTopTracksPlaylist(Context context) {

        // Get songs from store
        TopTracksStore store = new TopTracksStore(context);
        List<TopTracksPair> pairList = store.getPairList();
        Collections.sort(pairList, new Comparator<TopTracksPair>() {
            public int compare(TopTracksPair pair1, TopTracksPair pair2) {
                return pair2.getVal() - pair1.getVal();
            }
        });

        // Get number of songs
        int num = (int) (pairList.size() * 0.3);
        if (num < 3)
            num = pairList.size();
        else if (num > 6)
            num = 6;

        // Create song list
        List<Song> songList = new ArrayList<>();
        for (int i = 0; i < num; i++) {
            songList.add(SongLoader.findSongById(pairList.get(i).getKey()));
        }

        return songList;
    }

    private static List<Song> getSongsFromDefaultPlaylist(long playlistId, Context context) {

        // Initial model
        List<Song> songList = new ArrayList<>();

        // Get data
        String[] projection = { MediaStore.Audio.Playlists.Members.AUDIO_ID,
                MediaStore.Audio.Playlists.Members.TITLE, MediaStore.Audio.Playlists.Members.DATA,
                MediaStore.Audio.Playlists.Members.ARTIST, MediaStore.Audio.Playlists.Members.ARTIST_ID,
                MediaStore.Audio.Playlists.Members.ALBUM, MediaStore.Audio.Playlists.Members.ALBUM_ID,
                MediaStore.Audio.Playlists.Members.DURATION, MediaStore.Audio.Playlists.Members.DATE_ADDED };
        Cursor cursor = context.getContentResolver().query(
                MediaStore.Audio.Playlists.Members.getContentUri("external", playlistId), projection, null, null,
                null);

        // Go through cursor
        if (cursor != null && cursor.moveToFirst()) {
            do {

                // Get the data
                long id = cursor.getLong(0);
                String title = cursor.getString(1);
                String path = cursor.getString(2);
                String artist = cursor.getString(3);
                long artistId = cursor.getLong(4);
                String album = cursor.getString(5);
                long albumId = cursor.getLong(6);
                int duration = cursor.getInt(7);
                long date = cursor.getLong(8);

                // Create media player
                MediaPlayer mp = MediaPlayer.create(context, Uri.parse(path));
                if (mp != null) {

                    // Create model and add to list
                    Song song = new Song(id, title, path, artist, artistId, album, albumId, duration, date);
                    songList.add(song);
                }

            } while (cursor.moveToNext());
            cursor.close();
        }

        return songList;
    }

    public static void addSongsToPlaylist(long id, List<Song> songList, Context context) {

        // Ignore empty songs
        if (songList == null || songList.size() == 0)
            return;

        // Create values
        int count = getCountWithPlaylistId(context, id);
        ContentValues[] values = new ContentValues[songList.size()];
        for (int i = 0; i < songList.size(); i++) {
            values[i] = new ContentValues();
            values[i].put(MediaStore.Audio.Playlists.Members.PLAY_ORDER, i + count + 1);
            values[i].put(MediaStore.Audio.Playlists.Members.AUDIO_ID, songList.get(i).getId());
        }

        // Add to db
        Uri uri = MediaStore.Audio.Playlists.Members.getContentUri("external", id);
        ContentResolver resolver = context.getContentResolver();
        resolver.bulkInsert(uri, values);
        resolver.notifyChange(Uri.parse("content://media"), null);
    }

    private static int getCountWithPlaylistId(final Context context, final long playlistId) {

        // Get cursor
        Cursor cursor = context.getContentResolver().query(
                MediaStore.Audio.Playlists.Members.getContentUri("external", playlistId),
                new String[] { BaseColumns._ID }, MediaStore.Audio.AudioColumns.IS_MUSIC + "=1" + " AND "
                        + MediaStore.Audio.AudioColumns.TITLE + " != ''",
                null, null);

        // Get count
        if (cursor != null) {
            if (cursor.moveToFirst()) {
                return cursor.getCount();
            }
            cursor.close();
        }

        return 0;
    }

    /*
    ALBUM UTILITIES:
     */
    public static void renameAlbum(long inputId, String name, Context context) {

        // Update all names of albums in songs
        ContentValues values = new ContentValues();
        values.put(MediaStore.Audio.Media.ALBUM, name);
        context.getContentResolver().update(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, values,
                MediaStore.Audio.Media.ALBUM_ID + "=" + inputId, null);
    }

    public static List<Song> getSongsFromAlbum(long inputAlbumId, Context context) {

        // Get data
        String[] projection = { MediaStore.Audio.Media._ID, MediaStore.Audio.Media.TITLE,
                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.DURATION,
                MediaStore.Audio.Media.DATE_ADDED };
        Cursor cursor = context.getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, projection,
                "is_music != 0 and album_id = " + inputAlbumId, null, null);

        // Go through cursor
        List<Song> songList = new ArrayList<>();
        if (cursor != null && cursor.moveToFirst()) {
            do {

                // Get the data
                long id = cursor.getLong(0);
                String title = cursor.getString(1);
                String path = cursor.getString(2);
                String artist = cursor.getString(3);
                long artistId = cursor.getLong(4);
                String album = cursor.getString(5);
                long albumId = cursor.getLong(6);
                int duration = cursor.getInt(7);
                int date = cursor.getInt(8);

                // Create media player
                MediaPlayer mp = MediaPlayer.create(context, Uri.parse(path));
                if (mp != null) {

                    // Create model and add to list
                    Song song = new Song(id, title, path, artist, artistId, album, albumId, duration, date);
                    songList.add(song);
                }

            } while (cursor.moveToNext());
            cursor.close();
        }

        return songList;
    }

    public static Uri getAlbumArt(long albumId) {
        Uri uri = ContentUris.withAppendedId(Uri.parse("content://media/external/audio/albumart"), albumId);
        if (uri != null) {
            // Log.e(TAG, "Returning " + uri.getPath() + " for " + Long.toString(albumId));
            return uri;
        } else {
            return Uri.parse("android.resource://com.lunchareas.echo/" + R.drawable.ic_album);
        }
    }

    public static void changeAlbumArt(String path, long albumId, Context context) {

        // Delete data
        Uri artworkUri = Uri.parse("content://media/external/audio/albumart");
        try {
            context.getContentResolver().delete(ContentUris.withAppendedId(artworkUri, albumId), null, null);
        } catch (Exception e) {
            e.printStackTrace();
        }

        // Reinsert data
        if (new File(path).exists()) {
            ContentValues values = new ContentValues();
            values.put("album_id", albumId);
            values.put("_data", path);
            context.getContentResolver().insert(artworkUri, values);
        }
    }

    public static void changeAlbumArtWithSongId(String path, long songId, Context context) {

        // Find album id from song id
        long albumId = 0;
        boolean found = false;
        for (Song song : SongLoader.getSongList(context)) {
            if (song.getId() == songId) {
                albumId = song.getAlbumId();
                found = true;
            }
        }
        if (!found) {
            Log.e(TAG, "Could not find song with that ID when finding the album id.");
            return;
        }

        // Delete previous data
        Uri artworkUri = Uri.parse("content://media/external/audio/albumart");
        try {
            context.getContentResolver().delete(ContentUris.withAppendedId(artworkUri, albumId), null, null);
        } catch (Exception e) {
            e.printStackTrace();
        }

        // Reinsert data
        if (new File(path).exists()) {
            ContentValues values = new ContentValues();
            values.put("album_id", albumId);
            values.put("_data", path);
            context.getContentResolver().insert(artworkUri, values);
        }
    }

    /*
    ARTIST UTILITIES:
     */
    public static void renameArtist(long inputId, String name, Context context) {

        // Update all names of albums in songs
        ContentValues values = new ContentValues();
        values.put(MediaStore.Audio.Media.ARTIST, name);
        context.getContentResolver().update(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, values,
                MediaStore.Audio.Media.ARTIST_ID + "=" + inputId, null);
    }

    public static List<Song> getSongsFromArtist(long inputArtistId, Context context) {

        // Get data
        String[] projection = { MediaStore.Audio.Media._ID, MediaStore.Audio.Media.TITLE,
                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.DURATION,
                MediaStore.Audio.Media.DATE_ADDED };
        Cursor cursor = context.getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, projection,
                "is_music != 0 and artist_id = " + inputArtistId, null, null);

        // Go through cursor
        List<Song> songList = new ArrayList<>();
        if (cursor != null && cursor.moveToFirst()) {
            do {

                // Get the data
                long id = cursor.getLong(0);
                String title = cursor.getString(1);
                String path = cursor.getString(2);
                String artist = cursor.getString(3);
                long artistId = cursor.getLong(4);
                String album = cursor.getString(5);
                long albumId = cursor.getLong(6);
                int duration = cursor.getInt(7);
                int date = cursor.getInt(8);

                // Create media player
                MediaPlayer mp = MediaPlayer.create(context, Uri.parse(path));
                if (mp != null) {

                    // Create model and add to list
                    Song song = new Song(id, title, path, artist, artistId, album, albumId, duration, date);
                    songList.add(song);
                }

            } while (cursor.moveToNext());
            cursor.close();
        }

        return songList;
    }
}