[
  {
    "path": ".gitignore",
    "content": "# OSX\n\n*.DS_Store\n\n\n# Gradle files\nbuild/\n.gradle/\n*/build/\n\n\n# IDEA\n*.iml\n.idea/.name\n.idea/encodings.xml\n.idea/inspectionProfiles/Project_Default.xml\n.idea/inspectionProfiles/profiles_settings.xml\n.idea/misc.xml\n.idea/modules.xml\n.idea/scopes/scope_settings.xml\n.idea/vcs.xml\n.idea/workspace.xml\n.idea/libraries\n\n\n# Built application files\n*.apk\n*.ap_\n\n\n# Files for the Dalvik VM\n*.dex\n\n\n# Java class files\n*.class\n\n# Local configuration file (sdk path, etc)\nlocal.properties\n\n\n# Log Files\n*.log"
  },
  {
    "path": ".idea/compiler.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"CompilerConfiguration\">\n    <resourceExtensions />\n    <wildcardResourcePatterns>\n      <entry name=\"!?*.java\" />\n      <entry name=\"!?*.form\" />\n      <entry name=\"!?*.class\" />\n      <entry name=\"!?*.groovy\" />\n      <entry name=\"!?*.scala\" />\n      <entry name=\"!?*.flex\" />\n      <entry name=\"!?*.kt\" />\n      <entry name=\"!?*.clj\" />\n      <entry name=\"!?*.aj\" />\n    </wildcardResourcePatterns>\n    <annotationProcessing>\n      <profile default=\"true\" name=\"Default\" enabled=\"false\">\n        <processorPath useClasspath=\"true\" />\n      </profile>\n    </annotationProcessing>\n  </component>\n</project>"
  },
  {
    "path": ".idea/copyright/profiles_settings.xml",
    "content": "<component name=\"CopyrightManager\">\n  <settings default=\"\" />\n</component>"
  },
  {
    "path": ".idea/gradle.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"GradleSettings\">\n    <option name=\"linkedExternalProjectsSettings\">\n      <GradleProjectSettings>\n        <option name=\"distributionType\" value=\"DEFAULT_WRAPPED\" />\n        <option name=\"externalProjectPath\" value=\"$PROJECT_DIR$\" />\n        <option name=\"modules\">\n          <set>\n            <option value=\"$PROJECT_DIR$\" />\n            <option value=\"$PROJECT_DIR$/app\" />\n          </set>\n        </option>\n        <option name=\"myModules\">\n          <set>\n            <option value=\"$PROJECT_DIR$\" />\n            <option value=\"$PROJECT_DIR$/app\" />\n          </set>\n        </option>\n      </GradleProjectSettings>\n    </option>\n  </component>\n</project>"
  },
  {
    "path": ".idea/runConfigurations.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"RunConfigurationProducerService\">\n    <option name=\"ignoredProducers\">\n      <set>\n        <option value=\"org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer\" />\n        <option value=\"org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer\" />\n        <option value=\"org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer\" />\n      </set>\n    </option>\n  </component>\n</project>"
  },
  {
    "path": "README.md",
    "content": "# LGImageCompressor\nandroid图片压缩的处理\n"
  },
  {
    "path": "app/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "app/build.gradle",
    "content": "apply plugin: 'com.android.application'\n\nandroid {\n    compileSdkVersion 23\n    buildToolsVersion \"23.0.2\"\n\n    defaultConfig {\n        applicationId \"gui.com.lgimagecompressor\"\n        minSdkVersion 14\n        targetSdkVersion 23\n        versionCode 1\n        versionName \"1.0\"\n    }\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n}\n\ndependencies {\n    compile fileTree(dir: 'libs', include: ['*.jar'])\n    testCompile 'junit:junit:4.12'\n    compile 'com.android.support:appcompat-v7:23.4.0'\n}\n"
  },
  {
    "path": "app/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in /Users/guizhigang/Library/Android/sdk/tools/proguard/proguard-android.txt\n# You can edit the include path and order by changing the proguardFiles\n# directive in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# Add any project specific keep options here:\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n"
  },
  {
    "path": "app/src/androidTest/java/gui/com/lgimagecompressor/ApplicationTest.java",
    "content": "package gui.com.lgimagecompressor;\n\nimport android.app.Application;\nimport android.test.ApplicationTestCase;\n\n/**\n * <a href=\"http://d.android.com/tools/testing/testing_android.html\">Testing Fundamentals</a>\n */\npublic class ApplicationTest extends ApplicationTestCase<Application> {\n    public ApplicationTest() {\n        super(Application.class);\n    }\n}"
  },
  {
    "path": "app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"gui.com.lgimagecompressor\">\n\n    <uses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\" />\n    <uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\" />\n    <uses-permission android:name=\"android.permission.ACTION_IMAGE_CAPTURE\" />\n\n    <application\n        android:allowBackup=\"true\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:supportsRtl=\"true\"\n        android:theme=\"@style/AppTheme\">\n        <activity android:name=\".MainActivity\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n\n        <service\n            android:name=\".LGImgCompressorIntentService\"\n            android:exported=\"false\" />\n\n        <activity android:name=\".ServiceCompressActivity\" />\n\n        <service\n            android:name=\".LGImgCompressorService\"\n            android:process=\":remote\"/>\n\n        <activity android:name=\".BasicCompressActivity\"></activity>\n    </application>\n\n</manifest>\n<!--//android:process=\":remote\"-->"
  },
  {
    "path": "app/src/main/java/gui/com/lgimagecompressor/BasicCompressActivity.java",
    "content": "package gui.com.lgimagecompressor;\n\nimport android.content.Intent;\nimport android.content.pm.PackageManager;\nimport android.graphics.Bitmap;\nimport android.net.Uri;\nimport android.os.Environment;\nimport android.provider.MediaStore;\nimport android.support.annotation.NonNull;\nimport android.support.v4.app.ActivityCompat;\nimport android.support.v4.content.ContextCompat;\nimport android.support.v7.app.AppCompatActivity;\nimport android.os.Bundle;\nimport android.util.Log;\nimport android.view.View;\nimport android.widget.ImageView;\nimport android.widget.TextView;\nimport android.widget.Toast;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\n\npublic class BasicCompressActivity extends AppCompatActivity implements LGImgCompressor.CompressListener{\n    private final String TAG = MainActivity.class.getSimpleName();\n    private ImageView imageView;\n    private TextView imageInfo;\n    private final static int CAMERA_REQESTCODE = 100;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_basic_compress);\n\n        imageInfo = (TextView) findViewById(R.id.image_info);\n        imageView = (ImageView) findViewById(R.id.image_view);\n        imageView.setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                requestPermission();\n            }\n        });\n    }\n\n    //处理6.0动态权限问题\n    private void requestPermission() {\n        if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE)\n                != PackageManager.PERMISSION_GRANTED) {\n            ActivityCompat.requestPermissions(\n                    this,\n                    new String[]{android.Manifest.permission.WRITE_EXTERNAL_STORAGE},\n                    CAMERA_REQESTCODE);\n        } else {\n            takePictureFormCamera();\n        }\n    }\n\n    @Override\n    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {\n        if (requestCode == CAMERA_REQESTCODE) {\n            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {\n                takePictureFormCamera();\n            } else {\n                Toast.makeText(this, \"需要允许写入权限来存储图片\", Toast.LENGTH_LONG).show();\n            }\n        }\n    }\n\n    private File imageFile;\n\n    private void takePictureFormCamera() {\n        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);\n\n        String timeStamp = new SimpleDateFormat(\"yyyyMMddHHmmss\").format(new Date());\n        String fileName = timeStamp + \"_\";\n        File fileDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);\n        imageFile = null;\n        try {\n            imageFile = File.createTempFile(fileName, \".jpg\", fileDir);\n        } catch (IOException e) {\n            e.printStackTrace();\n        }\n\n        intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(imageFile));\n        startActivityForResult(intent, CAMERA_REQESTCODE);\n    }\n\n    @Override\n    protected void onActivityResult(int requestCode, int resultCode, Intent data) {\n        super.onActivityResult(requestCode, resultCode, data);\n        if (resultCode == RESULT_OK) {\n            if (requestCode == CAMERA_REQESTCODE) {\n                LGImgCompressor.getInstance(this).withListener(this).\n                        starCompress(Uri.fromFile(imageFile).toString(), 600, 800, 100);\n//                LGImgCompressor.getInstance(this).withListener(this).\n//                        starCompressWithDefault(Uri.fromFile(imageFile).toString());\n            }\n        }\n    }\n\n    @Override\n    public void onCompressStart() {\n        Log.d(TAG, \"onCompressStart\");\n    }\n\n    @Override\n    public void onCompressEnd(LGImgCompressor.CompressResult compressResult) {\n        Log.d(TAG, \"onCompressEnd outPath:\" + compressResult.getOutPath());\n        if (compressResult.getStatus() == LGImgCompressor.CompressResult.RESULT_ERROR)//压缩失败\n            return;\n\n        File file = new File(compressResult.getOutPath());\n        Bitmap bitmap = null;\n        try {\n            bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), Uri.fromFile(file));\n            imageView.setImageBitmap(bitmap);\n            float imageFileSize = file.length() / 1024f;\n            imageInfo.setText(\"image info width:\" + bitmap.getWidth() + \" \\nheight:\" + bitmap.getHeight() +\n                    \" \\nsize:\" + imageFileSize + \"kb\" + \"\\nimagePath:\" + file.getAbsolutePath());\n        } catch (IOException e) {\n            e.printStackTrace();\n        }\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/java/gui/com/lgimagecompressor/CompressServiceListener.java",
    "content": "package gui.com.lgimagecompressor;\n\nimport java.util.ArrayList;\n\n/**\n * Created by guizhigang on 16/5/28.\n */\npublic interface CompressServiceListener {\n    void onCompressServiceStart();\n    void onCompressServiceEnd(ArrayList<LGImgCompressor.CompressResult> compressResults);\n}\n"
  },
  {
    "path": "app/src/main/java/gui/com/lgimagecompressor/CompressServiceParam.java",
    "content": "package gui.com.lgimagecompressor;\n\nimport android.os.Parcel;\nimport android.os.Parcelable;\n\npublic class CompressServiceParam implements Parcelable {\n\n    private int outWidth;\n    private int outHeight;\n    private int maxFileSize;\n    private String srcImageUri;\n\n    public CompressServiceParam() {\n    }\n\n    protected CompressServiceParam(Parcel in) {\n        outWidth = in.readInt();\n        outHeight = in.readInt();\n        maxFileSize = in.readInt();\n        srcImageUri = in.readString();\n    }\n\n    public static final Creator<CompressServiceParam> CREATOR = new Creator<CompressServiceParam>() {\n        @Override\n        public CompressServiceParam createFromParcel(Parcel in) {\n            return new CompressServiceParam(in);\n        }\n\n        @Override\n        public CompressServiceParam[] newArray(int size) {\n            return new CompressServiceParam[size];\n        }\n    };\n\n    public int getOutWidth() {\n        return outWidth;\n    }\n\n    public void setOutWidth(int outWidth) {\n        this.outWidth = outWidth;\n    }\n\n    public int getOutHeight() {\n        return outHeight;\n    }\n\n    public void setOutHeight(int outHeight) {\n        this.outHeight = outHeight;\n    }\n\n    public int getMaxFileSize() {\n        return maxFileSize;\n    }\n\n    public void setMaxFileSize(int maxFileSize) {\n        this.maxFileSize = maxFileSize;\n    }\n\n    public String getSrcImageUri() {\n        return srcImageUri;\n    }\n\n    public void setSrcImageUri(String srcImageUri) {\n        this.srcImageUri = srcImageUri;\n    }\n\n    @Override\n    public int describeContents() {\n        return 0;\n    }\n\n    @Override\n    public void writeToParcel(Parcel dest, int flags) {\n        dest.writeInt(outWidth);\n        dest.writeInt(outHeight);\n        dest.writeInt(maxFileSize);\n        dest.writeString(srcImageUri);\n    }\n}"
  },
  {
    "path": "app/src/main/java/gui/com/lgimagecompressor/Constanse.java",
    "content": "package gui.com.lgimagecompressor;\n\n/**\n * Created by guizhigang on 16/5/28.\n */\npublic abstract class Constanse {\n    public static final String COMPRESS_PARAM = \"gui.com.lgimagecompressor.extra.PARAM\";\n    public static final String ACTION_COMPRESS_BROADCAST = \"gui.com.lgimagecompressor.message.broadcast\";\n    public static final String KEY_COMPRESS_PROCCESSING = \"gui.com.lgimagecompressor.message.proccessing\";\n    public static final String KEY_COMPRESS_FLAG = \"gui.com.lgimagecompressor.message.flag\";\n    public static final String KEY_COMPRESS_RESULT = \"gui.com.lgimagecompressor.message.result\";\n\n    public static final int FLAG_BEGAIIN = 0;\n    public static final int FLAG_END = 1;\n}\n"
  },
  {
    "path": "app/src/main/java/gui/com/lgimagecompressor/LGImgCompressor.java",
    "content": "package gui.com.lgimagecompressor;\n\nimport android.content.Context;\nimport android.database.Cursor;\nimport android.graphics.Bitmap;\nimport android.graphics.BitmapFactory;\nimport android.graphics.Matrix;\nimport android.media.ExifInterface;\nimport android.net.Uri;\nimport android.os.AsyncTask;\nimport android.os.Environment;\nimport android.os.Parcel;\nimport android.os.Parcelable;\nimport android.provider.MediaStore;\n\nimport java.io.BufferedOutputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.File;\nimport java.io.FileNotFoundException;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\n\n/**\n * Created by guizhigang on 16/5/25.\n */\npublic class LGImgCompressor {\n    private static LGImgCompressor instance = null;\n    private Context context;\n    private CompressListener compressListener;\n    private static final int DEFAULT_OUTWIDTH = 720;\n    private static final int DEFAULT_OUTHEIGHT = 1080;\n    private static final int DEFAULT_MAXFILESIZE = 1024;//KB\n\n    private LGImgCompressor(Context context) {\n        this.context = context;\n    }\n\n    public static LGImgCompressor getInstance(Context context) {\n        if (instance == null) {\n            synchronized (LGImgCompressor.class) {\n                if (instance == null)\n                    instance = new LGImgCompressor(context.getApplicationContext());\n            }\n        }\n        return instance;\n    }\n\n    public LGImgCompressor withListener(CompressListener compressListener) {\n        this.compressListener = compressListener;\n        return this;\n    }\n\n    /**\n     * 通过uri地址获取文件路径\n     * @param uri\n     * @return\n     */\n    private String getFilePathFromUri(String uri) {\n        Uri pathUri = Uri.parse(uri);\n        Cursor cursor = context.getContentResolver().query(pathUri, null, null, null, null);\n        if (cursor == null) {\n            return pathUri.getPath();\n        } else {\n            cursor.moveToFirst();\n            int index = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);\n            String str = cursor.getString(index);\n            cursor.close();\n            return str;\n        }\n    }\n\n    /**\n     * Can't compress a recycled bitmap\n     * @param srcImageUri     原始图片的uri路径\n     * @param outWidth        期望的输出图片的宽度\n     * @param outHeight       期望的输出图片的高度\n     * @param maxFileSize       期望的输出图片的最大占用的存储空间\n     * @return\n     */\n    public String compressImage(String srcImageUri, int outWidth, int outHeight, int maxFileSize) {\n        String srcImagePath = getFilePathFromUri(srcImageUri);\n\n        //进行大小缩放来达到压缩的目的\n        BitmapFactory.Options options = new BitmapFactory.Options();\n        options.inJustDecodeBounds = true;\n        BitmapFactory.decodeFile(srcImagePath, options);\n        //根据原始图片的宽高比和期望的输出图片的宽高比计算最终输出的图片的宽和高\n        float srcWidth = options.outWidth;\n        float srcHeight = options.outHeight;\n        float maxWidth = outWidth;\n        float maxHeight = outHeight;\n        float srcRatio = srcWidth / srcHeight;\n        float outRatio = maxWidth / maxHeight;\n        float actualOutWidth = srcWidth;\n        float actualOutHeight = srcHeight;\n\n        if (srcWidth > maxWidth || srcHeight > maxHeight) {\n            //如果输入比率小于输出比率,则最终输出的宽度以maxHeight为准()\n            //比如输入比为10:20 输出比是300:10 如果要保证输出图片的宽高比和原始图片的宽高比相同,则最终输出图片的高为10\n            //宽度为10/20 * 10 = 5  最终输出图片的比率为5:10 和原始输入的比率相同\n\n            //同理如果输入比率大于输出比率,则最终输出的高度以maxHeight为准()\n            //比如输入比为20:10 输出比是5:100 如果要保证输出图片的宽高比和原始图片的宽高比相同,则最终输出图片的宽为5\n            //高度需要根据输入图片的比率计算获得 为5 / 20/10= 2.5  最终输出图片的比率为5:2.5 和原始输入的比率相同\n            if (srcRatio < outRatio) {\n                actualOutHeight = maxHeight;\n                actualOutWidth = actualOutHeight * srcRatio;\n            } else if (srcRatio > outRatio) {\n                actualOutWidth = maxWidth;\n                actualOutHeight = actualOutWidth / srcRatio;\n            } else {\n                actualOutWidth = maxWidth;\n                actualOutHeight = maxHeight;\n            }\n        }\n        options.inSampleSize = computSampleSize(options, actualOutWidth, actualOutHeight);\n        options.inJustDecodeBounds = false;\n        Bitmap scaledBitmap = null;\n        try {\n            scaledBitmap = BitmapFactory.decodeFile(srcImagePath, options);\n        } catch (OutOfMemoryError e) {\n            e.printStackTrace();\n        }\n        if (scaledBitmap == null) {\n            return null;//压缩失败\n        }\n        //生成最终输出的bitmap\n        Bitmap actualOutBitmap = Bitmap.createScaledBitmap(scaledBitmap, (int) actualOutWidth, (int) actualOutHeight, true);\n        if(actualOutBitmap != scaledBitmap)\n            scaledBitmap.recycle();\n\n        //处理图片旋转问题\n        ExifInterface exif = null;\n        try {\n            exif = new ExifInterface(srcImagePath);\n            int orientation = exif.getAttributeInt(\n                    ExifInterface.TAG_ORIENTATION, 0);\n            Matrix matrix = new Matrix();\n            if (orientation == ExifInterface.ORIENTATION_ROTATE_90) {\n                matrix.postRotate(90);\n            } else if (orientation == ExifInterface.ORIENTATION_ROTATE_180) {\n                matrix.postRotate(180);\n            } else if (orientation == ExifInterface.ORIENTATION_ROTATE_270) {\n                matrix.postRotate(270);\n            }\n            actualOutBitmap = Bitmap.createBitmap(actualOutBitmap, 0, 0,\n                    actualOutBitmap.getWidth(), actualOutBitmap.getHeight(), matrix, true);\n        } catch (IOException e) {\n            e.printStackTrace();\n            return null;\n        }\n\n        //进行有损压缩\n        ByteArrayOutputStream baos = new ByteArrayOutputStream();\n        int options_ = 100;\n        actualOutBitmap.compress(Bitmap.CompressFormat.JPEG, options_, baos);//质量压缩方法，把压缩后的数据存放到baos中 (100表示不压缩，0表示压缩到最小)\n\n        int baosLength = baos.toByteArray().length;\n\n        while (baosLength / 1024 > maxFileSize) {//循环判断如果压缩后图片是否大于maxMemmorrySize,大于继续压缩\n            baos.reset();//重置baos即让下一次的写入覆盖之前的内容\n            options_ = Math.max(0, options_ - 10);//图片质量每次减少10\n            actualOutBitmap.compress(Bitmap.CompressFormat.JPEG, options_, baos);//将压缩后的图片保存到baos中\n            baosLength = baos.toByteArray().length;\n            if (options_ == 0)//如果图片的质量已降到最低则，不再进行压缩\n                break;\n        }\n        actualOutBitmap.recycle();\n\n        //将bitmap保存到指定路径\n        FileOutputStream fos = null;\n        String filePath = getOutputFileName(srcImagePath);\n        try {\n            fos = new FileOutputStream(filePath);\n            //包装缓冲流,提高写入速度\n            BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fos);\n            bufferedOutputStream.write(baos.toByteArray());\n            bufferedOutputStream.flush();\n        } catch (FileNotFoundException e) {\n            return null;\n        } catch (IOException e) {\n            return null;\n        } finally {\n            if (baos != null) {\n                try {\n                    baos.close();\n                } catch (IOException e) {\n                    e.printStackTrace();\n                }\n            }\n            if (fos != null) {\n                try {\n                    fos.close();\n                } catch (IOException e) {\n                    e.printStackTrace();\n                }\n            }\n        }\n\n        return filePath;\n    }\n\n    private int computSampleSize(BitmapFactory.Options options, float reqWidth, float reqHeight) {\n        float srcWidth = options.outWidth;//20\n        float srcHeight = options.outHeight;//10\n        int sampleSize = 1;\n        if (srcWidth > reqWidth || srcHeight > reqHeight) {\n            int withRatio = Math.round(srcWidth / reqWidth);\n            int heightRatio = Math.round(srcHeight / reqHeight);\n            sampleSize = Math.min(withRatio, heightRatio);\n        }\n        return sampleSize;\n    }\n\n    private String getOutputFileName(String srcFilePath) {\n        File srcFile = new File(srcFilePath);\n        File file = new File(Environment.getExternalStorageDirectory().getPath(), \"LGImgCompressor/Images\");\n        if (!file.exists()) {\n            file.mkdirs();\n        }\n        String uriSting = (file.getAbsolutePath() + File.separator + srcFile.getName());\n        return uriSting;\n    }\n\n    public void starCompress(String srcImageUri, int outWidth, int outHeight, int maxFileSize) {\n        new CompressTask().execute(srcImageUri, \"\" + outWidth, \"\" + outHeight, \"\" + maxFileSize);\n    }\n\n    public void starCompressWithDefault(String srcImageUri) {\n        new CompressTask().execute(srcImageUri, \"\" + DEFAULT_OUTWIDTH, \"\" + DEFAULT_OUTHEIGHT, \"\" + DEFAULT_MAXFILESIZE);\n    }\n\n    public static class CompressResult implements Parcelable{\n        public static final int RESULT_OK = 0;\n        public static final int RESULT_ERROR = 1;\n        private int status = RESULT_OK;\n        private String srcPath;\n        private String outPath;\n\n        public CompressResult(){\n\n        }\n\n        protected CompressResult(Parcel in) {\n            status = in.readInt();\n            srcPath = in.readString();\n            outPath = in.readString();\n        }\n\n        public static final Creator<CompressResult> CREATOR = new Creator<CompressResult>() {\n            @Override\n            public CompressResult createFromParcel(Parcel in) {\n                return new CompressResult(in);\n            }\n\n            @Override\n            public CompressResult[] newArray(int size) {\n                return new CompressResult[size];\n            }\n        };\n\n        @Override\n        public int describeContents() {\n            return 0;\n        }\n\n        @Override\n        public void writeToParcel(Parcel dest, int flags) {\n            dest.writeInt(status);\n            dest.writeString(srcPath);\n            dest.writeString(outPath);\n        }\n\n        public int getStatus() {\n            return status;\n        }\n\n        public void setStatus(int status) {\n            this.status = status;\n        }\n\n        public String getSrcPath() {\n            return srcPath;\n        }\n\n        public void setSrcPath(String srcPath) {\n            this.srcPath = srcPath;\n        }\n\n        public String getOutPath() {\n            return outPath;\n        }\n\n        public void setOutPath(String outPath) {\n            this.outPath = outPath;\n        }\n    }\n    /**\n     * 压缩结果回到监听类\n     */\n    public interface CompressListener {\n        void onCompressStart();\n\n        void onCompressEnd(CompressResult imageOutPath);\n    }\n\n    private class CompressTask extends AsyncTask<String, Void, CompressResult> {\n\n        @Override\n        protected CompressResult doInBackground(String... params) {\n            String path = params[0];\n            int outWidth = Integer.parseInt(params[1]);\n            int outHeight = Integer.parseInt(params[2]);\n            int maxFileSize = Integer.parseInt(params[3]);\n            CompressResult compressResult = new CompressResult();\n            String outPutPath = null;\n            try {\n                outPutPath = compressImage(path, outWidth, outHeight, maxFileSize);\n            }catch (Exception e){\n            }\n            compressResult.setSrcPath(path);\n            compressResult.setOutPath(outPutPath);\n            if(outPutPath == null){\n                compressResult.setStatus(CompressResult.RESULT_ERROR);\n            }\n            return compressResult;\n        }\n\n        @Override\n        protected void onPreExecute() {\n            if (compressListener != null) {\n                compressListener.onCompressStart();\n            }\n        }\n\n        @Override\n        protected void onPostExecute(CompressResult compressResult) {\n            if (compressListener != null) {\n                compressListener.onCompressEnd(compressResult);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/gui/com/lgimagecompressor/LGImgCompressorIntentService.java",
    "content": "package gui.com.lgimagecompressor;\n\nimport android.app.IntentService;\nimport android.content.Intent;\nimport android.content.Context;\nimport android.util.Log;\n\nimport java.util.ArrayList;\n\npublic class LGImgCompressorIntentService extends IntentService {\n    private final String TAG = LGImgCompressorIntentService.class.getSimpleName();\n\n    private static final String ACTION_COMPRESS = \"gui.com.lgimagecompressor.action.COMPRESS\";\n\n    private ArrayList<LGImgCompressor.CompressResult> compressResults = new ArrayList<>();//存储压缩任务的返回结果\n\n    public LGImgCompressorIntentService() {\n        super(\"LGImgCompressorIntentService\");\n        setIntentRedelivery(false);//避免出异常后service重新启动\n    }\n\n    @Override\n    public void onCreate() {\n        super.onCreate();\n        Intent intent = new Intent(Constanse.ACTION_COMPRESS_BROADCAST);\n        intent.putExtra(Constanse.KEY_COMPRESS_FLAG,Constanse.FLAG_BEGAIIN);\n        sendBroadcast(intent);\n        Log.d(TAG,\"onCreate...\");\n    }\n\n    @Override\n    public void onDestroy() {\n        super.onDestroy();\n        Intent intent = new Intent(Constanse.ACTION_COMPRESS_BROADCAST);\n        intent.putExtra(Constanse.KEY_COMPRESS_FLAG,Constanse.FLAG_END);\n        intent.putParcelableArrayListExtra(Constanse.KEY_COMPRESS_RESULT,compressResults);\n        sendBroadcast(intent);\n        compressResults.clear();\n        Log.d(TAG,\"onDestroy...\");\n    }\n\n    public static void startActionCompress(Context context, CompressServiceParam param) {\n        Intent intent = new Intent(context, LGImgCompressorIntentService.class);\n        intent.setAction(ACTION_COMPRESS);\n        intent.putExtra(Constanse.COMPRESS_PARAM, param);\n        context.startService(intent);\n    }\n\n    @Override\n    protected void onHandleIntent(Intent intent) {\n        if (intent != null) {\n            final String action = intent.getAction();\n            if (ACTION_COMPRESS.equals(action)) {\n                final CompressServiceParam param1 = intent.getParcelableExtra(Constanse.COMPRESS_PARAM);\n                handleActionCompress(param1);\n            }\n        }\n    }\n\n    private void handleActionCompress(CompressServiceParam param) {\n        int outwidth = param.getOutWidth();\n        int outHieight = param.getOutHeight();\n        int maxFileSize = param.getMaxFileSize();\n        String srcImageUri = param.getSrcImageUri();\n        LGImgCompressor.CompressResult compressResult = new LGImgCompressor.CompressResult();\n        String outPutPath = null;\n        try {\n            outPutPath = LGImgCompressor.getInstance(this).compressImage(srcImageUri, outwidth, outHieight, maxFileSize);\n        } catch (Exception e) {\n        }\n        compressResult.setSrcPath(srcImageUri);\n        compressResult.setOutPath(outPutPath);\n        if (outPutPath == null) {\n            compressResult.setStatus(LGImgCompressor.CompressResult.RESULT_ERROR);\n        }\n        compressResults.add(compressResult);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/gui/com/lgimagecompressor/LGImgCompressorService.java",
    "content": "package gui.com.lgimagecompressor;\n\nimport android.app.Service;\nimport android.content.Intent;\nimport android.os.IBinder;\nimport android.util.Log;\n\nimport java.util.ArrayList;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\n\npublic class LGImgCompressorService extends Service {\n    private static final String TAG = \"GImgCompressorService\";\n\n    private ArrayList<LGImgCompressor.CompressResult> compressResults = new ArrayList<>();\n    public LGImgCompressorService() {\n    }\n\n    @Override\n    public void onCreate() {\n        super.onCreate();\n        Log.d(TAG,\"onCreate...\");\n//        executorService = Executors.newCachedThreadPool();\n        executorService = Executors.newFixedThreadPool(10);\n        Intent intent = new Intent(Constanse.ACTION_COMPRESS_BROADCAST);\n        intent.putExtra(Constanse.KEY_COMPRESS_FLAG,Constanse.FLAG_BEGAIIN);\n        sendBroadcast(intent);\n    }\n\n    @Override\n    public void onDestroy() {\n        super.onDestroy();\n        Log.d(TAG,\"onDestroy...\");\n        Intent intent = new Intent(Constanse.ACTION_COMPRESS_BROADCAST);\n        intent.putExtra(Constanse.KEY_COMPRESS_FLAG,Constanse.FLAG_END);\n        intent.putParcelableArrayListExtra(Constanse.KEY_COMPRESS_RESULT,compressResults);\n        sendBroadcast(intent);\n        compressResults.clear();\n        executorService.shutdownNow();\n    }\n\n    @Override\n    public int onStartCommand(Intent intent, int flags, int startId) {\n        doCompressImages(intent,startId);\n        return Service.START_NOT_STICKY;\n    }\n\n    private int taskNumber;\n    private ExecutorService executorService;\n    private final Object lock = new Object();\n\n    private void doCompressImages(final Intent intent,final int taskId){\n        final ArrayList<CompressServiceParam> paramArrayList = intent.getParcelableArrayListExtra(Constanse.COMPRESS_PARAM);\n        synchronized (lock){\n            taskNumber += paramArrayList.size();\n        }\n        //如果paramArrayList过大,为了避免\"The application may be doing too much work on its main thread\"的问题,将任务的创建和执行统一放在后台线程中执行\n        new Thread(new Runnable() {\n            @Override\n            public void run() {\n                for (int i = 0; i < paramArrayList.size(); ++i){\n                    executorService.execute(new CompressTask(paramArrayList.get(i),taskId));\n                }\n            }\n        }).start();\n    }\n\n    private class CompressTask implements Runnable{\n        private CompressServiceParam param;\n        private int taskId ;\n\n        private CompressTask(CompressServiceParam compressServiceParam,int taskId){\n            this.param = compressServiceParam;\n            this.taskId = taskId;\n        }\n\n        @Override\n        public void run() {\n            Log.d(TAG,taskId + \" do compress begain...\" + Thread.currentThread().getId());\n            int outwidth = param.getOutWidth();\n            int outHieight = param.getOutHeight();\n            int maxFileSize = param.getMaxFileSize();\n            String srcImageUri = param.getSrcImageUri();\n            LGImgCompressor.CompressResult compressResult = new LGImgCompressor.CompressResult();\n            String outPutPath = null;\n            try {\n                outPutPath = LGImgCompressor.getInstance(LGImgCompressorService.this).compressImage(\n                        srcImageUri, outwidth, outHieight, maxFileSize);\n            } catch (Exception e) {\n            }\n            compressResult.setSrcPath(srcImageUri);\n            compressResult.setOutPath(outPutPath);\n            if (outPutPath == null) {\n                compressResult.setStatus(LGImgCompressor.CompressResult.RESULT_ERROR);\n            }\n            Log.d(TAG,taskId + \" do compress end...\" + Thread.currentThread().getId());\n            synchronized (lock){\n                compressResults.add(compressResult);\n                taskNumber--;\n                if(taskNumber <= 0){\n                    stopSelf(taskId);\n                }\n            }\n        }\n    }\n\n    @Override\n    public IBinder onBind(Intent intent) {\n        throw null;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/gui/com/lgimagecompressor/MainActivity.java",
    "content": "package gui.com.lgimagecompressor;\n\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.support.v7.app.AppCompatActivity;\nimport android.view.View;\n\npublic class MainActivity extends AppCompatActivity {\n    private final String TAG = MainActivity.class.getSimpleName();\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_main);\n        findViewById(R.id.basic_test).setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                Intent intent = new Intent(MainActivity.this,BasicCompressActivity.class);\n                startActivity(intent);\n            }\n        });\n        findViewById(R.id.from_service).setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                Intent intent = new Intent(MainActivity.this,ServiceCompressActivity.class);\n                startActivity(intent);\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/gui/com/lgimagecompressor/ServiceCompressActivity.java",
    "content": "package gui.com.lgimagecompressor;\n\nimport android.content.BroadcastReceiver;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.IntentFilter;\nimport android.database.Cursor;\nimport android.net.Uri;\nimport android.os.Environment;\nimport android.provider.MediaStore;\nimport android.support.v7.app.AppCompatActivity;\nimport android.os.Bundle;\nimport android.util.Log;\nimport android.view.View;\nimport android.widget.TextView;\n\nimport java.io.File;\nimport java.util.ArrayList;\n\npublic class ServiceCompressActivity extends AppCompatActivity {\n    private final String TAG = ServiceCompressActivity.class.getSimpleName();\n\n    private long serviceStartTime;\n    private CompressingReciver reciver;\n    private TextView infoView;\n\n    private class CompressingReciver extends BroadcastReceiver {\n        @Override\n        public void onReceive(Context context, Intent intent) {\n            Log.d(TAG, \"onReceive:\" + Thread.currentThread().getId());\n            int flag = intent.getIntExtra(Constanse.KEY_COMPRESS_FLAG,-1);\n            Log.d(TAG,\" flag:\" + flag);\n            if(flag == Constanse.FLAG_BEGAIIN){\n                Log.d(TAG, \"onCompressServiceStart\");\n                serviceStartTime = System.currentTimeMillis();\n                updateInfo(\"compress begain...\");\n                return;\n            }\n\n            if(flag == Constanse.FLAG_END){\n                ArrayList<LGImgCompressor.CompressResult> compressResults =\n                        (ArrayList<LGImgCompressor.CompressResult>)intent.getSerializableExtra(Constanse.KEY_COMPRESS_RESULT);\n                Log.d(TAG, compressResults.size() + \"compressed done\");\n                Log.d(TAG, \"compress \" + compressResults.size() + \" files used total time:\" + (System.currentTimeMillis() - serviceStartTime));\n                updateInfo(compressResults.size() + \" files compressed done \\nused total time:\" + (System.currentTimeMillis() - serviceStartTime) + \"ms\");\n            }\n        }\n    }\n\n    private void updateInfo(String message){\n        infoView.setText(message);\n    }\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_intent_service_compress);\n\n        reciver = new CompressingReciver();\n        IntentFilter intentFilter = new IntentFilter(Constanse.ACTION_COMPRESS_BROADCAST);\n        registerReceiver(reciver, intentFilter);\n\n        infoView = (TextView)findViewById(R.id.compress_info);\n        final int maxSize = 0;\n\n        findViewById(R.id.intent_service).setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                ArrayList<Uri> compressFiles = getImagesPathFormAlbum();\n                Log.d(TAG, compressFiles.size() + \"compresse begain\");\n                int size = compressFiles.size() > 10 ? 10:compressFiles.size();\n                for (int i = 0; i < compressFiles.size(); ++i) {\n                    Uri uri = compressFiles.get(i);\n                    CompressServiceParam param = new CompressServiceParam();\n                    param.setOutHeight(800);\n                    param.setOutWidth(600);\n                    param.setMaxFileSize(400);\n                    param.setSrcImageUri(uri.toString());\n                    LGImgCompressorIntentService.startActionCompress(ServiceCompressActivity.this, param);\n                }\n            }\n        });\n\n        findViewById(R.id.service).setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                ArrayList<Uri> compressFiles = getImagesPathFormAlbum();\n                int size = compressFiles.size() > 10 ? 10:compressFiles.size();\n                ArrayList<CompressServiceParam> tasks = new ArrayList<CompressServiceParam>(compressFiles.size());\n\n                for (int i = 0; i < compressFiles.size(); ++i) {\n                    Uri uri = compressFiles.get(i);\n                    CompressServiceParam param = new CompressServiceParam();\n                    param.setOutHeight(800);\n                    param.setOutWidth(600);\n                    param.setMaxFileSize(400);\n                    param.setSrcImageUri(uri.toString());\n                    tasks.add(param);\n                }\n                Log.d(TAG, compressFiles.size() + \"compresse begain\");\n                Intent intent = new Intent(ServiceCompressActivity.this, LGImgCompressorService.class);\n                intent.putParcelableArrayListExtra(Constanse.COMPRESS_PARAM, tasks);\n                startService(intent);\n            }\n        });\n    }\n\n    private ArrayList<Uri> getImagesPathFormAlbum() {\n        ArrayList<Uri> paths = new ArrayList<>();\n        //selection: 指定查询条件\n        String selection = MediaStore.Images.Media.MIME_TYPE + \"=? or \" + MediaStore.Images.Media.MIME_TYPE + \"=?\";\n\n        //定义selection参数匹配值\n        String[] selectionArgs = {\"image/jpeg\", \"image/png\"};\n\n        Cursor cursor = getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, null,\n                selection,\n                selectionArgs,\n                MediaStore.Images.Media.DATE_MODIFIED);\n        if (cursor != null && cursor.moveToFirst()) {\n            do {\n                long id = cursor.getInt(cursor.getColumnIndex(MediaStore.Images.Media._ID));\n                String title = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.TITLE));\n                String url = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));\n                long size = (int) cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.SIZE));\n                long lastModified = (int) cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATE_MODIFIED));\n                //排除size为0的无效文件\n                if (size != 0) {\n                    Uri uri = Uri.parse(url);\n                    paths.add(uri);\n                }\n            } while (cursor.moveToNext());\n            cursor.close();\n        }\n        return paths;\n    }\n\n    @Override\n    protected void onDestroy() {\n        super.onDestroy();\n        if(reciver != null){\n            unregisterReceiver(reciver);\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/res/layout/activity_basic_compress.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:paddingBottom=\"@dimen/activity_vertical_margin\"\n    android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n    android:paddingRight=\"@dimen/activity_horizontal_margin\"\n    android:paddingTop=\"@dimen/activity_vertical_margin\"\n    tools:context=\"gui.com.lgimagecompressor.MainActivity\">\n\n    <ImageView\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:id=\"@+id/image_view\"\n        android:minWidth=\"50dp\"\n        android:minHeight=\"50dp\"\n        android:background=\"@android:color/darker_gray\"\n        android:layout_centerHorizontal=\"true\"/>\n\n    <TextView\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"点击图片拍照\"\n        android:layout_below=\"@+id/image_view\"\n        android:layout_centerHorizontal=\"true\"/>\n\n    <TextView\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:id=\"@+id/image_info\"\n        android:layout_alignParentBottom=\"true\"\n        android:text=\"image info:\"/>\n\n</RelativeLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/activity_intent_service_compress.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:paddingBottom=\"@dimen/activity_vertical_margin\"\n    android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n    android:paddingRight=\"@dimen/activity_horizontal_margin\"\n    android:paddingTop=\"@dimen/activity_vertical_margin\"\n    tools:context=\".ServiceCompressActivity\">\n    <Button\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:id=\"@+id/intent_service\"\n        android:text=\"compress from intentService\"/>\n\n    <Button\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:id=\"@+id/service\"\n        android:text=\"compress from service\"\n        android:layout_below=\"@+id/intent_service\"/>\n\n    <Button\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"test\"\n        android:layout_below=\"@+id/service\"/>\n\n    <TextView\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentBottom=\"true\"\n        android:id=\"@+id/compress_info\"\n        android:text=\"info:\"/>\n</RelativeLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:paddingBottom=\"@dimen/activity_vertical_margin\"\n    android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n    android:paddingRight=\"@dimen/activity_horizontal_margin\"\n    android:paddingTop=\"@dimen/activity_vertical_margin\"\n    tools:context=\"gui.com.lgimagecompressor.MainActivity\">\n\n    <Button\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"Basic Compress Test\"\n        android:id=\"@+id/basic_test\"/>\n\n    <Button\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:id=\"@+id/from_service\"\n        android:text=\"compress from service\"\n        android:layout_below=\"@+id/basic_test\"/>\n\n</RelativeLayout>\n"
  },
  {
    "path": "app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"colorPrimary\">#3F51B5</color>\n    <color name=\"colorPrimaryDark\">#303F9F</color>\n    <color name=\"colorAccent\">#FF4081</color>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/dimens.xml",
    "content": "<resources>\n    <!-- Default screen margins, per the Android Design guidelines. -->\n    <dimen name=\"activity_horizontal_margin\">16dp</dimen>\n    <dimen name=\"activity_vertical_margin\">16dp</dimen>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">LGImageCompressor</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/styles.xml",
    "content": "<resources>\n\n    <!-- Base application theme. -->\n    <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.DarkActionBar\">\n        <!-- Customize your theme here. -->\n        <item name=\"colorPrimary\">@color/colorPrimary</item>\n        <item name=\"colorPrimaryDark\">@color/colorPrimaryDark</item>\n        <item name=\"colorAccent\">@color/colorAccent</item>\n    </style>\n\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-w820dp/dimens.xml",
    "content": "<resources>\n    <!-- Example customization of dimensions originally defined in res/values/dimens.xml\n         (such as screen margins) for screens with more than 820dp of available width. This\n         would include 7\" and 10\" devices in landscape (~960dp and ~1280dp respectively). -->\n    <dimen name=\"activity_horizontal_margin\">64dp</dimen>\n</resources>\n"
  },
  {
    "path": "app/src/test/java/gui/com/lgimagecompressor/ExampleUnitTest.java",
    "content": "package gui.com.lgimagecompressor;\n\nimport org.junit.Test;\n\nimport static org.junit.Assert.*;\n\n/**\n * To work on unit tests, switch the Test Artifact in the Build Variants view.\n */\npublic class ExampleUnitTest {\n    @Test\n    public void addition_isCorrect() throws Exception {\n        assertEquals(4, 2 + 2);\n    }\n}"
  },
  {
    "path": "app/src/test/java/gui/com/lgimagecompressor/LGImgCompressorServiceTest.java",
    "content": "package gui.com.lgimagecompressor;\n\nimport android.provider.Settings;\nimport android.util.Log;\n\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport java.util.ArrayList;\n\nimport static org.junit.Assert.*;\n\n/**\n * Created by guizhigang on 16/5/29.\n */\npublic class LGImgCompressorServiceTest {\n    private Object lock = new Object();\n    private int taskNumber;\n    private ArrayList<Integer> results = new ArrayList<>();\n\n    private class CompressTask implements Runnable{\n        private int taskId ;\n\n        private CompressTask(int taskId){\n            this.taskId = taskId;\n        }\n\n        @Override\n        public void run() {\n            System.out.println(taskId + \" do compress begain...\" + Thread.currentThread().getId());\n            try {\n                Thread.sleep(30);\n            } catch (InterruptedException e) {\n                e.printStackTrace();\n            }\n            System.out.println(taskId + \" do compress end...\" + Thread.currentThread().getId());\n            synchronized (lock){\n                System.out.println(taskId + \" make taskNumber release before \" + taskNumber);\n                results.add(taskId);\n                taskNumber--;\n                System.out.println(taskId + \" make taskNumber release end \" + taskNumber);\n                if(taskNumber <= 0){\n                    System.out.println(\"all task done...\");\n                }\n            }\n        }\n    }\n\n    @Test\n    public void startServer(){\n        taskNumber = 8;\n        int count = taskNumber;\n        for (int i = 1; i <= count; i++){\n            new Thread(new CompressTask(i)).start();\n        }\n        synchronized (lock){\n            try {\n                Thread.sleep(taskNumber * 100);\n            } catch (InterruptedException e) {\n                e.printStackTrace();\n            }\n        }\n    }\n\n    @Before\n    public void setUp() throws Exception {\n    }\n\n    @After\n    public void tearDown() throws Exception {\n\n    }\n}"
  },
  {
    "path": "build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\nbuildscript {\n    repositories {\n        jcenter()\n    }\n    dependencies {\n        classpath 'com.android.tools.build:gradle:2.1.0'\n\n        // NOTE: Do not place your application dependencies here; they belong\n        // in the individual module build.gradle files\n    }\n}\n\nallprojects {\n    repositories {\n        jcenter()\n    }\n}\n\ntask clean(type: Delete) {\n    delete rootProject.buildDir\n}\n"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "#Mon Dec 28 10:00:20 PST 2015\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-2.10-all.zip\n"
  },
  {
    "path": "gradle.properties",
    "content": "# Project-wide Gradle settings.\n\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will override*\n# any settings specified in this file.\n\n# For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\n# Default value: -Xmx10248m -XX:MaxPermSize=256m\n# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8\n\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true"
  },
  {
    "path": "gradlew",
    "content": "#!/usr/bin/env bash\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS=\"\"\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn ( ) {\n    echo \"$*\"\n}\n\ndie ( ) {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\nesac\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >/dev/null\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >/dev/null\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n    JAVACMD=`cygpath --unix \"$JAVACMD\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=$((i+1))\n    done\n    case $i in\n        (0) set -- ;;\n        (1) set -- \"$args0\" ;;\n        (2) set -- \"$args0\" \"$args1\" ;;\n        (3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        (4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        (5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        (6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        (7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        (8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        (9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules\nfunction splitJvmOpts() {\n    JVM_OPTS=(\"$@\")\n}\neval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\nJVM_OPTS[${#JVM_OPTS[*]}]=\"-Dorg.gradle.appname=$APP_BASE_NAME\"\n\nexec \"$JAVACMD\" \"${JVM_OPTS[@]}\" -classpath \"$CLASSPATH\" org.gradle.wrapper.GradleWrapperMain \"$@\"\n"
  },
  {
    "path": "gradlew.bat",
    "content": "@if \"%DEBUG%\" == \"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif \"%ERRORLEVEL%\" == \"0\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:init\r\n@rem Get command-line arguments, handling Windowz variants\r\n\r\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\r\nif \"%@eval[2+2]\" == \"4\" goto 4NT_args\r\n\r\n:win9xME_args\r\n@rem Slurp the command line arguments.\r\nset CMD_LINE_ARGS=\r\nset _SKIP=2\r\n\r\n:win9xME_args_slurp\r\nif \"x%~1\" == \"x\" goto execute\r\n\r\nset CMD_LINE_ARGS=%*\r\ngoto execute\r\n\r\n:4NT_args\r\n@rem Get arguments from the 4NT Shell from JP Software\r\nset CMD_LINE_ARGS=%$\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\r\nexit /b 1\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "settings.gradle",
    "content": "include ':app'\n"
  }
]