package com.mercandalli.android.apps.files.file.audio; import android.annotation.SuppressLint; import android.content.Context; import android.database.Cursor; import android.os.Build; import android.os.Handler; import android.os.Looper; import android.provider.MediaStore; import android.support.annotation.NonNull; import android.text.Spanned; import com.mercandalli.android.apps.files.file.FileManager; import com.mercandalli.android.apps.files.file.FileModel; import com.mercandalli.android.apps.files.file.FileTypeModel; import com.mercandalli.android.apps.files.file.FileTypeModelENUM; import com.mercandalli.android.apps.files.file.audio.artist.Artist; import com.mercandalli.android.apps.files.file.audio.metadata.FileAudioMetaDataUtils; import com.mercandalli.android.apps.files.file.local.provider.FileLocalProviderManager; import com.mercandalli.android.library.base.precondition.Preconditions; import java.io.File; import java.io.FilenameFilter; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import static com.mercandalli.android.library.base.java.FileUtils.getExtensionFromPath; import static com.mercandalli.android.library.base.java.FileUtils.getNameFromPath; import static com.mercandalli.android.library.base.java.FileUtils.getParentPathFromPath; /** * A {@link FileModel} Manager. */ /* package */ class FileAudioManagerImpl extends FileAudioManagerNotifier { @NonNull protected final Context mContextApp; @NonNull protected final FileLocalProviderManager mFileLocalProviderManager; @NonNull protected final FileManager mFileManager; /* Cache */ @NonNull protected final List<FileAudioModel> mCacheAllLocalMusics = new ArrayList<>(); @NonNull protected final List<FileModel> mCacheLocalMusicFolders = new ArrayList<>(); protected boolean mIsGetAllLocalMusicLaunched; protected boolean mIsGetLocalMusicFoldersLaunched; @NonNull private final Handler mUiHandler; @NonNull private final Thread mUiThread; /** * The manager constructor. * * @param contextApp The {@link Context} of this application. */ public FileAudioManagerImpl(final @NonNull Context contextApp) { Preconditions.checkNotNull(contextApp); mContextApp = contextApp.getApplicationContext(); mFileLocalProviderManager = FileLocalProviderManager.getInstance(contextApp); mFileManager = FileManager.getInstance(contextApp); mFileLocalProviderManager.registerFileProviderListener(new FileLocalProviderManager.FileProviderListener() { @Override public void onFileProviderReloadStarted() { super.onFileProviderReloadStarted(); clearCache(); } }); final Looper mainLooper = Looper.getMainLooper(); mUiHandler = new Handler(mainLooper); mUiThread = mainLooper.getThread(); // Cache. mIsGetLocalMusicFoldersLaunched = true; mIsGetAllLocalMusicLaunched = true; mFileLocalProviderManager.getFileAudioPaths(new FileLocalProviderManager.GetFileAudioListener() { @Override public void onGetFileAudio(@NonNull final List<String> fileAudioPaths) { new Thread() { @Override public void run() { threadWorkerCreateLocalMusicFolders(fileAudioPaths); threadWorkerCreateAllLocalMusic(fileAudioPaths); } }.start(); } }); } /** * {@inheritDoc} */ @Override @SuppressLint("NewApi") public void getAllLocalMusic() { if (mIsGetAllLocalMusicLaunched) { return; } mIsGetAllLocalMusicLaunched = true; if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1) { notifyAllLocalMusicListenerFailed(); return; } if (!mCacheAllLocalMusics.isEmpty()) { notifyAllLocalMusicListenerSucceeded(mCacheAllLocalMusics, false); return; } mFileLocalProviderManager.getFileAudioPaths(new FileLocalProviderManager.GetFileAudioListener() { @Override public void onGetFileAudio(@NonNull final List<String> fileAudioPaths) { new Thread() { @Override public void run() { threadWorkerCreateAllLocalMusic(fileAudioPaths); } }.start(); } }); } /** * {@inheritDoc} */ @Override public void getLocalMusic( final FileModel fileModelDirectParent) { Preconditions.checkNotNull(fileModelDirectParent); if (!fileModelDirectParent.isDirectory()) { notifyLocalMusicListenerFailed(); return; } final File file = fileModelDirectParent.getFile(); if (file == null || !file.exists()) { return; } final List<FileAudioModel> files = new ArrayList<>(); final List<File> fs = Arrays.asList(file.listFiles( new FilenameFilter() { @Override public boolean accept(final File file, final String name) { return (new FileTypeModel(getExtensionFromPath(name))) .equals(FileTypeModelENUM.AUDIO.type); } } )); for (final File f : fs) { files.add(new FileAudioModel.FileAudioModelBuilder() .file(f).build()); } notifyLocalMusicListenerSucceeded(files); } /** * {@inheritDoc} */ @Override @SuppressLint("NewApi") public void getLocalMusicFolders() { if (mIsGetLocalMusicFoldersLaunched) { return; } mIsGetLocalMusicFoldersLaunched = true; if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1) { notifyLocalMusicFoldersListenerFailed(); return; } if (!mCacheLocalMusicFolders.isEmpty()) { notifyLocalMusicFoldersListenerSucceeded(mCacheLocalMusicFolders, false); return; } mFileLocalProviderManager.getFileAudioPaths(new FileLocalProviderManager.GetFileAudioListener() { @Override public void onGetFileAudio(@NonNull final List<String> fileAudioPaths) { new Thread() { @Override public void run() { threadWorkerCreateLocalMusicFolders(fileAudioPaths); } }.start(); } }); } /** * {@inheritDoc} */ @Override @SuppressLint("NewApi") public void getAllLocalMusicAlbums() { if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1) { notifyAllLocalMusicAlbumsListenerFailed(); return; } new Thread(new Runnable() { @Override public void run() { getAllLocalMusicAlbumsInternal(); } }).start(); } /** * {@inheritDoc} */ @Override public void getAllLocalMusicArtists() { } @Override public boolean setFileAudioMetaData( final File file, final String newTitle, final String newArtist, final String newAlbum) { return setFileAudioMetaDataPrivate(file, newTitle, newArtist, newAlbum); } @Override public boolean setFileAudioMetaData( final FileAudioModel fileAudio, final String newTitle, final String newArtist, final String newAlbum) { Preconditions.checkNotNull(fileAudio); final File file = fileAudio.getFile(); return file != null && setFileAudioMetaDataPrivate(file, newTitle, newArtist, newAlbum); } @Override public Spanned toSpanned(final Context context, final FileAudioModel fileAudioModel) { return mFileManager.toSpanned(context, fileAudioModel); } //region Create. private void threadWorkerCreateLocalMusicFolders(@NonNull final List<String> fileAudioPaths) { final Map<String, MutableInt> directories = new HashMap<>(); for (final String path : fileAudioPaths) { final String parentPath = getParentPathFromPath(path); final MutableInt count = directories.get(parentPath); if (count == null) { directories.put(parentPath, new MutableInt()); } else { count.increment(); } } final List<FileModel> result = new ArrayList<>(); for (String path : directories.keySet()) { result.add(new FileModel.FileModelBuilder() .id(path.hashCode()) .url(path) .name(getNameFromPath(path)) .isDirectory(true) .countAudio(directories.get(path).value) .isOnline(false) .build()); } // Sorting Collections.sort(result, new Comparator<FileModel>() { @Override public int compare(final FileModel fileModel1, final FileModel fileModel2) { return fileModel2.getCountAudio() - fileModel1.getCountAudio(); } }); notifyLocalMusicFoldersListenerSucceeded(result, true); } private void threadWorkerCreateAllLocalMusic(@NonNull final List<String> fileAudioPaths) { final List<FileAudioModel> files = new ArrayList<>(); for (final String path : fileAudioPaths) { final File file = new File(path); if (file.exists() && !file.isDirectory()) { files.add(new FileAudioModel.FileAudioModelBuilder().file(file).build()); } } notifyAllLocalMusicListenerSucceeded(files, true); } //endregion Create. private boolean setFileAudioMetaDataPrivate( final File file, final String newTitle, final String newArtist, final String newAlbum) { Preconditions.checkNotNull(file); final boolean result = file.exists() && FileAudioMetaDataUtils.setMetaData(file, newTitle, newArtist, newAlbum); if (result) { notifyOnMusicFile(); } return result; } public void clearCache() { mCacheAllLocalMusics.clear(); mCacheLocalMusicFolders.clear(); } //region notify listeners private void notifyAllLocalMusicListenerSucceeded( final List<FileAudioModel> fileModels, final boolean cacheResult) { if (mUiThread != Thread.currentThread()) { mUiHandler.post(new Runnable() { @Override public void run() { notifyAllLocalMusicListenerSucceeded(fileModels, cacheResult); } }); return; } if (cacheResult) { mCacheAllLocalMusics.clear(); mCacheAllLocalMusics.addAll(fileModels); } if (!mIsGetAllLocalMusicLaunched) { return; } mIsGetAllLocalMusicLaunched = false; synchronized (mGetAllLocalMusicListeners) { for (int i = 0, size = mGetAllLocalMusicListeners.size(); i < size; i++) { mGetAllLocalMusicListeners.get(i).onAllLocalMusicSucceeded(fileModels); } } } private void notifyAllLocalMusicListenerFailed() { synchronized (mGetAllLocalMusicListeners) { for (int i = 0, size = mGetAllLocalMusicListeners.size(); i < size; i++) { mGetAllLocalMusicListeners.get(i).onAllLocalMusicFailed(); } } } private void notifyAllLocalMusicArtistsListenerSucceeded(final List<Artist> artists) { synchronized (mGetAllLocalMusicArtistsListeners) { for (int i = 0, size = mGetAllLocalMusicArtistsListeners.size(); i < size; i++) { mGetAllLocalMusicArtistsListeners.get(i).onAllLocalMusicArtistsSucceeded(artists); } } } private void notifyAllLocalMusicAlbumsListenerSucceeded(final List<Album> albums) { if (mUiThread != Thread.currentThread()) { mUiHandler.post(new Runnable() { @Override public void run() { notifyAllLocalMusicAlbumsListenerSucceeded(albums); } }); return; } synchronized (mGetAllLocalMusicAlbumsListeners) { for (int i = 0, size = mGetAllLocalMusicAlbumsListeners.size(); i < size; i++) { mGetAllLocalMusicAlbumsListeners.get(i).onAllLocalMusicAlbumsSucceeded(albums); } } } private void notifyAllLocalMusicAlbumsListenerFailed() { synchronized (mGetAllLocalMusicAlbumsListeners) { for (int i = 0, size = mGetAllLocalMusicAlbumsListeners.size(); i < size; i++) { mGetAllLocalMusicAlbumsListeners.get(i).onAllLocalMusicAlbumsFailed(); } } } protected void notifyLocalMusicFoldersListenerSucceeded( final List<FileModel> fileModelFolders, final boolean cacheResult) { if (mUiThread != Thread.currentThread()) { mUiHandler.post(new Runnable() { @Override public void run() { notifyLocalMusicFoldersListenerSucceeded(fileModelFolders, cacheResult); } }); return; } if (cacheResult) { mCacheLocalMusicFolders.clear(); mCacheLocalMusicFolders.addAll(fileModelFolders); } if (!mIsGetLocalMusicFoldersLaunched) { return; } mIsGetLocalMusicFoldersLaunched = false; synchronized (mGetLocalMusicFoldersListeners) { for (int i = 0, size = mGetLocalMusicFoldersListeners.size(); i < size; i++) { mGetLocalMusicFoldersListeners.get(i).onLocalMusicFoldersSucceeded(fileModelFolders); } } } protected void notifyLocalMusicFoldersListenerFailed() { synchronized (mGetLocalMusicFoldersListeners) { for (int i = 0, size = mGetLocalMusicFoldersListeners.size(); i < size; i++) { mGetLocalMusicFoldersListeners.get(i).onLocalMusicFoldersFailed(); } } } private void notifyLocalMusicListenerSucceeded(final List<FileAudioModel> fileModels) { synchronized (mGetLocalMusicListeners) { for (int i = 0, size = mGetLocalMusicListeners.size(); i < size; i++) { mGetLocalMusicListeners.get(i).onLocalMusicSucceeded(fileModels); } } } private void notifyLocalMusicListenerFailed() { synchronized (mGetLocalMusicListeners) { for (int i = 0, size = mGetLocalMusicListeners.size(); i < size; i++) { mGetLocalMusicListeners.get(i).onLocalMusicFailed(); } } } private void notifyOnMusicFile() { clearCache(); synchronized (mMusicsChangeListeners) { for (int i = 0, size = mMusicsChangeListeners.size(); i < size; i++) { mMusicsChangeListeners.get(i).onMusicsContentChange(); } } } //endregion notify listeners private void getAllLocalMusicAlbumsInternal() { // Used to count the number of music inside. final Map<Integer, Album> albums = new HashMap<>(); final Cursor cursor = mContextApp.getContentResolver().query( MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI, new String[]{ MediaStore.Audio.Albums._ID, MediaStore.Audio.Albums.ALBUM, MediaStore.Audio.Albums.NUMBER_OF_SONGS}, null, null, null); if (cursor != null) { if (cursor.moveToFirst()) { do { final int id = cursor.getInt(cursor.getColumnIndex(MediaStore.Audio.Albums._ID)); final String name = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Albums.ALBUM)); final int numberOfSongs = cursor.getInt(cursor.getColumnIndex(MediaStore.Audio.Albums.NUMBER_OF_SONGS)); if (!albums.containsKey(id) && name != null) { albums.put(id, new Album( id, name, numberOfSongs)); } } while (cursor.moveToNext()); } cursor.close(); } notifyAllLocalMusicAlbumsListenerSucceeded(new ArrayList<>(albums.values())); } }