package com.mercandalli.android.apps.files.file.local.provider; import android.annotation.SuppressLint; import android.content.Context; import android.database.Cursor; import android.net.Uri; import android.os.Build; import android.os.Handler; import android.os.Looper; import android.provider.MediaStore; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.util.Log; import com.mercandalli.android.apps.files.file.FileTypeModelENUM; import com.mercandalli.android.library.base.precondition.Preconditions; import java.util.ArrayList; import java.util.List; import static com.mercandalli.android.apps.files.file.FileUtils.isAudioPath; import static com.mercandalli.android.apps.files.file.FileUtils.isImagePath; import static com.mercandalli.android.apps.files.file.FileUtils.isVideoPath; /** * Tha main {@link FileLocalProviderManager} implementation. * Manage {@link List}s of path. */ public class FileLocalProviderManagerImpl extends FileLocalProviderManager { private static final String STRING_SQL_OR = " OR "; private static final String STRING_SQL_LIKE = " LIKE ?"; private static final String TAG = "FileLocalProviderMa"; @NonNull private final Handler mUiHandler; @NonNull private final Thread mUiThread; @NonNull private final Context mContextApp; @NonNull private final List<String> mFilePaths = new ArrayList<>(); @NonNull private final List<String> mFileAudioPaths = new ArrayList<>(); @NonNull private final List<String> mFileImagePaths = new ArrayList<>(); @NonNull private final List<String> mFileVideoPaths = new ArrayList<>(); @NonNull private final List<GetFilePathsListener> mGetFilePathsListeners = new ArrayList<>(); @NonNull private final List<GetFileAudioListener> mGetFileAudioListeners = new ArrayList<>(); @NonNull private final List<GetFileImageListener> mGetFileImageListeners = new ArrayList<>(); @NonNull private final List<GetFileVideoListener> mGetFileVideoListeners = new ArrayList<>(); private boolean mIsLoadLaunched = false; private boolean mIsLoaded = false; @NonNull protected final List<FileProviderListener> mFileProviderListeners = new ArrayList<>(); /** * The manager constructor. * * @param contextApp The {@link Context} of this application. */ protected FileLocalProviderManagerImpl(@NonNull final Context contextApp) { Preconditions.checkNotNull(contextApp); mContextApp = contextApp; final Looper mainLooper = Looper.getMainLooper(); mUiHandler = new Handler(mainLooper); mUiThread = mainLooper.getThread(); } @SuppressLint("NewApi") public void load() { load(null); } @SuppressLint("NewApi") public void load(@Nullable final FileProviderListener fileProviderListener) { if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1) { notifyLoadFailed(fileProviderListener, LOADING_ERROR_ANDROID_API); return; } if (mIsLoadLaunched) { notifyLoadFailed(fileProviderListener, LOADING_ERROR_ALREADY_LAUNCHED); return; } mIsLoadLaunched = true; mIsLoaded = false; synchronized (mFileProviderListeners) { for (final FileProviderListener fileProviderManager : mFileProviderListeners) { fileProviderManager.onFileProviderReloadStarted(); } } new Thread() { @Override public void run() { final List<String> filePaths = new ArrayList<>(); final List<String> fileAudioPaths = new ArrayList<>(); final List<String> fileImagePaths = new ArrayList<>(); final List<String> fileVideoPaths = new ArrayList<>(); final String[] PROJECTION = {MediaStore.Files.FileColumns.DATA}; final Uri allSongsUri = MediaStore.Files.getContentUri("external"); final List<String> searchArray = new ArrayList<>(); final String mediaTypeKey = MediaStore.Files.FileColumns.MEDIA_TYPE; final StringBuilder selection = new StringBuilder("( " + mediaTypeKey + " = " + MediaStore.Files.FileColumns.MEDIA_TYPE_AUDIO + STRING_SQL_OR + mediaTypeKey + " = " + MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE + STRING_SQL_OR + mediaTypeKey + " = " + MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO); for (final String end : FileTypeModelENUM.AUDIO.type.getExtensions()) { selection.append(STRING_SQL_OR + MediaStore.Files.FileColumns.DATA + STRING_SQL_LIKE); searchArray.add('%' + end); } for (final String end : FileTypeModelENUM.IMAGE.type.getExtensions()) { selection.append(STRING_SQL_OR + MediaStore.Files.FileColumns.DATA + STRING_SQL_LIKE); searchArray.add('%' + end); } for (final String end : FileTypeModelENUM.VIDEO.type.getExtensions()) { selection.append(STRING_SQL_OR + MediaStore.Files.FileColumns.DATA + STRING_SQL_LIKE); searchArray.add('%' + end); } selection.append(" )"); final Cursor cursor = mContextApp.getContentResolver().query( allSongsUri, PROJECTION, selection.toString(), searchArray.toArray(new String[searchArray.size()]), MediaStore.Files.FileColumns.TITLE + " ASC"); if (cursor != null) { if (cursor.moveToFirst()) { do { final String path = cursor.getString(cursor.getColumnIndex(MediaStore.Files.FileColumns.DATA)); final String pathLower = path.toLowerCase(); if (isAudioPath(pathLower)) { fileAudioPaths.add(path); } else if (isImagePath(pathLower)) { fileImagePaths.add(path); } else if (isVideoPath(pathLower)) { fileVideoPaths.add(path); } filePaths.add(path); } while (cursor.moveToNext()); } cursor.close(); } notifyLoadSucceeded(filePaths, fileAudioPaths, fileImagePaths, fileVideoPaths, fileProviderListener); } }.start(); } @NonNull @Override public List<String> getFilePaths() { return new ArrayList<>(mFilePaths); } @Override public void getFilePaths(final GetFilePathsListener getFilePathsListener) { if (mIsLoaded) { getFilePathsListener.onGetFile(new ArrayList<>(mFilePaths)); return; } synchronized (mGetFilePathsListeners) { //noinspection SimplifiableIfStatement if (getFilePathsListener == null || mGetFilePathsListeners.contains(getFilePathsListener)) { // We don't allow to register null listener // And a listener can only be added once. return; } mGetFilePathsListeners.add(getFilePathsListener); } } @Override public void removeGetFilePathsListener(final GetFilePathsListener getFilePathsListener) { synchronized (mGetFilePathsListeners) { mGetFilePathsListeners.remove(getFilePathsListener); } } @Override public void getFileAudioPaths(final GetFileAudioListener getFileAudioListener) { if (mIsLoaded) { getFileAudioListener.onGetFileAudio(new ArrayList<>(mFileAudioPaths)); return; } synchronized (mGetFileAudioListeners) { //noinspection SimplifiableIfStatement if (getFileAudioListener == null || mGetFileAudioListeners.contains(getFileAudioListener)) { // We don't allow to register null listener // And a listener can only be added once. return; } mGetFileAudioListeners.add(getFileAudioListener); } } @Override public void removeGetFileAudioListener(final GetFileAudioListener getFileAudioListener) { synchronized (mGetFileAudioListeners) { mGetFileAudioListeners.remove(getFileAudioListener); } } @Override public void getFileImagePaths(final GetFileImageListener getFileImageListener) { if (mIsLoaded) { getFileImageListener.onGetFileImage(new ArrayList<>(mFileImagePaths)); return; } synchronized (mGetFileImageListeners) { //noinspection SimplifiableIfStatement if (getFileImageListener == null || mGetFileImageListeners.contains(getFileImageListener)) { // We don't allow to register null listener // And a listener can only be added once. return; } mGetFileImageListeners.add(getFileImageListener); } } @Override public void removeGetFileImageListener(final GetFileImageListener getFileImageListener) { synchronized (mGetFileImageListeners) { mGetFileImageListeners.remove(getFileImageListener); } } @Override public void getFileVideoPaths(final GetFileVideoListener getFileVideoListener) { if (mIsLoaded) { getFileVideoListener.onGetFileVideo(new ArrayList<>(mFileVideoPaths)); return; } synchronized (mGetFileVideoListeners) { //noinspection SimplifiableIfStatement if (getFileVideoListener == null || mGetFileVideoListeners.contains(getFileVideoListener)) { // We don't allow to register null listener // And a listener can only be added once. return; } mGetFileVideoListeners.add(getFileVideoListener); } } @Override public void removeGetFileVideoListener(final GetFileVideoListener getFileVideoListener) { synchronized (mGetFileVideoListeners) { mGetFileVideoListeners.remove(getFileVideoListener); } } @Override public void clearCache() { mFileAudioPaths.clear(); mFileImagePaths.clear(); mFilePaths.clear(); } public boolean registerFileProviderListener(final FileProviderListener fileProviderListener) { synchronized (mFileProviderListeners) { //noinspection SimplifiableIfStatement if (fileProviderListener == null || mFileProviderListeners.contains(fileProviderListener)) { // We don't allow to register null listener // And a listener can only be added once. return false; } return mFileProviderListeners.add(fileProviderListener); } } public boolean unregisterFileProviderListener(final FileProviderListener fileProviderListener) { synchronized (mFileProviderListeners) { return mFileProviderListeners.remove(fileProviderListener); } } private void notifyLoadSucceeded( @NonNull final List<String> filePaths, @NonNull final List<String> fileAudioPaths, @NonNull final List<String> fileImagePaths, @NonNull final List<String> fileVideoPaths, @Nullable final FileProviderListener fileProviderListener) { if (mUiThread != Thread.currentThread()) { mUiHandler.post(new Runnable() { @Override public void run() { notifyLoadSucceeded(filePaths, fileAudioPaths, fileImagePaths, fileVideoPaths, fileProviderListener); } }); return; } mFilePaths.clear(); mFilePaths.addAll(filePaths); mFileAudioPaths.clear(); mFileAudioPaths.addAll(fileAudioPaths); mFileImagePaths.clear(); mFileImagePaths.addAll(fileImagePaths); mFileVideoPaths.clear(); mFileVideoPaths.addAll(fileVideoPaths); mIsLoaded = true; if (fileProviderListener != null) { fileProviderListener.onFileProviderAllBasicLoaded(filePaths); fileProviderListener.onFileProviderAudioLoaded(fileAudioPaths); fileProviderListener.onFileProviderImageLoaded(fileImagePaths); fileProviderListener.onFileProviderVideoLoaded(fileVideoPaths); } synchronized (mGetFilePathsListeners) { for (final GetFilePathsListener getFilePathsListener : mGetFilePathsListeners) { getFilePathsListener.onGetFile(filePaths); } mGetFilePathsListeners.clear(); } synchronized (mGetFileAudioListeners) { for (final GetFileAudioListener getFileAudioListener : mGetFileAudioListeners) { getFileAudioListener.onGetFileAudio(fileAudioPaths); } mGetFileAudioListeners.clear(); } synchronized (mGetFileImageListeners) { for (final GetFileImageListener getFileImageListener : mGetFileImageListeners) { getFileImageListener.onGetFileImage(fileImagePaths); } mGetFileImageListeners.clear(); } synchronized (mGetFileVideoListeners) { for (final GetFileVideoListener getFileVideoListener : mGetFileVideoListeners) { getFileVideoListener.onGetFileVideo(fileVideoPaths); } mGetFileVideoListeners.clear(); } synchronized (mFileProviderListeners) { for (final FileProviderListener fileProviderManager : mFileProviderListeners) { fileProviderManager.onFileProviderAllBasicLoaded(filePaths); fileProviderManager.onFileProviderAudioLoaded(fileAudioPaths); fileProviderManager.onFileProviderImageLoaded(fileImagePaths); fileProviderManager.onFileProviderVideoLoaded(fileVideoPaths); } } mIsLoadLaunched = false; } private void notifyLoadFailed( @Nullable final FileProviderListener fileProviderListener, @LoadingError final int error) { Log.e(TAG, "notifyLoadFailed LoadingError=" + error); if (fileProviderListener != null) { fileProviderListener.onFileProviderFailed(error); } synchronized (mFileProviderListeners) { for (final FileProviderListener fileProviderManager : mFileProviderListeners) { fileProviderManager.onFileProviderFailed(error); } } } }