diff --git a/app/build.gradle b/app/build.gradle index 8e25847d2..e086684f2 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -196,7 +196,7 @@ dependencies { implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar']) implementation fileTree(dir: 'aliyun-libs', include: ['*.jar', '*.aar']) implementation 'androidx.appcompat:appcompat:1.4.1' - implementation 'com.google.android.material:material:1.4.+' + implementation 'com.google.android.material:material:1.6.1' implementation 'androidx.constraintlayout:constraintlayout:2.1.3' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' diff --git a/library/src/module_common/java/com/nnbc123/library/common/support/PathHelper.kt b/library/src/module_common/java/com/nnbc123/library/common/support/PathHelper.kt new file mode 100644 index 000000000..77c1821af --- /dev/null +++ b/library/src/module_common/java/com/nnbc123/library/common/support/PathHelper.kt @@ -0,0 +1,242 @@ +package com.nnbc123.library.common.support + +import com.chuhai.utils.AppUtils +import com.chuhai.utils.PathUtils +import com.nnbc123.library.utils.codec.MD5Utils +import java.io.File +import java.text.SimpleDateFormat +import java.util.* + +/** + * Created by Max on 2023/11/2 18:14 + * Desc:路径助手 + */ +object PathHelper { + //动画后缀 + const val SUFFIX_TYPE_SVGA = ".svga" + const val SUFFIX_TYPE_MP4 = ".mp4" + + /** + * 获取应用外部Cache目录路径(优先外置存储,其次内置存储) + */ + fun getExternalCachePath(): String { + val path = PathUtils.getExternalAppCachePath(AppUtils.getApp()) + if (!path.isNullOrEmpty()) { + // /storage/emulated/0/Android/data/package/cache + return path + } + // /data/data/package/cache + return PathUtils.getInternalAppCachePath(AppUtils.getApp()) + } + + /** + * 获取应用外部Files目录路径(优先外置存储,其次内置存储) + */ + fun getExternalFilesPath(): String { + val path = PathUtils.getExternalAppFilesPath(AppUtils.getApp()) + if (!path.isNullOrEmpty()) { + // /storage/emulated/0/Android/data/package/files + return path + } + // /data/data/package/files + return PathUtils.getInternalAppFilesPath(AppUtils.getApp()) + } + + /** + * 获取应用内置Cache目录路径 + */ + fun getInternalCachePath(): String { + // /data/data/package/cache + return PathUtils.getInternalAppCachePath(AppUtils.getApp()) + } + + /** + * 获取应用内置Files目录路径 + */ + fun getInternalFilesPath(): String { + // /data/data/package/files + return PathUtils.getInternalAppFilesPath(AppUtils.getApp()) + } + + /** + * 获取应用外部临时Cache目录路径 + */ + fun getExternalTempCachePath(): String { + // /storage/emulated/0/Android/data/package/cache/temp + // or + // /data/data/package/cache/temp + return PathUtils.plusPathNotNull(getExternalCachePath(), "temp") + } + + /** + * 获取应用内置临时Cache目录路径 + */ + fun getInternalTempCachePath(): String { + // /data/data/package/cache/temp + return PathUtils.plusPathNotNull(getInternalCachePath(), "temp") + } + + /** + * 获取应用外部下载目录路径 + */ + fun getExternalDownloadPath(): String { + val path = PathUtils.getExternalAppDownloadPath(AppUtils.getApp()) + if (!path.isNullOrEmpty()) { + // /storage/emulated/0/Android/data/package/files/Download + return path + } + // /data/data/package/files/download + return PathUtils.plusPathNotNull(getInternalFilesPath(), "download") + } + + /** + * 获取应用外部下载缓存目录路径 + */ + fun getExternalDownloadCachePath(): String { + // /storage/emulated/0/Android/data/package/cache/download + return PathUtils.plusPathNotNull(getExternalCachePath(), "download") + } + + /** + * 获取应用内部下载目录路径 + */ + fun getInternalDownloadPath(): String { + // /data/data/package/files/download + return PathUtils.plusPathNotNull(getInternalFilesPath(), "download") + } + + /** + * 生成外部下载文件缓存文件路径 + * @param url 资源地址 + */ + fun generateExternalDownloadCacheFilePath(url: String): String { + // /storage/emulated/0/Android/data/package/cache/download/**.** + return PathUtils.plusPathNotNull(getExternalDownloadCachePath(), generateNameByUrl(url)) + } + + /** + * 获取下载文件路径(文件路径) + * @param url 资源地址 + */ + fun generateExternalDownloadFilePath(url: String): String { + // /storage/emulated/0/Android/data/package/files/Download/**.** + return PathUtils.plusPathNotNull(getExternalDownloadPath(), generateNameByUrl(url)) + } + + /** + * 生成外部临时缓存文件路径 + * @param url 资源地址 + */ + fun generateExternalTempCacheFilePath(url: String): String { + // /storage/emulated/0/Android/data/package/cache/temp/**.** + return PathUtils.plusPathNotNull(getExternalTempCachePath(), generateNameByUrl(url)) + } + + /** + * 获取日志文件目录 + */ + fun getLogDirectory(): String { + return PathUtils.plusPathNotNull(getInternalFilesPath(), "log${File.separator}log") + } + + /** + * 获取日志文件目录 + */ + fun getLogCacheDirectory(): String { + return PathUtils.plusPathNotNull(getInternalFilesPath(), "log${File.separator}cache") + } + + /** + * 获取声网RTC日志文件目录 + */ + fun getAgoraRtcLogDirectory(): String { + return PathUtils.plusPathNotNull(getExternalFilesPath(), "agora${File.separator}rtc") + } + + /** + * 获取声网RTM日志文件目录 + */ + fun getAgoraRtmLogDirectory(): String { + return PathUtils.plusPathNotNull(getExternalFilesPath(), "agora${File.separator}rtm") + } + + /** + * 生成礼物文件存储文件路径 PS:项目中所有的MP4、SVGA都在此维护 + * @param url 礼物地址 + */ + fun generateGiftDownloadFilePath(url: String): String { + return PathUtils.plusPathNotNull( + PathUtils.plusPathNotNull(getInternalDownloadPath(), "gift"), + generateNameByUrl(url) + ) + } + + /** + * 获取.9图下载文件目录 + */ + fun getNinePatchDownloadDirectory(): String { + return PathUtils.plusPathNotNull(getInternalDownloadPath(), "nine_patch") + } + + /** + * 获取网络请求缓存文件路径 + */ + fun getHttpCachePath(): String { + return PathUtils.plusPathNotNull(getInternalCachePath(), "http") + } + + /** + * 获取Web网页缓存文件路径 + */ + fun getWebCachePath(): String { + return PathUtils.plusPathNotNull(getInternalCachePath(), "web") + } + + /** + * 根据Url生成名称 + * @param url url + */ + fun generateNameByUrl(url: String): String { + // md5路径+后缀 + return MD5Utils.getMD5String(url) + (getSuffixType(url) ?: "") + } + + /** + * 根据文件生成名称 + * @param filePath filePath + * @param sign 签名(为了避免多用户上传同样路径的文件而引起的覆盖冲突,一般情况下拼接上当前用户标识即可) + */ + fun generateNameByFile(filePath: String, sign: String): String { + // md5路径+后缀 + return MD5Utils.getMD5String(sign + filePath) + (getSuffixType(filePath) ?: "") + } + + /** + * 获取后缀名称 + * @param path 路径 + * @return 后缀格式 .mp4 .gif 等 + */ + fun getSuffixType(path: String): String? { + if (path.isEmpty()) { + return null + } + val dotIndex = path.indexOfLast { + '.' == it + } + val separatorIndex = path.indexOfLast { + '/' == it + } + if (dotIndex >= 0 && dotIndex > separatorIndex) { + val suffix = path.substring(dotIndex) + val askIndex = suffix.indexOfLast { + '?' == it + } + return if (askIndex >= 0) { + suffix.substring(0, askIndex) + } else { + suffix + } + } + return null + } +} \ No newline at end of file diff --git a/library/src/module_easyphoto/java/com/nnbc123/library/easyphoto/builder/AlbumBuilder.java b/library/src/module_easyphoto/java/com/nnbc123/library/easyphoto/builder/AlbumBuilder.java index 4ab4f834e..304bb3741 100644 --- a/library/src/module_easyphoto/java/com/nnbc123/library/easyphoto/builder/AlbumBuilder.java +++ b/library/src/module_easyphoto/java/com/nnbc123/library/easyphoto/builder/AlbumBuilder.java @@ -257,6 +257,18 @@ public class AlbumBuilder { return AlbumBuilder.this; } + + /** + * 设置显示照片的最大文件大小 + * + * @param maxFileSize 最大文件大小,单位Bytes + * @return AlbumBuilder + */ + public AlbumBuilder setMaxFileSize(long maxFileSize) { + Setting.maxSize = maxFileSize; + return AlbumBuilder.this; + } + /** * 设置显示照片的最小宽度 * diff --git a/library/src/module_easyphoto/java/com/nnbc123/library/easyphoto/models/album/AlbumModel.java b/library/src/module_easyphoto/java/com/nnbc123/library/easyphoto/models/album/AlbumModel.java index 19298a1fd..5f0aec0ce 100644 --- a/library/src/module_easyphoto/java/com/nnbc123/library/easyphoto/models/album/AlbumModel.java +++ b/library/src/module_easyphoto/java/com/nnbc123/library/easyphoto/models/album/AlbumModel.java @@ -162,7 +162,7 @@ public class AlbumModel { continue; } - if (size < Setting.minSize) { + if (size < Setting.minSize || size > Setting.maxSize) { continue; } diff --git a/library/src/module_easyphoto/java/com/nnbc123/library/easyphoto/setting/Setting.java b/library/src/module_easyphoto/java/com/nnbc123/library/easyphoto/setting/Setting.java index 2d46f90e7..56a4ee92a 100644 --- a/library/src/module_easyphoto/java/com/nnbc123/library/easyphoto/setting/Setting.java +++ b/library/src/module_easyphoto/java/com/nnbc123/library/easyphoto/setting/Setting.java @@ -23,6 +23,7 @@ public class Setting { public static int minWidth = 1; public static int minHeight = 1; public static long minSize = 1; + public static long maxSize = Integer.MAX_VALUE; public static int count = 1; public static WeakReference photosAdView = null; @@ -66,6 +67,7 @@ public class Setting { minWidth = 1; minHeight = 1; minSize = 1; + maxSize = Integer.MAX_VALUE; count = 1; photosAdView = null; diff --git a/library/src/module_utils/java/com/chuhai/utils/ImageUtils.java b/library/src/module_utils/java/com/chuhai/utils/ImageUtils.java new file mode 100644 index 000000000..4f462579e --- /dev/null +++ b/library/src/module_utils/java/com/chuhai/utils/ImageUtils.java @@ -0,0 +1,2091 @@ +package com.chuhai.utils; + +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.Bitmap.CompressFormat; +import android.graphics.BitmapFactory; +import android.graphics.BitmapShader; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.ColorMatrix; +import android.graphics.ColorMatrixColorFilter; +import android.graphics.LinearGradient; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.PixelFormat; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.PorterDuffXfermode; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.Shader; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.media.ExifInterface; +import android.os.Build; +import android.renderscript.Allocation; +import android.renderscript.Element; +import android.renderscript.RenderScript; +import android.renderscript.ScriptIntrinsicBlur; +import android.view.View; + +import androidx.annotation.ColorInt; +import androidx.annotation.DrawableRes; +import androidx.annotation.FloatRange; +import androidx.annotation.IntRange; +import androidx.annotation.NonNull; +import androidx.annotation.RequiresApi; +import androidx.core.content.ContextCompat; + +import java.io.BufferedOutputStream; +import java.io.ByteArrayOutputStream; +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.io.OutputStream; + +/** + *
+ *     author: Blankj
+ *     blog  : http://blankj.com
+ *     user_ic_time  : 2016/08/12
+ *     desc  : utils home_ic_about_us image
+ * 
+ */ +public final class ImageUtils { + + private ImageUtils() { + throw new UnsupportedOperationException("u can't instantiate me..."); + } + + /** + * Bitmap to bytes. + * + * @param bitmap The bitmap. + * @param format The format of bitmap. + * @return bytes + */ + public static byte[] bitmap2Bytes(final Bitmap bitmap, final CompressFormat format) { + if (bitmap == null) return null; + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + bitmap.compress(format, 100, baos); + return baos.toByteArray(); + } + + /** + * Bytes to bitmap. + * + * @param bytes The bytes. + * @return bitmap + */ + public static Bitmap bytes2Bitmap(final byte[] bytes) { + return (bytes == null || bytes.length == 0) + ? null + : BitmapFactory.decodeByteArray(bytes, 0, bytes.length); + } + + /** + * Drawable to bitmap. + * + * @param drawable The drawable. + * @return bitmap + */ + public static Bitmap drawable2Bitmap(final Drawable drawable) { + if (drawable instanceof BitmapDrawable) { + BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable; + if (bitmapDrawable.getBitmap() != null) { + return bitmapDrawable.getBitmap(); + } + } + Bitmap bitmap; + if (drawable.getIntrinsicWidth() <= 0 || drawable.getIntrinsicHeight() <= 0) { + bitmap = Bitmap.createBitmap(1, 1, + drawable.getOpacity() != PixelFormat.OPAQUE + ? Bitmap.Config.ARGB_8888 + : Bitmap.Config.RGB_565); + } else { + bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), + drawable.getIntrinsicHeight(), + drawable.getOpacity() != PixelFormat.OPAQUE + ? Bitmap.Config.ARGB_8888 + : Bitmap.Config.RGB_565); + } + Canvas canvas = new Canvas(bitmap); + drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); + drawable.draw(canvas); + return bitmap; + } + + /** + * Bitmap to drawable. + * + * @param bitmap The bitmap. + * @return drawable + */ + public static Drawable bitmap2Drawable(final Bitmap bitmap) { + return bitmap == null ? null : new BitmapDrawable(AppUtils.getApp().getResources(), bitmap); + } + + /** + * Drawable to bytes. + * + * @param drawable The drawable. + * @param format The format of bitmap. + * @return bytes + */ + public static byte[] drawable2Bytes(final Drawable drawable, final CompressFormat format) { + return drawable == null ? null : bitmap2Bytes(drawable2Bitmap(drawable), format); + } + + /** + * Bytes to drawable. + * + * @param bytes The bytes. + * @return drawable + */ + public static Drawable bytes2Drawable(final byte[] bytes) { + return bitmap2Drawable(bytes2Bitmap(bytes)); + } + + /** + * View to bitmap. + * + * @param view The view. + * @return bitmap + */ + public static Bitmap view2Bitmap(final View view) { + if (view == null) return null; + Bitmap ret = Bitmap.createBitmap(view.getWidth(), + view.getHeight(), + Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(ret); + Drawable bgDrawable = view.getBackground(); + if (bgDrawable != null) { + bgDrawable.draw(canvas); + } else { + canvas.drawColor(Color.WHITE); + } + view.draw(canvas); + return ret; + } + + /** + * Return bitmap. + * + * @param file The file. + * @return bitmap + */ + public static Bitmap getBitmap(final File file) { + if (file == null) return null; + return BitmapFactory.decodeFile(file.getAbsolutePath()); + } + + /** + * Return bitmap. + * + * @param file The file. + * @param maxWidth The maximum width. + * @param maxHeight The maximum height. + * @return bitmap + */ + public static Bitmap getBitmap(final File file, final int maxWidth, final int maxHeight) { + if (file == null) return null; + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; + BitmapFactory.decodeFile(file.getAbsolutePath(), options); + options.inSampleSize = calculateInSampleSize(options, maxWidth, maxHeight); + options.inJustDecodeBounds = false; + return BitmapFactory.decodeFile(file.getAbsolutePath(), options); + } + + /** + * Return bitmap. + * + * @param filePath The path of file. + * @return bitmap + */ + public static Bitmap getBitmap(final String filePath) { + if (isSpace(filePath)) return null; + return BitmapFactory.decodeFile(filePath); + } + + /** + * Return bitmap. + * + * @param filePath The path of file. + * @param maxWidth The maximum width. + * @param maxHeight The maximum height. + * @return bitmap + */ + public static Bitmap getBitmap(final String filePath, final int maxWidth, final int maxHeight) { + if (isSpace(filePath)) return null; + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; + BitmapFactory.decodeFile(filePath, options); + options.inSampleSize = calculateInSampleSize(options, maxWidth, maxHeight); + options.inJustDecodeBounds = false; + return BitmapFactory.decodeFile(filePath, options); + } + + /** + * Return bitmap. + * + * @param is The input stream. + * @return bitmap + */ + public static Bitmap getBitmap(final InputStream is) { + if (is == null) return null; + return BitmapFactory.decodeStream(is); + } + + /** + * Return bitmap. + * + * @param is The input stream. + * @param maxWidth The maximum width. + * @param maxHeight The maximum height. + * @return bitmap + */ + public static Bitmap getBitmap(final InputStream is, final int maxWidth, final int maxHeight) { + if (is == null) return null; + byte[] bytes = input2Byte(is); + return getBitmap(bytes, 0, maxWidth, maxHeight); + } + + /** + * Return bitmap. + * + * @param data The data. + * @param offset The offset. + * @return bitmap + */ + public static Bitmap getBitmap(final byte[] data, final int offset) { + if (data.length == 0) return null; + return BitmapFactory.decodeByteArray(data, offset, data.length); + } + + /** + * Return bitmap. + * + * @param data The data. + * @param offset The offset. + * @param maxWidth The maximum width. + * @param maxHeight The maximum height. + * @return bitmap + */ + public static Bitmap getBitmap(final byte[] data, + final int offset, + final int maxWidth, + final int maxHeight) { + if (data.length == 0) return null; + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; + BitmapFactory.decodeByteArray(data, offset, data.length, options); + options.inSampleSize = calculateInSampleSize(options, maxWidth, maxHeight); + options.inJustDecodeBounds = false; + return BitmapFactory.decodeByteArray(data, offset, data.length, options); + } + + /** + * Return bitmap. + * + * @param resId The resource id. + * @return bitmap + */ + public static Bitmap getBitmap(@DrawableRes final int resId) { + Drawable drawable = ContextCompat.getDrawable(AppUtils.getApp(), resId); + Canvas canvas = new Canvas(); + Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), + drawable.getIntrinsicHeight(), + Bitmap.Config.ARGB_8888); + canvas.setBitmap(bitmap); + drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); + drawable.draw(canvas); + return bitmap; + } + + /** + * Return bitmap. + * + * @param resId The resource id. + * @param maxWidth The maximum width. + * @param maxHeight The maximum height. + * @return bitmap + */ + public static Bitmap getBitmap(@DrawableRes final int resId, + final int maxWidth, + final int maxHeight) { + BitmapFactory.Options options = new BitmapFactory.Options(); + final Resources resources = AppUtils.getApp().getResources(); + options.inJustDecodeBounds = true; + BitmapFactory.decodeResource(resources, resId, options); + options.inSampleSize = calculateInSampleSize(options, maxWidth, maxHeight); + options.inJustDecodeBounds = false; + return BitmapFactory.decodeResource(resources, resId, options); + } + + /** + * Return bitmap. + * + * @param fd The file descriptor. + * @return bitmap + */ + public static Bitmap getBitmap(final FileDescriptor fd) { + if (fd == null) return null; + return BitmapFactory.decodeFileDescriptor(fd); + } + + /** + * Return bitmap. + * + * @param fd The file descriptor + * @param maxWidth The maximum width. + * @param maxHeight The maximum height. + * @return bitmap + */ + public static Bitmap getBitmap(final FileDescriptor fd, + final int maxWidth, + final int maxHeight) { + if (fd == null) return null; + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; + BitmapFactory.decodeFileDescriptor(fd, null, options); + options.inSampleSize = calculateInSampleSize(options, maxWidth, maxHeight); + options.inJustDecodeBounds = false; + return BitmapFactory.decodeFileDescriptor(fd, null, options); + } + + /** + * Return the bitmap with the specified color. + * + * @param src The source of bitmap. + * @param color The color. + * @return the bitmap with the specified color + */ + public static Bitmap drawColor(@NonNull final Bitmap src, @ColorInt final int color) { + return drawColor(src, color, false); + } + + /** + * Return the bitmap with the specified color. + * + * @param src The source of bitmap. + * @param color The color. + * @param recycle True to recycle the source of bitmap, false otherwise. + * @return the bitmap with the specified color + */ + public static Bitmap drawColor(@NonNull final Bitmap src, + @ColorInt final int color, + final boolean recycle) { + if (isEmptyBitmap(src)) return null; + Bitmap ret = recycle ? src : src.copy(src.getConfig(), true); + Canvas canvas = new Canvas(ret); + canvas.drawColor(color, PorterDuff.Mode.DARKEN); + return ret; + } + + + /** + * Return the bitmap with the specified background color. + * + * @param src The source of bitmap. + * @param color The color. + * @return the bitmap with the specified color + */ + public static Bitmap drawBackgroundColor(@NonNull final Bitmap src, + @ColorInt final int color) { + if (isEmptyBitmap(src)) return null; + Bitmap out = Bitmap.createBitmap(src.getWidth(), src.getHeight(), src.getConfig()); + Canvas canvas = new Canvas(out); + Paint paint = new Paint(); + paint.setColor(color); + canvas.drawColor(color); + canvas.drawBitmap(src, 0, 0, paint); + return out; + } + + /** + * Return the scaled bitmap. + * + * @param src The source of bitmap. + * @param newWidth The new width. + * @param newHeight The new height. + * @return the scaled bitmap + */ + public static Bitmap scale(final Bitmap src, final int newWidth, final int newHeight) { + return scale(src, newWidth, newHeight, false); + } + + /** + * Return the scaled bitmap. + * + * @param src The source of bitmap. + * @param newWidth The new width. + * @param newHeight The new height. + * @param recycle True to recycle the source of bitmap, false otherwise. + * @return the scaled bitmap + */ + public static Bitmap scale(final Bitmap src, + final int newWidth, + final int newHeight, + final boolean recycle) { + if (isEmptyBitmap(src)) return null; + Bitmap ret = Bitmap.createScaledBitmap(src, newWidth, newHeight, true); + if (recycle && !src.isRecycled() && ret != src) src.recycle(); + return ret; + } + + /** + * Return the scaled bitmap + * + * @param src The source of bitmap. + * @param scaleWidth The scale of width. + * @param scaleHeight The scale of height. + * @return the scaled bitmap + */ + public static Bitmap scale(final Bitmap src, final float scaleWidth, final float scaleHeight) { + return scale(src, scaleWidth, scaleHeight, false); + } + + /** + * Return the scaled bitmap + * + * @param src The source of bitmap. + * @param scaleWidth The scale of width. + * @param scaleHeight The scale of height. + * @param recycle True to recycle the source of bitmap, false otherwise. + * @return the scaled bitmap + */ + public static Bitmap scale(final Bitmap src, + final float scaleWidth, + final float scaleHeight, + final boolean recycle) { + if (isEmptyBitmap(src)) return null; + Matrix matrix = new Matrix(); + matrix.setScale(scaleWidth, scaleHeight); + Bitmap ret = Bitmap.createBitmap(src, 0, 0, src.getWidth(), src.getHeight(), matrix, true); + if (recycle && !src.isRecycled() && ret != src) src.recycle(); + return ret; + } + + /** + * Return the clipped bitmap. + * + * @param src The source of bitmap. + * @param x The x coordinate of the first pixel. + * @param y The y coordinate of the first pixel. + * @param width The width. + * @param height The height. + * @return the clipped bitmap + */ + public static Bitmap clip(final Bitmap src, + final int x, + final int y, + final int width, + final int height) { + return clip(src, x, y, width, height, false); + } + + /** + * Return the clipped bitmap. + * + * @param src The source of bitmap. + * @param x The x coordinate of the first pixel. + * @param y The y coordinate of the first pixel. + * @param width The width. + * @param height The height. + * @param recycle True to recycle the source of bitmap, false otherwise. + * @return the clipped bitmap + */ + public static Bitmap clip(final Bitmap src, + final int x, + final int y, + final int width, + final int height, + final boolean recycle) { + if (isEmptyBitmap(src)) return null; + Bitmap ret = Bitmap.createBitmap(src, x, y, width, height); + if (recycle && !src.isRecycled() && ret != src) src.recycle(); + return ret; + } + + /** + * Return the skewed bitmap. + * + * @param src The source of bitmap. + * @param kx The skew factor of x. + * @param ky The skew factor of y. + * @return the skewed bitmap + */ + public static Bitmap skew(final Bitmap src, final float kx, final float ky) { + return skew(src, kx, ky, 0, 0, false); + } + + /** + * Return the skewed bitmap. + * + * @param src The source of bitmap. + * @param kx The skew factor of x. + * @param ky The skew factor of y. + * @param recycle True to recycle the source of bitmap, false otherwise. + * @return the skewed bitmap + */ + public static Bitmap skew(final Bitmap src, + final float kx, + final float ky, + final boolean recycle) { + return skew(src, kx, ky, 0, 0, recycle); + } + + /** + * Return the skewed bitmap. + * + * @param src The source of bitmap. + * @param kx The skew factor of x. + * @param ky The skew factor of y. + * @param px The x coordinate of the pivot point. + * @param py The y coordinate of the pivot point. + * @return the skewed bitmap + */ + public static Bitmap skew(final Bitmap src, + final float kx, + final float ky, + final float px, + final float py) { + return skew(src, kx, ky, px, py, false); + } + + /** + * Return the skewed bitmap. + * + * @param src The source of bitmap. + * @param kx The skew factor of x. + * @param ky The skew factor of y. + * @param px The x coordinate of the pivot point. + * @param py The y coordinate of the pivot point. + * @param recycle True to recycle the source of bitmap, false otherwise. + * @return the skewed bitmap + */ + public static Bitmap skew(final Bitmap src, + final float kx, + final float ky, + final float px, + final float py, + final boolean recycle) { + if (isEmptyBitmap(src)) return null; + Matrix matrix = new Matrix(); + matrix.setSkew(kx, ky, px, py); + Bitmap ret = Bitmap.createBitmap(src, 0, 0, src.getWidth(), src.getHeight(), matrix, true); + if (recycle && !src.isRecycled() && ret != src) src.recycle(); + return ret; + } + + /** + * Return the rotated bitmap. + * + * @param src The source of bitmap. + * @param degrees The number of degrees. + * @param px The x coordinate of the pivot point. + * @param py The y coordinate of the pivot point. + * @return the rotated bitmap + */ + public static Bitmap rotate(final Bitmap src, + final int degrees, + final float px, + final float py) { + return rotate(src, degrees, px, py, false); + } + + /** + * Return the rotated bitmap. + * + * @param src The source of bitmap. + * @param degrees The number of degrees. + * @param px The x coordinate of the pivot point. + * @param py The y coordinate of the pivot point. + * @param recycle True to recycle the source of bitmap, false otherwise. + * @return the rotated bitmap + */ + public static Bitmap rotate(final Bitmap src, + final int degrees, + final float px, + final float py, + final boolean recycle) { + if (isEmptyBitmap(src)) return null; + if (degrees == 0) return src; + Matrix matrix = new Matrix(); + matrix.setRotate(degrees, px, py); + Bitmap ret = Bitmap.createBitmap(src, 0, 0, src.getWidth(), src.getHeight(), matrix, true); + if (recycle && !src.isRecycled() && ret != src) src.recycle(); + return ret; + } + + /** + * Return the rotated degree. + * + * @param filePath The path of file. + * @return the rotated degree + */ + public static int getRotateDegree(final String filePath) { + try { + ExifInterface exifInterface = new ExifInterface(filePath); + int orientation = exifInterface.getAttributeInt( + ExifInterface.TAG_ORIENTATION, + ExifInterface.ORIENTATION_NORMAL + ); + switch (orientation) { + case ExifInterface.ORIENTATION_ROTATE_90: + return 90; + case ExifInterface.ORIENTATION_ROTATE_180: + return 180; + case ExifInterface.ORIENTATION_ROTATE_270: + return 270; + default: + return 0; + } + } catch (IOException e) { + e.printStackTrace(); + return -1; + } + } + + /** + * Return the round bitmap. + * + * @param src The source of bitmap. + * @return the round bitmap + */ + public static Bitmap toRound(final Bitmap src) { + return toRound(src, 0, 0, false); + } + + /** + * Return the round bitmap. + * + * @param src The source of bitmap. + * @param recycle True to recycle the source of bitmap, false otherwise. + * @return the round bitmap + */ + public static Bitmap toRound(final Bitmap src, final boolean recycle) { + return toRound(src, 0, 0, recycle); + } + + /** + * Return the round bitmap. + * + * @param src The source of bitmap. + * @param borderSize The size of border. + * @param borderColor The color of border. + * @return the round bitmap + */ + public static Bitmap toRound(final Bitmap src, + @IntRange(from = 0) int borderSize, + @ColorInt int borderColor) { + return toRound(src, borderSize, borderColor, false); + } + + /** + * Return the round bitmap. + * + * @param src The source of bitmap. + * @param recycle True to recycle the source of bitmap, false otherwise. + * @param borderSize The size of border. + * @param borderColor The color of border. + * @return the round bitmap + */ + public static Bitmap toRound(final Bitmap src, + @IntRange(from = 0) int borderSize, + @ColorInt int borderColor, + final boolean recycle) { + if (isEmptyBitmap(src)) return null; + int width = src.getWidth(); + int height = src.getHeight(); + int size = Math.min(width, height); + Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); + Bitmap ret = Bitmap.createBitmap(width, height, src.getConfig()); + float center = size / 2f; + RectF rectF = new RectF(0, 0, width, height); + rectF.inset((width - size) / 2f, (height - size) / 2f); + Matrix matrix = new Matrix(); + matrix.setTranslate(rectF.left, rectF.top); + if (width != height) { + matrix.preScale((float) size / width, (float) size / height); + } + BitmapShader shader = new BitmapShader(src, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); + shader.setLocalMatrix(matrix); + paint.setShader(shader); + Canvas canvas = new Canvas(ret); + canvas.drawRoundRect(rectF, center, center, paint); + if (borderSize > 0) { + paint.setShader(null); + paint.setColor(borderColor); + paint.setStyle(Paint.Style.STROKE); + paint.setStrokeWidth(borderSize); + float radius = center - borderSize / 2f; + canvas.drawCircle(width / 2f, height / 2f, radius, paint); + } + if (recycle && !src.isRecycled() && ret != src) src.recycle(); + return ret; + } + + /** + * 圆角 bitmap + * + * @param bitmap 图片 + * @param round 圆角 + * @param recycle 是否释放原来的bitmap + */ + public static Bitmap toRound(final Bitmap bitmap, final float round, final boolean recycle) { + if (bitmap == null) { + return null; + } + try { + Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), + bitmap.getHeight(), Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(output); + Paint paint = new Paint(); + Rect rect = new Rect(0, 0, bitmap.getWidth(), + bitmap.getHeight()); + RectF rectF = new RectF(new Rect(0, 0, bitmap.getWidth(), + bitmap.getHeight())); + paint.setAntiAlias(true); + canvas.drawARGB(0, 0, 0, 0); + paint.setColor(Color.BLACK); + canvas.drawRoundRect(rectF, round, round, paint); + paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); + + Rect src = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()); + canvas.drawBitmap(bitmap, src, rect, paint); + if (recycle && !bitmap.isRecycled() && output != bitmap) bitmap.recycle(); + return output; + } catch (Exception e) { + return bitmap; + } + + } + + /** + * Return the round corner bitmap. + * + * @param src The source of bitmap. + * @param radius The radius of corner. + * @return the round corner bitmap + */ + public static Bitmap toRoundCorner(final Bitmap src, final float radius) { + return toRoundCorner(src, radius, 0, 0, false); + } + + /** + * Return the round corner bitmap. + * + * @param src The source of bitmap. + * @param radius The radius of corner. + * @param recycle True to recycle the source of bitmap, false otherwise. + * @return the round corner bitmap + */ + public static Bitmap toRoundCorner(final Bitmap src, + final float radius, + final boolean recycle) { + return toRoundCorner(src, radius, 0, 0, recycle); + } + + /** + * Return the round corner bitmap. + * + * @param src The source of bitmap. + * @param radius The radius of corner. + * @param borderSize The size of border. + * @param borderColor The color of border. + * @return the round corner bitmap + */ + public static Bitmap toRoundCorner(final Bitmap src, + final float radius, + @IntRange(from = 0) int borderSize, + @ColorInt int borderColor) { + return toRoundCorner(src, radius, borderSize, borderColor, false); + } + + /** + * Return the round corner bitmap. + * + * @param src The source of bitmap. + * @param radius The radius of corner. + * @param borderSize The size of border. + * @param borderColor The color of border. + * @param recycle True to recycle the source of bitmap, false otherwise. + * @return the round corner bitmap + */ + public static Bitmap toRoundCorner(final Bitmap src, + final float radius, + @IntRange(from = 0) int borderSize, + @ColorInt int borderColor, + final boolean recycle) { + if (isEmptyBitmap(src)) return null; + int width = src.getWidth(); + int height = src.getHeight(); + Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); + Bitmap ret = Bitmap.createBitmap(width, height, src.getConfig()); + BitmapShader shader = new BitmapShader(src, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); + paint.setShader(shader); + Canvas canvas = new Canvas(ret); + RectF rectF = new RectF(0, 0, width, height); + float halfBorderSize = borderSize / 2f; + rectF.inset(halfBorderSize, halfBorderSize); + canvas.drawRoundRect(rectF, radius, radius, paint); + if (borderSize > 0) { + paint.setShader(null); + paint.setColor(borderColor); + paint.setStyle(Paint.Style.STROKE); + paint.setStrokeWidth(borderSize); + paint.setStrokeCap(Paint.Cap.ROUND); + canvas.drawRoundRect(rectF, radius, radius, paint); + } + if (recycle && !src.isRecycled() && ret != src) src.recycle(); + return ret; + } + + /** + * Return the round corner bitmap with border. + * + * @param src The source of bitmap. + * @param borderSize The size of border. + * @param color The color of border. + * @param cornerRadius The radius of corner. + * @return the round corner bitmap with border + */ + public static Bitmap addCornerBorder(final Bitmap src, + @IntRange(from = 1) final int borderSize, + @ColorInt final int color, + @FloatRange(from = 0) final float cornerRadius) { + return addBorder(src, borderSize, color, false, cornerRadius, false); + } + + /** + * Return the round corner bitmap with border. + * + * @param src The source of bitmap. + * @param borderSize The size of border. + * @param color The color of border. + * @param cornerRadius The radius of corner. + * @param recycle True to recycle the source of bitmap, false otherwise. + * @return the round corner bitmap with border + */ + public static Bitmap addCornerBorder(final Bitmap src, + @IntRange(from = 1) final int borderSize, + @ColorInt final int color, + @FloatRange(from = 0) final float cornerRadius, + final boolean recycle) { + return addBorder(src, borderSize, color, false, cornerRadius, recycle); + } + + /** + * Return the round bitmap with border. + * + * @param src The source of bitmap. + * @param borderSize The size of border. + * @param color The color of border. + * @return the round bitmap with border + */ + public static Bitmap addCircleBorder(final Bitmap src, + @IntRange(from = 1) final int borderSize, + @ColorInt final int color) { + return addBorder(src, borderSize, color, true, 0, false); + } + + /** + * Return the round bitmap with border. + * + * @param src The source of bitmap. + * @param borderSize The size of border. + * @param color The color of border. + * @param recycle True to recycle the source of bitmap, false otherwise. + * @return the round bitmap with border + */ + public static Bitmap addCircleBorder(final Bitmap src, + @IntRange(from = 1) final int borderSize, + @ColorInt final int color, + final boolean recycle) { + return addBorder(src, borderSize, color, true, 0, recycle); + } + + /** + * Return the bitmap with border. + * + * @param src The source of bitmap. + * @param borderSize The size of border. + * @param color The color of border. + * @param isCircle True to draw circle, false to draw corner. + * @param cornerRadius The radius of corner. + * @param recycle True to recycle the source of bitmap, false otherwise. + * @return the bitmap with border + */ + private static Bitmap addBorder(final Bitmap src, + @IntRange(from = 1) final int borderSize, + @ColorInt final int color, + final boolean isCircle, + final float cornerRadius, + final boolean recycle) { + if (isEmptyBitmap(src)) return null; + Bitmap ret = recycle ? src : src.copy(src.getConfig(), true); + int width = ret.getWidth(); + int height = ret.getHeight(); + Canvas canvas = new Canvas(ret); + Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); + paint.setColor(color); + paint.setStyle(Paint.Style.STROKE); + paint.setStrokeWidth(borderSize); + if (isCircle) { + float radius = Math.min(width, height) / 2f - borderSize / 2f; + canvas.drawCircle(width / 2f, height / 2f, radius, paint); + } else { + int halfBorderSize = borderSize >> 1; + RectF rectF = new RectF(halfBorderSize, halfBorderSize, + width - halfBorderSize, height - halfBorderSize); + canvas.drawRoundRect(rectF, cornerRadius, cornerRadius, paint); + } + return ret; + } + + /** + * Return the bitmap with reflection. + * + * @param src The source of bitmap. + * @param reflectionHeight The height of reflection. + * @return the bitmap with reflection + */ + public static Bitmap addReflection(final Bitmap src, final int reflectionHeight) { + return addReflection(src, reflectionHeight, false); + } + + /** + * Return the bitmap with reflection. + * + * @param src The source of bitmap. + * @param reflectionHeight The height of reflection. + * @param recycle True to recycle the source of bitmap, false otherwise. + * @return the bitmap with reflection + */ + public static Bitmap addReflection(final Bitmap src, + final int reflectionHeight, + final boolean recycle) { + if (isEmptyBitmap(src)) return null; + final int REFLECTION_GAP = 0; + int srcWidth = src.getWidth(); + int srcHeight = src.getHeight(); + Matrix matrix = new Matrix(); + matrix.preScale(1, -1); + Bitmap reflectionBitmap = Bitmap.createBitmap(src, 0, srcHeight - reflectionHeight, + srcWidth, reflectionHeight, matrix, false); + Bitmap ret = Bitmap.createBitmap(srcWidth, srcHeight + reflectionHeight, src.getConfig()); + Canvas canvas = new Canvas(ret); + canvas.drawBitmap(src, 0, 0, null); + canvas.drawBitmap(reflectionBitmap, 0, srcHeight + REFLECTION_GAP, null); + Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); + LinearGradient shader = new LinearGradient( + 0, srcHeight, + 0, ret.getHeight() + REFLECTION_GAP, + 0x70FFFFFF, + 0x00FFFFFF, + Shader.TileMode.MIRROR); + paint.setShader(shader); + paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN)); + canvas.drawRect(0, srcHeight + REFLECTION_GAP, srcWidth, ret.getHeight(), paint); + if (!reflectionBitmap.isRecycled()) reflectionBitmap.recycle(); + if (recycle && !src.isRecycled() && ret != src) src.recycle(); + return ret; + } + + /** + * Return the bitmap with text watermarking. + * + * @param src The source of bitmap. + * @param content The content of text. + * @param textSize The size of text. + * @param color The color of text. + * @param x The x coordinate of the first pixel. + * @param y The y coordinate of the first pixel. + * @return the bitmap with text watermarking + */ + public static Bitmap addTextWatermark(final Bitmap src, + final String content, + final int textSize, + @ColorInt final int color, + final float x, + final float y) { + return addTextWatermark(src, content, textSize, color, x, y, false); + } + + /** + * Return the bitmap with text watermarking. + * + * @param src The source of bitmap. + * @param content The content of text. + * @param textSize The size of text. + * @param color The color of text. + * @param x The x coordinate of the first pixel. + * @param y The y coordinate of the first pixel. + * @param recycle True to recycle the source of bitmap, false otherwise. + * @return the bitmap with text watermarking + */ + public static Bitmap addTextWatermark(final Bitmap src, + final String content, + final float textSize, + @ColorInt final int color, + final float x, + final float y, + final boolean recycle) { + if (isEmptyBitmap(src) || content == null) return null; + Bitmap ret = src.copy(src.getConfig(), true); + Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); + Canvas canvas = new Canvas(ret); + paint.setColor(color); + paint.setTextSize(textSize); + Rect bounds = new Rect(); + paint.getTextBounds(content, 0, content.length(), bounds); + canvas.drawText(content, x, y + textSize, paint); + if (recycle && !src.isRecycled() && ret != src) src.recycle(); + return ret; + } + + /** + * Return the bitmap with image watermarking. + * + * @param src The source of bitmap. + * @param watermark The image watermarking. + * @param x The x coordinate of the first pixel. + * @param y The y coordinate of the first pixel. + * @param alpha The alpha of watermark. + * @return the bitmap with image watermarking + */ + public static Bitmap addImageWatermark(final Bitmap src, + final Bitmap watermark, + final int x, final int y, + final int alpha) { + return addImageWatermark(src, watermark, x, y, alpha, false); + } + + /** + * Return the bitmap with image watermarking. + * + * @param src The source of bitmap. + * @param watermark The image watermarking. + * @param x The x coordinate of the first pixel. + * @param y The y coordinate of the first pixel. + * @param alpha The alpha of watermark. + * @param recycle True to recycle the source of bitmap, false otherwise. + * @return the bitmap with image watermarking + */ + public static Bitmap addImageWatermark(final Bitmap src, + final Bitmap watermark, + final int x, + final int y, + final int alpha, + final boolean recycle) { + if (isEmptyBitmap(src)) return null; + Bitmap ret = src.copy(src.getConfig(), true); + if (!isEmptyBitmap(watermark)) { + Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); + Canvas canvas = new Canvas(ret); + paint.setAlpha(alpha); + canvas.drawBitmap(watermark, x, y, paint); + } + if (recycle && !src.isRecycled() && ret != src) src.recycle(); + return ret; + } + + /** + * Return the alpha bitmap. + * + * @param src The source of bitmap. + * @return the alpha bitmap + */ + public static Bitmap toAlpha(final Bitmap src) { + return toAlpha(src, false); + } + + /** + * Return the alpha bitmap. + * + * @param src The source of bitmap. + * @param recycle True to recycle the source of bitmap, false otherwise. + * @return the alpha bitmap + */ + public static Bitmap toAlpha(final Bitmap src, final Boolean recycle) { + if (isEmptyBitmap(src)) return null; + Bitmap ret = src.extractAlpha(); + if (recycle && !src.isRecycled() && ret != src) src.recycle(); + return ret; + } + + /** + * Return the gray bitmap. + * + * @param src The source of bitmap. + * @return the gray bitmap + */ + public static Bitmap toGray(final Bitmap src) { + return toGray(src, false); + } + + /** + * Return the gray bitmap. + * + * @param src The source of bitmap. + * @param recycle True to recycle the source of bitmap, false otherwise. + * @return the gray bitmap + */ + public static Bitmap toGray(final Bitmap src, final boolean recycle) { + if (isEmptyBitmap(src)) return null; + Bitmap ret = Bitmap.createBitmap(src.getWidth(), src.getHeight(), src.getConfig()); + Canvas canvas = new Canvas(ret); + Paint paint = new Paint(); + ColorMatrix colorMatrix = new ColorMatrix(); + colorMatrix.setSaturation(0); + ColorMatrixColorFilter colorMatrixColorFilter = new ColorMatrixColorFilter(colorMatrix); + paint.setColorFilter(colorMatrixColorFilter); + canvas.drawBitmap(src, 0, 0, paint); + if (recycle && !src.isRecycled() && ret != src) src.recycle(); + return ret; + } + + /** + * Return the blur bitmap fast. + *

zoom out, blur, zoom in

+ * + * @param src The source of bitmap. + * @param scale The scale(0...1). + * @param radius The radius(0...25). + * @return the blur bitmap + */ + public static Bitmap fastBlur(final Bitmap src, + @FloatRange( + from = 0, to = 1, fromInclusive = false + ) final float scale, + @FloatRange( + from = 0, to = 25, fromInclusive = false + ) final float radius) { + return fastBlur(src, scale, radius, false, false); + } + + /** + * Return the blur bitmap fast. + *

zoom out, blur, zoom in

+ * + * @param src The source of bitmap. + * @param scale The scale(0...1). + * @param radius The radius(0...25). + * @return the blur bitmap + */ + public static Bitmap fastBlur(final Bitmap src, + @FloatRange( + from = 0, to = 1, fromInclusive = false + ) final float scale, + @FloatRange( + from = 0, to = 25, fromInclusive = false + ) final float radius, + final boolean recycle) { + return fastBlur(src, scale, radius, recycle, false); + } + + /** + * Return the blur bitmap fast. + *

zoom out, blur, zoom in

+ * + * @param src The source of bitmap. + * @param scale The scale(0...1). + * @param radius The radius(0...25). + * @param recycle True to recycle the source of bitmap, false otherwise. + * @param isReturnScale True to return the scale blur bitmap, false otherwise. + * @return the blur bitmap + */ + public static Bitmap fastBlur(final Bitmap src, + @FloatRange( + from = 0, to = 1, fromInclusive = false + ) final float scale, + @FloatRange( + from = 0, to = 25, fromInclusive = false + ) final float radius, + final boolean recycle, + final boolean isReturnScale) { + if (isEmptyBitmap(src)) return null; + int width = src.getWidth(); + int height = src.getHeight(); + Matrix matrix = new Matrix(); + matrix.setScale(scale, scale); + Bitmap scaleBitmap = + Bitmap.createBitmap(src, 0, 0, src.getWidth(), src.getHeight(), matrix, true); + Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG); + Canvas canvas = new Canvas(); + PorterDuffColorFilter filter = new PorterDuffColorFilter( + Color.TRANSPARENT, PorterDuff.Mode.SRC_ATOP); + paint.setColorFilter(filter); + canvas.scale(scale, scale); + canvas.drawBitmap(scaleBitmap, 0, 0, paint); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + scaleBitmap = renderScriptBlur(scaleBitmap, radius, recycle); + } else { + scaleBitmap = stackBlur(scaleBitmap, (int) radius, recycle); + } + if (scale == 1 || isReturnScale) { + if (recycle && !src.isRecycled() && scaleBitmap != src) src.recycle(); + return scaleBitmap; + } + Bitmap ret = Bitmap.createScaledBitmap(scaleBitmap, width, height, true); + if (!scaleBitmap.isRecycled()) scaleBitmap.recycle(); + if (recycle && !src.isRecycled() && ret != src) src.recycle(); + return ret; + } + + /** + * Return the blur bitmap using render script. + * + * @param src The source of bitmap. + * @param radius The radius(0...25). + * @return the blur bitmap + */ + @RequiresApi(Build.VERSION_CODES.JELLY_BEAN_MR1) + public static Bitmap renderScriptBlur(final Bitmap src, + @FloatRange( + from = 0, to = 25, fromInclusive = false + ) final float radius) { + return renderScriptBlur(src, radius, false); + } + + /** + * Return the blur bitmap using render script. + * + * @param src The source of bitmap. + * @param radius The radius(0...25). + * @param recycle True to recycle the source of bitmap, false otherwise. + * @return the blur bitmap + */ + @RequiresApi(Build.VERSION_CODES.JELLY_BEAN_MR1) + public static Bitmap renderScriptBlur(final Bitmap src, + @FloatRange( + from = 0, to = 25, fromInclusive = false + ) final float radius, + final boolean recycle) { + RenderScript rs = null; + Bitmap ret = recycle ? src : src.copy(src.getConfig(), true); + try { + rs = RenderScript.create(AppUtils.getApp()); + rs.setMessageHandler(new RenderScript.RSMessageHandler()); + Allocation input = Allocation.createFromBitmap(rs, + ret, + Allocation.MipmapControl.MIPMAP_NONE, + Allocation.USAGE_SCRIPT); + Allocation output = Allocation.createTyped(rs, input.getType()); + ScriptIntrinsicBlur blurScript = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs)); + blurScript.setInput(input); + blurScript.setRadius(radius); + blurScript.forEach(output); + output.copyTo(ret); + } finally { + if (rs != null) { + rs.destroy(); + } + } + return ret; + } + + /** + * Return the blur bitmap using stack. + * + * @param src The source of bitmap. + * @param radius The radius(0...25). + * @return the blur bitmap + */ + public static Bitmap stackBlur(final Bitmap src, final int radius) { + return stackBlur(src, radius, false); + } + + /** + * Return the blur bitmap using stack. + * + * @param src The source of bitmap. + * @param radius The radius(0...25). + * @param recycle True to recycle the source of bitmap, false otherwise. + * @return the blur bitmap + */ + public static Bitmap stackBlur(final Bitmap src, int radius, final boolean recycle) { + Bitmap ret = recycle ? src : src.copy(src.getConfig(), true); + if (radius < 1) { + radius = 1; + } + int w = ret.getWidth(); + int h = ret.getHeight(); + + int[] pix = new int[w * h]; + ret.getPixels(pix, 0, w, 0, 0, w, h); + + int wm = w - 1; + int hm = h - 1; + int wh = w * h; + int div = radius + radius + 1; + + int[] r = new int[wh]; + int[] g = new int[wh]; + int[] b = new int[wh]; + int rsum, gsum, bsum, x, y, i, p, yp, yi, yw; + int[] vmin = new int[Math.max(w, h)]; + + int divsum = (div + 1) >> 1; + divsum *= divsum; + int[] dv = new int[256 * divsum]; + for (i = 0; i < 256 * divsum; i++) { + dv[i] = (i / divsum); + } + + yw = yi = 0; + + int[][] stack = new int[div][3]; + int stackpointer; + int stackstart; + int[] sir; + int rbs; + int r1 = radius + 1; + int routsum, goutsum, boutsum; + int rinsum, ginsum, binsum; + + for (y = 0; y < h; y++) { + rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0; + for (i = -radius; i <= radius; i++) { + p = pix[yi + Math.min(wm, Math.max(i, 0))]; + sir = stack[i + radius]; + sir[0] = (p & 0xff0000) >> 16; + sir[1] = (p & 0x00ff00) >> 8; + sir[2] = (p & 0x0000ff); + rbs = r1 - Math.abs(i); + rsum += sir[0] * rbs; + gsum += sir[1] * rbs; + bsum += sir[2] * rbs; + if (i > 0) { + rinsum += sir[0]; + ginsum += sir[1]; + binsum += sir[2]; + } else { + routsum += sir[0]; + goutsum += sir[1]; + boutsum += sir[2]; + } + } + stackpointer = radius; + + for (x = 0; x < w; x++) { + + r[yi] = dv[rsum]; + g[yi] = dv[gsum]; + b[yi] = dv[bsum]; + + rsum -= routsum; + gsum -= goutsum; + bsum -= boutsum; + + stackstart = stackpointer - radius + div; + sir = stack[stackstart % div]; + + routsum -= sir[0]; + goutsum -= sir[1]; + boutsum -= sir[2]; + + if (y == 0) { + vmin[x] = Math.min(x + radius + 1, wm); + } + p = pix[yw + vmin[x]]; + + sir[0] = (p & 0xff0000) >> 16; + sir[1] = (p & 0x00ff00) >> 8; + sir[2] = (p & 0x0000ff); + + rinsum += sir[0]; + ginsum += sir[1]; + binsum += sir[2]; + + rsum += rinsum; + gsum += ginsum; + bsum += binsum; + + stackpointer = (stackpointer + 1) % div; + sir = stack[(stackpointer) % div]; + + routsum += sir[0]; + goutsum += sir[1]; + boutsum += sir[2]; + + rinsum -= sir[0]; + ginsum -= sir[1]; + binsum -= sir[2]; + + yi++; + } + yw += w; + } + for (x = 0; x < w; x++) { + rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0; + yp = -radius * w; + for (i = -radius; i <= radius; i++) { + yi = Math.max(0, yp) + x; + + sir = stack[i + radius]; + + sir[0] = r[yi]; + sir[1] = g[yi]; + sir[2] = b[yi]; + + rbs = r1 - Math.abs(i); + + rsum += r[yi] * rbs; + gsum += g[yi] * rbs; + bsum += b[yi] * rbs; + + if (i > 0) { + rinsum += sir[0]; + ginsum += sir[1]; + binsum += sir[2]; + } else { + routsum += sir[0]; + goutsum += sir[1]; + boutsum += sir[2]; + } + + if (i < hm) { + yp += w; + } + } + yi = x; + stackpointer = radius; + for (y = 0; y < h; y++) { + // Preserve alpha channel: ( 0xff000000 & pix[yi] ) + pix[yi] = (0xff000000 & pix[yi]) | (dv[rsum] << 16) | (dv[gsum] << 8) | dv[bsum]; + + rsum -= routsum; + gsum -= goutsum; + bsum -= boutsum; + + stackstart = stackpointer - radius + div; + sir = stack[stackstart % div]; + + routsum -= sir[0]; + goutsum -= sir[1]; + boutsum -= sir[2]; + + if (x == 0) { + vmin[y] = Math.min(y + r1, hm) * w; + } + p = x + vmin[y]; + + sir[0] = r[p]; + sir[1] = g[p]; + sir[2] = b[p]; + + rinsum += sir[0]; + ginsum += sir[1]; + binsum += sir[2]; + + rsum += rinsum; + gsum += ginsum; + bsum += binsum; + + stackpointer = (stackpointer + 1) % div; + sir = stack[stackpointer]; + + routsum += sir[0]; + goutsum += sir[1]; + boutsum += sir[2]; + + rinsum -= sir[0]; + ginsum -= sir[1]; + binsum -= sir[2]; + + yi += w; + } + } + ret.setPixels(pix, 0, w, 0, 0, w, h); + return ret; + } + + /** + * Save the bitmap. + * + * @param src The source of bitmap. + * @param filePath The path of file. + * @param format The format of the image. + * @return {@code true}: success
{@code false}: fail + */ + public static boolean save(final Bitmap src, + final String filePath, + final CompressFormat format) { + return save(src, getFileByPath(filePath), format, false); + } + + /** + * Save the bitmap. + * + * @param src The source of bitmap. + * @param file The file. + * @param format The format of the image. + * @return {@code true}: success
{@code false}: fail + */ + public static boolean save(final Bitmap src, final File file, final CompressFormat format) { + return save(src, file, format, false); + } + + /** + * Save the bitmap. + * + * @param src The source of bitmap. + * @param filePath The path of file. + * @param format The format of the image. + * @param recycle True to recycle the source of bitmap, false otherwise. + * @return {@code true}: success
{@code false}: fail + */ + public static boolean save(final Bitmap src, + final String filePath, + final CompressFormat format, + final boolean recycle) { + return save(src, getFileByPath(filePath), format, recycle); + } + + /** + * Save the bitmap. + * + * @param src The source of bitmap. + * @param file The file. + * @param format The format of the image. + * @param recycle True to recycle the source of bitmap, false otherwise. + * @return {@code true}: success
{@code false}: fail + */ + public static boolean save(final Bitmap src, + final File file, + final CompressFormat format, + final boolean recycle) { + if (isEmptyBitmap(src) || !createFileByDeleteOldFile(file)) return false; + OutputStream os = null; + boolean ret = false; + try { + os = new BufferedOutputStream(new FileOutputStream(file)); + ret = src.compress(format, 100, os); + if (recycle && !src.isRecycled()) src.recycle(); + } catch (IOException e) { + e.printStackTrace(); + } finally { + try { + if (os != null) { + os.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + return ret; + } + + /** + * Return whether it is a image according to the file name. + * + * @param file The file. + * @return {@code true}: yes
{@code false}: no + */ + public static boolean isImage(final File file) { + if (file == null || !file.exists()) { + return false; + } + return isImage(file.getPath()); + } + + /** + * Return whether it is a image according to the file name. + * + * @param filePath The path of file. + * @return {@code true}: yes
{@code false}: no + */ + public static boolean isImage(final String filePath) { + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; + try { + Bitmap bitmap = BitmapFactory.decodeFile(filePath, options); + return options.outWidth != -1 && options.outHeight != -1; + } catch (Exception e) { + return false; + } + } + + /** + * Return the type of image. + * + * @param filePath The path of file. + * @return the type of image + */ + public static ImageType getImageType(final String filePath) { + return getImageType(getFileByPath(filePath)); + } + + /** + * Return the type of image. + * + * @param file The file. + * @return the type of image + */ + public static ImageType getImageType(final File file) { + if (file == null) return null; + InputStream is = null; + try { + is = new FileInputStream(file); + ImageType type = getImageType(is); + if (type != null) { + return type; + } + } catch (IOException e) { + e.printStackTrace(); + } finally { + try { + if (is != null) { + is.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + return null; + } + + private static ImageType getImageType(final InputStream is) { + if (is == null) return null; + try { + byte[] bytes = new byte[12]; + return is.read(bytes) != -1 ? getImageType(bytes) : null; + } catch (IOException e) { + e.printStackTrace(); + return null; + } + } + + private static ImageType getImageType(final byte[] bytes) { + String type = bytes2HexString(bytes).toUpperCase(); + if (type.contains("FFD8FF")) { + return ImageType.TYPE_JPG; + } else if (type.contains("89504E47")) { + return ImageType.TYPE_PNG; + } else if (type.contains("47494638")) { + return ImageType.TYPE_GIF; + } else if (type.contains("49492A00") || type.contains("4D4D002A")) { + return ImageType.TYPE_TIFF; + } else if (type.contains("424D")) { + return ImageType.TYPE_BMP; + } else if (type.startsWith("52494646") && type.endsWith("57454250")) {//524946461c57000057454250-12个字节 + return ImageType.TYPE_WEBP; + } else if (type.contains("00000100") || type.contains("00000200")) { + return ImageType.TYPE_ICO; + } else { + return ImageType.TYPE_UNKNOWN; + } + } + + private static final char[] hexDigits = + {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + + private static String bytes2HexString(final byte[] bytes) { + if (bytes == null) return ""; + int len = bytes.length; + if (len <= 0) return ""; + char[] ret = new char[len << 1]; + for (int i = 0, j = 0; i < len; i++) { + ret[j++] = hexDigits[bytes[i] >> 4 & 0x0f]; + ret[j++] = hexDigits[bytes[i] & 0x0f]; + } + return new String(ret); + } + + + private static boolean isJPEG(final byte[] b) { + return b.length >= 2 + && (b[0] == (byte) 0xFF) && (b[1] == (byte) 0xD8); + } + + private static boolean isGIF(final byte[] b) { + return b.length >= 6 + && b[0] == 'G' && b[1] == 'I' + && b[2] == 'F' && b[3] == '8' + && (b[4] == '7' || b[4] == '9') && b[5] == 'a'; + } + + private static boolean isPNG(final byte[] b) { + return b.length >= 8 + && (b[0] == (byte) 137 && b[1] == (byte) 80 + && b[2] == (byte) 78 && b[3] == (byte) 71 + && b[4] == (byte) 13 && b[5] == (byte) 10 + && b[6] == (byte) 26 && b[7] == (byte) 10); + } + + private static boolean isBMP(final byte[] b) { + return b.length >= 2 + && (b[0] == 0x42) && (b[1] == 0x4d); + } + + private static boolean isEmptyBitmap(final Bitmap src) { + return src == null || src.getWidth() == 0 || src.getHeight() == 0; + } + + /////////////////////////////////////////////////////////////////////////// + // home_ic_about_us compress + /////////////////////////////////////////////////////////////////////////// + + /** + * Return the compressed bitmap using scale. + * + * @param src The source of bitmap. + * @param newWidth The new width. + * @param newHeight The new height. + * @return the compressed bitmap + */ + public static Bitmap compressByScale(final Bitmap src, + final int newWidth, + final int newHeight) { + return scale(src, newWidth, newHeight, false); + } + + /** + * Return the compressed bitmap using scale. + * + * @param src The source of bitmap. + * @param newWidth The new width. + * @param newHeight The new height. + * @param recycle True to recycle the source of bitmap, false otherwise. + * @return the compressed bitmap + */ + public static Bitmap compressByScale(final Bitmap src, + final int newWidth, + final int newHeight, + final boolean recycle) { + return scale(src, newWidth, newHeight, recycle); + } + + /** + * Return the compressed bitmap using scale. + * + * @param src The source of bitmap. + * @param scaleWidth The scale of width. + * @param scaleHeight The scale of height. + * @return the compressed bitmap + */ + public static Bitmap compressByScale(final Bitmap src, + final float scaleWidth, + final float scaleHeight) { + return scale(src, scaleWidth, scaleHeight, false); + } + + /** + * Return the compressed bitmap using scale. + * + * @param src The source of bitmap. + * @param scaleWidth The scale of width. + * @param scaleHeight The scale of height. + * @param recycle True to recycle the source of bitmap, false otherwise. + * @return he compressed bitmap + */ + public static Bitmap compressByScale(final Bitmap src, + final float scaleWidth, + final float scaleHeight, + final boolean recycle) { + return scale(src, scaleWidth, scaleHeight, recycle); + } + + /** + * Return the compressed bitmap using quality. + * + * @param src The source of bitmap. + * @param quality The quality. + * @return the compressed bitmap + */ + public static Bitmap compressByQuality(final Bitmap src, + @IntRange(from = 0, to = 100) final int quality) { + return compressByQuality(src, quality, false); + } + + /** + * Return the compressed bitmap using quality. + * + * @param src The source of bitmap. + * @param quality The quality. + * @param recycle True to recycle the source of bitmap, false otherwise. + * @return the compressed bitmap + */ + public static Bitmap compressByQuality(final Bitmap src, + @IntRange(from = 0, to = 100) final int quality, + final boolean recycle) { + if (isEmptyBitmap(src)) return null; + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + src.compress(CompressFormat.JPEG, quality, baos); + byte[] bytes = baos.toByteArray(); + if (recycle && !src.isRecycled()) src.recycle(); + return BitmapFactory.decodeByteArray(bytes, 0, bytes.length); + } + + /** + * Return the compressed bitmap using quality. + * + * @param src The source of bitmap. + * @param maxByteSize The maximum size of byte. + * @return the compressed bitmap + */ + public static Bitmap compressByQuality(final Bitmap src, final long maxByteSize) { + return compressByQuality(src, maxByteSize, false); + } + + /** + * Return the compressed bitmap using quality. + * + * @param src The source of bitmap. + * @param maxByteSize The maximum size of byte. + * @param recycle True to recycle the source of bitmap, false otherwise. + * @return the compressed bitmap + */ + public static Bitmap compressByQuality(final Bitmap src, + final long maxByteSize, + final boolean recycle) { + if (isEmptyBitmap(src) || maxByteSize <= 0) return null; + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + src.compress(CompressFormat.JPEG, 100, baos); + byte[] bytes; + if (baos.size() <= maxByteSize) { + bytes = baos.toByteArray(); + } else { + baos.reset(); + src.compress(CompressFormat.JPEG, 0, baos); + if (baos.size() >= maxByteSize) { + bytes = baos.toByteArray(); + } else { + // find the best quality using binary search + int st = 0; + int end = 100; + int mid = 0; + while (st < end) { + mid = (st + end) / 2; + baos.reset(); + src.compress(CompressFormat.JPEG, mid, baos); + int len = baos.size(); + if (len == maxByteSize) { + break; + } else if (len > maxByteSize) { + end = mid - 1; + } else { + st = mid + 1; + } + } + if (end == mid - 1) { + baos.reset(); + src.compress(CompressFormat.JPEG, st, baos); + } + bytes = baos.toByteArray(); + } + } + if (recycle && !src.isRecycled()) src.recycle(); + return BitmapFactory.decodeByteArray(bytes, 0, bytes.length); + } + + /** + * Return the compressed bitmap using sample size. + * + * @param src The source of bitmap. + * @param sampleSize The sample size. + * @return the compressed bitmap + */ + + public static Bitmap compressBySampleSize(final Bitmap src, final int sampleSize) { + return compressBySampleSize(src, sampleSize, false); + } + + /** + * Return the compressed bitmap using sample size. + * + * @param src The source of bitmap. + * @param sampleSize The sample size. + * @param recycle True to recycle the source of bitmap, false otherwise. + * @return the compressed bitmap + */ + public static Bitmap compressBySampleSize(final Bitmap src, + final int sampleSize, + final boolean recycle) { + if (isEmptyBitmap(src)) return null; + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inSampleSize = sampleSize; + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + src.compress(CompressFormat.JPEG, 100, baos); + byte[] bytes = baos.toByteArray(); + if (recycle && !src.isRecycled()) src.recycle(); + return BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options); + } + + /** + * Return the compressed bitmap using sample size. + * + * @param src The source of bitmap. + * @param maxWidth The maximum width. + * @param maxHeight The maximum height. + * @return the compressed bitmap + */ + public static Bitmap compressBySampleSize(final Bitmap src, + final int maxWidth, + final int maxHeight) { + return compressBySampleSize(src, maxWidth, maxHeight, false); + } + + /** + * Return the compressed bitmap using sample size. + * + * @param src The source of bitmap. + * @param maxWidth The maximum width. + * @param maxHeight The maximum height. + * @param recycle True to recycle the source of bitmap, false otherwise. + * @return the compressed bitmap + */ + public static Bitmap compressBySampleSize(final Bitmap src, + final int maxWidth, + final int maxHeight, + final boolean recycle) { + if (isEmptyBitmap(src)) return null; + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + src.compress(CompressFormat.JPEG, 100, baos); + byte[] bytes = baos.toByteArray(); + BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options); + options.inSampleSize = calculateInSampleSize(options, maxWidth, maxHeight); + options.inJustDecodeBounds = false; + if (recycle && !src.isRecycled()) src.recycle(); + return BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options); + } + + /** + * Return the size of bitmap. + * + * @param filePath The path of file. + * @return the size of bitmap + */ + public static int[] getSize(String filePath) { + return getSize(getFileByPath(filePath)); + } + + /** + * Return the size of bitmap. + * + * @param file The file. + * @return the size of bitmap + */ + public static int[] getSize(File file) { + if (file == null) return new int[]{0, 0}; + BitmapFactory.Options opts = new BitmapFactory.Options(); + opts.inJustDecodeBounds = true; + BitmapFactory.decodeFile(file.getAbsolutePath(), opts); + return new int[]{opts.outWidth, opts.outHeight}; + } + + /** + * Return the sample size. + * + * @param options The options. + * @param maxWidth The maximum width. + * @param maxHeight The maximum height. + * @return the sample size + */ + private static int calculateInSampleSize(final BitmapFactory.Options options, + final int maxWidth, + final int maxHeight) { + int height = options.outHeight; + int width = options.outWidth; + int inSampleSize = 1; + while (height > maxHeight || width > maxWidth) { + height >>= 1; + width >>= 1; + inSampleSize <<= 1; + } + return inSampleSize; + } + + /////////////////////////////////////////////////////////////////////////// + // other utils methods + /////////////////////////////////////////////////////////////////////////// + + private static File getFileByPath(final String filePath) { + return isSpace(filePath) ? null : new File(filePath); + } + + private static boolean createFileByDeleteOldFile(final File file) { + if (file == null) return false; + if (file.exists() && !file.delete()) return false; + if (!createOrExistsDir(file.getParentFile())) return false; + try { + return file.createNewFile(); + } catch (IOException e) { + e.printStackTrace(); + return false; + } + } + + private static boolean createOrExistsDir(final File file) { + return file != null && (file.exists() ? file.isDirectory() : file.mkdirs()); + } + + private static boolean isSpace(final String s) { + if (s == null) return true; + for (int i = 0, len = s.length(); i < len; ++i) { + if (!Character.isWhitespace(s.charAt(i))) { + return false; + } + } + return true; + } + + private static byte[] input2Byte(final InputStream is) { + if (is == null) return null; + try { + ByteArrayOutputStream os = new ByteArrayOutputStream(); + byte[] b = new byte[1024]; + int len; + while ((len = is.read(b, 0, 1024)) != -1) { + os.write(b, 0, len); + } + return os.toByteArray(); + } catch (IOException e) { + e.printStackTrace(); + return null; + } finally { + try { + is.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + public enum ImageType { + TYPE_JPG("jpg"), + + TYPE_PNG("png"), + + TYPE_GIF("gif"), + + TYPE_TIFF("tiff"), + + TYPE_BMP("bmp"), + + TYPE_WEBP("webp"), + + TYPE_ICO("ico"), + + TYPE_UNKNOWN("unknown"); + + String value; + + ImageType(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + } +} \ No newline at end of file diff --git a/library/src/module_utils/java/com/chuhai/utils/PathUtils.kt b/library/src/module_utils/java/com/chuhai/utils/PathUtils.kt new file mode 100644 index 000000000..888e9c97b --- /dev/null +++ b/library/src/module_utils/java/com/chuhai/utils/PathUtils.kt @@ -0,0 +1,495 @@ +package com.chuhai.utils + +import android.app.Application +import android.os.Build +import android.os.Environment +import java.io.File + +/** + * getRootPath : 获取根路径 + * getDataPath : 获取数据路径 + * getDownloadCachePath : 获取下载缓存路径 + * getInternalAppDataPath : 获取内存应用数据路径 + * getInternalAppCodeCacheDir : 获取内存应用代码缓存路径 + * getInternalAppCachePath : 获取内存应用缓存路径 + * getInternalAppDbsPath : 获取内存应用数据库路径 + * getInternalAppDbPath : 获取内存应用数据库路径 + * getInternalAppFilesPath : 获取内存应用文件路径 + * getInternalAppSpPath : 获取内存应用 SP 路径 + * getInternalAppNoBackupFilesPath: 获取内存应用未备份文件路径 + * getExternalStoragePath : 获取外存路径 + * getExternalMusicPath : 获取外存音乐路径 + * getExternalPodcastsPath : 获取外存播客路径 + * getExternalRingtonesPath : 获取外存铃声路径 + * getExternalAlarmsPath : 获取外存闹铃路径 + * getExternalNotificationsPath : 获取外存通知路径 + * getExternalPicturesPath : 获取外存图片路径 + * getExternalMoviesPath : 获取外存影片路径 + * getExternalDownloadsPath : 获取外存下载路径 + * getExternalDcimPath : 获取外存数码相机图片路径 + * getExternalDocumentsPath : 获取外存文档路径 + * getExternalAppDataPath : 获取外存应用数据路径 + * getExternalAppCachePath : 获取外存应用缓存路径 + * getExternalAppFilesPath : 获取外存应用文件路径 + * getExternalAppMusicPath : 获取外存应用音乐路径 + * getExternalAppPodcastsPath : 获取外存应用播客路径 + * getExternalAppRingtonesPath : 获取外存应用铃声路径 + * getExternalAppAlarmsPath : 获取外存应用闹铃路径 + * getExternalAppNotificationsPath: 获取外存应用通知路径 + * getExternalAppPicturesPath : 获取外存应用图片路径 + * getExternalAppMoviesPath : 获取外存应用影片路径 + * getExternalAppDownloadPath : 获取外存应用下载路径 + * getExternalAppDcimPath : 获取外存应用数码相机图片路径 + * getExternalAppDocumentsPath : 获取外存应用文档路径 + * getExternalAppObbPath : 获取外存应用 OBB 路径 + * 路径 工具类 By https://github.com/Blankj/AndroidUtilCode -> PathUtils.java + * Created by Max on 2018/12/12. + */ +class PathUtils private constructor() { + init { + throw UnsupportedOperationException("u can't instantiate me...") + } + + companion object { + /** + * Return the path of /system. + * + * @return the path of /system + */ + val rootPath: String + get() = Environment.getRootDirectory().absolutePath + + /** + * Return the path of /data. + * + * @return the path of /data + */ + val dataPath: String + get() = Environment.getDataDirectory().absolutePath + + /** + * Return the path of /cache. + * + * @return the path of /cache + */ + val downloadCachePath: String + get() = Environment.getDownloadCacheDirectory().absolutePath + + /** + * Return the path of /data/data/package. + * + * @return the path of /data/data/package + */ + fun getInternalAppDataPath(application: Application): String { + return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { + application.applicationInfo.dataDir + } else application.dataDir.absolutePath + } + + /** + * Return the path of /data/data/package/code_cache. + * + * @return the path of /data/data/package/code_cache + */ + fun getInternalAppCodeCacheDir(application: Application): String { + return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + application.applicationInfo.dataDir + "/code_cache" + } else application.codeCacheDir.absolutePath + } + + /** + * Return the path of /data/data/package/cache. + * + * @return the path of /data/data/package/cache + */ + fun getInternalAppCachePath(application: Application): String { + return application.cacheDir.absolutePath + } + + /** + * Return the path of /data/data/package/databases. + * + * @return the path of /data/data/package/databases + */ + fun getInternalAppDbsPath(application: Application): String { + return application.applicationInfo.dataDir + "/databases" + } + + /** + * Return the path of /data/data/package/databases/name. + * + * @param name The name of database. + * @return the path of /data/data/package/databases/name + */ + fun getInternalAppDbPath(application: Application, name: String?): String { + return application.getDatabasePath(name).absolutePath + } + + /** + * Return the path of /data/data/package/files. + * + * @return the path of /data/data/package/files + */ + fun getInternalAppFilesPath(application: Application): String { + return application.filesDir.absolutePath + } + + /** + * Return the path of /data/data/package/shared_prefs. + * + * @return the path of /data/data/package/shared_prefs + */ + fun getInternalAppSpPath(application: Application): String { + return application.applicationInfo.dataDir + "shared_prefs" + } + + /** + * Return the path of /data/data/package/no_backup. + * + * @return the path of /data/data/package/no_backup + */ + fun getInternalAppNoBackupFilesPath(application: Application): String { + return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + application.applicationInfo.dataDir + "no_backup" + } else application.noBackupFilesDir.absolutePath + } + + /** + * Return the path of /storage/emulated/0. + * + * @return the path of /storage/emulated/0 + */ + val externalStoragePath: String? + get() = if (isExternalStorageDisable) null else Environment.getExternalStorageDirectory().absolutePath + + /** + * Return the path of /storage/emulated/0/Music. + * + * @return the path of /storage/emulated/0/Music + */ + val externalMusicPath: String? + get() = if (isExternalStorageDisable) null else Environment.getExternalStoragePublicDirectory( + Environment.DIRECTORY_MUSIC + ).absolutePath + + /** + * Return the path of /storage/emulated/0/Podcasts. + * + * @return the path of /storage/emulated/0/Podcasts + */ + val externalPodcastsPath: String? + get() = if (isExternalStorageDisable) null else Environment.getExternalStoragePublicDirectory( + Environment.DIRECTORY_PODCASTS + ).absolutePath + + /** + * Return the path of /storage/emulated/0/Ringtones. + * + * @return the path of /storage/emulated/0/Ringtones + */ + val externalRingtonesPath: String? + get() = if (isExternalStorageDisable) null else Environment.getExternalStoragePublicDirectory( + Environment.DIRECTORY_RINGTONES + ).absolutePath + + /** + * Return the path of /storage/emulated/0/Alarms. + * + * @return the path of /storage/emulated/0/Alarms + */ + val externalAlarmsPath: String? + get() = if (isExternalStorageDisable) null else Environment.getExternalStoragePublicDirectory( + Environment.DIRECTORY_ALARMS + ).absolutePath + + /** + * Return the path of /storage/emulated/0/Notifications. + * + * @return the path of /storage/emulated/0/Notifications + */ + val externalNotificationsPath: String? + get() = if (isExternalStorageDisable) null else Environment.getExternalStoragePublicDirectory( + Environment.DIRECTORY_NOTIFICATIONS + ).absolutePath + + /** + * Return the path of /storage/emulated/0/Pictures. + * + * @return the path of /storage/emulated/0/Pictures + */ + val externalPicturesPath: String? + get() = if (isExternalStorageDisable) null else Environment.getExternalStoragePublicDirectory( + Environment.DIRECTORY_PICTURES + ).absolutePath + + /** + * Return the path of /storage/emulated/0/Movies. + * + * @return the path of /storage/emulated/0/Movies + */ + val externalMoviesPath: String? + get() = if (isExternalStorageDisable) null else Environment.getExternalStoragePublicDirectory( + Environment.DIRECTORY_MOVIES + ).absolutePath + + /** + * Return the path of /storage/emulated/0/Download. + * + * @return the path of /storage/emulated/0/Download + */ + val externalDownloadsPath: String? + get() = if (isExternalStorageDisable) null else Environment.getExternalStoragePublicDirectory( + Environment.DIRECTORY_DOWNLOADS + ).absolutePath + + /** + * Return the path of /storage/emulated/0/DCIM. + * + * @return the path of /storage/emulated/0/DCIM + */ + val externalDcimPath: String? + get() = if (isExternalStorageDisable) null else Environment.getExternalStoragePublicDirectory( + Environment.DIRECTORY_DCIM + ).absolutePath + + /** + * Return the path of /storage/emulated/0/Documents. + * + * @return the path of /storage/emulated/0/Documents + */ + val externalDocumentsPath: String? + get() { + if (isExternalStorageDisable) return null + return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { + Environment.getExternalStorageDirectory().absolutePath + "/Documents" + } else Environment.getExternalStoragePublicDirectory( + Environment.DIRECTORY_DOCUMENTS + ).absolutePath + } + + /** + * Return the path of /storage/emulated/0/Android/data/package. + * + * @return the path of /storage/emulated/0/Android/data/package + */ + fun getExternalAppDataPath(application: Application): String? { + return if (isExternalStorageDisable) null else application.externalCacheDir?.parentFile?.absolutePath + } + + /** + * Return the path of /storage/emulated/0/Android/data/package/cache. + * + * @return the path of /storage/emulated/0/Android/data/package/cache + */ + fun getExternalAppCachePath(application: Application): String? { + return if (isExternalStorageDisable) null else application.externalCacheDir?.absolutePath + } + + /** + * Return the path of /storage/emulated/0/Android/data/package/files. + * + * @return the path of /storage/emulated/0/Android/data/package/files + */ + fun getExternalAppFilesPath(application: Application): String? { + return if (isExternalStorageDisable) null else application.getExternalFilesDir(null)?.absolutePath + } + + /** + * Return the path of /storage/emulated/0/Android/data/package/files/Music. + * + * @return the path of /storage/emulated/0/Android/data/package/files/Music + */ + fun getExternalAppMusicPath(application: Application): String? { + return if (isExternalStorageDisable) null else application.getExternalFilesDir( + Environment.DIRECTORY_MUSIC + )?.absolutePath + } + + /** + * Return the path of /storage/emulated/0/Android/data/package/files/Podcasts. + * + * @return the path of /storage/emulated/0/Android/data/package/files/Podcasts + */ + fun getExternalAppPodcastsPath(application: Application): String? { + return if (isExternalStorageDisable) null else application.getExternalFilesDir( + Environment.DIRECTORY_PODCASTS + )?.absolutePath + } + + /** + * Return the path of /storage/emulated/0/Android/data/package/files/Ringtones. + * + * @return the path of /storage/emulated/0/Android/data/package/files/Ringtones + */ + fun getExternalAppRingtonesPath(application: Application): String? { + return if (isExternalStorageDisable) null else application.getExternalFilesDir( + Environment.DIRECTORY_RINGTONES + )?.absolutePath + } + + /** + * Return the path of /storage/emulated/0/Android/data/package/files/Alarms. + * + * @return the path of /storage/emulated/0/Android/data/package/files/Alarms + */ + fun getExternalAppAlarmsPath(application: Application): String? { + return if (isExternalStorageDisable) null else application.getExternalFilesDir( + Environment.DIRECTORY_ALARMS + )?.absolutePath + } + + /** + * Return the path of /storage/emulated/0/Android/data/package/files/Notifications. + * + * @return the path of /storage/emulated/0/Android/data/package/files/Notifications + */ + fun getExternalAppNotificationsPath(application: Application): String? { + return if (isExternalStorageDisable) null else application.getExternalFilesDir( + Environment.DIRECTORY_NOTIFICATIONS + )?.absolutePath + } + + /** + * Return the path of /storage/emulated/0/Android/data/package/files/Pictures. + * + * @return path of /storage/emulated/0/Android/data/package/files/Pictures + */ + fun getExternalAppPicturesPath(application: Application): String? { + return if (isExternalStorageDisable) null else application.getExternalFilesDir( + Environment.DIRECTORY_PICTURES + )?.absolutePath + } + + /** + * Return the path of /storage/emulated/0/Android/data/package/files/Movies. + * + * @return the path of /storage/emulated/0/Android/data/package/files/Movies + */ + fun getExternalAppMoviesPath(application: Application): String? { + return if (isExternalStorageDisable) null else application.getExternalFilesDir( + Environment.DIRECTORY_MOVIES + )?.absolutePath + } + + /** + * Return the path of /storage/emulated/0/Android/data/package/files/Download. + * + * @return the path of /storage/emulated/0/Android/data/package/files/Download + */ + fun getExternalAppDownloadPath(application: Application): String? { + return if (isExternalStorageDisable) null else application.getExternalFilesDir( + Environment.DIRECTORY_DOWNLOADS + )?.absolutePath + } + + /** + * Return the path of /storage/emulated/0/Android/data/package/files/DCIM. + * + * @return the path of /storage/emulated/0/Android/data/package/files/DCIM + */ + fun getExternalAppDcimPath(application: Application): String? { + return if (isExternalStorageDisable) null else application.getExternalFilesDir( + Environment.DIRECTORY_DCIM + )?.absolutePath + } + + /** + * Return the path of /storage/emulated/0/Android/data/package/files/Documents. + * + * @return the path of /storage/emulated/0/Android/data/package/files/Documents + */ + fun getExternalAppDocumentsPath(application: Application): String? { + if (isExternalStorageDisable) return null + return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { + application.getExternalFilesDir(null)?.absolutePath + "/Documents" + } else application.getExternalFilesDir( + Environment.DIRECTORY_DOCUMENTS + )?.absolutePath + } + + /** + * Return the path of /storage/emulated/0/Android/obb/package. + * + * @return the path of /storage/emulated/0/Android/obb/package + */ + fun getExternalAppObbPath(application: Application): String? { + return if (isExternalStorageDisable) null else application.obbDir.absolutePath + } + + private val isExternalStorageDisable: Boolean + private get() = Environment.MEDIA_MOUNTED != Environment.getExternalStorageState() + + /** + * 判断sub是否在parent之下的文件或子文件夹

+ * + * @param parent + * @param sub + * @return + */ + fun isSub(parent: File, sub: File): Boolean { + return try { + sub.absolutePath.startsWith(parent.absolutePath) + } catch (e: Exception) { + false + } + } + + /** + * 获取子绝对路径与父绝对路径的相对路径 + * + * @param parentPath + * @param subPath + * @return + */ + fun getRelativePath(parentPath: String?, subPath: String?): String? { + return try { + if (parentPath == null || subPath == null) { + return null + } + if (subPath.startsWith(parentPath)) { + subPath.substring(parentPath.length) + } else { + null + } + } catch (e: Exception) { + null + } + } + + /** + * 拼接两个路径 + * + * @param pathA 路径A + * @param pathB 路径B + * @return 拼接后的路径 + */ + fun plusPath(pathA: String?, pathB: String?): String? { + if (pathA == null) { + return pathB + } + if (pathB == null) { + return pathA + } + return plusPathNotNull(pathA, pathB) + } + + /** + * 拼接两个路径 + * + * @param pathA 路径A + * @param pathB 路径B + * @return 拼接后的路径 + */ + fun plusPathNotNull(pathA: String, pathB: String): String { + val pathAEndSeparator = pathA.endsWith(File.separator) + val pathBStartSeparator = pathB.startsWith(File.separator) + return if (pathAEndSeparator && pathBStartSeparator) { + pathA + pathB.substring(1) + } else if (pathAEndSeparator || pathBStartSeparator) { + pathA + pathB + } else { + pathA + File.separator + pathB + } + } + } +} \ No newline at end of file