com.pavlospt.rxfile.RxFile.java Source code

Java tutorial

Introduction

Here is the source code for com.pavlospt.rxfile.RxFile.java

Source

package com.pavlospt.rxfile;

import android.content.ClipData;
import android.content.Context;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.ThumbnailUtils;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.provider.MediaStore;
import android.support.v4.provider.DocumentFile;
import android.util.Log;

import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLConnection;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.util.ArrayList;
import java.util.List;

import rx.Observable;
import rx.functions.Func0;
import rx.functions.Func1;

/**
 * Copyright 2015 Pavlos-Petros Tournaris
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
public class RxFile {

    private static final String TAG = RxFile.class.getSimpleName();

    private static boolean LOGGING_ENABLED = false;

    /*
    * Create a copy of the file found under the provided Uri, in the Library's cache folder.
    * */
    public static Observable<File> createFileFromUri(final Context context, final Uri data) {
        return Observable.defer(new Func0<Observable<File>>() {
            @Override
            public Observable<File> call() {
                try {
                    return Observable.just(fileFromUri(context, data));
                } catch (Exception e) {
                    logError("Exception:" + e.getMessage() + " line:67");
                    e.printStackTrace();
                    return Observable.error(e);
                }
            }
        });
    }

    /*
    * Create a copy of the files found under the provided ArrayList of Uris, in the Library's cache folder.
    * */
    public static Observable<List<File>> createFileFromUri(final Context context, final ArrayList<Uri> uris) {
        return Observable.defer(new Func0<Observable<List<File>>>() {
            @Override
            public Observable<List<File>> call() {

                List<File> filesRetrieved = new ArrayList<>(uris.size());

                for (Uri data : uris) {
                    try {
                        filesRetrieved.add(fileFromUri(context, data));
                    } catch (Exception e) {
                        logError("Exception:" + e.getMessage() + " line: 89");
                        e.printStackTrace();
                        return Observable.error(e);
                    }
                }

                return Observable.just(filesRetrieved);
            }
        });
    }

    /*
    * Create a copy of the files found under the ClipData item passed from MultiSelection, in the Library's cache folder.
    * */
    public static Observable<List<File>> createFilesFromClipData(final Context context, final ClipData clipData) {

        return Observable.defer(new Func0<Observable<List<File>>>() {
            @Override
            public Observable<List<File>> call() {
                int numOfUris = clipData.getItemCount();
                List<File> filesRetrieved = new ArrayList<>(numOfUris);

                for (int i = 0; i < numOfUris; i++) {
                    Uri data = clipData.getItemAt(i).getUri();
                    if (data != null) {
                        try {
                            filesRetrieved.add(fileFromUri(context, data));
                        } catch (Exception e) {
                            logError("Exception:" + e.getMessage() + " line: 117");
                            e.printStackTrace();
                            return Observable.error(e);
                        }
                    }
                }
                return Observable.just(filesRetrieved);
            }
        });

    }

    /*
    * Get a thumbnail from the provided Image or Video Uri.
    * */
    public static Observable<Bitmap> getThumbnail(Context context, Uri uri) {
        return getThumbnailFromUri(context, uri);
    }

    /*
    * Get a thumbnail from the provided Image or Video Uri in the specified size.
    * */
    public static Observable<Bitmap> getThumbnail(Context context, Uri uri, int requiredWidth, int requiredHeight) {
        return getThumbnailFromUriWithSize(context, uri, requiredWidth, requiredHeight);
    }

    /*
    * Get a thumbnail from the provided Image or Video Uri in the specified size and kind.
    * Kind is a value of MediaStore.Images.Thumbnails.MICRO_KIND or MediaStore.Images.Thumbnails.MINI_KIND
    * */
    public static Observable<Bitmap> getThumbnail(Context context, Uri uri, int requiredWidth, int requiredHeight,
            int kind) {
        return getThumbnailFromUriWithSizeAndKind(context, uri, requiredWidth, requiredHeight, kind);
    }

    /*
    * Get a thumbnail from the provided Image or Video Uri.
    * */
    private static Observable<Bitmap> getThumbnailFromUri(final Context context, final Uri data) {
        return getThumbnailFromUriWithSizeAndKind(context, data, 0, 0, MediaStore.Images.Thumbnails.MINI_KIND);
    }

    /*
    * Get a thumbnail from the provided Image or Video Uri in the specified size.
    * */
    private static Observable<Bitmap> getThumbnailFromUriWithSize(final Context context, final Uri data,
            final int requiredWidth, final int requiredHeight) {
        return getThumbnailFromUriWithSizeAndKind(context, data, requiredWidth, requiredHeight,
                MediaStore.Images.Thumbnails.MINI_KIND);
    }

    /*
    * Get a thumbnail from the provided Image or Video Uri in the specified size and kind.
    * Kind is a value of MediaStore.Images.Thumbnails.MICRO_KIND or MediaStore.Images.Thumbnails.MINI_KIND
    * */
    private static Observable<Bitmap> getThumbnailFromUriWithSizeAndKind(final Context context, final Uri data,
            final int requiredWidth, final int requiredHeight, final int kind) {
        return Observable.fromCallable(new Func0<Bitmap>() {
            @Override
            public Bitmap call() {
                Bitmap bitmap = null;
                ParcelFileDescriptor parcelFileDescriptor;
                final BitmapFactory.Options options = new BitmapFactory.Options();
                if (requiredWidth > 0 && requiredHeight > 0) {
                    options.inJustDecodeBounds = true;
                    options.inSampleSize = calculateInSampleSize(options, requiredWidth, requiredHeight);
                    options.inJustDecodeBounds = false;
                }
                if (!isMediaUri(data)) {
                    logDebug("Not a media uri:" + data);
                    if (isGoogleDriveDocument(data)) {
                        logDebug("Google Drive Uri:" + data);
                        DocumentFile file = DocumentFile.fromSingleUri(context, data);
                        if (file.getType().startsWith(Constants.IMAGE_TYPE)
                                || file.getType().startsWith(Constants.VIDEO_TYPE)) {
                            logDebug("Google Drive Uri:" + data + " (Video or Image)");
                            try {
                                parcelFileDescriptor = context.getContentResolver().openFileDescriptor(data,
                                        Constants.READ_MODE);
                                FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
                                bitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options);
                                parcelFileDescriptor.close();
                                return bitmap;
                            } catch (IOException e) {
                                logError("Exception:" + e.getMessage() + " line: 209");
                                e.printStackTrace();
                            }
                        }
                    } else if (data.getScheme().equals(Constants.FILE)) {
                        logDebug("Dropbox or other DocumentsProvider Uri:" + data);
                        try {
                            parcelFileDescriptor = context.getContentResolver().openFileDescriptor(data,
                                    Constants.READ_MODE);
                            FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
                            bitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options);
                            parcelFileDescriptor.close();
                            return bitmap;
                        } catch (IOException e) {
                            logError("Exception:" + e.getMessage() + " line: 223");
                            e.printStackTrace();
                        }
                    } else {
                        try {
                            parcelFileDescriptor = context.getContentResolver().openFileDescriptor(data,
                                    Constants.READ_MODE);
                            FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
                            bitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options);
                            parcelFileDescriptor.close();
                            return bitmap;
                        } catch (IOException e) {
                            logError("Exception:" + e.getMessage() + " line: 235");
                            e.printStackTrace();
                        }
                    }
                } else {
                    logDebug("Uri for thumbnail:" + data);
                    String[] parts = data.getLastPathSegment().split(":");
                    String fileId = parts[1];
                    Cursor cursor = null;
                    try {
                        cursor = context.getContentResolver().query(data, null, null, null, null);
                        if (cursor != null) {
                            logDebug("Cursor size:" + cursor.getCount());
                            if (cursor.moveToFirst()) {
                                if (data.toString().contains(Constants.VIDEO)) {
                                    bitmap = MediaStore.Video.Thumbnails.getThumbnail(context.getContentResolver(),
                                            Long.parseLong(fileId), kind, options);
                                } else if (data.toString().contains(Constants.IMAGE)) {
                                    bitmap = MediaStore.Images.Thumbnails.getThumbnail(context.getContentResolver(),
                                            Long.parseLong(fileId), kind, options);
                                }
                            }
                        }
                        return bitmap;
                    } catch (Exception e) {
                        logError("Exception:" + e.getMessage() + " line: 266");
                    } finally {
                        if (cursor != null)
                            cursor.close();
                    }
                }
                return bitmap;
            }
        });
    }

    /*
    * Get a file extension based on the given file name.
    * */
    public static Observable<String> getFileExtension(final String fileName) {
        return Observable.fromCallable(new Func0<String>() {
            @Override
            public String call() {
                return fileName.substring((fileName.lastIndexOf('.')) + 1);
            }
        });
    }

    /*
    * Check if a File exists.
    * */
    public static Observable<Boolean> ifExists(final String path) {
        return Observable.fromCallable(new Func0<Boolean>() {
            @Override
            public Boolean call() {
                return new File(path).exists();
            }
        });
    }

    /*
    * Get thumbnail from a File path.
    * */
    public static Observable<Bitmap> getThumbnail(String filePath) {
        return getFileType(filePath).filter(new Func1<String, Boolean>() {
            @Override
            public Boolean call(String s) {
                return s != null;
            }
        }).flatMap(new Func1<String, Observable<Bitmap>>() {
            @Override
            public Observable<Bitmap> call(String s) {
                if (s.equalsIgnoreCase(Constants.VIDEO))
                    return getVideoThumbnail(s);
                else if (s.equalsIgnoreCase(Constants.IMAGE))
                    return getThumbnailFromPath(s);
                return null;
            }
        });
    }

    /*
    * Get video thumbnail from a video file, by path.
    * */
    public static Observable<Bitmap> getVideoThumbnail(final String filePath) {
        return Observable.fromCallable(new Func0<Bitmap>() {
            @Override
            public Bitmap call() {
                return ThumbnailUtils.createVideoThumbnail(filePath, MediaStore.Images.Thumbnails.MINI_KIND);
            }
        });
    }

    /*
    * Get video thumbnail from a video file, by path, with the selected kind.
    * Kind is a value of MediaStore.Images.Thumbnails.MICRO_KIND or MediaStore.Images.Thumbnails.MINI_KIND
    * */
    public static Observable<Bitmap> getVideoThumbnailFromPathWithKind(final String path, final int kind) {
        return Observable.defer(new Func0<Observable<Bitmap>>() {
            @Override
            public Observable<Bitmap> call() {
                return Observable.just(ThumbnailUtils.createVideoThumbnail(path, kind));
            }
        });
    }

    /*
    * Get image thumbnail from an image file, by path.
    * */
    public static Observable<Bitmap> getThumbnailFromPath(String filePath) {
        final Bitmap sourceBitmap = BitmapFactory.decodeFile(filePath);
        return Observable.fromCallable(new Func0<Bitmap>() {
            @Override
            public Bitmap call() {
                return ThumbnailUtils.extractThumbnail(sourceBitmap, sourceBitmap.getWidth(),
                        sourceBitmap.getHeight());
            }
        });
    }

    public static Observable<String> getFileType(String filePath) {
        logDebug("Filepath in getFileType: " + filePath);
        final String[] parts = filePath.split("/");
        return Observable.fromCallable(new Func0<String>() {
            @Override
            public String call() {
                return parts.length > 0 ? URLConnection.guessContentTypeFromName(parts[0]) : null;
            }
        });
    }

    /*
    * Get path from Uri, for a FileDocument.
    * */
    public static Observable<String> getPathFromUriForFileDocument(final Context context, final Uri contentUri) {
        return Observable.fromCallable(new Func0<String>() {
            @Override
            public String call() {
                String pathFound = null;
                Cursor cursor = context.getContentResolver().query(contentUri, null, null, null, null);
                if (cursor != null) {
                    if (cursor.moveToFirst())
                        pathFound = cursor
                                .getString(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DATA));
                    cursor.close();
                    logDebug("Path found:" + pathFound);
                }
                return pathFound;
            }
        });
    }

    /*
    * Get path from Uri, for a MediaDocument.
    * */
    public static Observable<String> getPathFromUriForMediaDocument(final Context context, final Uri mediaUri,
            final String mediaDocumentId) {
        return Observable.fromCallable(new Func0<String>() {
            @Override
            public String call() {
                String pathFound = null;
                Cursor cursor = context.getContentResolver().query(mediaUri, null,
                        Constants.ID_COLUMN_VALUE + " =?", new String[] { mediaDocumentId }, null);
                if (cursor != null) {
                    if (cursor.moveToFirst())
                        pathFound = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA));
                    cursor.close();
                    logDebug("Path found:" + pathFound);
                }
                return pathFound;
            }
        });
    }

    /*
    * Get path from Uri, for an ImageDocument.
    * */
    public static Observable<String> getPathFromUriForImageDocument(final Context context,
            final String mediaDocumentId) {
        return Observable.fromCallable(new Func0<String>() {
            @Override
            public String call() {
                String pathFound = null;
                Cursor cursor = context.getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                        null, Constants.ID_COLUMN_VALUE + " =?", new String[] { mediaDocumentId }, null);
                if (cursor != null) {
                    if (cursor.moveToFirst())
                        pathFound = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA));
                    cursor.close();
                    logDebug("Path found:" + pathFound);
                }
                return pathFound;
            }
        });
    }

    /*
    * Get path from Uri, for a VideoDocument.
    * */
    public static Observable<String> getPathFromUriForVideoDocument(final Context context,
            final String mediaDocumentId) {
        return Observable.fromCallable(new Func0<String>() {
            @Override
            public String call() {
                String pathFound = null;
                Cursor cursor = context.getContentResolver().query(MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
                        null, Constants.ID_COLUMN_VALUE + " =?", new String[] { mediaDocumentId }, null);
                if (cursor != null) {
                    if (cursor.moveToFirst())
                        pathFound = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA));
                    cursor.close();
                    logDebug("Path found:" + pathFound);
                }
                return pathFound;
            }
        });
    }

    private static boolean isExternalStorageDocument(Uri uri) {
        return Constants.EXTERNAL_STORAGE_AUTHORITY.equals(uri.getAuthority());
    }

    private static boolean isDownloadsDocument(Uri uri) {
        return Constants.DOWNLOADS_DIRECTORY_AUTHORITY.equals(uri.getAuthority());
    }

    private static boolean isMediaDocument(Uri uri) {
        return Constants.MEDIA_DOCUMENTS_AUTHORITY.equals(uri.getAuthority());
    }

    private static boolean isGoogleDriveDocument(Uri uri) {
        return Constants.GOOGLE_DRIVE_DOCUMENT_AUTHORITY.equals(uri.getAuthority());
    }

    private static boolean isMediaUri(Uri uri) {
        if (uri.getAuthority().equals(Constants.MEDIA_DOCUMENTS_AUTHORITY)) {
            return uri.getLastPathSegment().contains(Constants.IMAGE)
                    || uri.getLastPathSegment().contains(Constants.VIDEO);
        }
        return Constants.MEDIA_AUTHORITY.equals(uri.getAuthority());
    }

    private static boolean checkWriteExternalPermission(Context context) {
        int res = context.checkCallingOrSelfPermission(Constants.WRITE_EXTERNAL_PERMISSION);
        return (res == PackageManager.PERMISSION_GRANTED);
    }

    public static String getMimeType(String fileName) {
        return URLConnection.guessContentTypeFromName(fileName);
    }

    private static void createDirectory(String path) {
        if (!checkExistence(path)) {
            File temp = new File(path);
            if (!temp.mkdir()) {
                logError("Something went wrong while creating directory: " + path);
            } else {
                logDebug("Directory: " + path + " created.");
            }
        } else {
            logDebug("Directory: " + path + " already exists.");
        }
    }

    private static boolean createFile(String path) throws IOException {
        if (!checkExistence(path)) {
            File temp = new File(path);
            if (!temp.createNewFile()) {
                logDebug("Something went wrong while creating file: " + path);
            } else {
                logError("File: " + path + " created.");
            }
        } else {
            logDebug("File: " + path + " already exists.");
            return false;
        }
        return true;
    }

    private static boolean checkExistence(String path) {
        logDebug("Check path: " + path);
        File temp = new File(path);
        return temp.exists();
    }

    private static void fastChannelCopy(final ReadableByteChannel src, final WritableByteChannel dest)
            throws IOException {
        final ByteBuffer buffer = ByteBuffer.allocateDirect(16 * 1024);
        while (src.read(buffer) != -1) {
            buffer.flip();
            dest.write(buffer);
            buffer.compact();
        }
        buffer.flip();
        while (buffer.hasRemaining()) {
            dest.write(buffer);
        }
    }

    private static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
        final int height = options.outHeight;
        final int width = options.outWidth;
        logDebug("Height: " + height + " Width: " + width);
        int inSampleSize = 1;

        if (height > reqHeight || width > reqWidth) {

            final int halfHeight = height / 2;
            final int halfWidth = width / 2;

            while ((halfHeight / inSampleSize) > reqHeight && (halfWidth / inSampleSize) > reqWidth) {
                inSampleSize *= 2;
            }
        }

        return inSampleSize;
    }

    private static File fileFromUri(Context context, Uri data) throws Exception {
        DocumentFile file = DocumentFile.fromSingleUri(context, data);
        String fileType = file.getType();
        String fileName = file.getName();
        File fileCreated;
        ParcelFileDescriptor parcelFileDescriptor = context.getContentResolver().openFileDescriptor(data,
                Constants.READ_MODE);
        InputStream inputStream = new FileInputStream(parcelFileDescriptor.getFileDescriptor());
        logDebug("External cache dir:" + context.getExternalCacheDir());
        String filePath = context.getExternalCacheDir() + Constants.FOLDER_SEPARATOR + fileName;
        String fileExtension = fileName.substring((fileName.lastIndexOf('.')) + 1);
        String mimeType = getMimeType(fileName);

        logDebug("From Google Drive guessed type: " + getMimeType(fileName));

        logDebug("Extension: " + fileExtension);

        if (fileType.equals(Constants.APPLICATION_PDF) && mimeType == null) {
            filePath += "." + Constants.PDF_EXTENSION;
        }

        if (!createFile(filePath)) {
            return new File(filePath);
        }

        ReadableByteChannel from = Channels.newChannel(inputStream);
        WritableByteChannel to = Channels.newChannel(new FileOutputStream(filePath));
        fastChannelCopy(from, to);
        from.close();
        to.close();
        fileCreated = new File(filePath);
        logDebug("Path for made file: " + fileCreated.getAbsolutePath());
        return fileCreated;
    }

    public static void setLoggingEnabled(boolean loggingEnabled) {
        LOGGING_ENABLED = loggingEnabled;
    }

    private static void logDebug(String message) {
        if (LOGGING_ENABLED)
            Log.d(TAG, message);
    }

    private static void logError(String message) {
        if (LOGGING_ENABLED)
            Log.e(TAG, message);
    }

}