| package com.doumee.lib_coremodel.util.luban; | 
|   | 
| import android.content.Context; | 
| import android.net.Uri; | 
| import android.os.AsyncTask; | 
| import android.os.Handler; | 
| import android.os.Looper; | 
| import android.os.Message; | 
| import android.text.TextUtils; | 
| import android.util.Log; | 
|   | 
| import java.io.File; | 
| import java.io.FileInputStream; | 
| import java.io.IOException; | 
| import java.io.InputStream; | 
| import java.util.ArrayList; | 
| import java.util.Iterator; | 
| import java.util.List; | 
|   | 
| @SuppressWarnings("unused") | 
| public class Luban implements Handler.Callback { | 
|   private static final String TAG = "Luban"; | 
|   private static final String DEFAULT_DISK_CACHE_DIR = "luban_disk_cache"; | 
|   | 
|   private static final int MSG_COMPRESS_SUCCESS = 0; | 
|   private static final int MSG_COMPRESS_START = 1; | 
|   private static final int MSG_COMPRESS_ERROR = 2; | 
|   | 
|   private String mTargetDir; | 
|   private boolean focusAlpha; | 
|   private int mLeastCompressSize; | 
|   private OnRenameListener mRenameListener; | 
|   private OnCompressListener mCompressListener; | 
|   private CompressionPredicate mCompressionPredicate; | 
|   private List<InputStreamProvider> mStreamProviders; | 
|   | 
|   private Handler mHandler; | 
|   | 
|   private Luban(Builder builder) { | 
|     this.mTargetDir = builder.mTargetDir; | 
|     this.mRenameListener = builder.mRenameListener; | 
|     this.mStreamProviders = builder.mStreamProviders; | 
|     this.mCompressListener = builder.mCompressListener; | 
|     this.mLeastCompressSize = builder.mLeastCompressSize; | 
|     this.mCompressionPredicate = builder.mCompressionPredicate; | 
|     mHandler = new Handler(Looper.getMainLooper(), this); | 
|   } | 
|   | 
|   public static Builder with(Context context) { | 
|     return new Builder(context); | 
|   } | 
|   | 
|   /** | 
|    * Returns a file with a cache image name in the private cache directory. | 
|    * | 
|    * @param context A context. | 
|    */ | 
|   private File getImageCacheFile(Context context, String suffix) { | 
|     if (TextUtils.isEmpty(mTargetDir)) { | 
|       mTargetDir = getImageCacheDir(context).getAbsolutePath(); | 
|     } | 
|   | 
|     String cacheBuilder = mTargetDir + "/" + | 
|         System.currentTimeMillis() + | 
|         (int) (Math.random() * 1000) + | 
|         (TextUtils.isEmpty(suffix) ? ".jpg" : suffix); | 
|   | 
|     return new File(cacheBuilder); | 
|   } | 
|   | 
|   private File getImageCustomFile(Context context, String filename) { | 
|     if (TextUtils.isEmpty(mTargetDir)) { | 
|       mTargetDir = getImageCacheDir(context).getAbsolutePath(); | 
|     } | 
|   | 
|     String cacheBuilder = mTargetDir + "/" + filename; | 
|   | 
|     return new File(cacheBuilder); | 
|   } | 
|   | 
|   /** | 
|    * Returns a directory with a default name in the private cache directory of the application to | 
|    * use to store retrieved audio. | 
|    * | 
|    * @param context A context. | 
|    * @see #getImageCacheDir(Context, String) | 
|    */ | 
|   private File getImageCacheDir(Context context) { | 
|     return getImageCacheDir(context, DEFAULT_DISK_CACHE_DIR); | 
|   } | 
|   | 
|   /** | 
|    * Returns a directory with the given name in the private cache directory of the application to | 
|    * use to store retrieved media and thumbnails. | 
|    * | 
|    * @param context   A context. | 
|    * @param cacheName The name of the subdirectory in which to store the cache. | 
|    * @see #getImageCacheDir(Context) | 
|    */ | 
|   private static File getImageCacheDir(Context context, String cacheName) { | 
|     File cacheDir = context.getExternalCacheDir(); | 
|     if (cacheDir != null) { | 
|       File result = new File(cacheDir, cacheName); | 
|       if (!result.mkdirs() && (!result.exists() || !result.isDirectory())) { | 
|         // File wasn't able to create a directory, or the result exists but not a directory | 
|         return null; | 
|       } | 
|       return result; | 
|     } | 
|     if (Log.isLoggable(TAG, Log.ERROR)) { | 
|       Log.e(TAG, "default disk cache dir is null"); | 
|     } | 
|     return null; | 
|   } | 
|   | 
|   /** | 
|    * start asynchronous compress thread | 
|    */ | 
|   private void launch(final Context context) { | 
|     if (mStreamProviders == null || mStreamProviders.size() == 0 && mCompressListener != null) { | 
|       mCompressListener.onError(new NullPointerException("image file cannot be null")); | 
|     } | 
|   | 
|     Iterator<InputStreamProvider> iterator = mStreamProviders.iterator(); | 
|   | 
|     while (iterator.hasNext()) { | 
|       final InputStreamProvider path = iterator.next(); | 
|   | 
|       AsyncTask.SERIAL_EXECUTOR.execute(new Runnable() { | 
|         @Override | 
|         public void run() { | 
|           try { | 
|             mHandler.sendMessage(mHandler.obtainMessage(MSG_COMPRESS_START)); | 
|   | 
|             File result = compress(context, path); | 
|   | 
|             mHandler.sendMessage(mHandler.obtainMessage(MSG_COMPRESS_SUCCESS, result)); | 
|           } catch (IOException e) { | 
|             mHandler.sendMessage(mHandler.obtainMessage(MSG_COMPRESS_ERROR, e)); | 
|           } | 
|         } | 
|       }); | 
|   | 
|       iterator.remove(); | 
|     } | 
|   } | 
|   | 
|   /** | 
|    * start compress and return the file | 
|    */ | 
|   private File get(InputStreamProvider input, Context context) throws IOException { | 
|     try { | 
|       return new Engine(input, getImageCacheFile(context, Checker.SINGLE.extSuffix(input)), focusAlpha).compress(); | 
|     } finally { | 
|       input.close(); | 
|     } | 
|   } | 
|   | 
|   private List<File> get(Context context) throws IOException { | 
|     List<File> results = new ArrayList<>(); | 
|     Iterator<InputStreamProvider> iterator = mStreamProviders.iterator(); | 
|   | 
|     while (iterator.hasNext()) { | 
|       results.add(compress(context, iterator.next())); | 
|       iterator.remove(); | 
|     } | 
|   | 
|     return results; | 
|   } | 
|   | 
|   private File compress(Context context, InputStreamProvider path) throws IOException { | 
|     try { | 
|       return compressReal(context,path); | 
|     } finally { | 
|       path.close(); | 
|     } | 
|   } | 
|   | 
|   private File compressReal(Context context, InputStreamProvider path) throws IOException { | 
|     File result; | 
|   | 
|     File outFile = getImageCacheFile(context, Checker.SINGLE.extSuffix(path)); | 
|   | 
|     if (mRenameListener != null) { | 
|       String filename = mRenameListener.rename(path.getPath()); | 
|       outFile = getImageCustomFile(context, filename); | 
|     } | 
|   | 
|     if (mCompressionPredicate != null) { | 
|       if (mCompressionPredicate.apply(path.getPath()) | 
|           && Checker.SINGLE.needCompress(mLeastCompressSize, path.getPath())) { | 
|         result = new Engine(path, outFile, focusAlpha).compress(); | 
|       } else { | 
|         result = new File(path.getPath()); | 
|       } | 
|     } else { | 
|       result = Checker.SINGLE.needCompress(mLeastCompressSize, path.getPath()) ? | 
|           new Engine(path, outFile, focusAlpha).compress() : | 
|           new File(path.getPath()); | 
|     } | 
|   | 
|     return result; | 
|   } | 
|   | 
|   @Override | 
|   public boolean handleMessage(Message msg) { | 
|     if (mCompressListener == null) { | 
|       return false; | 
|     } | 
|   | 
|     switch (msg.what) { | 
|       case MSG_COMPRESS_START: | 
|         mCompressListener.onStart(); | 
|         break; | 
|       case MSG_COMPRESS_SUCCESS: | 
|         mCompressListener.onSuccess((File) msg.obj); | 
|         break; | 
|       case MSG_COMPRESS_ERROR: | 
|         mCompressListener.onError((Throwable) msg.obj); | 
|         break; | 
|     } | 
|     return false; | 
|   } | 
|   | 
|   public static class Builder { | 
|     private Context context; | 
|     private String mTargetDir; | 
|     private boolean focusAlpha; | 
|     private int mLeastCompressSize = 100; | 
|     private OnRenameListener mRenameListener; | 
|     private OnCompressListener mCompressListener; | 
|     private CompressionPredicate mCompressionPredicate; | 
|     private List<InputStreamProvider> mStreamProviders; | 
|   | 
|     Builder(Context context) { | 
|       this.context = context; | 
|       this.mStreamProviders = new ArrayList<>(); | 
|     } | 
|   | 
|     private Luban build() { | 
|       return new Luban(this); | 
|     } | 
|   | 
|     public Builder load(InputStreamProvider inputStreamProvider) { | 
|       mStreamProviders.add(inputStreamProvider); | 
|       return this; | 
|     } | 
|   | 
|     public Builder load(final File file) { | 
|       mStreamProviders.add(new InputStreamAdapter() { | 
|         @Override | 
|         public InputStream openInternal() throws IOException { | 
|           return new FileInputStream(file); | 
|         } | 
|   | 
|         @Override | 
|         public String getPath() { | 
|           return file.getAbsolutePath(); | 
|         } | 
|       }); | 
|       return this; | 
|     } | 
|   | 
|     public Builder load(final String string) { | 
|       mStreamProviders.add(new InputStreamAdapter() { | 
|         @Override | 
|         public InputStream openInternal() throws IOException { | 
|           return new FileInputStream(string); | 
|         } | 
|   | 
|         @Override | 
|         public String getPath() { | 
|           return string; | 
|         } | 
|       }); | 
|       return this; | 
|     } | 
|   | 
|     public <T> Builder load(List<T> list) { | 
|       for (T src : list) { | 
|         if (src instanceof String) { | 
|           load((String) src); | 
|         } else if (src instanceof File) { | 
|           load((File) src); | 
|         } else if (src instanceof Uri) { | 
|           load((Uri) src); | 
|         } else { | 
|           throw new IllegalArgumentException("Incoming data type exception, it must be String, File, Uri or Bitmap"); | 
|         } | 
|       } | 
|       return this; | 
|     } | 
|   | 
|     public Builder load(final Uri uri) { | 
|       mStreamProviders.add(new InputStreamAdapter() { | 
|         @Override | 
|         public InputStream openInternal() throws IOException { | 
|           return context.getContentResolver().openInputStream(uri); | 
|         } | 
|   | 
|         @Override | 
|         public String getPath() { | 
|           return uri.getPath(); | 
|         } | 
|       }); | 
|       return this; | 
|     } | 
|   | 
|     public Builder putGear(int gear) { | 
|       return this; | 
|     } | 
|   | 
|     public Builder setRenameListener(OnRenameListener listener) { | 
|       this.mRenameListener = listener; | 
|       return this; | 
|     } | 
|   | 
|     public Builder setCompressListener(OnCompressListener listener) { | 
|       this.mCompressListener = listener; | 
|       return this; | 
|     } | 
|   | 
|     public Builder setTargetDir(String targetDir) { | 
|       this.mTargetDir = targetDir; | 
|       return this; | 
|     } | 
|   | 
|     /** | 
|      * Do I need to keep the image's alpha channel | 
|      * | 
|      * @param focusAlpha <p> true - to keep alpha channel, the compress speed will be slow. </p> | 
|      *                   <p> false - don't keep alpha channel, it might have a black background.</p> | 
|      */ | 
|     public Builder setFocusAlpha(boolean focusAlpha) { | 
|       this.focusAlpha = focusAlpha; | 
|       return this; | 
|     } | 
|   | 
|     /** | 
|      * do not compress when the origin image file size less than one value | 
|      * | 
|      * @param size the value of file size, unit KB, default 100K | 
|      */ | 
|     public Builder ignoreBy(int size) { | 
|       this.mLeastCompressSize = size; | 
|       return this; | 
|     } | 
|   | 
|     /** | 
|      * do compress image when return value was true, otherwise, do not compress the image file | 
|      * | 
|      * @param compressionPredicate A predicate callback that returns true or false for the given input path should be compressed. | 
|      */ | 
|     public Builder filter(CompressionPredicate compressionPredicate) { | 
|       this.mCompressionPredicate = compressionPredicate; | 
|       return this; | 
|     } | 
|   | 
|   | 
|     /** | 
|      * begin compress image with asynchronous | 
|      */ | 
|     public void launch() { | 
|       build().launch(context); | 
|     } | 
|   | 
|     public File get(final String path) throws IOException { | 
|       return build().get(new InputStreamAdapter() { | 
|         @Override | 
|         public InputStream openInternal() throws IOException { | 
|           return new FileInputStream(path); | 
|         } | 
|   | 
|         @Override | 
|         public String getPath() { | 
|           return path; | 
|         } | 
|       }, context); | 
|     } | 
|   | 
|     /** | 
|      * begin compress image with synchronize | 
|      * | 
|      * @return the thumb image file list | 
|      */ | 
|     public List<File> get() throws IOException { | 
|       return build().get(context); | 
|     } | 
|   } | 
| } |