[
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Create a report to help us improve\n\n---\n\n**Describe the bug**\nA clear and concise description of what the bug is.\n\n**To Reproduce**\nSteps to reproduce the behavior:\n1. Go to '...'\n2. Click on '....'\n3. Scroll down to '....'\n4. See error\n\n**Expected behavior**\nA clear and concise description of what you expected to happen.\n\n**Screenshots**\nIf applicable, add screenshots to help explain your problem.\n\n\n**Smartphone (please complete the following information):**\n - Device: [e.g. iPhone6]\n - OS: [e.g. iOS8.1]\n - Version [e.g. 22]\n\n**Additional context**\nAdd any other context about the problem here.\n"
  },
  {
    "path": ".gitignore",
    "content": "*.iml\n.gradle\n/local.properties\n.DS_Store\n/build\n/captures\n\n.idea/\n\n# .gitignore template from https://github.com/github/gitignore/blob/master/Android.gitignore\n# Built application files\n*.apk\n*.ap_\n\n# Files for the Dalvik VM\n*.dex\n\n# Java class files\n*.class\n\n# Generated files\nbin/\ngen/\nout/\n\n# Gradle build files\nmatisse/build\nsample/build\n\n\n\n# Proguard folder generated by Eclipse\nproguard/\n\n# Log Files\n*.log\n\n# Android Studio Navigation editor temp files\n.navigation/\n\n# Android Studio captures folder\ncaptures/\n\n\n# Keystore files\n*.jks\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: android\n\nandroid:\n  components:\n    - tools\n    - platform-tools\n    - build-tools-28.0.3\n    - android-28\n    - extra-android-m2repository\n\njdk:\n  - oraclejdk8\n\nnotifications:\n  email: false\n\nbefore_install:\n  - chmod +x gradlew\n  - mkdir \"$ANDROID_HOME/licenses\" || true\n  - echo -e \"\\d56f5187479451eabf01fb78af6dfcb131a6481e\" \"\\n24333f8a63b6825ea9c5514f83c2829b004d1fee\"> \"$ANDROID_HOME/licenses/android-sdk-license\"\n  - echo -e \"\\84831b9409646a918e30573bab4c9c91346d8abd\" > \"$ANDROID_HOME/licenses/android-sdk-preview-license\"\n\nscript:\n  - ./gradlew assemble check\n  - ./gradlew checkstyle\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.\n\n## Our Standards\n\nExamples of behavior that contributes to creating a positive environment include:\n\n* Using welcoming and inclusive language\n* Being respectful of differing viewpoints and experiences\n* Gracefully accepting constructive criticism\n* Focusing on what is best for the community\n* Showing empathy towards other community members\n\nExamples of unacceptable behavior by participants include:\n\n* The use of sexualized language or imagery and unwelcome sexual attention or advances\n* Trolling, insulting/derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or electronic address, without explicit permission\n* Other conduct which could reasonably be considered inappropriate in a professional setting\n\n## Our Responsibilities\n\nProject maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.\n\nProject maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.\n\n## Scope\n\nThis Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at gejiaheng@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.\n\nProject maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]\n\n[homepage]: http://contributor-covenant.org\n[version]: http://contributor-covenant.org/version/1/4/\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Matisse is an Open Source Project\n\n## You Should Know\n\n- To contribute with a small fix, simply create a pull request.\n- Better to open an issue to discuss with the team and the community if you're intended to work on something BIG. \n- Check out our [roadmap](https://github.com/zhihu/Matisse/wiki/Roadmap) to see if some features you want is on the way.\n- Better to use English to open issues and pull requests.\n\n## Code Style\n\nPlease follow [Code Style for Contributors](https://source.android.com/source/code-style) of AOSP except\n- Right margin is 120 characters instead of the default 100 value.\n\nAnd also run `./gradlew checkstyle` to check if there is any style issues before sending a PR."
  },
  {
    "path": "LICENSE.txt",
    "content": "\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright 2017 Zhihu Inc.\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "![Image](/image/banner.png)\n\n# Matisse\n[![Build Status](https://travis-ci.org/zhihu/Matisse.svg)](https://travis-ci.org/zhihu/Matisse) [ ![Download](https://api.bintray.com/packages/zhihu/maven/matisse/images/download.svg) ](https://bintray.com/zhihu/maven/matisse/_latestVersion)\n\nMatisse is a well-designed local image and video selector for Android. You can  \n- Use it in Activity or Fragment\n- Select images including JPEG, PNG, GIF and videos including MPEG, MP4 \n- Apply different themes, including two built-in themes and custom themes\n- Different image loaders\n- Define custom filter rules\n- More to find out yourself\n\n| Zhihu Style                    | Dracula Style                     | Preview                          |\n|:------------------------------:|:---------------------------------:|:--------------------------------:|\n|![](image/screenshot_zhihu.png) | ![](image/screenshot_dracula.png) | ![](image/screenshot_preview.png)|\n\n## Download\nGradle:\n\n```groovy\nrepositories {\n    jcenter()\n}\n\ndependencies {\n    implementation 'com.zhihu.android:matisse:$latest_version'\n}\n```\n\nCheck out [Matisse releases](https://github.com/zhihu/Matisse/releases) to see more unstable versions.\n\n## ProGuard\nIf you use [Glide](https://github.com/bumptech/glide) as your image engine, add rules as Glide's README says.  \nAnd add extra rule:\n```pro\n-dontwarn com.squareup.picasso.**\n```\n\nIf you use [Picasso](https://github.com/square/picasso) as your image engine, add rules as Picasso's README says.  \nAnd add extra rule:\n```pro\n-dontwarn com.bumptech.glide.**\n```\n**Attention**: The above progurad rules are correct.\n\n## How do I use Matisse?\n#### Permission\nThe library requires two permissions:\n- `android.permission.READ_EXTERNAL_STORAGE`\n- `android.permission.WRITE_EXTERNAL_STORAGE`\n\nSo if you are targeting Android 6.0+, you need to handle runtime permission request before next step.\n\n#### Simple usage snippet\n------\nStart `MatisseActivity` from current `Activity` or `Fragment`:\n\n```java\nMatisse.from(MainActivity.this)\n        .choose(MimeType.allOf())\n        .countable(true)\n        .maxSelectable(9)\n        .addFilter(new GifSizeFilter(320, 320, 5 * Filter.K * Filter.K))\n        .gridExpectedSize(getResources().getDimensionPixelSize(R.dimen.grid_expected_size))\n        .restrictOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED)\n        .thumbnailScale(0.85f)\n        .imageEngine(new GlideEngine())\n        .showPreview(false) // Default is `true`\n        .forResult(REQUEST_CODE_CHOOSE);\n```\n \n#### Themes\nThere are two built-in themes you can use to start `MatisseActivity`:\n- `R.style.Matisse_Zhihu` (light mode)\n- `R.style.Matisse_Dracula` (dark mode)  \n\nAnd Also you can define your own theme as you wish.\n\n#### Receive Result\nIn `onActivityResult()` callback of the starting `Activity` or `Fragment`:\n\n```java\nList<Uri> mSelected;\n\n@Override\nprotected void onActivityResult(int requestCode, int resultCode, Intent data) {\n    super.onActivityResult(requestCode, resultCode, data);\n    if (requestCode == REQUEST_CODE_CHOOSE && resultCode == RESULT_OK) {\n        mSelected = Matisse.obtainResult(data);\n        Log.d(\"Matisse\", \"mSelected: \" + mSelected);\n    }\n}\n```\n\n#### More\nFind more details about Matisse in [wiki](https://github.com/zhihu/Matisse/wiki).\n\n## Contributing\n[Matisse is an Open Source Project](https://github.com/zhihu/Matisse/blob/master/CONTRIBUTING.md)\n\n## Thanks\nThis library is inspired by [Laevatein](https://github.com/nohana/Laevatein) and uses some of its source code.\n\n## License\n\n    Copyright 2017 Zhihu Inc.\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n\n\n"
  },
  {
    "path": "build.gradle",
    "content": "/*\n * Copyright 2017 Zhihu Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\nbuildscript {\n    repositories {\n        jcenter()\n        google()\n    }\n    dependencies {\n        classpath 'com.android.tools.build:gradle:3.5.1'\n        classpath 'com.novoda:bintray-release:0.9.1'\n    }\n}\n\nallprojects {\n    repositories {\n        jcenter()\n        google()\n    }\n}\n\ntask clean(type: Delete) {\n    delete rootProject.buildDir\n}\n"
  },
  {
    "path": "checkstyle.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE module PUBLIC\n    \"-//Checkstyle//DTD Checkstyle Configuration 1.3//EN\"\n    \"https://checkstyle.org/dtds/configuration_1_3.dtd\">\n\n<module name=\"Checker\">\n    <!--module name=\"NewlineAtEndOfFile\"/-->\n    <module name=\"FileLength\"/>\n    <module name=\"FileTabCharacter\"/>\n\n    <!-- Trailing spaces -->\n    <module name=\"RegexpSingleline\">\n        <property name=\"format\" value=\"\\s+$\"/>\n        <property name=\"message\" value=\"Line has trailing spaces.\"/>\n    </module>\n\n    <!-- Space after 'for' and 'if' -->\n    <module name=\"RegexpSingleline\">\n        <property name=\"format\" value=\"^\\s*(for|if)\\b[^ ]\"/>\n        <property name=\"message\" value=\"Space needed before opening parenthesis.\"/>\n    </module>\n\n    <!-- For each spacing -->\n    <module name=\"RegexpSingleline\">\n        <property name=\"format\" value=\"^\\s*for \\(.*?([^ ]:|:[^ ])\"/>\n        <property name=\"message\" value=\"Space needed around ':' character.\"/>\n    </module>\n\n    <module name=\"TreeWalker\">\n        <!--<property name=\"cacheFile\" value=\"${checkstyle.cache.file}\"/>-->\n\n        <!-- Checks for Javadoc comments.                     -->\n        <!-- See http://checkstyle.sf.net/config_javadoc.html -->\n        <!--module name=\"JavadocMethod\"/-->\n        <!--module name=\"JavadocType\"/-->\n        <!--module name=\"JavadocVariable\"/-->\n        <!--module name=\"JavadocStyle\"/-->\n\n\n        <!-- Checks for Naming Conventions.                  -->\n        <!-- See http://checkstyle.sf.net/config_naming.html -->\n        <module name=\"ConstantName\"/>\n        <module name=\"LocalFinalVariableName\"/>\n        <module name=\"LocalVariableName\">\n            <property name=\"tokens\" value=\"VARIABLE_DEF\"/>\n            <property name=\"format\" value=\"^[a-z]([a-z0-9][a-zA-Z0-9]*)?$\"/>\n            <message key=\"name.invalidPattern\"\n                     value=\"Local variable name ''{0}'' must match pattern ''{1}''.\"/>\n        </module>\n        <module name=\"MemberName\">\n            <property name=\"format\" value=\"^m[a-zA-Z0-9]*$\"/>\n            <property name=\"applyToPublic\" value=\"false\"/>\n            <message key=\"name.invalidPattern\"\n                     value=\"Member ''{0}'' must start with a lowercase ''m'' (checked pattern ''{1}'').\"\n            />\n        </module>\n        <module name=\"MethodName\">\n            <property name=\"format\" value=\"^[a-z][a-zA-Z0-9_]*$\"/>\n        </module>\n        <module name=\"PackageName\"/>\n        <module name=\"ParameterName\"/>\n        <module name=\"StaticVariableName\">\n            <property name=\"format\" value=\"^s[a-zA-Z0-9]*$\"/>\n            <message key=\"name.invalidPattern\"\n                     value=\"Member ''{0}'' must start with a lowercase ''s'' (checked pattern ''{1}'').\"\n            />\n        </module>\n        <module name=\"TypeName\">\n            <property name=\"format\" value=\"^[A-Z][a-zA-Z0-9_]*$\"/>\n        </module>\n\n\n        <!-- Checks for imports                              -->\n        <!-- See http://checkstyle.sf.net/config_import.html -->\n        <module name=\"AvoidStarImport\"/>\n        <module name=\"IllegalImport\"/>\n        <module name=\"RedundantImport\"/>\n        <module name=\"UnusedImports\">\n            <property name=\"processJavadoc\" value=\"true\"/>\n        </module>\n\n\n        <!-- Checks for Size Violations.                    -->\n        <!-- See http://checkstyle.sf.net/config_sizes.html -->\n        <module name=\"LineLength\">\n            <property name=\"severity\" value=\"warning\"/>\n            <property name=\"max\" value=\"120\"/>\n            <property name=\"ignorePattern\"\n                      value=\"^package.*|^import.*|a href|href|http://|https://|ftp://\"/>\n        </module>\n        <!--<module name=\"MethodLength\"/>-->\n        <!--<module name=\"ParameterNumber\"/>-->\n\n\n        <!-- Checks for whitespace                               -->\n        <!-- See http://checkstyle.sf.net/config_whitespace.html -->\n        <module name=\"GenericWhitespace\"/>\n        <module name=\"EmptyForIteratorPad\"/>\n        <module name=\"MethodParamPad\"/>\n        <module name=\"NoWhitespaceAfter\"/>\n        <module name=\"NoWhitespaceBefore\"/>\n        <module name=\"OperatorWrap\"/>\n        <module name=\"ParenPad\"/>\n        <module name=\"TypecastParenPad\"/>\n        <module name=\"WhitespaceAfter\"/>\n        <module name=\"WhitespaceAround\"/>\n\n\n        <!-- Modifier Checks                                    -->\n        <!-- See http://checkstyle.sf.net/config_modifiers.html -->\n        <!--module name=\"ModifierOrder\"/-->\n        <module name=\"RedundantModifier\"/>\n\n\n        <!-- Checks for blocks. You know, those {}'s         -->\n        <!-- See http://checkstyle.sf.net/config_blocks.html -->\n        <module name=\"AvoidNestedBlocks\"/>\n        <!--<module name=\"EmptyBlock\"/>-->\n        <module name=\"LeftCurly\"/>\n        <module name=\"NeedBraces\">\n            <property name=\"tokens\" value=\"LITERAL_DO, LITERAL_ELSE, LITERAL_FOR, LITERAL_WHILE\"/>\n        </module>\n        <module name=\"RightCurly\"/>\n\n\n        <!-- Checks for common coding problems               -->\n        <!-- See http://checkstyle.sf.net/config_coding.html -->\n        <!--<module name=\"AvoidInlineConditionals\"/>-->\n        <module name=\"CovariantEquals\"/>\n        <!--<module name=\"DoubleCheckedLocking\"/>-->\n        <module name=\"EmptyStatement\"/>\n        <module name=\"EqualsAvoidNull\"/>\n        <module name=\"EqualsHashCode\"/>\n        <!--<module name=\"HiddenField\"/>-->\n        <module name=\"IllegalInstantiation\"/>\n        <module name=\"InnerAssignment\"/>\n        <!--<module name=\"MagicNumber\"/>-->\n        <module name=\"MissingSwitchDefault\"/>\n        <!--<module name=\"RedundantThrows\"/>-->\n        <module name=\"SimplifyBooleanExpression\"/>\n        <module name=\"SimplifyBooleanReturn\"/>\n\n        <!-- Checks for class design                         -->\n        <!-- See http://checkstyle.sf.net/config_design.html -->\n        <!--module name=\"DesignForExtension\"/-->\n        <!--module name=\"FinalClass\"/-->\n        <!--<module name=\"HideUtilityClassConstructor\"/>-->\n        <module name=\"InterfaceIsType\"/>\n        <!--<module name=\"VisibilityModifier\"/>-->\n\n\n        <!-- Miscellaneous other checks.                   -->\n        <!-- See http://checkstyle.sf.net/config_misc.html -->\n        <module name=\"ArrayTypeStyle\"/>\n        <!--module name=\"FinalParameters\"/-->\n        <module name=\"TodoComment\"/>\n        <module name=\"UpperEll\"/>\n    </module>\n</module>"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "#Thu Aug 22 11:37:43 CST 2019\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-5.4.1-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.\nandroid.enableJetifier=true\nandroid.useAndroidX=true\norg.gradle.jvmargs=-Xmx1536m\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\n"
  },
  {
    "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": "matisse/build.gradle",
    "content": "/*\n * Copyright 2017 Zhihu Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\napply plugin: 'com.android.library'\napply plugin: 'com.novoda.bintray-release'\napply plugin: 'checkstyle'\n\nandroid {\n    compileSdkVersion 29\n    buildToolsVersion '29.0.2'\n\n    defaultConfig {\n        minSdkVersion 14\n        targetSdkVersion 29\n    }\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_1_8\n        targetCompatibility JavaVersion.VERSION_1_8\n    }\n    lintOptions {\n        abortOnError false\n    }\n}\n\ndependencies {\n    implementation fileTree(dir: 'libs', include: ['*.jar'])\n\n    implementation \"androidx.appcompat:appcompat:1.1.0\"\n    implementation \"androidx.annotation:annotation:1.1.0\"\n    implementation \"androidx.recyclerview:recyclerview:1.0.0\"\n    implementation 'it.sephiroth.android.library.imagezoom:library:1.0.4'\n\n    compileOnly 'com.github.bumptech.glide:glide:4.9.0'\n    compileOnly 'com.squareup.picasso:picasso:2.5.2'\n}\n\n// jcenter configuration for novoda's bintray-release\n// $ ./gradlew clean build bintrayUpload -PbintrayUser=BINTRAY_USERNAME -PbintrayKey=BINTRAY_KEY -PdryRun=false\npublish {\n    userOrg = 'zhihu'\n    groupId = 'com.zhihu.android'\n    artifactId = 'matisse'\n    publishVersion = '0.5.3-beta3'\n    desc = 'A well-designed local image selector for Android'\n    website = 'https://www.zhihu.com/'\n}\n\ntask javadoc(type: Javadoc) {\n    options.encoding = \"utf-8\"\n}\n\ncheckstyle {\n    toolVersion = '7.6.1'\n}\n\ntasks.withType(Javadoc) {\n    options.addStringOption('Xdoclint:none', '-quiet')\n    options.addStringOption('encoding', 'UTF-8')\n}\n\ntask checkstyle(type:Checkstyle) {\n    description 'Runs Checkstyle inspection against matisse sourcesets.'\n    group = 'Code Quality'\n    configFile rootProject.file('checkstyle.xml')\n    ignoreFailures = false\n    showViolations true\n    classpath = files()\n    source 'src/main/java'\n}\n"
  },
  {
    "path": "matisse/gradle.properties",
    "content": ""
  },
  {
    "path": "matisse/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 /Library/android-sdk-macosx/tools/proguard/proguard-android.txt\n# You can edit the include path and order by changing the ProGuard\n# include property in project.properties.\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\n-dontwarn com.squareup.okhttp.**\n"
  },
  {
    "path": "matisse/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright 2017 Zhihu Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n  -->\n<manifest\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.zhihu.matisse\">\n\n    <uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\"/>\n    <uses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\"/>\n\n    <application>\n        <activity android:name=\"com.zhihu.matisse.ui.MatisseActivity\"/>\n        <activity android:name=\"com.zhihu.matisse.internal.ui.AlbumPreviewActivity\"/>\n        <activity android:name=\"com.zhihu.matisse.internal.ui.SelectedPreviewActivity\"/>\n    </application>\n</manifest>"
  },
  {
    "path": "matisse/src/main/java/com/zhihu/matisse/Matisse.java",
    "content": "/*\n * Copyright 2017 Zhihu Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.zhihu.matisse;\n\nimport android.app.Activity;\nimport android.content.Intent;\nimport android.net.Uri;\nimport androidx.annotation.Nullable;\nimport androidx.fragment.app.Fragment;\n\nimport com.zhihu.matisse.ui.MatisseActivity;\n\nimport java.lang.ref.WeakReference;\nimport java.util.List;\nimport java.util.Set;\n\n/**\n * Entry for Matisse's media selection.\n */\npublic final class Matisse {\n\n    private final WeakReference<Activity> mContext;\n    private final WeakReference<Fragment> mFragment;\n\n    private Matisse(Activity activity) {\n        this(activity, null);\n    }\n\n    private Matisse(Fragment fragment) {\n        this(fragment.getActivity(), fragment);\n    }\n\n    private Matisse(Activity activity, Fragment fragment) {\n        mContext = new WeakReference<>(activity);\n        mFragment = new WeakReference<>(fragment);\n    }\n\n    /**\n     * Start Matisse from an Activity.\n     * <p>\n     * This Activity's {@link Activity#onActivityResult(int, int, Intent)} will be called when user\n     * finishes selecting.\n     *\n     * @param activity Activity instance.\n     * @return Matisse instance.\n     */\n    public static Matisse from(Activity activity) {\n        return new Matisse(activity);\n    }\n\n    /**\n     * Start Matisse from a Fragment.\n     * <p>\n     * This Fragment's {@link Fragment#onActivityResult(int, int, Intent)} will be called when user\n     * finishes selecting.\n     *\n     * @param fragment Fragment instance.\n     * @return Matisse instance.\n     */\n    public static Matisse from(Fragment fragment) {\n        return new Matisse(fragment);\n    }\n\n    /**\n     * Obtain user selected media' {@link Uri} list in the starting Activity or Fragment.\n     *\n     * @param data Intent passed by {@link Activity#onActivityResult(int, int, Intent)} or\n     *             {@link Fragment#onActivityResult(int, int, Intent)}.\n     * @return User selected media' {@link Uri} list.\n     */\n    public static List<Uri> obtainResult(Intent data) {\n        return data.getParcelableArrayListExtra(MatisseActivity.EXTRA_RESULT_SELECTION);\n    }\n\n    /**\n     * Obtain user selected media path list in the starting Activity or Fragment.\n     *\n     * @param data Intent passed by {@link Activity#onActivityResult(int, int, Intent)} or\n     *             {@link Fragment#onActivityResult(int, int, Intent)}.\n     * @return User selected media path list.\n     */\n    public static List<String> obtainPathResult(Intent data) {\n        return data.getStringArrayListExtra(MatisseActivity.EXTRA_RESULT_SELECTION_PATH);\n    }\n\n    /**\n     * Obtain state whether user decide to use selected media in original\n     *\n     * @param data Intent passed by {@link Activity#onActivityResult(int, int, Intent)} or\n     *             {@link Fragment#onActivityResult(int, int, Intent)}.\n     * @return Whether use original photo\n     */\n    public static boolean obtainOriginalState(Intent data) {\n        return data.getBooleanExtra(MatisseActivity.EXTRA_RESULT_ORIGINAL_ENABLE, false);\n    }\n\n    /**\n     * MIME types the selection constrains on.\n     * <p>\n     * Types not included in the set will still be shown in the grid but can't be chosen.\n     *\n     * @param mimeTypes MIME types set user can choose from.\n     * @return {@link SelectionCreator} to build select specifications.\n     * @see MimeType\n     * @see SelectionCreator\n     */\n    public SelectionCreator choose(Set<MimeType> mimeTypes) {\n        return this.choose(mimeTypes, true);\n    }\n\n    /**\n     * MIME types the selection constrains on.\n     * <p>\n     * Types not included in the set will still be shown in the grid but can't be chosen.\n     *\n     * @param mimeTypes          MIME types set user can choose from.\n     * @param mediaTypeExclusive Whether can choose images and videos at the same time during one single choosing\n     *                           process. true corresponds to not being able to choose images and videos at the same\n     *                           time, and false corresponds to being able to do this.\n     * @return {@link SelectionCreator} to build select specifications.\n     * @see MimeType\n     * @see SelectionCreator\n     */\n    public SelectionCreator choose(Set<MimeType> mimeTypes, boolean mediaTypeExclusive) {\n        return new SelectionCreator(this, mimeTypes, mediaTypeExclusive);\n    }\n\n    @Nullable\n    Activity getActivity() {\n        return mContext.get();\n    }\n\n    @Nullable\n    Fragment getFragment() {\n        return mFragment != null ? mFragment.get() : null;\n    }\n\n}\n"
  },
  {
    "path": "matisse/src/main/java/com/zhihu/matisse/MimeType.java",
    "content": "/*\n * Copyright (C) 2014 nohana, Inc.\n * Copyright 2017 Zhihu Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an &quot;AS IS&quot; BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.zhihu.matisse;\n\nimport android.content.ContentResolver;\nimport android.net.Uri;\nimport android.text.TextUtils;\n\nimport androidx.collection.ArraySet;\n\nimport android.webkit.MimeTypeMap;\n\nimport com.zhihu.matisse.internal.utils.PhotoMetadataUtils;\n\nimport java.util.Arrays;\nimport java.util.EnumSet;\nimport java.util.Locale;\nimport java.util.Set;\n\n/**\n * MIME Type enumeration to restrict selectable media on the selection activity. Matisse only supports images and\n * videos.\n * <p>\n * Good example of mime types Android supports:\n * https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/media/java/android/media/MediaFile.java\n */\n@SuppressWarnings(\"unused\")\npublic enum MimeType {\n\n    // ============== images ==============\n    JPEG(\"image/jpeg\", arraySetOf(\n            \"jpg\",\n            \"jpeg\"\n    )),\n    PNG(\"image/png\", arraySetOf(\n            \"png\"\n    )),\n    GIF(\"image/gif\", arraySetOf(\n            \"gif\"\n    )),\n    BMP(\"image/x-ms-bmp\", arraySetOf(\n            \"bmp\"\n    )),\n    WEBP(\"image/webp\", arraySetOf(\n            \"webp\"\n    )),\n\n    // ============== videos ==============\n    MPEG(\"video/mpeg\", arraySetOf(\n            \"mpeg\",\n            \"mpg\"\n    )),\n    MP4(\"video/mp4\", arraySetOf(\n            \"mp4\",\n            \"m4v\"\n    )),\n    QUICKTIME(\"video/quicktime\", arraySetOf(\n            \"mov\"\n    )),\n    THREEGPP(\"video/3gpp\", arraySetOf(\n            \"3gp\",\n            \"3gpp\"\n    )),\n    THREEGPP2(\"video/3gpp2\", arraySetOf(\n            \"3g2\",\n            \"3gpp2\"\n    )),\n    MKV(\"video/x-matroska\", arraySetOf(\n            \"mkv\"\n    )),\n    WEBM(\"video/webm\", arraySetOf(\n            \"webm\"\n    )),\n    TS(\"video/mp2ts\", arraySetOf(\n            \"ts\"\n    )),\n    AVI(\"video/avi\", arraySetOf(\n            \"avi\"\n    ));\n\n    private final String mMimeTypeName;\n    private final Set<String> mExtensions;\n\n    MimeType(String mimeTypeName, Set<String> extensions) {\n        mMimeTypeName = mimeTypeName;\n        mExtensions = extensions;\n    }\n\n    public static Set<MimeType> ofAll() {\n        return EnumSet.allOf(MimeType.class);\n    }\n\n    public static Set<MimeType> of(MimeType type, MimeType... rest) {\n        return EnumSet.of(type, rest);\n    }\n\n    public static Set<MimeType> ofImage() {\n        return EnumSet.of(JPEG, PNG, GIF, BMP, WEBP);\n    }\n\n    public static Set<MimeType> ofImage(boolean onlyGif) {\n        return EnumSet.of(GIF);\n    }\n\n    public static Set<MimeType> ofGif() {\n        return ofImage(true);\n    }\n\n    public static Set<MimeType> ofVideo() {\n        return EnumSet.of(MPEG, MP4, QUICKTIME, THREEGPP, THREEGPP2, MKV, WEBM, TS, AVI);\n    }\n\n    public static boolean isImage(String mimeType) {\n        if (mimeType == null) return false;\n        return mimeType.startsWith(\"image\");\n    }\n\n    public static boolean isVideo(String mimeType) {\n        if (mimeType == null) return false;\n        return mimeType.startsWith(\"video\");\n    }\n\n    public static boolean isGif(String mimeType) {\n        if (mimeType == null) return false;\n        return mimeType.equals(MimeType.GIF.toString());\n    }\n\n    private static Set<String> arraySetOf(String... suffixes) {\n        return new ArraySet<>(Arrays.asList(suffixes));\n    }\n\n    @Override\n    public String toString() {\n        return mMimeTypeName;\n    }\n\n    public boolean checkType(ContentResolver resolver, Uri uri) {\n        MimeTypeMap map = MimeTypeMap.getSingleton();\n        if (uri == null) {\n            return false;\n        }\n        String type = map.getExtensionFromMimeType(resolver.getType(uri));\n        String path = null;\n        // lazy load the path and prevent resolve for multiple times\n        boolean pathParsed = false;\n        for (String extension : mExtensions) {\n            if (extension.equals(type)) {\n                return true;\n            }\n            if (!pathParsed) {\n                // we only resolve the path for one time\n                path = PhotoMetadataUtils.getPath(resolver, uri);\n                if (!TextUtils.isEmpty(path)) {\n                    path = path.toLowerCase(Locale.US);\n                }\n                pathParsed = true;\n            }\n            if (path != null && path.endsWith(extension)) {\n                return true;\n            }\n        }\n        return false;\n    }\n}\n"
  },
  {
    "path": "matisse/src/main/java/com/zhihu/matisse/SelectionCreator.java",
    "content": "/*\n * Copyright (C) 2014 nohana, Inc.\n * Copyright 2017 Zhihu Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an &quot;AS IS&quot; BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.zhihu.matisse;\n\nimport android.app.Activity;\nimport android.content.Intent;\nimport android.os.Build;\nimport androidx.annotation.IntDef;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.annotation.RequiresApi;\nimport androidx.annotation.StyleRes;\nimport androidx.fragment.app.Fragment;\n\nimport com.zhihu.matisse.engine.ImageEngine;\nimport com.zhihu.matisse.filter.Filter;\nimport com.zhihu.matisse.internal.entity.CaptureStrategy;\nimport com.zhihu.matisse.internal.entity.SelectionSpec;\nimport com.zhihu.matisse.listener.OnCheckedListener;\nimport com.zhihu.matisse.listener.OnSelectedListener;\nimport com.zhihu.matisse.ui.MatisseActivity;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.util.ArrayList;\nimport java.util.Set;\n\nimport static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;\nimport static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR;\nimport static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_FULL_USER;\nimport static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;\nimport static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED;\nimport static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;\nimport static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;\nimport static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;\nimport static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;\nimport static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_SENSOR;\nimport static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE;\nimport static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT;\nimport static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;\nimport static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_USER;\nimport static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE;\nimport static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT;\n\n/**\n * Fluent API for building media select specification.\n */\n@SuppressWarnings(\"unused\")\npublic final class SelectionCreator {\n    private final Matisse mMatisse;\n    private final SelectionSpec mSelectionSpec;\n\n    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)\n    @IntDef({\n            SCREEN_ORIENTATION_UNSPECIFIED,\n            SCREEN_ORIENTATION_LANDSCAPE,\n            SCREEN_ORIENTATION_PORTRAIT,\n            SCREEN_ORIENTATION_USER,\n            SCREEN_ORIENTATION_BEHIND,\n            SCREEN_ORIENTATION_SENSOR,\n            SCREEN_ORIENTATION_NOSENSOR,\n            SCREEN_ORIENTATION_SENSOR_LANDSCAPE,\n            SCREEN_ORIENTATION_SENSOR_PORTRAIT,\n            SCREEN_ORIENTATION_REVERSE_LANDSCAPE,\n            SCREEN_ORIENTATION_REVERSE_PORTRAIT,\n            SCREEN_ORIENTATION_FULL_SENSOR,\n            SCREEN_ORIENTATION_USER_LANDSCAPE,\n            SCREEN_ORIENTATION_USER_PORTRAIT,\n            SCREEN_ORIENTATION_FULL_USER,\n            SCREEN_ORIENTATION_LOCKED\n    })\n    @Retention(RetentionPolicy.SOURCE)\n    @interface ScreenOrientation {\n    }\n\n    /**\n     * Constructs a new specification builder on the context.\n     *\n     * @param matisse   a requester context wrapper.\n     * @param mimeTypes MIME type set to select.\n     */\n    SelectionCreator(Matisse matisse, @NonNull Set<MimeType> mimeTypes, boolean mediaTypeExclusive) {\n        mMatisse = matisse;\n        mSelectionSpec = SelectionSpec.getCleanInstance();\n        mSelectionSpec.mimeTypeSet = mimeTypes;\n        mSelectionSpec.mediaTypeExclusive = mediaTypeExclusive;\n        mSelectionSpec.orientation = SCREEN_ORIENTATION_UNSPECIFIED;\n    }\n\n    /**\n     * Whether to show only one media type if choosing medias are only images or videos.\n     *\n     * @param showSingleMediaType whether to show only one media type, either images or videos.\n     * @return {@link SelectionCreator} for fluent API.\n     * @see SelectionSpec#onlyShowImages()\n     * @see SelectionSpec#onlyShowVideos()\n     */\n    public SelectionCreator showSingleMediaType(boolean showSingleMediaType) {\n        mSelectionSpec.showSingleMediaType = showSingleMediaType;\n        return this;\n    }\n\n    /**\n     * Theme for media selecting Activity.\n     * <p>\n     * There are two built-in themes:\n     * 1. com.zhihu.matisse.R.style.Matisse_Zhihu;\n     * 2. com.zhihu.matisse.R.style.Matisse_Dracula\n     * you can define a custom theme derived from the above ones or other themes.\n     *\n     * @param themeId theme resource id. Default value is com.zhihu.matisse.R.style.Matisse_Zhihu.\n     * @return {@link SelectionCreator} for fluent API.\n     */\n    public SelectionCreator theme(@StyleRes int themeId) {\n        mSelectionSpec.themeId = themeId;\n        return this;\n    }\n\n    /**\n     * Show a auto-increased number or a check mark when user select media.\n     *\n     * @param countable true for a auto-increased number from 1, false for a check mark. Default\n     *                  value is false.\n     * @return {@link SelectionCreator} for fluent API.\n     */\n    public SelectionCreator countable(boolean countable) {\n        mSelectionSpec.countable = countable;\n        return this;\n    }\n\n    /**\n     * Maximum selectable count.\n     *\n     * @param maxSelectable Maximum selectable count. Default value is 1.\n     * @return {@link SelectionCreator} for fluent API.\n     */\n    public SelectionCreator maxSelectable(int maxSelectable) {\n        if (maxSelectable < 1)\n            throw new IllegalArgumentException(\"maxSelectable must be greater than or equal to one\");\n        if (mSelectionSpec.maxImageSelectable > 0 || mSelectionSpec.maxVideoSelectable > 0)\n            throw new IllegalStateException(\"already set maxImageSelectable and maxVideoSelectable\");\n        mSelectionSpec.maxSelectable = maxSelectable;\n        return this;\n    }\n\n    /**\n     * Only useful when {@link SelectionSpec#mediaTypeExclusive} set true and you want to set different maximum\n     * selectable files for image and video media types.\n     *\n     * @param maxImageSelectable Maximum selectable count for image.\n     * @param maxVideoSelectable Maximum selectable count for video.\n     * @return  {@link SelectionCreator} for fluent API.\n     */\n    public SelectionCreator maxSelectablePerMediaType(int maxImageSelectable, int maxVideoSelectable) {\n        if (maxImageSelectable < 1 || maxVideoSelectable < 1)\n            throw new IllegalArgumentException((\"max selectable must be greater than or equal to one\"));\n        mSelectionSpec.maxSelectable = -1;\n        mSelectionSpec.maxImageSelectable = maxImageSelectable;\n        mSelectionSpec.maxVideoSelectable = maxVideoSelectable;\n        return this;\n    }\n\n    /**\n     * Add filter to filter each selecting item.\n     *\n     * @param filter {@link Filter}\n     * @return {@link SelectionCreator} for fluent API.\n     */\n    public SelectionCreator addFilter(@NonNull Filter filter) {\n        if (mSelectionSpec.filters == null) {\n            mSelectionSpec.filters = new ArrayList<>();\n        }\n        if (filter == null) throw new IllegalArgumentException(\"filter cannot be null\");\n        mSelectionSpec.filters.add(filter);\n        return this;\n    }\n\n    /**\n     * Determines whether the photo capturing is enabled or not on the media grid view.\n     * <p>\n     * If this value is set true, photo capturing entry will appear only on All Media's page.\n     *\n     * @param enable Whether to enable capturing or not. Default value is false;\n     * @return {@link SelectionCreator} for fluent API.\n     */\n    public SelectionCreator capture(boolean enable) {\n        mSelectionSpec.capture = enable;\n        return this;\n    }\n\n    /**\n     * Show a original photo check options.Let users decide whether use original photo after select\n     *\n     * @param enable Whether to enable original photo or not\n     * @return {@link SelectionCreator} for fluent API.\n     */\n    public SelectionCreator originalEnable(boolean enable) {\n        mSelectionSpec.originalable = enable;\n        return this;\n    }\n\n\n    /**\n     * Determines Whether to hide top and bottom toolbar in PreView mode ,when user tap the picture\n     * @param enable\n     * @return {@link SelectionCreator} for fluent API.\n     */\n    public SelectionCreator autoHideToolbarOnSingleTap(boolean enable) {\n        mSelectionSpec.autoHideToobar = enable;\n        return this;\n    }\n\n    /**\n     * Maximum original size,the unit is MB. Only useful when {link@originalEnable} set true\n     *\n     * @param size Maximum original size. Default value is Integer.MAX_VALUE\n     * @return {@link SelectionCreator} for fluent API.\n     */\n    public SelectionCreator maxOriginalSize(int size) {\n        mSelectionSpec.originalMaxSize = size;\n        return this;\n    }\n\n    /**\n     * Capture strategy provided for the location to save photos including internal and external\n     * storage and also a authority for {@link androidx.core.content.FileProvider}.\n     *\n     * @param captureStrategy {@link CaptureStrategy}, needed only when capturing is enabled.\n     * @return {@link SelectionCreator} for fluent API.\n     */\n    public SelectionCreator captureStrategy(CaptureStrategy captureStrategy) {\n        mSelectionSpec.captureStrategy = captureStrategy;\n        return this;\n    }\n\n    /**\n     * Set the desired orientation of this activity.\n     *\n     * @param orientation An orientation constant as used in {@link ScreenOrientation}.\n     *                    Default value is {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_PORTRAIT}.\n     * @return {@link SelectionCreator} for fluent API.\n     * @see Activity#setRequestedOrientation(int)\n     */\n    public SelectionCreator restrictOrientation(@ScreenOrientation int orientation) {\n        mSelectionSpec.orientation = orientation;\n        return this;\n    }\n\n    /**\n     * Set a fixed span count for the media grid. Same for different screen orientations.\n     * <p>\n     * This will be ignored when {@link #gridExpectedSize(int)} is set.\n     *\n     * @param spanCount Requested span count.\n     * @return {@link SelectionCreator} for fluent API.\n     */\n    public SelectionCreator spanCount(int spanCount) {\n        if (spanCount < 1) throw new IllegalArgumentException(\"spanCount cannot be less than 1\");\n        mSelectionSpec.spanCount = spanCount;\n        return this;\n    }\n\n    /**\n     * Set expected size for media grid to adapt to different screen sizes. This won't necessarily\n     * be applied cause the media grid should fill the view container. The measured media grid's\n     * size will be as close to this value as possible.\n     *\n     * @param size Expected media grid size in pixel.\n     * @return {@link SelectionCreator} for fluent API.\n     */\n    public SelectionCreator gridExpectedSize(int size) {\n        mSelectionSpec.gridExpectedSize = size;\n        return this;\n    }\n\n    /**\n     * Photo thumbnail's scale compared to the View's size. It should be a float value in (0.0,\n     * 1.0].\n     *\n     * @param scale Thumbnail's scale in (0.0, 1.0]. Default value is 0.5.\n     * @return {@link SelectionCreator} for fluent API.\n     */\n    public SelectionCreator thumbnailScale(float scale) {\n        if (scale <= 0f || scale > 1f)\n            throw new IllegalArgumentException(\"Thumbnail scale must be between (0.0, 1.0]\");\n        mSelectionSpec.thumbnailScale = scale;\n        return this;\n    }\n\n    /**\n     * Provide an image engine.\n     * <p>\n     * There are two built-in image engines:\n     * 1. {@link com.zhihu.matisse.engine.impl.GlideEngine}\n     * 2. {@link com.zhihu.matisse.engine.impl.PicassoEngine}\n     * And you can implement your own image engine.\n     *\n     * @param imageEngine {@link ImageEngine}\n     * @return {@link SelectionCreator} for fluent API.\n     */\n    public SelectionCreator imageEngine(ImageEngine imageEngine) {\n        mSelectionSpec.imageEngine = imageEngine;\n        return this;\n    }\n\n    /**\n     * Set listener for callback immediately when user select or unselect something.\n     * <p>\n     * It's a redundant API with {@link Matisse#obtainResult(Intent)},\n     * we only suggest you to use this API when you need to do something immediately.\n     *\n     * @param listener {@link OnSelectedListener}\n     * @return {@link SelectionCreator} for fluent API.\n     */\n    @NonNull\n    public SelectionCreator setOnSelectedListener(@Nullable OnSelectedListener listener) {\n        mSelectionSpec.onSelectedListener = listener;\n        return this;\n    }\n\n    /**\n     * Set listener for callback immediately when user check or uncheck original.\n     *\n     * @param listener {@link OnSelectedListener}\n     * @return {@link SelectionCreator} for fluent API.\n     */\n    public SelectionCreator setOnCheckedListener(@Nullable OnCheckedListener listener) {\n        mSelectionSpec.onCheckedListener = listener;\n        return this;\n    }\n\n    /**\n     * Start to select media and wait for result.\n     *\n     * @param requestCode Identity of the request Activity or Fragment.\n     */\n    public void forResult(int requestCode) {\n        Activity activity = mMatisse.getActivity();\n        if (activity == null) {\n            return;\n        }\n\n        Intent intent = new Intent(activity, MatisseActivity.class);\n\n        Fragment fragment = mMatisse.getFragment();\n        if (fragment != null) {\n            fragment.startActivityForResult(intent, requestCode);\n        } else {\n            activity.startActivityForResult(intent, requestCode);\n        }\n    }\n\n    public SelectionCreator showPreview(boolean showPreview) {\n        mSelectionSpec.showPreview = showPreview;\n        return this;\n    }\n}\n"
  },
  {
    "path": "matisse/src/main/java/com/zhihu/matisse/engine/ImageEngine.java",
    "content": "/*\n * Copyright 2017 Zhihu Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.zhihu.matisse.engine;\n\nimport android.content.Context;\nimport android.graphics.drawable.Drawable;\nimport android.net.Uri;\nimport android.widget.ImageView;\n\n/**\n * Image loader interface. There are predefined {@link com.zhihu.matisse.engine.impl.GlideEngine}\n * and {@link com.zhihu.matisse.engine.impl.PicassoEngine}.\n */\n@SuppressWarnings(\"unused\")\npublic interface ImageEngine {\n\n    /**\n     * Load thumbnail of a static image resource.\n     *\n     * @param context     Context\n     * @param resize      Desired size of the origin image\n     * @param placeholder Placeholder drawable when image is not loaded yet\n     * @param imageView   ImageView widget\n     * @param uri         Uri of the loaded image\n     */\n    void loadThumbnail(Context context, int resize, Drawable placeholder, ImageView imageView, Uri uri);\n\n    /**\n     * Load thumbnail of a gif image resource. You don't have to load an animated gif when it's only\n     * a thumbnail tile.\n     *\n     * @param context     Context\n     * @param resize      Desired size of the origin image\n     * @param placeholder Placeholder drawable when image is not loaded yet\n     * @param imageView   ImageView widget\n     * @param uri         Uri of the loaded image\n     */\n    void loadGifThumbnail(Context context, int resize, Drawable placeholder, ImageView imageView, Uri uri);\n\n    /**\n     * Load a static image resource.\n     *\n     * @param context   Context\n     * @param resizeX   Desired x-size of the origin image\n     * @param resizeY   Desired y-size of the origin image\n     * @param imageView ImageView widget\n     * @param uri       Uri of the loaded image\n     */\n    void loadImage(Context context, int resizeX, int resizeY, ImageView imageView, Uri uri);\n\n    /**\n     * Load a gif image resource.\n     *\n     * @param context   Context\n     * @param resizeX   Desired x-size of the origin image\n     * @param resizeY   Desired y-size of the origin image\n     * @param imageView ImageView widget\n     * @param uri       Uri of the loaded image\n     */\n    void loadGifImage(Context context, int resizeX, int resizeY, ImageView imageView, Uri uri);\n\n    /**\n     * Whether this implementation supports animated gif.\n     * Just knowledge of it, convenient for users.\n     *\n     * @return true support animated gif, false do not support animated gif.\n     */\n    boolean supportAnimatedGif();\n}\n"
  },
  {
    "path": "matisse/src/main/java/com/zhihu/matisse/engine/impl/GlideEngine.java",
    "content": "/*\n * Copyright 2017 Zhihu Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.zhihu.matisse.engine.impl;\n\nimport android.content.Context;\nimport android.graphics.drawable.Drawable;\nimport android.net.Uri;\nimport android.widget.ImageView;\n\nimport com.bumptech.glide.Glide;\nimport com.bumptech.glide.Priority;\nimport com.bumptech.glide.request.RequestOptions;\nimport com.zhihu.matisse.engine.ImageEngine;\n\n/**\n * {@link ImageEngine} implementation using Glide.\n */\n\npublic class GlideEngine implements ImageEngine {\n\n    @Override\n    public void loadThumbnail(Context context, int resize, Drawable placeholder, ImageView imageView, Uri uri) {\n        Glide.with(context)\n                .asBitmap() // some .jpeg files are actually gif\n                .load(uri)\n                .apply(new RequestOptions()\n                        .override(resize, resize)\n                        .placeholder(placeholder)\n                        .centerCrop())\n                .into(imageView);\n    }\n\n    @Override\n    public void loadGifThumbnail(Context context, int resize, Drawable placeholder, ImageView imageView,\n                                 Uri uri) {\n        Glide.with(context)\n                .asBitmap() // some .jpeg files are actually gif\n                .load(uri)\n                .apply(new RequestOptions()\n                        .override(resize, resize)\n                        .placeholder(placeholder)\n                        .centerCrop())\n                .into(imageView);\n    }\n\n    @Override\n    public void loadImage(Context context, int resizeX, int resizeY, ImageView imageView, Uri uri) {\n        Glide.with(context)\n                .load(uri)\n                .apply(new RequestOptions()\n                        .override(resizeX, resizeY)\n                        .priority(Priority.HIGH)\n                        .fitCenter())\n                .into(imageView);\n    }\n\n    @Override\n    public void loadGifImage(Context context, int resizeX, int resizeY, ImageView imageView, Uri uri) {\n        Glide.with(context)\n                .asGif()\n                .load(uri)\n                .apply(new RequestOptions()\n                        .override(resizeX, resizeY)\n                        .priority(Priority.HIGH)\n                        .fitCenter())\n                .into(imageView);\n    }\n\n    @Override\n    public boolean supportAnimatedGif() {\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "matisse/src/main/java/com/zhihu/matisse/engine/impl/PicassoEngine.java",
    "content": "/*\n * Copyright 2017 Zhihu Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.zhihu.matisse.engine.impl;\n\nimport android.content.Context;\nimport android.graphics.drawable.Drawable;\nimport android.net.Uri;\nimport android.widget.ImageView;\n\nimport com.squareup.picasso.Picasso;\nimport com.zhihu.matisse.engine.ImageEngine;\n\n/**\n * {@link ImageEngine} implementation using Picasso.\n */\n\npublic class PicassoEngine implements ImageEngine {\n\n    @Override\n    public void loadThumbnail(Context context, int resize, Drawable placeholder, ImageView imageView, Uri uri) {\n        Picasso.with(context).load(uri).placeholder(placeholder)\n                .resize(resize, resize)\n                .centerCrop()\n                .into(imageView);\n    }\n\n    @Override\n    public void loadGifThumbnail(Context context, int resize, Drawable placeholder, ImageView imageView,\n                                 Uri uri) {\n        loadThumbnail(context, resize, placeholder, imageView, uri);\n    }\n\n    @Override\n    public void loadImage(Context context, int resizeX, int resizeY, ImageView imageView, Uri uri) {\n        Picasso.with(context).load(uri).resize(resizeX, resizeY).priority(Picasso.Priority.HIGH)\n                .centerInside().into(imageView);\n    }\n\n    @Override\n    public void loadGifImage(Context context, int resizeX, int resizeY, ImageView imageView, Uri uri) {\n        loadImage(context, resizeX, resizeY, imageView, uri);\n    }\n\n    @Override\n    public boolean supportAnimatedGif() {\n        return false;\n    }\n}\n"
  },
  {
    "path": "matisse/src/main/java/com/zhihu/matisse/filter/Filter.java",
    "content": "/*\n * Copyright 2017 Zhihu Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.zhihu.matisse.filter;\n\nimport android.content.Context;\n\nimport com.zhihu.matisse.MimeType;\nimport com.zhihu.matisse.SelectionCreator;\nimport com.zhihu.matisse.internal.entity.Item;\nimport com.zhihu.matisse.internal.entity.IncapableCause;\n\nimport java.util.Set;\n\n/**\n * Filter for choosing a {@link Item}. You can add multiple Filters through\n * {@link SelectionCreator#addFilter(Filter)}.\n */\n@SuppressWarnings(\"unused\")\npublic abstract class Filter {\n    /**\n     * Convenient constant for a minimum value.\n     */\n    public static final int MIN = 0;\n    /**\n     * Convenient constant for a maximum value.\n     */\n    public static final int MAX = Integer.MAX_VALUE;\n    /**\n     * Convenient constant for 1024.\n     */\n    public static final int K = 1024;\n\n    /**\n     * Against what mime types this filter applies.\n     */\n    protected abstract Set<MimeType> constraintTypes();\n\n    /**\n     * Invoked for filtering each item.\n     *\n     * @return null if selectable, {@link IncapableCause} if not selectable.\n     */\n    public abstract IncapableCause filter(Context context, Item item);\n\n    /**\n     * Whether an {@link Item} need filtering.\n     */\n    protected boolean needFiltering(Context context, Item item) {\n        for (MimeType type : constraintTypes()) {\n            if (type.checkType(context.getContentResolver(), item.getContentUri())) {\n                return true;\n            }\n        }\n        return false;\n    }\n}\n"
  },
  {
    "path": "matisse/src/main/java/com/zhihu/matisse/internal/entity/Album.java",
    "content": "/*\n * Copyright (C) 2014 nohana, Inc.\n * Copyright 2017 Zhihu Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an &quot;AS IS&quot; BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.zhihu.matisse.internal.entity;\n\nimport android.content.Context;\nimport android.database.Cursor;\nimport android.net.Uri;\nimport android.os.Parcel;\nimport android.os.Parcelable;\n\nimport androidx.annotation.Nullable;\n\nimport com.zhihu.matisse.R;\nimport com.zhihu.matisse.internal.loader.AlbumLoader;\n\npublic class Album implements Parcelable {\n    public static final Creator<Album> CREATOR = new Creator<Album>() {\n        @Nullable\n        @Override\n        public Album createFromParcel(Parcel source) {\n            return new Album(source);\n        }\n\n        @Override\n        public Album[] newArray(int size) {\n            return new Album[size];\n        }\n    };\n    public static final String ALBUM_ID_ALL = String.valueOf(-1);\n    public static final String ALBUM_NAME_ALL = \"All\";\n\n    private final String mId;\n    private final Uri mCoverUri;\n    private final String mDisplayName;\n    private long mCount;\n\n    public Album(String id, Uri coverUri, String albumName, long count) {\n        mId = id;\n        mCoverUri = coverUri;\n        mDisplayName = albumName;\n        mCount = count;\n    }\n\n    private Album(Parcel source) {\n        mId = source.readString();\n        mCoverUri = source.readParcelable(Uri.class.getClassLoader());\n        mDisplayName = source.readString();\n        mCount = source.readLong();\n    }\n\n    /**\n     * Constructs a new {@link Album} entity from the {@link Cursor}.\n     * This method is not responsible for managing cursor resource, such as close, iterate, and so on.\n     */\n    public static Album valueOf(Cursor cursor) {\n        String clumn = cursor.getString(cursor.getColumnIndex(AlbumLoader.COLUMN_URI));\n        return new Album(\n                cursor.getString(cursor.getColumnIndex(\"bucket_id\")),\n                Uri.parse(clumn != null ? clumn : \"\"),\n                cursor.getString(cursor.getColumnIndex(\"bucket_display_name\")),\n                cursor.getLong(cursor.getColumnIndex(AlbumLoader.COLUMN_COUNT)));\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.writeString(mId);\n        dest.writeParcelable(mCoverUri, 0);\n        dest.writeString(mDisplayName);\n        dest.writeLong(mCount);\n    }\n\n    public String getId() {\n        return mId;\n    }\n\n    public Uri getCoverUri() {\n        return mCoverUri;\n    }\n\n    public long getCount() {\n        return mCount;\n    }\n\n    public void addCaptureCount() {\n        mCount++;\n    }\n\n    public String getDisplayName(Context context) {\n        if (isAll()) {\n            return context.getString(R.string.album_name_all);\n        }\n        return mDisplayName;\n    }\n\n    public boolean isAll() {\n        return ALBUM_ID_ALL.equals(mId);\n    }\n\n    public boolean isEmpty() {\n        return mCount == 0;\n    }\n\n}"
  },
  {
    "path": "matisse/src/main/java/com/zhihu/matisse/internal/entity/CaptureStrategy.java",
    "content": "/*\n * Copyright 2017 Zhihu Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.zhihu.matisse.internal.entity;\n\npublic class CaptureStrategy {\n\n    public final boolean isPublic;\n    public final String authority;\n    public final String directory;\n\n    public CaptureStrategy(boolean isPublic, String authority) {\n        this(isPublic, authority, null);\n    }\n\n    public CaptureStrategy(boolean isPublic, String authority, String directory) {\n        this.isPublic = isPublic;\n        this.authority = authority;\n        this.directory = directory;\n    }\n}\n"
  },
  {
    "path": "matisse/src/main/java/com/zhihu/matisse/internal/entity/IncapableCause.java",
    "content": "/*\n * Copyright 2017 Zhihu Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an &quot;AS IS&quot; BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.zhihu.matisse.internal.entity;\n\nimport android.content.Context;\nimport androidx.annotation.IntDef;\nimport androidx.fragment.app.FragmentActivity;\nimport android.widget.Toast;\n\nimport com.zhihu.matisse.internal.ui.widget.IncapableDialog;\n\nimport java.lang.annotation.Retention;\n\nimport static java.lang.annotation.RetentionPolicy.SOURCE;\n\n@SuppressWarnings(\"unused\")\npublic class IncapableCause {\n    public static final int TOAST = 0x00;\n    public static final int DIALOG = 0x01;\n    public static final int NONE = 0x02;\n\n    @Retention(SOURCE)\n    @IntDef({TOAST, DIALOG, NONE})\n    public @interface Form {\n    }\n\n    private int mForm = TOAST;\n    private String mTitle;\n    private String mMessage;\n\n    public IncapableCause(String message) {\n        mMessage = message;\n    }\n\n    public IncapableCause(String title, String message) {\n        mTitle = title;\n        mMessage = message;\n    }\n\n    public IncapableCause(@Form int form, String message) {\n        mForm = form;\n        mMessage = message;\n    }\n\n    public IncapableCause(@Form int form, String title, String message) {\n        mForm = form;\n        mTitle = title;\n        mMessage = message;\n    }\n\n    public static void handleCause(Context context, IncapableCause cause) {\n        if (cause == null)\n            return;\n\n        switch (cause.mForm) {\n            case NONE:\n                // do nothing.\n                break;\n            case DIALOG:\n                IncapableDialog incapableDialog = IncapableDialog.newInstance(cause.mTitle, cause.mMessage);\n                incapableDialog.show(((FragmentActivity) context).getSupportFragmentManager(),\n                        IncapableDialog.class.getName());\n                break;\n            case TOAST:\n            default:\n                Toast.makeText(context, cause.mMessage, Toast.LENGTH_SHORT).show();\n                break;\n        }\n    }\n}\n"
  },
  {
    "path": "matisse/src/main/java/com/zhihu/matisse/internal/entity/Item.java",
    "content": "/*\n * Copyright (C) 2014 nohana, Inc.\n * Copyright 2017 Zhihu Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an &quot;AS IS&quot; BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.zhihu.matisse.internal.entity;\n\nimport android.content.ContentUris;\nimport android.database.Cursor;\nimport android.net.Uri;\nimport android.os.Parcel;\nimport android.os.Parcelable;\nimport android.provider.MediaStore;\nimport androidx.annotation.Nullable;\n\nimport com.zhihu.matisse.MimeType;\n\npublic class Item implements Parcelable {\n    public static final Creator<Item> CREATOR = new Creator<Item>() {\n        @Override\n        @Nullable\n        public Item createFromParcel(Parcel source) {\n            return new Item(source);\n        }\n\n        @Override\n        public Item[] newArray(int size) {\n            return new Item[size];\n        }\n    };\n    public static final long ITEM_ID_CAPTURE = -1;\n    public static final String ITEM_DISPLAY_NAME_CAPTURE = \"Capture\";\n    public final long id;\n    public final String mimeType;\n    public final Uri uri;\n    public final long size;\n    public final long duration; // only for video, in ms\n\n    private Item(long id, String mimeType, long size, long duration) {\n        this.id = id;\n        this.mimeType = mimeType;\n        Uri contentUri;\n        if (isImage()) {\n            contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;\n        } else if (isVideo()) {\n            contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;\n        } else {\n            // ?\n            contentUri = MediaStore.Files.getContentUri(\"external\");\n        }\n        this.uri = ContentUris.withAppendedId(contentUri, id);\n        this.size = size;\n        this.duration = duration;\n    }\n\n    private Item(Parcel source) {\n        id = source.readLong();\n        mimeType = source.readString();\n        uri = source.readParcelable(Uri.class.getClassLoader());\n        size = source.readLong();\n        duration = source.readLong();\n    }\n\n    public static Item valueOf(Cursor cursor) {\n        return new Item(cursor.getLong(cursor.getColumnIndex(MediaStore.Files.FileColumns._ID)),\n                cursor.getString(cursor.getColumnIndex(MediaStore.MediaColumns.MIME_TYPE)),\n                cursor.getLong(cursor.getColumnIndex(MediaStore.MediaColumns.SIZE)),\n                cursor.getLong(cursor.getColumnIndex(\"duration\")));\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.writeLong(id);\n        dest.writeString(mimeType);\n        dest.writeParcelable(uri, 0);\n        dest.writeLong(size);\n        dest.writeLong(duration);\n    }\n\n    public Uri getContentUri() {\n        return uri;\n    }\n\n    public boolean isCapture() {\n        return id == ITEM_ID_CAPTURE;\n    }\n\n    public boolean isImage() {\n        return MimeType.isImage(mimeType);\n    }\n\n    public boolean isGif() {\n        return MimeType.isGif(mimeType);\n    }\n\n    public boolean isVideo() {\n        return MimeType.isVideo(mimeType);\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (!(obj instanceof Item)) {\n            return false;\n        }\n\n        Item other = (Item) obj;\n        return id == other.id\n                && (mimeType != null && mimeType.equals(other.mimeType)\n                    || (mimeType == null && other.mimeType == null))\n                && (uri != null && uri.equals(other.uri)\n                    || (uri == null && other.uri == null))\n                && size == other.size\n                && duration == other.duration;\n    }\n\n    @Override\n    public int hashCode() {\n        int result = 1;\n        result = 31 * result + Long.valueOf(id).hashCode();\n        if (mimeType != null) {\n            result = 31 * result + mimeType.hashCode();\n        }\n        result = 31 * result + uri.hashCode();\n        result = 31 * result + Long.valueOf(size).hashCode();\n        result = 31 * result + Long.valueOf(duration).hashCode();\n        return result;\n    }\n}\n"
  },
  {
    "path": "matisse/src/main/java/com/zhihu/matisse/internal/entity/SelectionSpec.java",
    "content": "/*\n * Copyright (C) 2014 nohana, Inc.\n * Copyright 2017 Zhihu Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an &quot;AS IS&quot; BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.zhihu.matisse.internal.entity;\n\nimport android.content.pm.ActivityInfo;\n\nimport androidx.annotation.StyleRes;\n\nimport com.zhihu.matisse.MimeType;\nimport com.zhihu.matisse.R;\nimport com.zhihu.matisse.engine.ImageEngine;\nimport com.zhihu.matisse.engine.impl.GlideEngine;\nimport com.zhihu.matisse.filter.Filter;\nimport com.zhihu.matisse.listener.OnCheckedListener;\nimport com.zhihu.matisse.listener.OnSelectedListener;\n\nimport java.util.List;\nimport java.util.Set;\n\npublic final class SelectionSpec {\n\n    public Set<MimeType> mimeTypeSet;\n    public boolean mediaTypeExclusive;\n    public boolean showSingleMediaType;\n    @StyleRes\n    public int themeId;\n    public int orientation;\n    public boolean countable;\n    public int maxSelectable;\n    public int maxImageSelectable;\n    public int maxVideoSelectable;\n    public List<Filter> filters;\n    public boolean capture;\n    public CaptureStrategy captureStrategy;\n    public int spanCount;\n    public int gridExpectedSize;\n    public float thumbnailScale;\n    public ImageEngine imageEngine;\n    public boolean hasInited;\n    public OnSelectedListener onSelectedListener;\n    public boolean originalable;\n    public boolean autoHideToobar;\n    public int originalMaxSize;\n    public OnCheckedListener onCheckedListener;\n    public boolean showPreview;\n\n    private SelectionSpec() {\n    }\n\n    public static SelectionSpec getInstance() {\n        return InstanceHolder.INSTANCE;\n    }\n\n    public static SelectionSpec getCleanInstance() {\n        SelectionSpec selectionSpec = getInstance();\n        selectionSpec.reset();\n        return selectionSpec;\n    }\n\n    private void reset() {\n        mimeTypeSet = null;\n        mediaTypeExclusive = true;\n        showSingleMediaType = false;\n        themeId = R.style.Matisse_Zhihu;\n        orientation = 0;\n        countable = false;\n        maxSelectable = 1;\n        maxImageSelectable = 0;\n        maxVideoSelectable = 0;\n        filters = null;\n        capture = false;\n        captureStrategy = null;\n        spanCount = 3;\n        gridExpectedSize = 0;\n        thumbnailScale = 0.5f;\n        imageEngine = new GlideEngine();\n        hasInited = true;\n        originalable = false;\n        autoHideToobar = false;\n        originalMaxSize = Integer.MAX_VALUE;\n        showPreview = true;\n    }\n\n    public boolean singleSelectionModeEnabled() {\n        return !countable && (maxSelectable == 1 || (maxImageSelectable == 1 && maxVideoSelectable == 1));\n    }\n\n    public boolean needOrientationRestriction() {\n        return orientation != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;\n    }\n\n    public boolean onlyShowImages() {\n        return showSingleMediaType && MimeType.ofImage().containsAll(mimeTypeSet);\n    }\n\n    public boolean onlyShowVideos() {\n        return showSingleMediaType && MimeType.ofVideo().containsAll(mimeTypeSet);\n    }\n\n    public boolean onlyShowGif() {\n        return showSingleMediaType && MimeType.ofGif().equals(mimeTypeSet);\n    }\n\n    private static final class InstanceHolder {\n        private static final SelectionSpec INSTANCE = new SelectionSpec();\n    }\n}\n"
  },
  {
    "path": "matisse/src/main/java/com/zhihu/matisse/internal/loader/AlbumLoader.java",
    "content": "/*\n * Copyright (C) 2014 nohana, Inc.\n * Copyright 2017 Zhihu Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an &quot;AS IS&quot; BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.zhihu.matisse.internal.loader;\n\nimport android.content.ContentUris;\nimport android.content.Context;\nimport android.database.Cursor;\nimport android.database.MatrixCursor;\nimport android.database.MergeCursor;\nimport android.net.Uri;\nimport android.os.Build;\nimport android.provider.MediaStore;\n\nimport androidx.loader.content.CursorLoader;\n\nimport com.zhihu.matisse.MimeType;\nimport com.zhihu.matisse.internal.entity.Album;\nimport com.zhihu.matisse.internal.entity.SelectionSpec;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\n/**\n * Load all albums (grouped by bucket_id) into a single cursor.\n */\npublic class AlbumLoader extends CursorLoader {\n\n    private static final String COLUMN_BUCKET_ID = \"bucket_id\";\n    private static final String COLUMN_BUCKET_DISPLAY_NAME = \"bucket_display_name\";\n    public static final String COLUMN_URI = \"uri\";\n    public static final String COLUMN_COUNT = \"count\";\n    private static final Uri QUERY_URI = MediaStore.Files.getContentUri(\"external\");\n\n    private static final String[] COLUMNS = {\n            MediaStore.Files.FileColumns._ID,\n            COLUMN_BUCKET_ID,\n            COLUMN_BUCKET_DISPLAY_NAME,\n            MediaStore.MediaColumns.MIME_TYPE,\n            COLUMN_URI,\n            COLUMN_COUNT};\n\n    private static final String[] PROJECTION = {\n            MediaStore.Files.FileColumns._ID,\n            COLUMN_BUCKET_ID,\n            COLUMN_BUCKET_DISPLAY_NAME,\n            MediaStore.MediaColumns.MIME_TYPE,\n            \"COUNT(*) AS \" + COLUMN_COUNT};\n\n    private static final String[] PROJECTION_29 = {\n            MediaStore.Files.FileColumns._ID,\n            COLUMN_BUCKET_ID,\n            COLUMN_BUCKET_DISPLAY_NAME,\n            MediaStore.MediaColumns.MIME_TYPE};\n\n    // === params for showSingleMediaType: false ===\n    private static final String SELECTION =\n            \"(\" + MediaStore.Files.FileColumns.MEDIA_TYPE + \"=?\"\n                    + \" OR \"\n                    + MediaStore.Files.FileColumns.MEDIA_TYPE + \"=?)\"\n                    + \" AND \" + MediaStore.MediaColumns.SIZE + \">0\"\n                    + \") GROUP BY (bucket_id\";\n    private static final String SELECTION_29 =\n            \"(\" + MediaStore.Files.FileColumns.MEDIA_TYPE + \"=?\"\n                    + \" OR \"\n                    + MediaStore.Files.FileColumns.MEDIA_TYPE + \"=?)\"\n                    + \" AND \" + MediaStore.MediaColumns.SIZE + \">0\";\n    private static final String[] SELECTION_ARGS = {\n            String.valueOf(MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE),\n            String.valueOf(MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO),\n    };\n    // =============================================\n\n    // === params for showSingleMediaType: true ===\n    private static final String SELECTION_FOR_SINGLE_MEDIA_TYPE =\n            MediaStore.Files.FileColumns.MEDIA_TYPE + \"=?\"\n                    + \" AND \" + MediaStore.MediaColumns.SIZE + \">0\"\n                    + \") GROUP BY (bucket_id\";\n    private static final String SELECTION_FOR_SINGLE_MEDIA_TYPE_29 =\n            MediaStore.Files.FileColumns.MEDIA_TYPE + \"=?\"\n                    + \" AND \" + MediaStore.MediaColumns.SIZE + \">0\";\n\n    private static String[] getSelectionArgsForSingleMediaType(int mediaType) {\n        return new String[]{String.valueOf(mediaType)};\n    }\n    // =============================================\n\n    // === params for showSingleMediaType: true ===\n    private static final String SELECTION_FOR_SINGLE_MEDIA_GIF_TYPE =\n            MediaStore.Files.FileColumns.MEDIA_TYPE + \"=?\"\n                    + \" AND \" + MediaStore.MediaColumns.SIZE + \">0\"\n                    + \" AND \" + MediaStore.MediaColumns.MIME_TYPE + \"=?\"\n                    + \") GROUP BY (bucket_id\";\n    private static final String SELECTION_FOR_SINGLE_MEDIA_GIF_TYPE_29 =\n            MediaStore.Files.FileColumns.MEDIA_TYPE + \"=?\"\n                    + \" AND \" + MediaStore.MediaColumns.SIZE + \">0\"\n                    + \" AND \" + MediaStore.MediaColumns.MIME_TYPE + \"=?\";\n\n    private static String[] getSelectionArgsForSingleMediaGifType(int mediaType) {\n        return new String[]{String.valueOf(mediaType), \"image/gif\"};\n    }\n    // =============================================\n\n    private static final String BUCKET_ORDER_BY = \"datetaken DESC\";\n\n    private AlbumLoader(Context context, String selection, String[] selectionArgs) {\n        super(\n                context,\n                QUERY_URI,\n                beforeAndroidTen() ? PROJECTION : PROJECTION_29,\n                selection,\n                selectionArgs,\n                BUCKET_ORDER_BY\n        );\n    }\n\n    public static CursorLoader newInstance(Context context) {\n        String selection;\n        String[] selectionArgs;\n        if (SelectionSpec.getInstance().onlyShowGif()) {\n            selection = beforeAndroidTen()\n                    ? SELECTION_FOR_SINGLE_MEDIA_GIF_TYPE : SELECTION_FOR_SINGLE_MEDIA_GIF_TYPE_29;\n            selectionArgs = getSelectionArgsForSingleMediaGifType(\n                    MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE);\n        } else if (SelectionSpec.getInstance().onlyShowImages()) {\n            selection = beforeAndroidTen()\n                    ? SELECTION_FOR_SINGLE_MEDIA_TYPE : SELECTION_FOR_SINGLE_MEDIA_TYPE_29;\n            selectionArgs = getSelectionArgsForSingleMediaType(\n                    MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE);\n        } else if (SelectionSpec.getInstance().onlyShowVideos()) {\n            selection = beforeAndroidTen()\n                    ? SELECTION_FOR_SINGLE_MEDIA_TYPE : SELECTION_FOR_SINGLE_MEDIA_TYPE_29;\n            selectionArgs = getSelectionArgsForSingleMediaType(\n                    MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO);\n        } else {\n            selection = beforeAndroidTen() ? SELECTION : SELECTION_29;\n            selectionArgs = SELECTION_ARGS;\n        }\n        return new AlbumLoader(context, selection, selectionArgs);\n    }\n\n    @Override\n    public Cursor loadInBackground() {\n        Cursor albums = super.loadInBackground();\n        MatrixCursor allAlbum = new MatrixCursor(COLUMNS);\n\n        if (beforeAndroidTen()) {\n            int totalCount = 0;\n            Uri allAlbumCoverUri = null;\n            MatrixCursor otherAlbums = new MatrixCursor(COLUMNS);\n            if (albums != null) {\n                while (albums.moveToNext()) {\n                    long fileId = albums.getLong(\n                            albums.getColumnIndex(MediaStore.Files.FileColumns._ID));\n                    long bucketId = albums.getLong(\n                            albums.getColumnIndex(COLUMN_BUCKET_ID));\n                    String bucketDisplayName = albums.getString(\n                            albums.getColumnIndex(COLUMN_BUCKET_DISPLAY_NAME));\n                    String mimeType = albums.getString(\n                            albums.getColumnIndex(MediaStore.MediaColumns.MIME_TYPE));\n                    Uri uri = getUri(albums);\n                    int count = albums.getInt(albums.getColumnIndex(COLUMN_COUNT));\n\n                    otherAlbums.addRow(new String[]{\n                            Long.toString(fileId),\n                            Long.toString(bucketId), bucketDisplayName, mimeType, uri.toString(),\n                            String.valueOf(count)});\n                    totalCount += count;\n                }\n                if (albums.moveToFirst()) {\n                    allAlbumCoverUri = getUri(albums);\n                }\n            }\n\n            allAlbum.addRow(new String[]{\n                    Album.ALBUM_ID_ALL, Album.ALBUM_ID_ALL, Album.ALBUM_NAME_ALL, null,\n                    allAlbumCoverUri == null ? null : allAlbumCoverUri.toString(),\n                    String.valueOf(totalCount)});\n\n            return new MergeCursor(new Cursor[]{allAlbum, otherAlbums});\n        } else {\n            int totalCount = 0;\n            Uri allAlbumCoverUri = null;\n\n            // Pseudo GROUP BY\n            Map<Long, Long> countMap = new HashMap<>();\n            if (albums != null) {\n                while (albums.moveToNext()) {\n                    long bucketId = albums.getLong(albums.getColumnIndex(COLUMN_BUCKET_ID));\n\n                    Long count = countMap.get(bucketId);\n                    if (count == null) {\n                        count = 1L;\n                    } else {\n                        count++;\n                    }\n                    countMap.put(bucketId, count);\n                }\n            }\n\n            MatrixCursor otherAlbums = new MatrixCursor(COLUMNS);\n            if (albums != null) {\n                if (albums.moveToFirst()) {\n                    allAlbumCoverUri = getUri(albums);\n\n                    Set<Long> done = new HashSet<>();\n\n                    do {\n                        long bucketId = albums.getLong(albums.getColumnIndex(COLUMN_BUCKET_ID));\n\n                        if (done.contains(bucketId)) {\n                            continue;\n                        }\n\n                        long fileId = albums.getLong(\n                                albums.getColumnIndex(MediaStore.Files.FileColumns._ID));\n                        String bucketDisplayName = albums.getString(\n                                albums.getColumnIndex(COLUMN_BUCKET_DISPLAY_NAME));\n                        String mimeType = albums.getString(\n                                albums.getColumnIndex(MediaStore.MediaColumns.MIME_TYPE));\n                        Uri uri = getUri(albums);\n                        long count = countMap.get(bucketId);\n\n                        otherAlbums.addRow(new String[]{\n                                Long.toString(fileId),\n                                Long.toString(bucketId),\n                                bucketDisplayName,\n                                mimeType,\n                                uri.toString(),\n                                String.valueOf(count)});\n                        done.add(bucketId);\n\n                        totalCount += count;\n                    } while (albums.moveToNext());\n                }\n            }\n\n            allAlbum.addRow(new String[]{\n                    Album.ALBUM_ID_ALL,\n                    Album.ALBUM_ID_ALL, Album.ALBUM_NAME_ALL, null,\n                    allAlbumCoverUri == null ? null : allAlbumCoverUri.toString(),\n                    String.valueOf(totalCount)});\n\n            return new MergeCursor(new Cursor[]{allAlbum, otherAlbums});\n        }\n    }\n\n    private static Uri getUri(Cursor cursor) {\n        long id = cursor.getLong(cursor.getColumnIndex(MediaStore.Files.FileColumns._ID));\n        String mimeType = cursor.getString(\n                cursor.getColumnIndex(MediaStore.MediaColumns.MIME_TYPE));\n        Uri contentUri;\n\n        if (MimeType.isImage(mimeType)) {\n            contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;\n        } else if (MimeType.isVideo(mimeType)) {\n            contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;\n        } else {\n            // ?\n            contentUri = MediaStore.Files.getContentUri(\"external\");\n        }\n\n        Uri uri = ContentUris.withAppendedId(contentUri, id);\n        return uri;\n    }\n\n    @Override\n    public void onContentChanged() {\n        // FIXME a dirty way to fix loading multiple times\n    }\n\n    /**\n     * @return 是否是 Android 10 （Q） 之前的版本\n     */\n    private static boolean beforeAndroidTen() {\n        return android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.Q;\n    }\n}"
  },
  {
    "path": "matisse/src/main/java/com/zhihu/matisse/internal/loader/AlbumMediaLoader.java",
    "content": "/*\n * Copyright (C) 2014 nohana, Inc.\n * Copyright 2017 Zhihu Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an &quot;AS IS&quot; BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.zhihu.matisse.internal.loader;\n\nimport android.content.Context;\nimport android.database.Cursor;\nimport android.database.MatrixCursor;\nimport android.database.MergeCursor;\nimport android.net.Uri;\nimport android.provider.MediaStore;\n\nimport androidx.loader.content.CursorLoader;\n\nimport com.zhihu.matisse.internal.entity.Album;\nimport com.zhihu.matisse.internal.entity.Item;\nimport com.zhihu.matisse.internal.entity.SelectionSpec;\nimport com.zhihu.matisse.internal.utils.MediaStoreCompat;\n\n/**\n * Load images and videos into a single cursor.\n */\npublic class AlbumMediaLoader extends CursorLoader {\n    private static final Uri QUERY_URI = MediaStore.Files.getContentUri(\"external\");\n    private static final String[] PROJECTION = {\n            MediaStore.Files.FileColumns._ID,\n            MediaStore.MediaColumns.DISPLAY_NAME,\n            MediaStore.MediaColumns.MIME_TYPE,\n            MediaStore.MediaColumns.SIZE,\n            \"duration\"};\n\n    // === params for album ALL && showSingleMediaType: false ===\n    private static final String SELECTION_ALL =\n            \"(\" + MediaStore.Files.FileColumns.MEDIA_TYPE + \"=?\"\n                    + \" OR \"\n                    + MediaStore.Files.FileColumns.MEDIA_TYPE + \"=?)\"\n                    + \" AND \" + MediaStore.MediaColumns.SIZE + \">0\";\n    private static final String[] SELECTION_ALL_ARGS = {\n            String.valueOf(MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE),\n            String.valueOf(MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO),\n    };\n    // ===========================================================\n\n    // === params for album ALL && showSingleMediaType: true ===\n    private static final String SELECTION_ALL_FOR_SINGLE_MEDIA_TYPE =\n            MediaStore.Files.FileColumns.MEDIA_TYPE + \"=?\"\n                    + \" AND \" + MediaStore.MediaColumns.SIZE + \">0\";\n\n    private static String[] getSelectionArgsForSingleMediaType(int mediaType) {\n        return new String[]{String.valueOf(mediaType)};\n    }\n    // =========================================================\n\n    // === params for ordinary album && showSingleMediaType: false ===\n    private static final String SELECTION_ALBUM =\n            \"(\" + MediaStore.Files.FileColumns.MEDIA_TYPE + \"=?\"\n                    + \" OR \"\n                    + MediaStore.Files.FileColumns.MEDIA_TYPE + \"=?)\"\n                    + \" AND \"\n                    + \" bucket_id=?\"\n                    + \" AND \" + MediaStore.MediaColumns.SIZE + \">0\";\n\n    private static String[] getSelectionAlbumArgs(String albumId) {\n        return new String[]{\n                String.valueOf(MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE),\n                String.valueOf(MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO),\n                albumId\n        };\n    }\n    // ===============================================================\n\n    // === params for ordinary album && showSingleMediaType: true ===\n    private static final String SELECTION_ALBUM_FOR_SINGLE_MEDIA_TYPE =\n            MediaStore.Files.FileColumns.MEDIA_TYPE + \"=?\"\n                    + \" AND \"\n                    + \" bucket_id=?\"\n                    + \" AND \" + MediaStore.MediaColumns.SIZE + \">0\";\n\n    private static String[] getSelectionAlbumArgsForSingleMediaType(int mediaType, String albumId) {\n        return new String[]{String.valueOf(mediaType), albumId};\n    }\n    // ===============================================================\n\n    // === params for album ALL && showSingleMediaType: true && MineType==\"image/gif\"\n    private static final String SELECTION_ALL_FOR_GIF =\n            MediaStore.Files.FileColumns.MEDIA_TYPE + \"=?\"\n                    + \" AND \"\n                    + MediaStore.MediaColumns.MIME_TYPE + \"=?\"\n                    + \" AND \" + MediaStore.MediaColumns.SIZE + \">0\";\n\n    private static String[] getSelectionArgsForGifType(int mediaType) {\n        return new String[]{String.valueOf(mediaType), \"image/gif\"};\n    }\n    // ===============================================================\n\n    // === params for ordinary album && showSingleMediaType: true  && MineType==\"image/gif\" ===\n    private static final String SELECTION_ALBUM_FOR_GIF =\n            MediaStore.Files.FileColumns.MEDIA_TYPE + \"=?\"\n                    + \" AND \"\n                    + \" bucket_id=?\"\n                    + \" AND \"\n                    + MediaStore.MediaColumns.MIME_TYPE + \"=?\"\n                    + \" AND \" + MediaStore.MediaColumns.SIZE + \">0\";\n\n    private static String[] getSelectionAlbumArgsForGifType(int mediaType, String albumId) {\n        return new String[]{String.valueOf(mediaType), albumId, \"image/gif\"};\n    }\n    // ===============================================================\n\n    private static final String ORDER_BY = MediaStore.Images.Media.DATE_TAKEN + \" DESC\";\n    private final boolean mEnableCapture;\n\n    private AlbumMediaLoader(Context context, String selection, String[] selectionArgs, boolean capture) {\n        super(context, QUERY_URI, PROJECTION, selection, selectionArgs, ORDER_BY);\n        mEnableCapture = capture;\n    }\n\n    public static CursorLoader newInstance(Context context, Album album, boolean capture) {\n        String selection;\n        String[] selectionArgs;\n        boolean enableCapture;\n\n        if (album.isAll()) {\n            if (SelectionSpec.getInstance().onlyShowGif()) {\n                selection = SELECTION_ALL_FOR_GIF;\n                selectionArgs = getSelectionArgsForGifType(\n                        MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE);\n            } else if (SelectionSpec.getInstance().onlyShowImages()) {\n                selection = SELECTION_ALL_FOR_SINGLE_MEDIA_TYPE;\n                selectionArgs =\n                        getSelectionArgsForSingleMediaType(\n                                MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE);\n            } else if (SelectionSpec.getInstance().onlyShowVideos()) {\n                selection = SELECTION_ALL_FOR_SINGLE_MEDIA_TYPE;\n                selectionArgs =\n                        getSelectionArgsForSingleMediaType(\n                                MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO);\n            } else {\n                selection = SELECTION_ALL;\n                selectionArgs = SELECTION_ALL_ARGS;\n            }\n            enableCapture = capture;\n        } else {\n            if (SelectionSpec.getInstance().onlyShowGif()) {\n                selection = SELECTION_ALBUM_FOR_GIF;\n                selectionArgs =\n                        getSelectionAlbumArgsForGifType(\n                                MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE, album.getId());\n            } else if (SelectionSpec.getInstance().onlyShowImages()) {\n                selection = SELECTION_ALBUM_FOR_SINGLE_MEDIA_TYPE;\n                selectionArgs =\n                        getSelectionAlbumArgsForSingleMediaType(\n                                MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE,\n                                album.getId());\n            } else if (SelectionSpec.getInstance().onlyShowVideos()) {\n                selection = SELECTION_ALBUM_FOR_SINGLE_MEDIA_TYPE;\n                selectionArgs = getSelectionAlbumArgsForSingleMediaType(\n                        MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO,\n                        album.getId());\n            } else {\n                selection = SELECTION_ALBUM;\n                selectionArgs = getSelectionAlbumArgs(album.getId());\n            }\n            enableCapture = false;\n        }\n        return new AlbumMediaLoader(context, selection, selectionArgs, enableCapture);\n    }\n\n    @Override\n    public Cursor loadInBackground() {\n        Cursor result = super.loadInBackground();\n        if (!mEnableCapture || !MediaStoreCompat.hasCameraFeature(getContext())) {\n            return result;\n        }\n        MatrixCursor dummy = new MatrixCursor(PROJECTION);\n        dummy.addRow(new Object[]{Item.ITEM_ID_CAPTURE, Item.ITEM_DISPLAY_NAME_CAPTURE, \"\", 0, 0});\n        return new MergeCursor(new Cursor[]{dummy, result});\n    }\n\n    @Override\n    public void onContentChanged() {\n        // FIXME a dirty way to fix loading multiple times\n    }\n}\n"
  },
  {
    "path": "matisse/src/main/java/com/zhihu/matisse/internal/model/AlbumCollection.java",
    "content": "/*\n * Copyright (C) 2014 nohana, Inc.\n * Copyright 2017 Zhihu Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an &quot;AS IS&quot; BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.zhihu.matisse.internal.model;\n\nimport android.content.Context;\nimport android.database.Cursor;\nimport android.os.Bundle;\nimport androidx.fragment.app.FragmentActivity;\nimport androidx.loader.app.LoaderManager;\nimport androidx.loader.content.Loader;\n\nimport com.zhihu.matisse.internal.loader.AlbumLoader;\n\nimport java.lang.ref.WeakReference;\n\npublic class AlbumCollection implements LoaderManager.LoaderCallbacks<Cursor> {\n    private static final int LOADER_ID = 1;\n    private static final String STATE_CURRENT_SELECTION = \"state_current_selection\";\n    private WeakReference<Context> mContext;\n    private LoaderManager mLoaderManager;\n    private AlbumCallbacks mCallbacks;\n    private int mCurrentSelection;\n    private boolean mLoadFinished;\n\n    @Override\n    public Loader<Cursor> onCreateLoader(int id, Bundle args) {\n        Context context = mContext.get();\n        if (context == null) {\n            return null;\n        }\n        mLoadFinished = false;\n        return AlbumLoader.newInstance(context);\n    }\n\n    @Override\n    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {\n        Context context = mContext.get();\n        if (context == null) {\n            return;\n        }\n\n        if (!mLoadFinished) {\n            mLoadFinished = true;\n            mCallbacks.onAlbumLoad(data);\n        }\n    }\n\n    @Override\n    public void onLoaderReset(Loader<Cursor> loader) {\n        Context context = mContext.get();\n        if (context == null) {\n            return;\n        }\n\n        mCallbacks.onAlbumReset();\n    }\n\n    public void onCreate(FragmentActivity activity, AlbumCallbacks callbacks) {\n        mContext = new WeakReference<Context>(activity);\n        mLoaderManager = activity.getSupportLoaderManager();\n        mCallbacks = callbacks;\n    }\n\n    public void onRestoreInstanceState(Bundle savedInstanceState) {\n        if (savedInstanceState == null) {\n            return;\n        }\n\n        mCurrentSelection = savedInstanceState.getInt(STATE_CURRENT_SELECTION);\n    }\n\n    public void onSaveInstanceState(Bundle outState) {\n        outState.putInt(STATE_CURRENT_SELECTION, mCurrentSelection);\n    }\n\n    public void onDestroy() {\n        if (mLoaderManager != null) {\n            mLoaderManager.destroyLoader(LOADER_ID);\n        }\n        mCallbacks = null;\n    }\n\n    public void loadAlbums() {\n        mLoaderManager.initLoader(LOADER_ID, null, this);\n    }\n\n    public int getCurrentSelection() {\n        return mCurrentSelection;\n    }\n\n    public void setStateCurrentSelection(int currentSelection) {\n        mCurrentSelection = currentSelection;\n    }\n\n    public interface AlbumCallbacks {\n        void onAlbumLoad(Cursor cursor);\n\n        void onAlbumReset();\n    }\n}\n"
  },
  {
    "path": "matisse/src/main/java/com/zhihu/matisse/internal/model/AlbumMediaCollection.java",
    "content": "/*\n * Copyright (C) 2014 nohana, Inc.\n * Copyright 2017 Zhihu Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an &quot;AS IS&quot; BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.zhihu.matisse.internal.model;\n\nimport android.content.Context;\nimport android.database.Cursor;\nimport android.os.Bundle;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.fragment.app.FragmentActivity;\nimport androidx.loader.app.LoaderManager;\nimport androidx.loader.content.Loader;\n\nimport com.zhihu.matisse.internal.entity.Album;\nimport com.zhihu.matisse.internal.loader.AlbumMediaLoader;\n\nimport java.lang.ref.WeakReference;\n\npublic class AlbumMediaCollection implements LoaderManager.LoaderCallbacks<Cursor> {\n    private static final int LOADER_ID = 2;\n    private static final String ARGS_ALBUM = \"args_album\";\n    private static final String ARGS_ENABLE_CAPTURE = \"args_enable_capture\";\n    private WeakReference<Context> mContext;\n    private LoaderManager mLoaderManager;\n    private AlbumMediaCallbacks mCallbacks;\n\n    @Override\n    public Loader<Cursor> onCreateLoader(int id, Bundle args) {\n        Context context = mContext.get();\n        if (context == null) {\n            return null;\n        }\n\n        Album album = args.getParcelable(ARGS_ALBUM);\n        if (album == null) {\n            return null;\n        }\n\n        return AlbumMediaLoader.newInstance(context, album,\n                album.isAll() && args.getBoolean(ARGS_ENABLE_CAPTURE, false));\n    }\n\n    @Override\n    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {\n        Context context = mContext.get();\n        if (context == null) {\n            return;\n        }\n\n        mCallbacks.onAlbumMediaLoad(data);\n    }\n\n    @Override\n    public void onLoaderReset(Loader<Cursor> loader) {\n        Context context = mContext.get();\n        if (context == null) {\n            return;\n        }\n\n        mCallbacks.onAlbumMediaReset();\n    }\n\n    public void onCreate(@NonNull FragmentActivity context, @NonNull AlbumMediaCallbacks callbacks) {\n        mContext = new WeakReference<Context>(context);\n        mLoaderManager = context.getSupportLoaderManager();\n        mCallbacks = callbacks;\n    }\n\n    public void onDestroy() {\n        if (mLoaderManager != null) {\n            mLoaderManager.destroyLoader(LOADER_ID);\n        }\n        mCallbacks = null;\n    }\n\n    public void load(@Nullable Album target) {\n        load(target, false);\n    }\n\n    public void load(@Nullable Album target, boolean enableCapture) {\n        Bundle args = new Bundle();\n        args.putParcelable(ARGS_ALBUM, target);\n        args.putBoolean(ARGS_ENABLE_CAPTURE, enableCapture);\n        mLoaderManager.initLoader(LOADER_ID, args, this);\n    }\n\n    public interface AlbumMediaCallbacks {\n\n        void onAlbumMediaLoad(Cursor cursor);\n\n        void onAlbumMediaReset();\n    }\n}\n"
  },
  {
    "path": "matisse/src/main/java/com/zhihu/matisse/internal/model/SelectedItemCollection.java",
    "content": "/*\n * Copyright 2017 Zhihu Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.zhihu.matisse.internal.model;\n\nimport android.content.Context;\nimport android.content.res.Resources;\nimport android.net.Uri;\nimport android.os.Bundle;\n\nimport com.zhihu.matisse.R;\nimport com.zhihu.matisse.internal.entity.IncapableCause;\nimport com.zhihu.matisse.internal.entity.Item;\nimport com.zhihu.matisse.internal.entity.SelectionSpec;\nimport com.zhihu.matisse.internal.ui.widget.CheckView;\nimport com.zhihu.matisse.internal.utils.PathUtils;\nimport com.zhihu.matisse.internal.utils.PhotoMetadataUtils;\n\nimport java.util.ArrayList;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Set;\n\n@SuppressWarnings(\"unused\")\npublic class SelectedItemCollection {\n\n    public static final String STATE_SELECTION = \"state_selection\";\n    public static final String STATE_COLLECTION_TYPE = \"state_collection_type\";\n    /**\n     * Empty collection\n     */\n    public static final int COLLECTION_UNDEFINED = 0x00;\n    /**\n     * Collection only with images\n     */\n    public static final int COLLECTION_IMAGE = 0x01;\n    /**\n     * Collection only with videos\n     */\n    public static final int COLLECTION_VIDEO = 0x01 << 1;\n    /**\n     * Collection with images and videos.\n     */\n    public static final int COLLECTION_MIXED = COLLECTION_IMAGE | COLLECTION_VIDEO;\n    private final Context mContext;\n    private Set<Item> mItems;\n    private int mCollectionType = COLLECTION_UNDEFINED;\n\n    public SelectedItemCollection(Context context) {\n        mContext = context;\n    }\n\n    public void onCreate(Bundle bundle) {\n        if (bundle == null) {\n            mItems = new LinkedHashSet<>();\n        } else {\n            List<Item> saved = bundle.getParcelableArrayList(STATE_SELECTION);\n            mItems = new LinkedHashSet<>(saved);\n            mCollectionType = bundle.getInt(STATE_COLLECTION_TYPE, COLLECTION_UNDEFINED);\n        }\n    }\n\n    public void setDefaultSelection(List<Item> uris) {\n        mItems.addAll(uris);\n    }\n\n    public void onSaveInstanceState(Bundle outState) {\n        outState.putParcelableArrayList(STATE_SELECTION, new ArrayList<>(mItems));\n        outState.putInt(STATE_COLLECTION_TYPE, mCollectionType);\n    }\n\n    public Bundle getDataWithBundle() {\n        Bundle bundle = new Bundle();\n        bundle.putParcelableArrayList(STATE_SELECTION, new ArrayList<>(mItems));\n        bundle.putInt(STATE_COLLECTION_TYPE, mCollectionType);\n        return bundle;\n    }\n\n    public boolean add(Item item) {\n        if (typeConflict(item)) {\n            throw new IllegalArgumentException(\"Can't select images and videos at the same time.\");\n        }\n        boolean added = mItems.add(item);\n        if (added) {\n            if (mCollectionType == COLLECTION_UNDEFINED) {\n                if (item.isImage()) {\n                    mCollectionType = COLLECTION_IMAGE;\n                } else if (item.isVideo()) {\n                    mCollectionType = COLLECTION_VIDEO;\n                }\n            } else if (mCollectionType == COLLECTION_IMAGE) {\n                if (item.isVideo()) {\n                    mCollectionType = COLLECTION_MIXED;\n                }\n            } else if (mCollectionType == COLLECTION_VIDEO) {\n                if (item.isImage()) {\n                    mCollectionType = COLLECTION_MIXED;\n                }\n            }\n        }\n        return added;\n    }\n\n    public boolean remove(Item item) {\n        boolean removed = mItems.remove(item);\n        if (removed) {\n            if (mItems.size() == 0) {\n                mCollectionType = COLLECTION_UNDEFINED;\n            } else {\n                if (mCollectionType == COLLECTION_MIXED) {\n                    refineCollectionType();\n                }\n            }\n        }\n        return removed;\n    }\n\n    public void overwrite(ArrayList<Item> items, int collectionType) {\n        if (items.size() == 0) {\n            mCollectionType = COLLECTION_UNDEFINED;\n        } else {\n            mCollectionType = collectionType;\n        }\n        mItems.clear();\n        mItems.addAll(items);\n    }\n\n\n    public List<Item> asList() {\n        return new ArrayList<>(mItems);\n    }\n\n    public List<Uri> asListOfUri() {\n        List<Uri> uris = new ArrayList<>();\n        for (Item item : mItems) {\n            uris.add(item.getContentUri());\n        }\n        return uris;\n    }\n\n    public List<String> asListOfString() {\n        List<String> paths = new ArrayList<>();\n        for (Item item : mItems) {\n            paths.add(PathUtils.getPath(mContext, item.getContentUri()));\n        }\n        return paths;\n    }\n\n    public boolean isEmpty() {\n        return mItems == null || mItems.isEmpty();\n    }\n\n    public boolean isSelected(Item item) {\n        return mItems.contains(item);\n    }\n\n    public IncapableCause isAcceptable(Item item) {\n        if (maxSelectableReached()) {\n            int maxSelectable = currentMaxSelectable();\n            String cause;\n\n            try {\n                cause = mContext.getResources().getQuantityString(\n                        R.plurals.error_over_count,\n                        maxSelectable,\n                        maxSelectable\n                );\n            } catch (Resources.NotFoundException e) {\n                cause = mContext.getString(\n                        R.string.error_over_count,\n                        maxSelectable\n                );\n            } catch (NoClassDefFoundError e) {\n                cause = mContext.getString(\n                        R.string.error_over_count,\n                        maxSelectable\n                );\n            }\n\n            return new IncapableCause(cause);\n        } else if (typeConflict(item)) {\n            return new IncapableCause(mContext.getString(R.string.error_type_conflict));\n        }\n\n        return PhotoMetadataUtils.isAcceptable(mContext, item);\n    }\n\n    public boolean maxSelectableReached() {\n        return mItems.size() == currentMaxSelectable();\n    }\n\n    // depends\n    private int currentMaxSelectable() {\n        SelectionSpec spec = SelectionSpec.getInstance();\n        if (spec.maxSelectable > 0) {\n            return spec.maxSelectable;\n        } else if (mCollectionType == COLLECTION_IMAGE) {\n            return spec.maxImageSelectable;\n        } else if (mCollectionType == COLLECTION_VIDEO) {\n            return spec.maxVideoSelectable;\n        } else {\n            return spec.maxSelectable;\n        }\n    }\n\n    public int getCollectionType() {\n        return mCollectionType;\n    }\n\n    private void refineCollectionType() {\n        boolean hasImage = false;\n        boolean hasVideo = false;\n        for (Item i : mItems) {\n            if (i.isImage() && !hasImage) hasImage = true;\n            if (i.isVideo() && !hasVideo) hasVideo = true;\n        }\n        if (hasImage && hasVideo) {\n            mCollectionType = COLLECTION_MIXED;\n        } else if (hasImage) {\n            mCollectionType = COLLECTION_IMAGE;\n        } else if (hasVideo) {\n            mCollectionType = COLLECTION_VIDEO;\n        }\n    }\n\n    /**\n     * Determine whether there will be conflict media types. A user can only select images and videos at the same time\n     * while {@link SelectionSpec#mediaTypeExclusive} is set to false.\n     */\n    public boolean typeConflict(Item item) {\n        return SelectionSpec.getInstance().mediaTypeExclusive\n                && ((item.isImage() && (mCollectionType == COLLECTION_VIDEO || mCollectionType == COLLECTION_MIXED))\n                || (item.isVideo() && (mCollectionType == COLLECTION_IMAGE || mCollectionType == COLLECTION_MIXED)));\n    }\n\n    public int count() {\n        return mItems.size();\n    }\n\n    public int checkedNumOf(Item item) {\n        int index = new ArrayList<>(mItems).indexOf(item);\n        return index == -1 ? CheckView.UNCHECKED : index + 1;\n    }\n}\n"
  },
  {
    "path": "matisse/src/main/java/com/zhihu/matisse/internal/ui/AlbumPreviewActivity.java",
    "content": "/*\n * Copyright 2017 Zhihu Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.zhihu.matisse.internal.ui;\n\nimport android.database.Cursor;\nimport android.os.Bundle;\nimport androidx.annotation.Nullable;\n\nimport com.zhihu.matisse.internal.entity.Album;\nimport com.zhihu.matisse.internal.entity.Item;\nimport com.zhihu.matisse.internal.entity.SelectionSpec;\nimport com.zhihu.matisse.internal.model.AlbumMediaCollection;\nimport com.zhihu.matisse.internal.ui.adapter.PreviewPagerAdapter;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class AlbumPreviewActivity extends BasePreviewActivity implements\n        AlbumMediaCollection.AlbumMediaCallbacks {\n\n    public static final String EXTRA_ALBUM = \"extra_album\";\n    public static final String EXTRA_ITEM = \"extra_item\";\n\n    private AlbumMediaCollection mCollection = new AlbumMediaCollection();\n\n    private boolean mIsAlreadySetPosition;\n\n    @Override\n    protected void onCreate(@Nullable Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        if (!SelectionSpec.getInstance().hasInited) {\n            setResult(RESULT_CANCELED);\n            finish();\n            return;\n        }\n        mCollection.onCreate(this, this);\n        Album album = getIntent().getParcelableExtra(EXTRA_ALBUM);\n        mCollection.load(album);\n\n        Item item = getIntent().getParcelableExtra(EXTRA_ITEM);\n        if (mSpec.countable) {\n            mCheckView.setCheckedNum(mSelectedCollection.checkedNumOf(item));\n        } else {\n            mCheckView.setChecked(mSelectedCollection.isSelected(item));\n        }\n        updateSize(item);\n    }\n\n    @Override\n    protected void onDestroy() {\n        super.onDestroy();\n        mCollection.onDestroy();\n    }\n\n    @Override\n    public void onAlbumMediaLoad(Cursor cursor) {\n        List<Item> items = new ArrayList<>();\n        while (cursor.moveToNext()) {\n            items.add(Item.valueOf(cursor));\n        }\n//        cursor.close();\n\n        if (items.isEmpty()) {\n            return;\n        }\n\n        PreviewPagerAdapter adapter = (PreviewPagerAdapter) mPager.getAdapter();\n        adapter.addAll(items);\n        adapter.notifyDataSetChanged();\n        if (!mIsAlreadySetPosition) {\n            //onAlbumMediaLoad is called many times..\n            mIsAlreadySetPosition = true;\n            Item selected = getIntent().getParcelableExtra(EXTRA_ITEM);\n            int selectedIndex = items.indexOf(selected);\n            mPager.setCurrentItem(selectedIndex, false);\n            mPreviousPos = selectedIndex;\n        }\n    }\n\n    @Override\n    public void onAlbumMediaReset() {\n\n    }\n}\n"
  },
  {
    "path": "matisse/src/main/java/com/zhihu/matisse/internal/ui/BasePreviewActivity.java",
    "content": "/*\n * Copyright 2017 Zhihu Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.zhihu.matisse.internal.ui;\n\nimport android.app.Activity;\nimport android.content.Intent;\nimport android.graphics.Color;\nimport android.os.Bundle;\nimport androidx.annotation.Nullable;\nimport androidx.viewpager.widget.ViewPager;\nimport androidx.interpolator.view.animation.FastOutSlowInInterpolator;\nimport androidx.appcompat.app.AppCompatActivity;\nimport android.view.View;\nimport android.view.WindowManager;\nimport android.widget.FrameLayout;\nimport android.widget.LinearLayout;\nimport android.widget.TextView;\n\nimport com.zhihu.matisse.R;\nimport com.zhihu.matisse.internal.entity.IncapableCause;\nimport com.zhihu.matisse.internal.entity.Item;\nimport com.zhihu.matisse.internal.entity.SelectionSpec;\nimport com.zhihu.matisse.internal.model.SelectedItemCollection;\nimport com.zhihu.matisse.internal.ui.adapter.PreviewPagerAdapter;\nimport com.zhihu.matisse.internal.ui.widget.CheckRadioView;\nimport com.zhihu.matisse.internal.ui.widget.CheckView;\nimport com.zhihu.matisse.internal.ui.widget.IncapableDialog;\nimport com.zhihu.matisse.internal.utils.PhotoMetadataUtils;\nimport com.zhihu.matisse.internal.utils.Platform;\nimport com.zhihu.matisse.listener.OnFragmentInteractionListener;\n\npublic abstract class BasePreviewActivity extends AppCompatActivity implements View.OnClickListener,\n        ViewPager.OnPageChangeListener, OnFragmentInteractionListener {\n\n    public static final String EXTRA_DEFAULT_BUNDLE = \"extra_default_bundle\";\n    public static final String EXTRA_RESULT_BUNDLE = \"extra_result_bundle\";\n    public static final String EXTRA_RESULT_APPLY = \"extra_result_apply\";\n    public static final String EXTRA_RESULT_ORIGINAL_ENABLE = \"extra_result_original_enable\";\n    public static final String CHECK_STATE = \"checkState\";\n\n    protected final SelectedItemCollection mSelectedCollection = new SelectedItemCollection(this);\n    protected SelectionSpec mSpec;\n    protected ViewPager mPager;\n\n    protected PreviewPagerAdapter mAdapter;\n\n    protected CheckView mCheckView;\n    protected TextView mButtonBack;\n    protected TextView mButtonApply;\n    protected TextView mSize;\n\n    protected int mPreviousPos = -1;\n\n    private LinearLayout mOriginalLayout;\n    private CheckRadioView mOriginal;\n    protected boolean mOriginalEnable;\n\n    private FrameLayout mBottomToolbar;\n    private FrameLayout mTopToolbar;\n    private boolean mIsToolbarHide = false;\n\n    @Override\n    protected void onCreate(@Nullable Bundle savedInstanceState) {\n        setTheme(SelectionSpec.getInstance().themeId);\n        super.onCreate(savedInstanceState);\n        if (!SelectionSpec.getInstance().hasInited) {\n            setResult(RESULT_CANCELED);\n            finish();\n            return;\n        }\n        setContentView(R.layout.activity_media_preview);\n        if (Platform.hasKitKat()) {\n            getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);\n        }\n\n        mSpec = SelectionSpec.getInstance();\n        if (mSpec.needOrientationRestriction()) {\n            setRequestedOrientation(mSpec.orientation);\n        }\n\n        if (savedInstanceState == null) {\n            mSelectedCollection.onCreate(getIntent().getBundleExtra(EXTRA_DEFAULT_BUNDLE));\n            mOriginalEnable = getIntent().getBooleanExtra(EXTRA_RESULT_ORIGINAL_ENABLE, false);\n        } else {\n            mSelectedCollection.onCreate(savedInstanceState);\n            mOriginalEnable = savedInstanceState.getBoolean(CHECK_STATE);\n        }\n        mButtonBack = (TextView) findViewById(R.id.button_back);\n        mButtonApply = (TextView) findViewById(R.id.button_apply);\n        mSize = (TextView) findViewById(R.id.size);\n        mButtonBack.setOnClickListener(this);\n        mButtonApply.setOnClickListener(this);\n\n        mPager = (ViewPager) findViewById(R.id.pager);\n        mPager.addOnPageChangeListener(this);\n        mAdapter = new PreviewPagerAdapter(getSupportFragmentManager(), null);\n        mPager.setAdapter(mAdapter);\n        mCheckView = (CheckView) findViewById(R.id.check_view);\n        mCheckView.setCountable(mSpec.countable);\n        mBottomToolbar = findViewById(R.id.bottom_toolbar);\n        mTopToolbar = findViewById(R.id.top_toolbar);\n\n        mCheckView.setOnClickListener(new View.OnClickListener() {\n\n            @Override\n            public void onClick(View v) {\n                Item item = mAdapter.getMediaItem(mPager.getCurrentItem());\n                if (mSelectedCollection.isSelected(item)) {\n                    mSelectedCollection.remove(item);\n                    if (mSpec.countable) {\n                        mCheckView.setCheckedNum(CheckView.UNCHECKED);\n                    } else {\n                        mCheckView.setChecked(false);\n                    }\n                } else {\n                    if (assertAddSelection(item)) {\n                        mSelectedCollection.add(item);\n                        if (mSpec.countable) {\n                            mCheckView.setCheckedNum(mSelectedCollection.checkedNumOf(item));\n                        } else {\n                            mCheckView.setChecked(true);\n                        }\n                    }\n                }\n                updateApplyButton();\n\n                if (mSpec.onSelectedListener != null) {\n                    mSpec.onSelectedListener.onSelected(\n                            mSelectedCollection.asListOfUri(), mSelectedCollection.asListOfString());\n                }\n            }\n        });\n\n\n        mOriginalLayout = findViewById(R.id.originalLayout);\n        mOriginal = findViewById(R.id.original);\n        mOriginalLayout.setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n\n                int count = countOverMaxSize();\n                if (count > 0) {\n                    IncapableDialog incapableDialog = IncapableDialog.newInstance(\"\",\n                            getString(R.string.error_over_original_count, count, mSpec.originalMaxSize));\n                    incapableDialog.show(getSupportFragmentManager(),\n                            IncapableDialog.class.getName());\n                    return;\n                }\n\n                mOriginalEnable = !mOriginalEnable;\n                mOriginal.setChecked(mOriginalEnable);\n                if (!mOriginalEnable) {\n                    mOriginal.setColor(Color.WHITE);\n                }\n\n\n                if (mSpec.onCheckedListener != null) {\n                    mSpec.onCheckedListener.onCheck(mOriginalEnable);\n                }\n            }\n        });\n\n        updateApplyButton();\n    }\n\n    @Override\n    protected void onSaveInstanceState(Bundle outState) {\n        mSelectedCollection.onSaveInstanceState(outState);\n        outState.putBoolean(\"checkState\", mOriginalEnable);\n        super.onSaveInstanceState(outState);\n    }\n\n    @Override\n    public void onBackPressed() {\n        sendBackResult(false);\n        super.onBackPressed();\n    }\n\n    @Override\n    public void onClick(View v) {\n        if (v.getId() == R.id.button_back) {\n            onBackPressed();\n        } else if (v.getId() == R.id.button_apply) {\n            sendBackResult(true);\n            finish();\n        }\n    }\n\n    @Override\n    public void onClick() {\n        if (!mSpec.autoHideToobar) {\n            return;\n        }\n\n        if (mIsToolbarHide) {\n            mTopToolbar.animate()\n                    .setInterpolator(new FastOutSlowInInterpolator())\n                    .translationYBy(mTopToolbar.getMeasuredHeight())\n                    .start();\n            mBottomToolbar.animate()\n                    .translationYBy(-mBottomToolbar.getMeasuredHeight())\n                    .setInterpolator(new FastOutSlowInInterpolator())\n                    .start();\n        } else {\n            mTopToolbar.animate()\n                    .setInterpolator(new FastOutSlowInInterpolator())\n                    .translationYBy(-mTopToolbar.getMeasuredHeight())\n                    .start();\n            mBottomToolbar.animate()\n                    .setInterpolator(new FastOutSlowInInterpolator())\n                    .translationYBy(mBottomToolbar.getMeasuredHeight())\n                    .start();\n        }\n\n        mIsToolbarHide = !mIsToolbarHide;\n\n    }\n\n    @Override\n    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {\n\n    }\n\n    @Override\n    public void onPageSelected(int position) {\n        PreviewPagerAdapter adapter = (PreviewPagerAdapter) mPager.getAdapter();\n        if (mPreviousPos != -1 && mPreviousPos != position) {\n            ((PreviewItemFragment) adapter.instantiateItem(mPager, mPreviousPos)).resetView();\n\n            Item item = adapter.getMediaItem(position);\n            if (mSpec.countable) {\n                int checkedNum = mSelectedCollection.checkedNumOf(item);\n                mCheckView.setCheckedNum(checkedNum);\n                if (checkedNum > 0) {\n                    mCheckView.setEnabled(true);\n                } else {\n                    mCheckView.setEnabled(!mSelectedCollection.maxSelectableReached());\n                }\n            } else {\n                boolean checked = mSelectedCollection.isSelected(item);\n                mCheckView.setChecked(checked);\n                if (checked) {\n                    mCheckView.setEnabled(true);\n                } else {\n                    mCheckView.setEnabled(!mSelectedCollection.maxSelectableReached());\n                }\n            }\n            updateSize(item);\n        }\n        mPreviousPos = position;\n    }\n\n    @Override\n    public void onPageScrollStateChanged(int state) {\n\n    }\n\n    private void updateApplyButton() {\n        int selectedCount = mSelectedCollection.count();\n        if (selectedCount == 0) {\n            mButtonApply.setText(R.string.button_apply_default);\n            mButtonApply.setEnabled(false);\n        } else if (selectedCount == 1 && mSpec.singleSelectionModeEnabled()) {\n            mButtonApply.setText(R.string.button_apply_default);\n            mButtonApply.setEnabled(true);\n        } else {\n            mButtonApply.setEnabled(true);\n            mButtonApply.setText(getString(R.string.button_apply, selectedCount));\n        }\n\n        if (mSpec.originalable) {\n            mOriginalLayout.setVisibility(View.VISIBLE);\n            updateOriginalState();\n        } else {\n            mOriginalLayout.setVisibility(View.GONE);\n        }\n    }\n\n\n    private void updateOriginalState() {\n        mOriginal.setChecked(mOriginalEnable);\n        if (!mOriginalEnable) {\n            mOriginal.setColor(Color.WHITE);\n        }\n\n        if (countOverMaxSize() > 0) {\n\n            if (mOriginalEnable) {\n                IncapableDialog incapableDialog = IncapableDialog.newInstance(\"\",\n                        getString(R.string.error_over_original_size, mSpec.originalMaxSize));\n                incapableDialog.show(getSupportFragmentManager(),\n                        IncapableDialog.class.getName());\n\n                mOriginal.setChecked(false);\n                mOriginal.setColor(Color.WHITE);\n                mOriginalEnable = false;\n            }\n        }\n    }\n\n\n    private int countOverMaxSize() {\n        int count = 0;\n        int selectedCount = mSelectedCollection.count();\n        for (int i = 0; i < selectedCount; i++) {\n            Item item = mSelectedCollection.asList().get(i);\n            if (item.isImage()) {\n                float size = PhotoMetadataUtils.getSizeInMB(item.size);\n                if (size > mSpec.originalMaxSize) {\n                    count++;\n                }\n            }\n        }\n        return count;\n    }\n\n    protected void updateSize(Item item) {\n        if (item.isGif()) {\n            mSize.setVisibility(View.VISIBLE);\n            mSize.setText(PhotoMetadataUtils.getSizeInMB(item.size) + \"M\");\n        } else {\n            mSize.setVisibility(View.GONE);\n        }\n\n        if (item.isVideo()) {\n            mOriginalLayout.setVisibility(View.GONE);\n        } else if (mSpec.originalable) {\n            mOriginalLayout.setVisibility(View.VISIBLE);\n        }\n    }\n\n    protected void sendBackResult(boolean apply) {\n        Intent intent = new Intent();\n        intent.putExtra(EXTRA_RESULT_BUNDLE, mSelectedCollection.getDataWithBundle());\n        intent.putExtra(EXTRA_RESULT_APPLY, apply);\n        intent.putExtra(EXTRA_RESULT_ORIGINAL_ENABLE, mOriginalEnable);\n        setResult(Activity.RESULT_OK, intent);\n    }\n\n    private boolean assertAddSelection(Item item) {\n        IncapableCause cause = mSelectedCollection.isAcceptable(item);\n        IncapableCause.handleCause(this, cause);\n        return cause == null;\n    }\n}\n"
  },
  {
    "path": "matisse/src/main/java/com/zhihu/matisse/internal/ui/MediaSelectionFragment.java",
    "content": "/*\n * Copyright 2017 Zhihu Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.zhihu.matisse.internal.ui;\n\nimport android.content.Context;\nimport android.database.Cursor;\nimport android.os.Bundle;\nimport androidx.annotation.Nullable;\nimport androidx.fragment.app.Fragment;\nimport androidx.recyclerview.widget.GridLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport com.zhihu.matisse.R;\nimport com.zhihu.matisse.internal.entity.Album;\nimport com.zhihu.matisse.internal.entity.Item;\nimport com.zhihu.matisse.internal.entity.SelectionSpec;\nimport com.zhihu.matisse.internal.model.AlbumMediaCollection;\nimport com.zhihu.matisse.internal.model.SelectedItemCollection;\nimport com.zhihu.matisse.internal.ui.adapter.AlbumMediaAdapter;\nimport com.zhihu.matisse.internal.ui.widget.MediaGridInset;\nimport com.zhihu.matisse.internal.utils.UIUtils;\n\npublic class MediaSelectionFragment extends Fragment implements\n        AlbumMediaCollection.AlbumMediaCallbacks, AlbumMediaAdapter.CheckStateListener,\n        AlbumMediaAdapter.OnMediaClickListener {\n\n    public static final String EXTRA_ALBUM = \"extra_album\";\n\n    private final AlbumMediaCollection mAlbumMediaCollection = new AlbumMediaCollection();\n    private RecyclerView mRecyclerView;\n    private AlbumMediaAdapter mAdapter;\n    private SelectionProvider mSelectionProvider;\n    private AlbumMediaAdapter.CheckStateListener mCheckStateListener;\n    private AlbumMediaAdapter.OnMediaClickListener mOnMediaClickListener;\n\n    public static MediaSelectionFragment newInstance(Album album) {\n        MediaSelectionFragment fragment = new MediaSelectionFragment();\n        Bundle args = new Bundle();\n        args.putParcelable(EXTRA_ALBUM, album);\n        fragment.setArguments(args);\n        return fragment;\n    }\n\n    @Override\n    public void onAttach(Context context) {\n        super.onAttach(context);\n        if (context instanceof SelectionProvider) {\n            mSelectionProvider = (SelectionProvider) context;\n        } else {\n            throw new IllegalStateException(\"Context must implement SelectionProvider.\");\n        }\n        if (context instanceof AlbumMediaAdapter.CheckStateListener) {\n            mCheckStateListener = (AlbumMediaAdapter.CheckStateListener) context;\n        }\n        if (context instanceof AlbumMediaAdapter.OnMediaClickListener) {\n            mOnMediaClickListener = (AlbumMediaAdapter.OnMediaClickListener) context;\n        }\n    }\n\n    @Nullable\n    @Override\n    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,\n                             @Nullable Bundle savedInstanceState) {\n        return inflater.inflate(R.layout.fragment_media_selection, container, false);\n    }\n\n    @Override\n    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {\n        super.onViewCreated(view, savedInstanceState);\n        mRecyclerView = (RecyclerView) view.findViewById(R.id.recyclerview);\n    }\n\n    @Override\n    public void onActivityCreated(@Nullable Bundle savedInstanceState) {\n        super.onActivityCreated(savedInstanceState);\n        Album album = getArguments().getParcelable(EXTRA_ALBUM);\n\n        mAdapter = new AlbumMediaAdapter(getContext(),\n                mSelectionProvider.provideSelectedItemCollection(), mRecyclerView);\n        mAdapter.registerCheckStateListener(this);\n        mAdapter.registerOnMediaClickListener(this);\n        mRecyclerView.setHasFixedSize(true);\n\n        int spanCount;\n        SelectionSpec selectionSpec = SelectionSpec.getInstance();\n        if (selectionSpec.gridExpectedSize > 0) {\n            spanCount = UIUtils.spanCount(getContext(), selectionSpec.gridExpectedSize);\n        } else {\n            spanCount = selectionSpec.spanCount;\n        }\n        mRecyclerView.setLayoutManager(new GridLayoutManager(getContext(), spanCount));\n\n        int spacing = getResources().getDimensionPixelSize(R.dimen.media_grid_spacing);\n        mRecyclerView.addItemDecoration(new MediaGridInset(spanCount, spacing, false));\n        mRecyclerView.setAdapter(mAdapter);\n        mAlbumMediaCollection.onCreate(getActivity(), this);\n        mAlbumMediaCollection.load(album, selectionSpec.capture);\n    }\n\n    @Override\n    public void onDestroyView() {\n        super.onDestroyView();\n        mAlbumMediaCollection.onDestroy();\n    }\n\n    public void refreshMediaGrid() {\n        mAdapter.notifyDataSetChanged();\n    }\n\n    public void refreshSelection() {\n        mAdapter.refreshSelection();\n    }\n\n    @Override\n    public void onAlbumMediaLoad(Cursor cursor) {\n        mAdapter.swapCursor(cursor);\n    }\n\n    @Override\n    public void onAlbumMediaReset() {\n        mAdapter.swapCursor(null);\n    }\n\n    @Override\n    public void onUpdate() {\n        // notify outer Activity that check state changed\n        if (mCheckStateListener != null) {\n            mCheckStateListener.onUpdate();\n        }\n    }\n\n    @Override\n    public void onMediaClick(Album album, Item item, int adapterPosition) {\n        if (mOnMediaClickListener != null) {\n            mOnMediaClickListener.onMediaClick((Album) getArguments().getParcelable(EXTRA_ALBUM),\n                    item, adapterPosition);\n        }\n    }\n\n    public interface SelectionProvider {\n        SelectedItemCollection provideSelectedItemCollection();\n    }\n}\n"
  },
  {
    "path": "matisse/src/main/java/com/zhihu/matisse/internal/ui/PreviewItemFragment.java",
    "content": "/*\n * Copyright 2017 Zhihu Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.zhihu.matisse.internal.ui;\n\nimport android.content.ActivityNotFoundException;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.graphics.Point;\nimport android.os.Bundle;\nimport androidx.annotation.Nullable;\nimport androidx.fragment.app.Fragment;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.Toast;\n\nimport com.zhihu.matisse.R;\nimport com.zhihu.matisse.internal.entity.Item;\nimport com.zhihu.matisse.internal.entity.SelectionSpec;\nimport com.zhihu.matisse.internal.utils.PhotoMetadataUtils;\nimport com.zhihu.matisse.listener.OnFragmentInteractionListener;\n\nimport it.sephiroth.android.library.imagezoom.ImageViewTouch;\nimport it.sephiroth.android.library.imagezoom.ImageViewTouchBase;\n\npublic class PreviewItemFragment extends Fragment {\n\n    private static final String ARGS_ITEM = \"args_item\";\n    private OnFragmentInteractionListener mListener;\n\n    public static PreviewItemFragment newInstance(Item item) {\n        PreviewItemFragment fragment = new PreviewItemFragment();\n        Bundle bundle = new Bundle();\n        bundle.putParcelable(ARGS_ITEM, item);\n        fragment.setArguments(bundle);\n        return fragment;\n    }\n\n    @Override\n    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\n        return inflater.inflate(R.layout.fragment_preview_item, container, false);\n    }\n\n    @Override\n    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {\n        super.onViewCreated(view, savedInstanceState);\n        final Item item = getArguments().getParcelable(ARGS_ITEM);\n        if (item == null) {\n            return;\n        }\n\n        View videoPlayButton = view.findViewById(R.id.video_play_button);\n        if (item.isVideo()) {\n            videoPlayButton.setVisibility(View.VISIBLE);\n            videoPlayButton.setOnClickListener(new View.OnClickListener() {\n                @Override\n                public void onClick(View v) {\n                    Intent intent = new Intent(Intent.ACTION_VIEW);\n                    intent.setDataAndType(item.uri, \"video/*\");\n                    try {\n                        startActivity(intent);\n                    } catch (ActivityNotFoundException e) {\n                        Toast.makeText(getContext(), R.string.error_no_video_activity, Toast.LENGTH_SHORT).show();\n                    }\n                }\n            });\n        } else {\n            videoPlayButton.setVisibility(View.GONE);\n        }\n\n        ImageViewTouch image = (ImageViewTouch) view.findViewById(R.id.image_view);\n        image.setDisplayType(ImageViewTouchBase.DisplayType.FIT_TO_SCREEN);\n\n        image.setSingleTapListener(new ImageViewTouch.OnImageViewTouchSingleTapListener() {\n            @Override\n            public void onSingleTapConfirmed() {\n                if (mListener != null) {\n                    mListener.onClick();\n                }\n            }\n        });\n\n        Point size = PhotoMetadataUtils.getBitmapSize(item.getContentUri(), getActivity());\n        if (item.isGif()) {\n            SelectionSpec.getInstance().imageEngine.loadGifImage(getContext(), size.x, size.y, image,\n                    item.getContentUri());\n        } else {\n            SelectionSpec.getInstance().imageEngine.loadImage(getContext(), size.x, size.y, image,\n                    item.getContentUri());\n        }\n    }\n\n    public void resetView() {\n        if (getView() != null) {\n            ((ImageViewTouch) getView().findViewById(R.id.image_view)).resetMatrix();\n        }\n    }\n\n\n    @Override\n    public void onAttach(Context context) {\n        super.onAttach(context);\n        if (context instanceof OnFragmentInteractionListener) {\n            mListener = (OnFragmentInteractionListener) context;\n        } else {\n            throw new RuntimeException(context.toString()\n                    + \" must implement OnFragmentInteractionListener\");\n        }\n    }\n\n    @Override\n    public void onDetach() {\n        super.onDetach();\n        mListener = null;\n    }\n}\n"
  },
  {
    "path": "matisse/src/main/java/com/zhihu/matisse/internal/ui/SelectedPreviewActivity.java",
    "content": "/*\n * Copyright 2017 Zhihu Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.zhihu.matisse.internal.ui;\n\nimport android.os.Bundle;\nimport androidx.annotation.Nullable;\n\nimport com.zhihu.matisse.internal.entity.Item;\nimport com.zhihu.matisse.internal.entity.SelectionSpec;\nimport com.zhihu.matisse.internal.model.SelectedItemCollection;\n\nimport java.util.List;\n\npublic class SelectedPreviewActivity extends BasePreviewActivity {\n\n    @Override\n    protected void onCreate(@Nullable Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        if (!SelectionSpec.getInstance().hasInited) {\n            setResult(RESULT_CANCELED);\n            finish();\n            return;\n        }\n\n        Bundle bundle = getIntent().getBundleExtra(EXTRA_DEFAULT_BUNDLE);\n        List<Item> selected = bundle.getParcelableArrayList(SelectedItemCollection.STATE_SELECTION);\n        mAdapter.addAll(selected);\n        mAdapter.notifyDataSetChanged();\n        if (mSpec.countable) {\n            mCheckView.setCheckedNum(1);\n        } else {\n            mCheckView.setChecked(true);\n        }\n        mPreviousPos = 0;\n        updateSize(selected.get(0));\n    }\n\n}\n"
  },
  {
    "path": "matisse/src/main/java/com/zhihu/matisse/internal/ui/adapter/AlbumMediaAdapter.java",
    "content": "/*\n * Copyright 2017 Zhihu Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.zhihu.matisse.internal.ui.adapter;\n\nimport android.content.Context;\nimport android.content.res.TypedArray;\nimport android.database.Cursor;\nimport android.graphics.PorterDuff;\nimport android.graphics.drawable.Drawable;\nimport androidx.recyclerview.widget.GridLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.ImageView;\nimport android.widget.TextView;\n\nimport com.zhihu.matisse.R;\nimport com.zhihu.matisse.internal.entity.Album;\nimport com.zhihu.matisse.internal.entity.Item;\nimport com.zhihu.matisse.internal.entity.SelectionSpec;\nimport com.zhihu.matisse.internal.entity.IncapableCause;\nimport com.zhihu.matisse.internal.model.SelectedItemCollection;\nimport com.zhihu.matisse.internal.ui.widget.CheckView;\nimport com.zhihu.matisse.internal.ui.widget.MediaGrid;\n\npublic class AlbumMediaAdapter extends\n        RecyclerViewCursorAdapter<RecyclerView.ViewHolder> implements\n        MediaGrid.OnMediaGridClickListener {\n\n    private static final int VIEW_TYPE_CAPTURE = 0x01;\n    private static final int VIEW_TYPE_MEDIA = 0x02;\n    private final SelectedItemCollection mSelectedCollection;\n    private final Drawable mPlaceholder;\n    private SelectionSpec mSelectionSpec;\n    private CheckStateListener mCheckStateListener;\n    private OnMediaClickListener mOnMediaClickListener;\n    private RecyclerView mRecyclerView;\n    private int mImageResize;\n\n    public AlbumMediaAdapter(Context context, SelectedItemCollection selectedCollection, RecyclerView recyclerView) {\n        super(null);\n        mSelectionSpec = SelectionSpec.getInstance();\n        mSelectedCollection = selectedCollection;\n\n        TypedArray ta = context.getTheme().obtainStyledAttributes(new int[]{R.attr.item_placeholder});\n        mPlaceholder = ta.getDrawable(0);\n        ta.recycle();\n\n        mRecyclerView = recyclerView;\n    }\n\n    @Override\n    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {\n        if (viewType == VIEW_TYPE_CAPTURE) {\n            View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.photo_capture_item, parent, false);\n            CaptureViewHolder holder = new CaptureViewHolder(v);\n            holder.itemView.setOnClickListener(new View.OnClickListener() {\n                @Override\n                public void onClick(View v) {\n                    if (v.getContext() instanceof OnPhotoCapture) {\n                        ((OnPhotoCapture) v.getContext()).capture();\n                    }\n                }\n            });\n            return holder;\n        } else if (viewType == VIEW_TYPE_MEDIA) {\n            View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.media_grid_item, parent, false);\n            return new MediaViewHolder(v);\n        }\n        return null;\n    }\n\n    @Override\n    protected void onBindViewHolder(final RecyclerView.ViewHolder holder, Cursor cursor) {\n        if (holder instanceof CaptureViewHolder) {\n            CaptureViewHolder captureViewHolder = (CaptureViewHolder) holder;\n            Drawable[] drawables = captureViewHolder.mHint.getCompoundDrawables();\n            TypedArray ta = holder.itemView.getContext().getTheme().obtainStyledAttributes(\n                    new int[]{R.attr.capture_textColor});\n            int color = ta.getColor(0, 0);\n            ta.recycle();\n\n            for (int i = 0; i < drawables.length; i++) {\n                Drawable drawable = drawables[i];\n                if (drawable != null) {\n                    final Drawable.ConstantState state = drawable.getConstantState();\n                    if (state == null) {\n                        continue;\n                    }\n\n                    Drawable newDrawable = state.newDrawable().mutate();\n                    newDrawable.setColorFilter(color, PorterDuff.Mode.SRC_IN);\n                    newDrawable.setBounds(drawable.getBounds());\n                    drawables[i] = newDrawable;\n                }\n            }\n            captureViewHolder.mHint.setCompoundDrawables(drawables[0], drawables[1], drawables[2], drawables[3]);\n        } else if (holder instanceof MediaViewHolder) {\n            MediaViewHolder mediaViewHolder = (MediaViewHolder) holder;\n\n            final Item item = Item.valueOf(cursor);\n            mediaViewHolder.mMediaGrid.preBindMedia(new MediaGrid.PreBindInfo(\n                    getImageResize(mediaViewHolder.mMediaGrid.getContext()),\n                    mPlaceholder,\n                    mSelectionSpec.countable,\n                    holder\n            ));\n            mediaViewHolder.mMediaGrid.bindMedia(item);\n            mediaViewHolder.mMediaGrid.setOnMediaGridClickListener(this);\n            setCheckStatus(item, mediaViewHolder.mMediaGrid);\n        }\n    }\n\n    private void setCheckStatus(Item item, MediaGrid mediaGrid) {\n        if (mSelectionSpec.countable) {\n            int checkedNum = mSelectedCollection.checkedNumOf(item);\n            if (checkedNum > 0) {\n                mediaGrid.setCheckEnabled(true);\n                mediaGrid.setCheckedNum(checkedNum);\n            } else {\n                if (mSelectedCollection.maxSelectableReached()) {\n                    mediaGrid.setCheckEnabled(false);\n                    mediaGrid.setCheckedNum(CheckView.UNCHECKED);\n                } else {\n                    mediaGrid.setCheckEnabled(true);\n                    mediaGrid.setCheckedNum(checkedNum);\n                }\n            }\n        } else {\n            boolean selected = mSelectedCollection.isSelected(item);\n            if (selected) {\n                mediaGrid.setCheckEnabled(true);\n                mediaGrid.setChecked(true);\n            } else {\n                if (mSelectedCollection.maxSelectableReached()) {\n                    mediaGrid.setCheckEnabled(false);\n                    mediaGrid.setChecked(false);\n                } else {\n                    mediaGrid.setCheckEnabled(true);\n                    mediaGrid.setChecked(false);\n                }\n            }\n        }\n    }\n\n    @Override\n    public void onThumbnailClicked(ImageView thumbnail, Item item, RecyclerView.ViewHolder holder) {\n        if (mSelectionSpec.showPreview) {\n            if (mOnMediaClickListener != null) {\n                mOnMediaClickListener.onMediaClick(null, item, holder.getAdapterPosition());\n            }\n        } else {\n            updateSelectedItem(item, holder);\n        }\n    }\n\n    @Override\n    public void onCheckViewClicked(CheckView checkView, Item item, RecyclerView.ViewHolder holder) {\n        updateSelectedItem(item, holder);\n    }\n\n    private void updateSelectedItem(Item item, RecyclerView.ViewHolder holder) {\n        if (mSelectionSpec.countable) {\n            int checkedNum = mSelectedCollection.checkedNumOf(item);\n            if (checkedNum == CheckView.UNCHECKED) {\n                if (assertAddSelection(holder.itemView.getContext(), item)) {\n                    mSelectedCollection.add(item);\n                    notifyCheckStateChanged();\n                }\n            } else {\n                mSelectedCollection.remove(item);\n                notifyCheckStateChanged();\n            }\n        } else {\n            if (mSelectedCollection.isSelected(item)) {\n                mSelectedCollection.remove(item);\n                notifyCheckStateChanged();\n            } else {\n                if (assertAddSelection(holder.itemView.getContext(), item)) {\n                    mSelectedCollection.add(item);\n                    notifyCheckStateChanged();\n                }\n            }\n        }\n    }\n\n    private void notifyCheckStateChanged() {\n        notifyDataSetChanged();\n        if (mCheckStateListener != null) {\n            mCheckStateListener.onUpdate();\n        }\n    }\n\n    @Override\n    public int getItemViewType(int position, Cursor cursor) {\n        return Item.valueOf(cursor).isCapture() ? VIEW_TYPE_CAPTURE : VIEW_TYPE_MEDIA;\n    }\n\n    private boolean assertAddSelection(Context context, Item item) {\n        IncapableCause cause = mSelectedCollection.isAcceptable(item);\n        IncapableCause.handleCause(context, cause);\n        return cause == null;\n    }\n\n\n    public void registerCheckStateListener(CheckStateListener listener) {\n        mCheckStateListener = listener;\n    }\n\n    public void unregisterCheckStateListener() {\n        mCheckStateListener = null;\n    }\n\n    public void registerOnMediaClickListener(OnMediaClickListener listener) {\n        mOnMediaClickListener = listener;\n    }\n\n    public void unregisterOnMediaClickListener() {\n        mOnMediaClickListener = null;\n    }\n\n    public void refreshSelection() {\n        GridLayoutManager layoutManager = (GridLayoutManager) mRecyclerView.getLayoutManager();\n        int first = layoutManager.findFirstVisibleItemPosition();\n        int last = layoutManager.findLastVisibleItemPosition();\n        if (first == -1 || last == -1) {\n            return;\n        }\n        Cursor cursor = getCursor();\n        for (int i = first; i <= last; i++) {\n            RecyclerView.ViewHolder holder = mRecyclerView.findViewHolderForAdapterPosition(first);\n            if (holder instanceof MediaViewHolder) {\n                if (cursor.moveToPosition(i)) {\n                    setCheckStatus(Item.valueOf(cursor), ((MediaViewHolder) holder).mMediaGrid);\n                }\n            }\n        }\n    }\n\n    private int getImageResize(Context context) {\n        if (mImageResize == 0) {\n            RecyclerView.LayoutManager lm = mRecyclerView.getLayoutManager();\n            int spanCount = ((GridLayoutManager) lm).getSpanCount();\n            int screenWidth = context.getResources().getDisplayMetrics().widthPixels;\n            int availableWidth = screenWidth - context.getResources().getDimensionPixelSize(\n                    R.dimen.media_grid_spacing) * (spanCount - 1);\n            mImageResize = availableWidth / spanCount;\n            mImageResize = (int) (mImageResize * mSelectionSpec.thumbnailScale);\n        }\n        return mImageResize;\n    }\n\n    public interface CheckStateListener {\n        void onUpdate();\n    }\n\n    public interface OnMediaClickListener {\n        void onMediaClick(Album album, Item item, int adapterPosition);\n    }\n\n    public interface OnPhotoCapture {\n        void capture();\n    }\n\n    private static class MediaViewHolder extends RecyclerView.ViewHolder {\n\n        private MediaGrid mMediaGrid;\n\n        MediaViewHolder(View itemView) {\n            super(itemView);\n            mMediaGrid = (MediaGrid) itemView;\n        }\n    }\n\n    private static class CaptureViewHolder extends RecyclerView.ViewHolder {\n\n        private TextView mHint;\n\n        CaptureViewHolder(View itemView) {\n            super(itemView);\n\n            mHint = (TextView) itemView.findViewById(R.id.hint);\n        }\n    }\n\n}\n"
  },
  {
    "path": "matisse/src/main/java/com/zhihu/matisse/internal/ui/adapter/AlbumsAdapter.java",
    "content": "/*\n * Copyright 2017 Zhihu Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.zhihu.matisse.internal.ui.adapter;\n\nimport android.content.Context;\nimport android.content.res.TypedArray;\nimport android.database.Cursor;\nimport android.graphics.drawable.Drawable;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.CursorAdapter;\nimport android.widget.ImageView;\nimport android.widget.TextView;\n\nimport com.zhihu.matisse.R;\nimport com.zhihu.matisse.internal.entity.Album;\nimport com.zhihu.matisse.internal.entity.SelectionSpec;\n\npublic class AlbumsAdapter extends CursorAdapter {\n\n    private final Drawable mPlaceholder;\n\n    public AlbumsAdapter(Context context, Cursor c, boolean autoRequery) {\n        super(context, c, autoRequery);\n\n        TypedArray ta = context.getTheme().obtainStyledAttributes(\n                new int[]{R.attr.album_thumbnail_placeholder});\n        mPlaceholder = ta.getDrawable(0);\n        ta.recycle();\n    }\n\n    public AlbumsAdapter(Context context, Cursor c, int flags) {\n        super(context, c, flags);\n\n        TypedArray ta = context.getTheme().obtainStyledAttributes(\n                new int[]{R.attr.album_thumbnail_placeholder});\n        mPlaceholder = ta.getDrawable(0);\n        ta.recycle();\n    }\n\n    @Override\n    public View newView(Context context, Cursor cursor, ViewGroup parent) {\n        return LayoutInflater.from(context).inflate(R.layout.album_list_item, parent, false);\n    }\n\n    @Override\n    public void bindView(View view, Context context, Cursor cursor) {\n        Album album = Album.valueOf(cursor);\n        ((TextView) view.findViewById(R.id.album_name)).setText(album.getDisplayName(context));\n        ((TextView) view.findViewById(R.id.album_media_count)).setText(String.valueOf(album.getCount()));\n\n        // do not need to load animated Gif\n        SelectionSpec.getInstance().imageEngine.loadThumbnail(context, context.getResources().getDimensionPixelSize(R\n                        .dimen.media_grid_size), mPlaceholder,\n                (ImageView) view.findViewById(R.id.album_cover), album.getCoverUri());\n    }\n}\n"
  },
  {
    "path": "matisse/src/main/java/com/zhihu/matisse/internal/ui/adapter/PreviewPagerAdapter.java",
    "content": "/*\n * Copyright 2017 Zhihu Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.zhihu.matisse.internal.ui.adapter;\n\nimport androidx.fragment.app.Fragment;\nimport androidx.fragment.app.FragmentManager;\nimport androidx.fragment.app.FragmentPagerAdapter;\nimport android.view.ViewGroup;\n\nimport com.zhihu.matisse.internal.entity.Item;\nimport com.zhihu.matisse.internal.ui.PreviewItemFragment;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class PreviewPagerAdapter extends FragmentPagerAdapter {\n\n    private ArrayList<Item> mItems = new ArrayList<>();\n    private OnPrimaryItemSetListener mListener;\n\n    public PreviewPagerAdapter(FragmentManager manager, OnPrimaryItemSetListener listener) {\n        super(manager);\n        mListener = listener;\n    }\n\n    @Override\n    public Fragment getItem(int position) {\n        return PreviewItemFragment.newInstance(mItems.get(position));\n    }\n\n    @Override\n    public int getCount() {\n        return mItems.size();\n    }\n\n    @Override\n    public void setPrimaryItem(ViewGroup container, int position, Object object) {\n        super.setPrimaryItem(container, position, object);\n        if (mListener != null) {\n            mListener.onPrimaryItemSet(position);\n        }\n    }\n\n    public Item getMediaItem(int position) {\n        return mItems.get(position);\n    }\n\n    public void addAll(List<Item> items) {\n        mItems.addAll(items);\n    }\n\n    interface OnPrimaryItemSetListener {\n\n        void onPrimaryItemSet(int position);\n    }\n\n}\n"
  },
  {
    "path": "matisse/src/main/java/com/zhihu/matisse/internal/ui/adapter/RecyclerViewCursorAdapter.java",
    "content": "/*\n * Copyright 2017 Zhihu Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.zhihu.matisse.internal.ui.adapter;\n\nimport android.database.Cursor;\nimport android.provider.MediaStore;\nimport androidx.recyclerview.widget.RecyclerView;\n\npublic abstract class RecyclerViewCursorAdapter<VH extends RecyclerView.ViewHolder> extends\n        RecyclerView.Adapter<VH> {\n\n    private Cursor mCursor;\n    private int mRowIDColumn;\n\n    RecyclerViewCursorAdapter(Cursor c) {\n        setHasStableIds(true);\n        swapCursor(c);\n    }\n\n    protected abstract void onBindViewHolder(VH holder, Cursor cursor);\n\n    @Override\n    public void onBindViewHolder(VH holder, int position) {\n        if (!isDataValid(mCursor)) {\n            throw new IllegalStateException(\"Cannot bind view holder when cursor is in invalid state.\");\n        }\n        if (!mCursor.moveToPosition(position)) {\n            throw new IllegalStateException(\"Could not move cursor to position \" + position\n                    + \" when trying to bind view holder\");\n        }\n\n        onBindViewHolder(holder, mCursor);\n    }\n\n    @Override\n    public int getItemViewType(int position) {\n        if (!mCursor.moveToPosition(position)) {\n            throw new IllegalStateException(\"Could not move cursor to position \" + position\n                    + \" when trying to get item view type.\");\n        }\n        return getItemViewType(position, mCursor);\n    }\n\n    protected abstract int getItemViewType(int position, Cursor cursor);\n\n    @Override\n    public int getItemCount() {\n        if (isDataValid(mCursor)) {\n            return mCursor.getCount();\n        } else {\n            return 0;\n        }\n    }\n\n    @Override\n    public long getItemId(int position) {\n        if (!isDataValid(mCursor)) {\n            throw new IllegalStateException(\"Cannot lookup item id when cursor is in invalid state.\");\n        }\n        if (!mCursor.moveToPosition(position)) {\n            throw new IllegalStateException(\"Could not move cursor to position \" + position\n                    + \" when trying to get an item id\");\n        }\n\n        return mCursor.getLong(mRowIDColumn);\n    }\n\n    public void swapCursor(Cursor newCursor) {\n        if (newCursor == mCursor) {\n            return;\n        }\n\n        if (newCursor != null) {\n            mCursor = newCursor;\n            mRowIDColumn = mCursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns._ID);\n            // notify the observers about the new cursor\n            notifyDataSetChanged();\n        } else {\n            notifyItemRangeRemoved(0, getItemCount());\n            mCursor = null;\n            mRowIDColumn = -1;\n        }\n    }\n\n    public Cursor getCursor() {\n        return mCursor;\n    }\n\n    private boolean isDataValid(Cursor cursor) {\n        return cursor != null && !cursor.isClosed();\n    }\n}\n"
  },
  {
    "path": "matisse/src/main/java/com/zhihu/matisse/internal/ui/widget/AlbumsSpinner.java",
    "content": "/*\n * Copyright 2017 Zhihu Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.zhihu.matisse.internal.ui.widget;\n\nimport android.content.Context;\nimport android.content.res.TypedArray;\nimport android.database.Cursor;\nimport android.graphics.PorterDuff;\nimport android.graphics.drawable.Drawable;\nimport androidx.annotation.NonNull;\nimport androidx.appcompat.widget.ListPopupWindow;\nimport android.view.View;\nimport android.widget.AdapterView;\nimport android.widget.CursorAdapter;\nimport android.widget.TextView;\n\nimport com.zhihu.matisse.R;\nimport com.zhihu.matisse.internal.entity.Album;\nimport com.zhihu.matisse.internal.utils.Platform;\n\npublic class AlbumsSpinner {\n\n    private static final int MAX_SHOWN_COUNT = 6;\n    private CursorAdapter mAdapter;\n    private TextView mSelected;\n    private ListPopupWindow mListPopupWindow;\n    private AdapterView.OnItemSelectedListener mOnItemSelectedListener;\n\n    public AlbumsSpinner(@NonNull Context context) {\n        mListPopupWindow = new ListPopupWindow(context, null, R.attr.listPopupWindowStyle);\n        mListPopupWindow.setModal(true);\n        float density = context.getResources().getDisplayMetrics().density;\n        mListPopupWindow.setContentWidth((int) (216 * density));\n        mListPopupWindow.setHorizontalOffset((int) (16 * density));\n        mListPopupWindow.setVerticalOffset((int) (-48 * density));\n\n        mListPopupWindow.setOnItemClickListener(new AdapterView.OnItemClickListener() {\n\n            @Override\n            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {\n                AlbumsSpinner.this.onItemSelected(parent.getContext(), position);\n                if (mOnItemSelectedListener != null) {\n                    mOnItemSelectedListener.onItemSelected(parent, view, position, id);\n                }\n            }\n        });\n    }\n\n    public void setOnItemSelectedListener(AdapterView.OnItemSelectedListener listener) {\n        mOnItemSelectedListener = listener;\n    }\n\n    public void setSelection(Context context, int position) {\n        mListPopupWindow.setSelection(position);\n        onItemSelected(context, position);\n    }\n\n    private void onItemSelected(Context context, int position) {\n        mListPopupWindow.dismiss();\n        Cursor cursor = mAdapter.getCursor();\n        cursor.moveToPosition(position);\n        Album album = Album.valueOf(cursor);\n        String displayName = album.getDisplayName(context);\n        if (mSelected.getVisibility() == View.VISIBLE) {\n            mSelected.setText(displayName);\n        } else {\n            if (Platform.hasICS()) {\n                mSelected.setAlpha(0.0f);\n                mSelected.setVisibility(View.VISIBLE);\n                mSelected.setText(displayName);\n                mSelected.animate().alpha(1.0f).setDuration(context.getResources().getInteger(\n                        android.R.integer.config_longAnimTime)).start();\n            } else {\n                mSelected.setVisibility(View.VISIBLE);\n                mSelected.setText(displayName);\n            }\n\n        }\n    }\n\n    public void setAdapter(CursorAdapter adapter) {\n        mListPopupWindow.setAdapter(adapter);\n        mAdapter = adapter;\n    }\n\n    public void setSelectedTextView(TextView textView) {\n        mSelected = textView;\n        // tint dropdown arrow icon\n        Drawable[] drawables = mSelected.getCompoundDrawables();\n        Drawable right = drawables[2];\n        TypedArray ta = mSelected.getContext().getTheme().obtainStyledAttributes(\n                new int[]{R.attr.album_element_color});\n        int color = ta.getColor(0, 0);\n        ta.recycle();\n        right.setColorFilter(color, PorterDuff.Mode.SRC_IN);\n\n        mSelected.setVisibility(View.GONE);\n        mSelected.setOnClickListener(new View.OnClickListener() {\n\n            @Override\n            public void onClick(View v) {\n                int itemHeight = v.getResources().getDimensionPixelSize(R.dimen.album_item_height);\n                mListPopupWindow.setHeight(\n                        mAdapter.getCount() > MAX_SHOWN_COUNT ? itemHeight * MAX_SHOWN_COUNT\n                                : itemHeight * mAdapter.getCount());\n                mListPopupWindow.show();\n            }\n        });\n        mSelected.setOnTouchListener(mListPopupWindow.createDragToOpenListener(mSelected));\n    }\n\n    public void setPopupAnchorView(View view) {\n        mListPopupWindow.setAnchorView(view);\n    }\n\n}\n"
  },
  {
    "path": "matisse/src/main/java/com/zhihu/matisse/internal/ui/widget/CheckRadioView.java",
    "content": "package com.zhihu.matisse.internal.ui.widget;\n\nimport android.content.Context;\nimport android.graphics.PorterDuff;\nimport android.graphics.drawable.Drawable;\nimport androidx.core.content.res.ResourcesCompat;\nimport androidx.appcompat.widget.AppCompatImageView;\nimport android.util.AttributeSet;\n\nimport com.zhihu.matisse.R;\n\npublic class CheckRadioView extends AppCompatImageView {\n\n    private Drawable mDrawable;\n\n    private int mSelectedColor;\n    private int mUnSelectUdColor;\n\n    public CheckRadioView(Context context) {\n        super(context);\n        init();\n    }\n\n\n\n    public CheckRadioView(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        init();\n    }\n\n    private void init() {\n        mSelectedColor = ResourcesCompat.getColor(\n                getResources(), R.color.zhihu_item_checkCircle_backgroundColor,\n                getContext().getTheme());\n        mUnSelectUdColor = ResourcesCompat.getColor(\n                getResources(), R.color.zhihu_check_original_radio_disable,\n                getContext().getTheme());\n        setChecked(false);\n    }\n\n    public void setChecked(boolean enable) {\n        if (enable) {\n            setImageResource(R.drawable.ic_preview_radio_on);\n            mDrawable = getDrawable();\n            mDrawable.setColorFilter(mSelectedColor, PorterDuff.Mode.SRC_IN);\n        } else {\n            setImageResource(R.drawable.ic_preview_radio_off);\n            mDrawable = getDrawable();\n            mDrawable.setColorFilter(mUnSelectUdColor, PorterDuff.Mode.SRC_IN);\n        }\n    }\n\n\n    public void setColor(int color) {\n        if (mDrawable == null) {\n            mDrawable = getDrawable();\n        }\n        mDrawable.setColorFilter(color, PorterDuff.Mode.SRC_IN);\n    }\n}\n"
  },
  {
    "path": "matisse/src/main/java/com/zhihu/matisse/internal/ui/widget/CheckView.java",
    "content": "/*\n * Copyright 2017 Zhihu Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.zhihu.matisse.internal.ui.widget;\n\nimport android.content.Context;\nimport android.content.res.TypedArray;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.graphics.Paint;\nimport android.graphics.PorterDuff;\nimport android.graphics.PorterDuffXfermode;\nimport android.graphics.RadialGradient;\nimport android.graphics.Rect;\nimport android.graphics.Shader;\nimport android.graphics.Typeface;\nimport android.graphics.drawable.Drawable;\nimport androidx.core.content.res.ResourcesCompat;\nimport android.text.TextPaint;\nimport android.util.AttributeSet;\nimport android.view.View;\n\nimport com.zhihu.matisse.R;\n\npublic class CheckView extends View {\n\n    public static final int UNCHECKED = Integer.MIN_VALUE;\n    private static final float STROKE_WIDTH = 3.0f; // dp\n    private static final float SHADOW_WIDTH = 6.0f; // dp\n    private static final int SIZE = 48; // dp\n    private static final float STROKE_RADIUS = 11.5f; // dp\n    private static final float BG_RADIUS = 11.0f; // dp\n    private static final int CONTENT_SIZE = 16; // dp\n    private boolean mCountable;\n    private boolean mChecked;\n    private int mCheckedNum;\n    private Paint mStrokePaint;\n    private Paint mBackgroundPaint;\n    private TextPaint mTextPaint;\n    private Paint mShadowPaint;\n    private Drawable mCheckDrawable;\n    private float mDensity;\n    private Rect mCheckRect;\n    private boolean mEnabled = true;\n\n    public CheckView(Context context) {\n        super(context);\n        init(context);\n    }\n\n    public CheckView(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        init(context);\n    }\n\n    public CheckView(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        init(context);\n    }\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        // fixed size 48dp x 48dp\n        int sizeSpec = MeasureSpec.makeMeasureSpec((int) (SIZE * mDensity), MeasureSpec.EXACTLY);\n        super.onMeasure(sizeSpec, sizeSpec);\n    }\n\n    private void init(Context context) {\n        mDensity = context.getResources().getDisplayMetrics().density;\n\n        mStrokePaint = new Paint();\n        mStrokePaint.setAntiAlias(true);\n        mStrokePaint.setStyle(Paint.Style.STROKE);\n        mStrokePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER));\n        mStrokePaint.setStrokeWidth(STROKE_WIDTH * mDensity);\n        TypedArray ta = getContext().getTheme().obtainStyledAttributes(new int[]{R.attr.item_checkCircle_borderColor});\n        int defaultColor = ResourcesCompat.getColor(\n                getResources(), R.color.zhihu_item_checkCircle_borderColor,\n                getContext().getTheme());\n        int color = ta.getColor(0, defaultColor);\n        ta.recycle();\n        mStrokePaint.setColor(color);\n\n        mCheckDrawable = ResourcesCompat.getDrawable(context.getResources(),\n                R.drawable.ic_check_white_18dp, context.getTheme());\n    }\n\n    public void setChecked(boolean checked) {\n        if (mCountable) {\n            throw new IllegalStateException(\"CheckView is countable, call setCheckedNum() instead.\");\n        }\n        mChecked = checked;\n        invalidate();\n    }\n\n    public void setCountable(boolean countable) {\n        mCountable = countable;\n    }\n\n    public void setCheckedNum(int checkedNum) {\n        if (!mCountable) {\n            throw new IllegalStateException(\"CheckView is not countable, call setChecked() instead.\");\n        }\n        if (checkedNum != UNCHECKED && checkedNum <= 0) {\n            throw new IllegalArgumentException(\"checked num can't be negative.\");\n        }\n        mCheckedNum = checkedNum;\n        invalidate();\n    }\n\n    public void setEnabled(boolean enabled) {\n        if (mEnabled != enabled) {\n            mEnabled = enabled;\n            invalidate();\n        }\n    }\n\n    @Override\n    protected void onDraw(Canvas canvas) {\n        super.onDraw(canvas);\n\n        // draw outer and inner shadow\n        initShadowPaint();\n        canvas.drawCircle((float) SIZE * mDensity / 2, (float) SIZE * mDensity / 2,\n                (STROKE_RADIUS + STROKE_WIDTH / 2 + SHADOW_WIDTH) * mDensity, mShadowPaint);\n\n        // draw white stroke\n        canvas.drawCircle((float) SIZE * mDensity / 2, (float) SIZE * mDensity / 2,\n                STROKE_RADIUS * mDensity, mStrokePaint);\n\n        // draw content\n        if (mCountable) {\n            if (mCheckedNum != UNCHECKED) {\n                initBackgroundPaint();\n                canvas.drawCircle((float) SIZE * mDensity / 2, (float) SIZE * mDensity / 2,\n                        BG_RADIUS * mDensity, mBackgroundPaint);\n                initTextPaint();\n                String text = String.valueOf(mCheckedNum);\n                int baseX = (int) (canvas.getWidth() - mTextPaint.measureText(text)) / 2;\n                int baseY = (int) (canvas.getHeight() - mTextPaint.descent() - mTextPaint.ascent()) / 2;\n                canvas.drawText(text, baseX, baseY, mTextPaint);\n            }\n        } else {\n            if (mChecked) {\n                initBackgroundPaint();\n                canvas.drawCircle((float) SIZE * mDensity / 2, (float) SIZE * mDensity / 2,\n                        BG_RADIUS * mDensity, mBackgroundPaint);\n\n                mCheckDrawable.setBounds(getCheckRect());\n                mCheckDrawable.draw(canvas);\n            }\n        }\n\n        // enable hint\n        setAlpha(mEnabled ? 1.0f : 0.5f);\n    }\n\n    private void initShadowPaint() {\n        if (mShadowPaint == null) {\n            mShadowPaint = new Paint();\n            mShadowPaint.setAntiAlias(true);\n            // all in dp\n            float outerRadius = STROKE_RADIUS + STROKE_WIDTH / 2;\n            float innerRadius = outerRadius - STROKE_WIDTH;\n            float gradientRadius = outerRadius + SHADOW_WIDTH;\n            float stop0 = (innerRadius - SHADOW_WIDTH) / gradientRadius;\n            float stop1 = innerRadius / gradientRadius;\n            float stop2 = outerRadius / gradientRadius;\n            float stop3 = 1.0f;\n            mShadowPaint.setShader(\n                    new RadialGradient((float) SIZE * mDensity / 2,\n                            (float) SIZE * mDensity / 2,\n                            gradientRadius * mDensity,\n                            new int[]{Color.parseColor(\"#00000000\"), Color.parseColor(\"#0D000000\"),\n                                    Color.parseColor(\"#0D000000\"), Color.parseColor(\"#00000000\")},\n                            new float[]{stop0, stop1, stop2, stop3},\n                            Shader.TileMode.CLAMP));\n        }\n    }\n\n    private void initBackgroundPaint() {\n        if (mBackgroundPaint == null) {\n            mBackgroundPaint = new Paint();\n            mBackgroundPaint.setAntiAlias(true);\n            mBackgroundPaint.setStyle(Paint.Style.FILL);\n            TypedArray ta = getContext().getTheme()\n                    .obtainStyledAttributes(new int[]{R.attr.item_checkCircle_backgroundColor});\n            int defaultColor = ResourcesCompat.getColor(\n                    getResources(), R.color.zhihu_item_checkCircle_backgroundColor,\n                    getContext().getTheme());\n            int color = ta.getColor(0, defaultColor);\n            ta.recycle();\n            mBackgroundPaint.setColor(color);\n        }\n    }\n\n    private void initTextPaint() {\n        if (mTextPaint == null) {\n            mTextPaint = new TextPaint();\n            mTextPaint.setAntiAlias(true);\n            mTextPaint.setColor(Color.WHITE);\n            mTextPaint.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.BOLD));\n            mTextPaint.setTextSize(12.0f * mDensity);\n        }\n    }\n\n    // rect for drawing checked number or mark\n    private Rect getCheckRect() {\n        if (mCheckRect == null) {\n            int rectPadding = (int) (SIZE * mDensity / 2 - CONTENT_SIZE * mDensity / 2);\n            mCheckRect = new Rect(rectPadding, rectPadding,\n                    (int) (SIZE * mDensity - rectPadding), (int) (SIZE * mDensity - rectPadding));\n        }\n\n        return mCheckRect;\n    }\n}\n"
  },
  {
    "path": "matisse/src/main/java/com/zhihu/matisse/internal/ui/widget/IncapableDialog.java",
    "content": "/*\n * Copyright 2017 Zhihu Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.zhihu.matisse.internal.ui.widget;\n\nimport android.app.Dialog;\nimport android.content.DialogInterface;\nimport android.os.Bundle;\nimport androidx.annotation.NonNull;\nimport androidx.fragment.app.DialogFragment;\nimport androidx.appcompat.app.AlertDialog;\nimport android.text.TextUtils;\n\nimport com.zhihu.matisse.R;\n\npublic class IncapableDialog extends DialogFragment {\n\n    public static final String EXTRA_TITLE = \"extra_title\";\n    public static final String EXTRA_MESSAGE = \"extra_message\";\n\n    public static IncapableDialog newInstance(String title, String message) {\n        IncapableDialog dialog = new IncapableDialog();\n        Bundle args = new Bundle();\n        args.putString(EXTRA_TITLE, title);\n        args.putString(EXTRA_MESSAGE, message);\n        dialog.setArguments(args);\n        return dialog;\n    }\n\n    @NonNull\n    @Override\n    public Dialog onCreateDialog(Bundle savedInstanceState) {\n        String title = getArguments().getString(EXTRA_TITLE);\n        String message = getArguments().getString(EXTRA_MESSAGE);\n\n        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());\n        if (!TextUtils.isEmpty(title)) {\n            builder.setTitle(title);\n        }\n        if (!TextUtils.isEmpty(message)) {\n            builder.setMessage(message);\n        }\n        builder.setPositiveButton(R.string.button_ok, new DialogInterface.OnClickListener() {\n            @Override\n            public void onClick(DialogInterface dialog, int which) {\n                dialog.dismiss();\n            }\n        });\n\n        return builder.create();\n    }\n}\n"
  },
  {
    "path": "matisse/src/main/java/com/zhihu/matisse/internal/ui/widget/MediaGrid.java",
    "content": "/*\n * Copyright 2017 Zhihu Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.zhihu.matisse.internal.ui.widget;\n\nimport android.content.Context;\nimport android.graphics.drawable.Drawable;\nimport androidx.recyclerview.widget.RecyclerView;\nimport android.text.format.DateUtils;\nimport android.util.AttributeSet;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.widget.ImageView;\nimport android.widget.TextView;\n\nimport com.zhihu.matisse.R;\nimport com.zhihu.matisse.internal.entity.Item;\nimport com.zhihu.matisse.internal.entity.SelectionSpec;\n\npublic class MediaGrid extends SquareFrameLayout implements View.OnClickListener {\n\n    private ImageView mThumbnail;\n    private CheckView mCheckView;\n    private ImageView mGifTag;\n    private TextView mVideoDuration;\n\n    private Item mMedia;\n    private PreBindInfo mPreBindInfo;\n    private OnMediaGridClickListener mListener;\n\n    public MediaGrid(Context context) {\n        super(context);\n        init(context);\n    }\n\n    public MediaGrid(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        init(context);\n    }\n\n    private void init(Context context) {\n        LayoutInflater.from(context).inflate(R.layout.media_grid_content, this, true);\n\n        mThumbnail = (ImageView) findViewById(R.id.media_thumbnail);\n        mCheckView = (CheckView) findViewById(R.id.check_view);\n        mGifTag = (ImageView) findViewById(R.id.gif);\n        mVideoDuration = (TextView) findViewById(R.id.video_duration);\n\n        mThumbnail.setOnClickListener(this);\n        mCheckView.setOnClickListener(this);\n    }\n\n    @Override\n    public void onClick(View v) {\n        if (mListener != null) {\n            if (v == mThumbnail) {\n                mListener.onThumbnailClicked(mThumbnail, mMedia, mPreBindInfo.mViewHolder);\n            } else if (v == mCheckView) {\n                mListener.onCheckViewClicked(mCheckView, mMedia, mPreBindInfo.mViewHolder);\n            }\n        }\n    }\n\n    public void preBindMedia(PreBindInfo info) {\n        mPreBindInfo = info;\n    }\n\n    public void bindMedia(Item item) {\n        mMedia = item;\n        setGifTag();\n        initCheckView();\n        setImage();\n        setVideoDuration();\n    }\n\n    public Item getMedia() {\n        return mMedia;\n    }\n\n    private void setGifTag() {\n        mGifTag.setVisibility(mMedia.isGif() ? View.VISIBLE : View.GONE);\n    }\n\n    private void initCheckView() {\n        mCheckView.setCountable(mPreBindInfo.mCheckViewCountable);\n    }\n\n    public void setCheckEnabled(boolean enabled) {\n        mCheckView.setEnabled(enabled);\n    }\n\n    public void setCheckedNum(int checkedNum) {\n        mCheckView.setCheckedNum(checkedNum);\n    }\n\n    public void setChecked(boolean checked) {\n        mCheckView.setChecked(checked);\n    }\n\n    private void setImage() {\n        if (mMedia.isGif()) {\n            SelectionSpec.getInstance().imageEngine.loadGifThumbnail(getContext(), mPreBindInfo.mResize,\n                    mPreBindInfo.mPlaceholder, mThumbnail, mMedia.getContentUri());\n        } else {\n            SelectionSpec.getInstance().imageEngine.loadThumbnail(getContext(), mPreBindInfo.mResize,\n                    mPreBindInfo.mPlaceholder, mThumbnail, mMedia.getContentUri());\n        }\n    }\n\n    private void setVideoDuration() {\n        if (mMedia.isVideo()) {\n            mVideoDuration.setVisibility(VISIBLE);\n            mVideoDuration.setText(DateUtils.formatElapsedTime(mMedia.duration / 1000));\n        } else {\n            mVideoDuration.setVisibility(GONE);\n        }\n    }\n\n    public void setOnMediaGridClickListener(OnMediaGridClickListener listener) {\n        mListener = listener;\n    }\n\n    public void removeOnMediaGridClickListener() {\n        mListener = null;\n    }\n\n    public interface OnMediaGridClickListener {\n\n        void onThumbnailClicked(ImageView thumbnail, Item item, RecyclerView.ViewHolder holder);\n\n        void onCheckViewClicked(CheckView checkView, Item item, RecyclerView.ViewHolder holder);\n    }\n\n    public static class PreBindInfo {\n        int mResize;\n        Drawable mPlaceholder;\n        boolean mCheckViewCountable;\n        RecyclerView.ViewHolder mViewHolder;\n\n        public PreBindInfo(int resize, Drawable placeholder, boolean checkViewCountable,\n                           RecyclerView.ViewHolder viewHolder) {\n            mResize = resize;\n            mPlaceholder = placeholder;\n            mCheckViewCountable = checkViewCountable;\n            mViewHolder = viewHolder;\n        }\n    }\n\n}\n"
  },
  {
    "path": "matisse/src/main/java/com/zhihu/matisse/internal/ui/widget/MediaGridInset.java",
    "content": "/*\n * Copyright 2017 Zhihu Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.zhihu.matisse.internal.ui.widget;\n\nimport android.graphics.Rect;\nimport androidx.recyclerview.widget.RecyclerView;\nimport android.view.View;\n\npublic class MediaGridInset extends RecyclerView.ItemDecoration {\n\n    private int mSpanCount;\n    private int mSpacing;\n    private boolean mIncludeEdge;\n\n    public MediaGridInset(int spanCount, int spacing, boolean includeEdge) {\n        this.mSpanCount = spanCount;\n        this.mSpacing = spacing;\n        this.mIncludeEdge = includeEdge;\n    }\n\n    @Override\n    public void getItemOffsets(Rect outRect, View view, RecyclerView parent,\n                               RecyclerView.State state) {\n        int position = parent.getChildAdapterPosition(view); // item position\n        int column = position % mSpanCount; // item column\n\n        if (mIncludeEdge) {\n            // spacing - column * ((1f / spanCount) * spacing)\n            outRect.left = mSpacing - column * mSpacing / mSpanCount;\n            // (column + 1) * ((1f / spanCount) * spacing)\n            outRect.right = (column + 1) * mSpacing / mSpanCount;\n\n            if (position < mSpanCount) { // top edge\n                outRect.top = mSpacing;\n            }\n            outRect.bottom = mSpacing; // item bottom\n        } else {\n            // column * ((1f / spanCount) * spacing)\n            outRect.left = column * mSpacing / mSpanCount;\n            // spacing - (column + 1) * ((1f / spanCount) * spacing)\n            outRect.right = mSpacing - (column + 1) * mSpacing / mSpanCount;\n            if (position >= mSpanCount) {\n                outRect.top = mSpacing; // item top\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "matisse/src/main/java/com/zhihu/matisse/internal/ui/widget/PreviewViewPager.java",
    "content": "/*\n * Copyright 2017 Zhihu Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.zhihu.matisse.internal.ui.widget;\n\nimport android.content.Context;\nimport androidx.viewpager.widget.ViewPager;\nimport android.util.AttributeSet;\nimport android.view.View;\n\nimport it.sephiroth.android.library.imagezoom.ImageViewTouch;\n\npublic class PreviewViewPager extends ViewPager {\n\n    public PreviewViewPager(Context context, AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    @Override\n    protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) {\n        if (v instanceof ImageViewTouch) {\n            return ((ImageViewTouch) v).canScroll(dx) || super.canScroll(v, checkV, dx, x, y);\n        }\n        return super.canScroll(v, checkV, dx, x, y);\n    }\n}\n"
  },
  {
    "path": "matisse/src/main/java/com/zhihu/matisse/internal/ui/widget/RoundedRectangleImageView.java",
    "content": "/*\n * Copyright 2017 Zhihu Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.zhihu.matisse.internal.ui.widget;\n\nimport android.content.Context;\nimport android.graphics.Canvas;\nimport android.graphics.Path;\nimport android.graphics.RectF;\nimport androidx.appcompat.widget.AppCompatImageView;\nimport android.util.AttributeSet;\n\npublic class RoundedRectangleImageView extends AppCompatImageView {\n\n    private float mRadius; // dp\n    private Path mRoundedRectPath;\n    private RectF mRectF;\n\n    public RoundedRectangleImageView(Context context) {\n        super(context);\n        init(context);\n    }\n\n    public RoundedRectangleImageView(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        init(context);\n    }\n\n    public RoundedRectangleImageView(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        init(context);\n    }\n\n    private void init(Context context) {\n        float density = context.getResources().getDisplayMetrics().density;\n        mRadius = 2.0f * density;\n        mRoundedRectPath = new Path();\n        mRectF = new RectF();\n    }\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        super.onMeasure(widthMeasureSpec, heightMeasureSpec);\n        mRectF.set(0.0f, 0.0f, getMeasuredWidth(), getMeasuredHeight());\n        mRoundedRectPath.addRoundRect(mRectF, mRadius, mRadius, Path.Direction.CW);\n    }\n\n    @Override\n    protected void onDraw(Canvas canvas) {\n        canvas.clipPath(mRoundedRectPath);\n        super.onDraw(canvas);\n    }\n}\n"
  },
  {
    "path": "matisse/src/main/java/com/zhihu/matisse/internal/ui/widget/SquareFrameLayout.java",
    "content": "/*\n * Copyright 2017 Zhihu Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.zhihu.matisse.internal.ui.widget;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.widget.FrameLayout;\n\npublic class SquareFrameLayout extends FrameLayout {\n\n    public SquareFrameLayout(Context context) {\n        super(context);\n    }\n\n    public SquareFrameLayout(Context context, AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        super.onMeasure(widthMeasureSpec, widthMeasureSpec);\n    }\n}\n"
  },
  {
    "path": "matisse/src/main/java/com/zhihu/matisse/internal/utils/ExifInterfaceCompat.java",
    "content": "/*\n * Copyright 2017 Zhihu Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.zhihu.matisse.internal.utils;\n\nimport android.media.ExifInterface;\nimport android.text.TextUtils;\nimport android.util.Log;\n\nimport java.io.IOException;\nimport java.text.ParseException;\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\nimport java.util.TimeZone;\n\n/**\n * Bug fixture for ExifInterface constructor.\n */\nfinal class ExifInterfaceCompat {\n    private static final String TAG = ExifInterfaceCompat.class.getSimpleName();\n    private static final int EXIF_DEGREE_FALLBACK_VALUE = -1;\n\n    /**\n     * Do not instantiate this class.\n     */\n    private ExifInterfaceCompat() {\n    }\n\n    /**\n     * Creates new instance of {@link ExifInterface}.\n     * Original constructor won't check filename value, so if null value has been passed,\n     * the process will be killed because of SIGSEGV.\n     * Google Play crash report system cannot perceive this crash, so this method will throw\n     * {@link NullPointerException} when the filename is null.\n     *\n     * @param filename a JPEG filename.\n     * @return {@link ExifInterface} instance.\n     * @throws IOException something wrong with I/O.\n     */\n    public static ExifInterface newInstance(String filename) throws IOException {\n        if (filename == null) throw new NullPointerException(\"filename should not be null\");\n        return new ExifInterface(filename);\n    }\n\n    private static Date getExifDateTime(String filepath) {\n        ExifInterface exif;\n        try {\n            // ExifInterface does not check whether file path is null or not,\n            // so passing null file path argument to its constructor causing SIGSEGV.\n            // We should avoid such a situation by checking file path string.\n            exif = newInstance(filepath);\n        } catch (IOException ex) {\n            Log.e(TAG, \"cannot read exif\", ex);\n            return null;\n        }\n\n        String date = exif.getAttribute(ExifInterface.TAG_DATETIME);\n        if (TextUtils.isEmpty(date)) {\n            return null;\n        }\n        try {\n            SimpleDateFormat formatter = new SimpleDateFormat(\"yyyy:MM:dd HH:mm:ss\");\n            formatter.setTimeZone(TimeZone.getTimeZone(\"UTC\"));\n            return formatter.parse(date);\n        } catch (ParseException e) {\n            Log.d(TAG, \"failed to parse date taken\", e);\n        }\n        return null;\n    }\n\n    /**\n     * Read exif info and get datetime value of the photo.\n     *\n     * @param filepath to get datetime\n     * @return when a photo taken.\n     */\n    public static long getExifDateTimeInMillis(String filepath) {\n        Date datetime = getExifDateTime(filepath);\n        if (datetime == null) {\n            return -1;\n        }\n        return datetime.getTime();\n    }\n\n    /**\n     * Read exif info and get orientation value of the photo.\n     *\n     * @param filepath to get exif.\n     * @return exif orientation value\n     */\n    public static int getExifOrientation(String filepath) {\n        ExifInterface exif;\n        try {\n            // ExifInterface does not check whether file path is null or not,\n            // so passing null file path argument to its constructor causing SIGSEGV.\n            // We should avoid such a situation by checking file path string.\n            exif = newInstance(filepath);\n        } catch (IOException ex) {\n            Log.e(TAG, \"cannot read exif\", ex);\n            return EXIF_DEGREE_FALLBACK_VALUE;\n        }\n\n        int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, EXIF_DEGREE_FALLBACK_VALUE);\n        if (orientation == EXIF_DEGREE_FALLBACK_VALUE) {\n            return 0;\n        }\n        // We only recognize a subset of orientation tag values.\n        switch (orientation) {\n            case ExifInterface.ORIENTATION_ROTATE_90:\n                return 90;\n            case ExifInterface.ORIENTATION_ROTATE_180:\n                return 180;\n            case ExifInterface.ORIENTATION_ROTATE_270:\n                return 270;\n            default:\n                return 0;\n        }\n    }\n}"
  },
  {
    "path": "matisse/src/main/java/com/zhihu/matisse/internal/utils/MediaStoreCompat.java",
    "content": "/*\n * Copyright 2017 Zhihu Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.zhihu.matisse.internal.utils;\n\nimport android.app.Activity;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.pm.PackageManager;\nimport android.content.pm.ResolveInfo;\nimport android.net.Uri;\nimport android.os.Build;\nimport android.os.Environment;\nimport android.provider.MediaStore;\nimport androidx.fragment.app.Fragment;\nimport androidx.core.content.FileProvider;\nimport androidx.core.os.EnvironmentCompat;\n\nimport com.zhihu.matisse.internal.entity.CaptureStrategy;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.lang.ref.WeakReference;\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Locale;\n\npublic class MediaStoreCompat {\n\n    private final WeakReference<Activity> mContext;\n    private final WeakReference<Fragment> mFragment;\n    private       CaptureStrategy         mCaptureStrategy;\n    private       Uri                     mCurrentPhotoUri;\n    private       String                  mCurrentPhotoPath;\n\n    public MediaStoreCompat(Activity activity) {\n        mContext = new WeakReference<>(activity);\n        mFragment = null;\n    }\n\n    public MediaStoreCompat(Activity activity, Fragment fragment) {\n        mContext = new WeakReference<>(activity);\n        mFragment = new WeakReference<>(fragment);\n    }\n\n    /**\n     * Checks whether the device has a camera feature or not.\n     *\n     * @param context a context to check for camera feature.\n     * @return true if the device has a camera feature. false otherwise.\n     */\n    public static boolean hasCameraFeature(Context context) {\n        PackageManager pm = context.getApplicationContext().getPackageManager();\n        return pm.hasSystemFeature(PackageManager.FEATURE_CAMERA);\n    }\n\n    public void setCaptureStrategy(CaptureStrategy strategy) {\n        mCaptureStrategy = strategy;\n    }\n\n    public void dispatchCaptureIntent(Context context, int requestCode) {\n        Intent captureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);\n        if (captureIntent.resolveActivity(context.getPackageManager()) != null) {\n            File photoFile = null;\n            try {\n                photoFile = createImageFile();\n            } catch (IOException e) {\n                e.printStackTrace();\n            }\n\n            if (photoFile != null) {\n                mCurrentPhotoPath = photoFile.getAbsolutePath();\n                mCurrentPhotoUri = FileProvider.getUriForFile(mContext.get(),\n                        mCaptureStrategy.authority, photoFile);\n                captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, mCurrentPhotoUri);\n                captureIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);\n                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {\n                    List<ResolveInfo> resInfoList = context.getPackageManager()\n                            .queryIntentActivities(captureIntent, PackageManager.MATCH_DEFAULT_ONLY);\n                    for (ResolveInfo resolveInfo : resInfoList) {\n                        String packageName = resolveInfo.activityInfo.packageName;\n                        context.grantUriPermission(packageName, mCurrentPhotoUri,\n                                Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);\n                    }\n                }\n                if (mFragment != null) {\n                    mFragment.get().startActivityForResult(captureIntent, requestCode);\n                } else {\n                    mContext.get().startActivityForResult(captureIntent, requestCode);\n                }\n            }\n        }\n    }\n\n    @SuppressWarnings(\"ResultOfMethodCallIgnored\")\n    private File createImageFile() throws IOException {\n        // Create an image file name\n        String timeStamp =\n                new SimpleDateFormat(\"yyyyMMdd_HHmmss\", Locale.getDefault()).format(new Date());\n        String imageFileName = String.format(\"JPEG_%s.jpg\", timeStamp);\n        File storageDir;\n        if (mCaptureStrategy.isPublic) {\n            storageDir = Environment.getExternalStoragePublicDirectory(\n                    Environment.DIRECTORY_PICTURES);\n            if (!storageDir.exists()) storageDir.mkdirs();\n        } else {\n            storageDir = mContext.get().getExternalFilesDir(Environment.DIRECTORY_PICTURES);\n        }\n        if (mCaptureStrategy.directory != null) {\n            storageDir = new File(storageDir, mCaptureStrategy.directory);\n            if (!storageDir.exists()) storageDir.mkdirs();\n        }\n\n        // Avoid joining path components manually\n        File tempFile = new File(storageDir, imageFileName);\n\n        // Handle the situation that user's external storage is not ready\n        if (!Environment.MEDIA_MOUNTED.equals(EnvironmentCompat.getStorageState(tempFile))) {\n            return null;\n        }\n\n        return tempFile;\n    }\n\n    public Uri getCurrentPhotoUri() {\n        return mCurrentPhotoUri;\n    }\n\n    public String getCurrentPhotoPath() {\n        return mCurrentPhotoPath;\n    }\n}\n"
  },
  {
    "path": "matisse/src/main/java/com/zhihu/matisse/internal/utils/PathUtils.java",
    "content": "package com.zhihu.matisse.internal.utils;\n\nimport android.annotation.TargetApi;\nimport android.content.ContentUris;\nimport android.content.Context;\nimport android.database.Cursor;\nimport android.net.Uri;\nimport android.os.Build;\nimport android.os.Environment;\nimport android.provider.DocumentsContract;\nimport android.provider.MediaStore;\n\n/**\n * http://stackoverflow.com/a/27271131/4739220\n */\n\npublic class PathUtils {\n    /**\n     * Get a file path from a Uri. This will get the the path for Storage Access\n     * Framework Documents, as well as the _data field for the MediaStore and\n     * other file-based ContentProviders.\n     *\n     * @param context The context.\n     * @param uri     The Uri to query.\n     * @author paulburke\n     */\n    @TargetApi(Build.VERSION_CODES.KITKAT)\n    public static String getPath(final Context context, final Uri uri) {\n        // DocumentProvider\n        if (Platform.hasKitKat() && DocumentsContract.isDocumentUri(context, uri)) {\n            // ExternalStorageProvider\n            if (isExternalStorageDocument(uri)) {\n                final String docId = DocumentsContract.getDocumentId(uri);\n                final String[] split = docId.split(\":\");\n                final String type = split[0];\n\n                if (\"primary\".equalsIgnoreCase(type)) {\n                    return Environment.getExternalStorageDirectory() + \"/\" + split[1];\n                }\n\n                // TODO handle non-primary volumes\n            } else if (isDownloadsDocument(uri)) { // DownloadsProvider\n\n                final String id = DocumentsContract.getDocumentId(uri);\n                final Uri contentUri = ContentUris.withAppendedId(\n                        Uri.parse(\"content://downloads/public_downloads\"), Long.valueOf(id));\n\n                return getDataColumn(context, contentUri, null, null);\n            } else if (isMediaDocument(uri)) { // MediaProvider\n                final String docId = DocumentsContract.getDocumentId(uri);\n                final String[] split = docId.split(\":\");\n                final String type = split[0];\n\n                Uri contentUri = null;\n                if (\"image\".equals(type)) {\n                    contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;\n                } else if (\"video\".equals(type)) {\n                    contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;\n                } else if (\"audio\".equals(type)) {\n                    contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;\n                }\n\n                final String selection = \"_id=?\";\n                final String[] selectionArgs = new String[]{\n                        split[1]\n                };\n\n                return getDataColumn(context, contentUri, selection, selectionArgs);\n            }\n        } else if (\"content\".equalsIgnoreCase(uri.getScheme())) { // MediaStore (and general)\n            return getDataColumn(context, uri, null, null);\n        } else if (\"file\".equalsIgnoreCase(uri.getScheme())) { // File\n            return uri.getPath();\n        }\n\n        return null;\n    }\n\n    /**\n     * Get the value of the data column for this Uri. This is useful for\n     * MediaStore Uris, and other file-based ContentProviders.\n     *\n     * @param context       The context.\n     * @param uri           The Uri to query.\n     * @param selection     (Optional) Filter used in the query.\n     * @param selectionArgs (Optional) Selection arguments used in the query.\n     * @return The value of the _data column, which is typically a file path.\n     */\n    public static String getDataColumn(Context context, Uri uri, String selection,\n                                       String[] selectionArgs) {\n\n        Cursor cursor = null;\n        final String column = \"_data\";\n        final String[] projection = {\n                column\n        };\n\n        try {\n            cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);\n            if (cursor != null && cursor.moveToFirst()) {\n                final int columnIndex = cursor.getColumnIndexOrThrow(column);\n                return cursor.getString(columnIndex);\n            }\n        } finally {\n            if (cursor != null)\n                cursor.close();\n        }\n        return null;\n    }\n\n\n    /**\n     * @param uri The Uri to check.\n     * @return Whether the Uri authority is ExternalStorageProvider.\n     */\n    public static boolean isExternalStorageDocument(Uri uri) {\n        return \"com.android.externalstorage.documents\".equals(uri.getAuthority());\n    }\n\n    /**\n     * @param uri The Uri to check.\n     * @return Whether the Uri authority is DownloadsProvider.\n     */\n    public static boolean isDownloadsDocument(Uri uri) {\n        return \"com.android.providers.downloads.documents\".equals(uri.getAuthority());\n    }\n\n    /**\n     * @param uri The Uri to check.\n     * @return Whether the Uri authority is MediaProvider.\n     */\n    public static boolean isMediaDocument(Uri uri) {\n        return \"com.android.providers.media.documents\".equals(uri.getAuthority());\n    }\n}\n"
  },
  {
    "path": "matisse/src/main/java/com/zhihu/matisse/internal/utils/PhotoMetadataUtils.java",
    "content": "/*\n * Copyright (C) 2014 nohana, Inc.\n * Copyright 2017 Zhihu Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an &quot;AS IS&quot; BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.zhihu.matisse.internal.utils;\n\nimport android.app.Activity;\nimport android.content.ContentResolver;\nimport android.content.Context;\nimport android.database.Cursor;\nimport android.graphics.BitmapFactory;\nimport android.graphics.Point;\nimport android.media.ExifInterface;\nimport android.net.Uri;\nimport android.provider.MediaStore;\nimport android.util.DisplayMetrics;\nimport android.util.Log;\n\nimport com.zhihu.matisse.MimeType;\nimport com.zhihu.matisse.R;\nimport com.zhihu.matisse.filter.Filter;\nimport com.zhihu.matisse.internal.entity.Item;\nimport com.zhihu.matisse.internal.entity.SelectionSpec;\nimport com.zhihu.matisse.internal.entity.IncapableCause;\n\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.text.DecimalFormat;\nimport java.text.NumberFormat;\nimport java.util.Locale;\n\npublic final class PhotoMetadataUtils {\n    private static final String TAG = PhotoMetadataUtils.class.getSimpleName();\n    private static final int MAX_WIDTH = 1600;\n    private static final String SCHEME_CONTENT = \"content\";\n\n    private PhotoMetadataUtils() {\n        throw new AssertionError(\"oops! the utility class is about to be instantiated...\");\n    }\n\n    public static int getPixelsCount(ContentResolver resolver, Uri uri) {\n        Point size = getBitmapBound(resolver, uri);\n        return size.x * size.y;\n    }\n\n    public static Point getBitmapSize(Uri uri, Activity activity) {\n        ContentResolver resolver = activity.getContentResolver();\n        Point imageSize = getBitmapBound(resolver, uri);\n        int w = imageSize.x;\n        int h = imageSize.y;\n        if (PhotoMetadataUtils.shouldRotate(resolver, uri)) {\n            w = imageSize.y;\n            h = imageSize.x;\n        }\n        if (h == 0) return new Point(MAX_WIDTH, MAX_WIDTH);\n        DisplayMetrics metrics = new DisplayMetrics();\n        activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);\n        float screenWidth = (float) metrics.widthPixels;\n        float screenHeight = (float) metrics.heightPixels;\n        float widthScale = screenWidth / w;\n        float heightScale = screenHeight / h;\n        if (widthScale > heightScale) {\n            return new Point((int) (w * widthScale), (int) (h * heightScale));\n        }\n        return new Point((int) (w * widthScale), (int) (h * heightScale));\n    }\n\n    public static Point getBitmapBound(ContentResolver resolver, Uri uri) {\n        InputStream is = null;\n        try {\n            BitmapFactory.Options options = new BitmapFactory.Options();\n            options.inJustDecodeBounds = true;\n            is = resolver.openInputStream(uri);\n            BitmapFactory.decodeStream(is, null, options);\n            int width = options.outWidth;\n            int height = options.outHeight;\n            return new Point(width, height);\n        } catch (FileNotFoundException e) {\n            return new Point(0, 0);\n        } finally {\n            if (is != null) {\n                try {\n                    is.close();\n                } catch (IOException e) {\n                    e.printStackTrace();\n                }\n            }\n        }\n    }\n\n    public static String getPath(ContentResolver resolver, Uri uri) {\n        if (uri == null) {\n            return null;\n        }\n\n        if (SCHEME_CONTENT.equals(uri.getScheme())) {\n            Cursor cursor = null;\n            try {\n                cursor = resolver.query(uri, new String[]{MediaStore.Images.ImageColumns.DATA},\n                        null, null, null);\n                if (cursor == null || !cursor.moveToFirst()) {\n                    return null;\n                }\n                return cursor.getString(cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA));\n            } finally {\n                if (cursor != null) {\n                    cursor.close();\n                }\n            }\n        }\n        return uri.getPath();\n    }\n\n    public static IncapableCause isAcceptable(Context context, Item item) {\n        if (!isSelectableType(context, item)) {\n            return new IncapableCause(context.getString(R.string.error_file_type));\n        }\n\n        if (SelectionSpec.getInstance().filters != null) {\n            for (Filter filter : SelectionSpec.getInstance().filters) {\n                IncapableCause incapableCause = filter.filter(context, item);\n                if (incapableCause != null) {\n                    return incapableCause;\n                }\n            }\n        }\n        return null;\n    }\n\n    private static boolean isSelectableType(Context context, Item item) {\n        if (context == null) {\n            return false;\n        }\n\n        ContentResolver resolver = context.getContentResolver();\n        for (MimeType type : SelectionSpec.getInstance().mimeTypeSet) {\n            if (type.checkType(resolver, item.getContentUri())) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    private static boolean shouldRotate(ContentResolver resolver, Uri uri) {\n        ExifInterface exif;\n        try {\n            exif = ExifInterfaceCompat.newInstance(getPath(resolver, uri));\n        } catch (IOException e) {\n            Log.e(TAG, \"could not read exif info of the image: \" + uri);\n            return false;\n        }\n        int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, -1);\n        return orientation == ExifInterface.ORIENTATION_ROTATE_90\n                || orientation == ExifInterface.ORIENTATION_ROTATE_270;\n    }\n\n    public static float getSizeInMB(long sizeInBytes) {\n        DecimalFormat df = (DecimalFormat) NumberFormat.getNumberInstance(Locale.US);\n        df.applyPattern(\"0.0\");\n        String result = df.format((float) sizeInBytes / 1024 / 1024);\n        Log.e(TAG, \"getSizeInMB: \" + result);\n        result = result.replaceAll(\",\", \".\"); // in some case , 0.0 will be 0,0\n        return Float.valueOf(result);\n    }\n}\n"
  },
  {
    "path": "matisse/src/main/java/com/zhihu/matisse/internal/utils/Platform.java",
    "content": "package com.zhihu.matisse.internal.utils;\n\nimport android.os.Build;\n\n/**\n * @author JoongWon Baik\n */\npublic class Platform {\n    public static boolean hasICS() {\n        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH;\n    }\n\n    public static boolean hasKitKat() {\n        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;\n    }\n}\n"
  },
  {
    "path": "matisse/src/main/java/com/zhihu/matisse/internal/utils/SingleMediaScanner.java",
    "content": "package com.zhihu.matisse.internal.utils;\n\nimport android.content.Context;\nimport android.media.MediaScannerConnection;\nimport android.net.Uri;\n\n/**\n * @author 工藤\n * @email gougou@16fan.com\n * create at 2018年10月23日12:17:59\n * description:媒体扫描\n */\npublic class SingleMediaScanner implements MediaScannerConnection.MediaScannerConnectionClient {\n\n    private MediaScannerConnection mMsc;\n    private String mPath;\n    private ScanListener mListener;\n\n    public interface ScanListener {\n\n        /**\n         * scan finish\n         */\n        void onScanFinish();\n    }\n\n    public SingleMediaScanner(Context context, String mPath, ScanListener mListener) {\n        this.mPath = mPath;\n        this.mListener = mListener;\n        this.mMsc = new MediaScannerConnection(context, this);\n        this.mMsc.connect();\n    }\n\n    @Override public void onMediaScannerConnected() {\n        mMsc.scanFile(mPath, null);\n    }\n\n    @Override public void onScanCompleted(String mPath, Uri mUri) {\n        mMsc.disconnect();\n        if (mListener != null) {\n            mListener.onScanFinish();\n        }\n    }\n}\n"
  },
  {
    "path": "matisse/src/main/java/com/zhihu/matisse/internal/utils/UIUtils.java",
    "content": "/*\n * Copyright 2017 Zhihu Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.zhihu.matisse.internal.utils;\n\nimport android.content.Context;\n\npublic class UIUtils {\n\n    public static int spanCount(Context context, int gridExpectedSize) {\n        int screenWidth = context.getResources().getDisplayMetrics().widthPixels;\n        float expected = (float) screenWidth / (float) gridExpectedSize;\n        int spanCount = Math.round(expected);\n        if (spanCount == 0) {\n            spanCount = 1;\n        }\n        return spanCount;\n    }\n\n}\n"
  },
  {
    "path": "matisse/src/main/java/com/zhihu/matisse/listener/OnCheckedListener.java",
    "content": "package com.zhihu.matisse.listener;\n\n\n/**\n *  when original is enabled , callback immediately when user check or uncheck original.\n */\npublic interface OnCheckedListener {\n    void onCheck(boolean isChecked);\n}\n"
  },
  {
    "path": "matisse/src/main/java/com/zhihu/matisse/listener/OnFragmentInteractionListener.java",
    "content": "package com.zhihu.matisse.listener;\n\n/**\n *  PreViewItemFragment 和  BasePreViewActivity 通信的接口 ，为了方便拿到 ImageViewTouch 的点击事件\n */\npublic interface OnFragmentInteractionListener {\n    /**\n     *  ImageViewTouch 被点击了\n     */\n    void onClick();\n}\n"
  },
  {
    "path": "matisse/src/main/java/com/zhihu/matisse/listener/OnSelectedListener.java",
    "content": "/*\n * Copyright 2017 Zhihu Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.zhihu.matisse.listener;\n\nimport android.net.Uri;\nimport androidx.annotation.NonNull;\n\nimport java.util.List;\n\npublic interface OnSelectedListener {\n    /**\n     * @param uriList the selected item {@link Uri} list.\n     * @param pathList the selected item file path list.\n     */\n    void onSelected(@NonNull List<Uri> uriList, @NonNull List<String> pathList);\n}\n"
  },
  {
    "path": "matisse/src/main/java/com/zhihu/matisse/ui/MatisseActivity.java",
    "content": "/*\n * Copyright 2017 Zhihu Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.zhihu.matisse.ui;\n\nimport android.app.Activity;\nimport android.content.Intent;\nimport android.content.res.TypedArray;\nimport android.database.Cursor;\nimport android.graphics.PorterDuff;\nimport android.graphics.drawable.Drawable;\nimport android.net.Uri;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.os.Looper;\nimport androidx.annotation.Nullable;\nimport androidx.fragment.app.Fragment;\nimport androidx.appcompat.app.ActionBar;\nimport androidx.appcompat.app.AppCompatActivity;\nimport androidx.appcompat.widget.Toolbar;\n\nimport android.util.Log;\nimport android.view.MenuItem;\nimport android.view.View;\nimport android.widget.AdapterView;\nimport android.widget.LinearLayout;\nimport android.widget.TextView;\n\nimport com.zhihu.matisse.R;\nimport com.zhihu.matisse.internal.entity.Album;\nimport com.zhihu.matisse.internal.entity.Item;\nimport com.zhihu.matisse.internal.entity.SelectionSpec;\nimport com.zhihu.matisse.internal.model.AlbumCollection;\nimport com.zhihu.matisse.internal.model.SelectedItemCollection;\nimport com.zhihu.matisse.internal.ui.AlbumPreviewActivity;\nimport com.zhihu.matisse.internal.ui.BasePreviewActivity;\nimport com.zhihu.matisse.internal.ui.MediaSelectionFragment;\nimport com.zhihu.matisse.internal.ui.SelectedPreviewActivity;\nimport com.zhihu.matisse.internal.ui.adapter.AlbumMediaAdapter;\nimport com.zhihu.matisse.internal.ui.adapter.AlbumsAdapter;\nimport com.zhihu.matisse.internal.ui.widget.AlbumsSpinner;\nimport com.zhihu.matisse.internal.ui.widget.CheckRadioView;\nimport com.zhihu.matisse.internal.ui.widget.IncapableDialog;\nimport com.zhihu.matisse.internal.utils.MediaStoreCompat;\nimport com.zhihu.matisse.internal.utils.PathUtils;\nimport com.zhihu.matisse.internal.utils.PhotoMetadataUtils;\n\nimport com.zhihu.matisse.internal.utils.SingleMediaScanner;\nimport java.util.ArrayList;\n\n/**\n * Main Activity to display albums and media content (images/videos) in each album\n * and also support media selecting operations.\n */\npublic class MatisseActivity extends AppCompatActivity implements\n        AlbumCollection.AlbumCallbacks, AdapterView.OnItemSelectedListener,\n        MediaSelectionFragment.SelectionProvider, View.OnClickListener,\n        AlbumMediaAdapter.CheckStateListener, AlbumMediaAdapter.OnMediaClickListener,\n        AlbumMediaAdapter.OnPhotoCapture {\n\n    public static final String EXTRA_RESULT_SELECTION = \"extra_result_selection\";\n    public static final String EXTRA_RESULT_SELECTION_PATH = \"extra_result_selection_path\";\n    public static final String EXTRA_RESULT_ORIGINAL_ENABLE = \"extra_result_original_enable\";\n    private static final int REQUEST_CODE_PREVIEW = 23;\n    private static final int REQUEST_CODE_CAPTURE = 24;\n    public static final String CHECK_STATE = \"checkState\";\n    private final AlbumCollection mAlbumCollection = new AlbumCollection();\n    private MediaStoreCompat mMediaStoreCompat;\n    private SelectedItemCollection mSelectedCollection = new SelectedItemCollection(this);\n    private SelectionSpec mSpec;\n\n    private AlbumsSpinner mAlbumsSpinner;\n    private AlbumsAdapter mAlbumsAdapter;\n    private TextView mButtonPreview;\n    private TextView mButtonApply;\n    private View mContainer;\n    private View mEmptyView;\n\n    private LinearLayout mOriginalLayout;\n    private CheckRadioView mOriginal;\n    private boolean mOriginalEnable;\n\n    @Override\n    protected void onCreate(@Nullable Bundle savedInstanceState) {\n        // programmatically set theme before super.onCreate()\n        mSpec = SelectionSpec.getInstance();\n        setTheme(mSpec.themeId);\n        super.onCreate(savedInstanceState);\n        if (!mSpec.hasInited) {\n            setResult(RESULT_CANCELED);\n            finish();\n            return;\n        }\n        setContentView(R.layout.activity_matisse);\n\n        if (mSpec.needOrientationRestriction()) {\n            setRequestedOrientation(mSpec.orientation);\n        }\n\n        if (mSpec.capture) {\n            mMediaStoreCompat = new MediaStoreCompat(this);\n            if (mSpec.captureStrategy == null)\n                throw new RuntimeException(\"Don't forget to set CaptureStrategy.\");\n            mMediaStoreCompat.setCaptureStrategy(mSpec.captureStrategy);\n        }\n\n        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);\n        setSupportActionBar(toolbar);\n        ActionBar actionBar = getSupportActionBar();\n        actionBar.setDisplayShowTitleEnabled(false);\n        actionBar.setDisplayHomeAsUpEnabled(true);\n        Drawable navigationIcon = toolbar.getNavigationIcon();\n        TypedArray ta = getTheme().obtainStyledAttributes(new int[]{R.attr.album_element_color});\n        int color = ta.getColor(0, 0);\n        ta.recycle();\n        navigationIcon.setColorFilter(color, PorterDuff.Mode.SRC_IN);\n\n        mButtonPreview = (TextView) findViewById(R.id.button_preview);\n        mButtonApply = (TextView) findViewById(R.id.button_apply);\n        mButtonPreview.setOnClickListener(this);\n        mButtonApply.setOnClickListener(this);\n        mContainer = findViewById(R.id.container);\n        mEmptyView = findViewById(R.id.empty_view);\n        mOriginalLayout = findViewById(R.id.originalLayout);\n        mOriginal = findViewById(R.id.original);\n        mOriginalLayout.setOnClickListener(this);\n\n        mSelectedCollection.onCreate(savedInstanceState);\n        if (savedInstanceState != null) {\n            mOriginalEnable = savedInstanceState.getBoolean(CHECK_STATE);\n        }\n        updateBottomToolbar();\n\n        mAlbumsAdapter = new AlbumsAdapter(this, null, false);\n        mAlbumsSpinner = new AlbumsSpinner(this);\n        mAlbumsSpinner.setOnItemSelectedListener(this);\n        mAlbumsSpinner.setSelectedTextView((TextView) findViewById(R.id.selected_album));\n        mAlbumsSpinner.setPopupAnchorView(findViewById(R.id.toolbar));\n        mAlbumsSpinner.setAdapter(mAlbumsAdapter);\n        mAlbumCollection.onCreate(this, this);\n        mAlbumCollection.onRestoreInstanceState(savedInstanceState);\n        mAlbumCollection.loadAlbums();\n    }\n\n    @Override\n    protected void onSaveInstanceState(Bundle outState) {\n        super.onSaveInstanceState(outState);\n        mSelectedCollection.onSaveInstanceState(outState);\n        mAlbumCollection.onSaveInstanceState(outState);\n        outState.putBoolean(\"checkState\", mOriginalEnable);\n    }\n\n    @Override\n    protected void onDestroy() {\n        super.onDestroy();\n        mAlbumCollection.onDestroy();\n        mSpec.onCheckedListener = null;\n        mSpec.onSelectedListener = null;\n    }\n\n    @Override\n    public boolean onOptionsItemSelected(MenuItem item) {\n        if (item.getItemId() == android.R.id.home) {\n            onBackPressed();\n            return true;\n        }\n        return super.onOptionsItemSelected(item);\n    }\n\n    @Override\n    public void onBackPressed() {\n        setResult(Activity.RESULT_CANCELED);\n        super.onBackPressed();\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            return;\n\n        if (requestCode == REQUEST_CODE_PREVIEW) {\n            Bundle resultBundle = data.getBundleExtra(BasePreviewActivity.EXTRA_RESULT_BUNDLE);\n            ArrayList<Item> selected = resultBundle.getParcelableArrayList(SelectedItemCollection.STATE_SELECTION);\n            mOriginalEnable = data.getBooleanExtra(BasePreviewActivity.EXTRA_RESULT_ORIGINAL_ENABLE, false);\n            int collectionType = resultBundle.getInt(SelectedItemCollection.STATE_COLLECTION_TYPE,\n                    SelectedItemCollection.COLLECTION_UNDEFINED);\n            if (data.getBooleanExtra(BasePreviewActivity.EXTRA_RESULT_APPLY, false)) {\n                Intent result = new Intent();\n                ArrayList<Uri> selectedUris = new ArrayList<>();\n                ArrayList<String> selectedPaths = new ArrayList<>();\n                if (selected != null) {\n                    for (Item item : selected) {\n                        selectedUris.add(item.getContentUri());\n                        selectedPaths.add(PathUtils.getPath(this, item.getContentUri()));\n                    }\n                }\n                result.putParcelableArrayListExtra(EXTRA_RESULT_SELECTION, selectedUris);\n                result.putStringArrayListExtra(EXTRA_RESULT_SELECTION_PATH, selectedPaths);\n                result.putExtra(EXTRA_RESULT_ORIGINAL_ENABLE, mOriginalEnable);\n                setResult(RESULT_OK, result);\n                finish();\n            } else {\n                mSelectedCollection.overwrite(selected, collectionType);\n                Fragment mediaSelectionFragment = getSupportFragmentManager().findFragmentByTag(\n                        MediaSelectionFragment.class.getSimpleName());\n                if (mediaSelectionFragment instanceof MediaSelectionFragment) {\n                    ((MediaSelectionFragment) mediaSelectionFragment).refreshMediaGrid();\n                }\n                updateBottomToolbar();\n            }\n        } else if (requestCode == REQUEST_CODE_CAPTURE) {\n            // Just pass the data back to previous calling Activity.\n            Uri contentUri = mMediaStoreCompat.getCurrentPhotoUri();\n            String path = mMediaStoreCompat.getCurrentPhotoPath();\n            ArrayList<Uri> selected = new ArrayList<>();\n            selected.add(contentUri);\n            ArrayList<String> selectedPath = new ArrayList<>();\n            selectedPath.add(path);\n            Intent result = new Intent();\n            result.putParcelableArrayListExtra(EXTRA_RESULT_SELECTION, selected);\n            result.putStringArrayListExtra(EXTRA_RESULT_SELECTION_PATH, selectedPath);\n            setResult(RESULT_OK, result);\n            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)\n                MatisseActivity.this.revokeUriPermission(contentUri,\n                        Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);\n\n            new SingleMediaScanner(this.getApplicationContext(), path, new SingleMediaScanner.ScanListener() {\n                @Override public void onScanFinish() {\n                    Log.i(\"SingleMediaScanner\", \"scan finish!\");\n                }\n            });\n            finish();\n        }\n    }\n\n    private void updateBottomToolbar() {\n\n        int selectedCount = mSelectedCollection.count();\n        if (selectedCount == 0) {\n            mButtonPreview.setEnabled(false);\n            mButtonApply.setEnabled(false);\n            mButtonApply.setText(getString(R.string.button_apply_default));\n        } else if (selectedCount == 1 && mSpec.singleSelectionModeEnabled()) {\n            mButtonPreview.setEnabled(true);\n            mButtonApply.setText(R.string.button_apply_default);\n            mButtonApply.setEnabled(true);\n        } else {\n            mButtonPreview.setEnabled(true);\n            mButtonApply.setEnabled(true);\n            mButtonApply.setText(getString(R.string.button_apply, selectedCount));\n        }\n\n\n        if (mSpec.originalable) {\n            mOriginalLayout.setVisibility(View.VISIBLE);\n            updateOriginalState();\n        } else {\n            mOriginalLayout.setVisibility(View.INVISIBLE);\n        }\n\n\n    }\n\n\n    private void updateOriginalState() {\n        mOriginal.setChecked(mOriginalEnable);\n        if (countOverMaxSize() > 0) {\n\n            if (mOriginalEnable) {\n                IncapableDialog incapableDialog = IncapableDialog.newInstance(\"\",\n                        getString(R.string.error_over_original_size, mSpec.originalMaxSize));\n                incapableDialog.show(getSupportFragmentManager(),\n                        IncapableDialog.class.getName());\n\n                mOriginal.setChecked(false);\n                mOriginalEnable = false;\n            }\n        }\n    }\n\n\n    private int countOverMaxSize() {\n        int count = 0;\n        int selectedCount = mSelectedCollection.count();\n        for (int i = 0; i < selectedCount; i++) {\n            Item item = mSelectedCollection.asList().get(i);\n\n            if (item.isImage()) {\n                float size = PhotoMetadataUtils.getSizeInMB(item.size);\n                if (size > mSpec.originalMaxSize) {\n                    count++;\n                }\n            }\n        }\n        return count;\n    }\n\n    @Override\n    public void onClick(View v) {\n        if (v.getId() == R.id.button_preview) {\n            Intent intent = new Intent(this, SelectedPreviewActivity.class);\n            intent.putExtra(BasePreviewActivity.EXTRA_DEFAULT_BUNDLE, mSelectedCollection.getDataWithBundle());\n            intent.putExtra(BasePreviewActivity.EXTRA_RESULT_ORIGINAL_ENABLE, mOriginalEnable);\n            startActivityForResult(intent, REQUEST_CODE_PREVIEW);\n        } else if (v.getId() == R.id.button_apply) {\n            Intent result = new Intent();\n            ArrayList<Uri> selectedUris = (ArrayList<Uri>) mSelectedCollection.asListOfUri();\n            result.putParcelableArrayListExtra(EXTRA_RESULT_SELECTION, selectedUris);\n            ArrayList<String> selectedPaths = (ArrayList<String>) mSelectedCollection.asListOfString();\n            result.putStringArrayListExtra(EXTRA_RESULT_SELECTION_PATH, selectedPaths);\n            result.putExtra(EXTRA_RESULT_ORIGINAL_ENABLE, mOriginalEnable);\n            setResult(RESULT_OK, result);\n            finish();\n        } else if (v.getId() == R.id.originalLayout) {\n            int count = countOverMaxSize();\n            if (count > 0) {\n                IncapableDialog incapableDialog = IncapableDialog.newInstance(\"\",\n                        getString(R.string.error_over_original_count, count, mSpec.originalMaxSize));\n                incapableDialog.show(getSupportFragmentManager(),\n                        IncapableDialog.class.getName());\n                return;\n            }\n\n            mOriginalEnable = !mOriginalEnable;\n            mOriginal.setChecked(mOriginalEnable);\n\n            if (mSpec.onCheckedListener != null) {\n                mSpec.onCheckedListener.onCheck(mOriginalEnable);\n            }\n        }\n    }\n\n    @Override\n    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {\n        mAlbumCollection.setStateCurrentSelection(position);\n        mAlbumsAdapter.getCursor().moveToPosition(position);\n        Album album = Album.valueOf(mAlbumsAdapter.getCursor());\n        if (album.isAll() && SelectionSpec.getInstance().capture) {\n            album.addCaptureCount();\n        }\n        onAlbumSelected(album);\n    }\n\n    @Override\n    public void onNothingSelected(AdapterView<?> parent) {\n\n    }\n\n    @Override\n    public void onAlbumLoad(final Cursor cursor) {\n        mAlbumsAdapter.swapCursor(cursor);\n        // select default album.\n        Handler handler = new Handler(Looper.getMainLooper());\n        handler.post(new Runnable() {\n\n            @Override\n            public void run() {\n                cursor.moveToPosition(mAlbumCollection.getCurrentSelection());\n                mAlbumsSpinner.setSelection(MatisseActivity.this,\n                        mAlbumCollection.getCurrentSelection());\n                Album album = Album.valueOf(cursor);\n                if (album.isAll() && SelectionSpec.getInstance().capture) {\n                    album.addCaptureCount();\n                }\n                onAlbumSelected(album);\n            }\n        });\n    }\n\n    @Override\n    public void onAlbumReset() {\n        mAlbumsAdapter.swapCursor(null);\n    }\n\n    private void onAlbumSelected(Album album) {\n        if (album.isAll() && album.isEmpty()) {\n            mContainer.setVisibility(View.GONE);\n            mEmptyView.setVisibility(View.VISIBLE);\n        } else {\n            mContainer.setVisibility(View.VISIBLE);\n            mEmptyView.setVisibility(View.GONE);\n            Fragment fragment = MediaSelectionFragment.newInstance(album);\n            getSupportFragmentManager()\n                    .beginTransaction()\n                    .replace(R.id.container, fragment, MediaSelectionFragment.class.getSimpleName())\n                    .commitAllowingStateLoss();\n        }\n    }\n\n    @Override\n    public void onUpdate() {\n        // notify bottom toolbar that check state changed.\n        updateBottomToolbar();\n\n        if (mSpec.onSelectedListener != null) {\n            mSpec.onSelectedListener.onSelected(\n                    mSelectedCollection.asListOfUri(), mSelectedCollection.asListOfString());\n        }\n    }\n\n    @Override\n    public void onMediaClick(Album album, Item item, int adapterPosition) {\n        Intent intent = new Intent(this, AlbumPreviewActivity.class);\n        intent.putExtra(AlbumPreviewActivity.EXTRA_ALBUM, album);\n        intent.putExtra(AlbumPreviewActivity.EXTRA_ITEM, item);\n        intent.putExtra(BasePreviewActivity.EXTRA_DEFAULT_BUNDLE, mSelectedCollection.getDataWithBundle());\n        intent.putExtra(BasePreviewActivity.EXTRA_RESULT_ORIGINAL_ENABLE, mOriginalEnable);\n        startActivityForResult(intent, REQUEST_CODE_PREVIEW);\n    }\n\n    @Override\n    public SelectedItemCollection provideSelectedItemCollection() {\n        return mSelectedCollection;\n    }\n\n    @Override\n    public void capture() {\n        if (mMediaStoreCompat != null) {\n            mMediaStoreCompat.dispatchCaptureIntent(this, REQUEST_CODE_CAPTURE);\n        }\n    }\n\n}\n"
  },
  {
    "path": "matisse/src/main/res/color/dracula_bottom_toolbar_apply.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright 2017 Zhihu Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n  -->\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:color=\"@color/dracula_bottom_toolbar_apply_text_disable\"\n          android:state_enabled=\"false\"/>\n    <item android:color=\"@color/dracula_bottom_toolbar_apply_text\"/>\n</selector>"
  },
  {
    "path": "matisse/src/main/res/color/dracula_bottom_toolbar_preview.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright 2017 Zhihu Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n  -->\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:color=\"@color/dracula_bottom_toolbar_preview_text_disable\"\n          android:state_enabled=\"false\"/>\n    <item android:color=\"@color/dracula_bottom_toolbar_preview_text\"/>\n</selector>"
  },
  {
    "path": "matisse/src/main/res/color/dracula_preview_bottom_toolbar_apply.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright 2017 Zhihu Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n  -->\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:color=\"@color/dracula_preview_bottom_toolbar_apply_text_disable\"\n          android:state_enabled=\"false\"/>\n    <item android:color=\"@color/dracula_preview_bottom_toolbar_apply_text\"/>\n</selector>"
  },
  {
    "path": "matisse/src/main/res/color/zhihu_bottom_toolbar_apply.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright 2017 Zhihu Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n  -->\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:color=\"@color/zhihu_bottom_toolbar_apply_text_disable\"\n          android:state_enabled=\"false\"/>\n    <item android:color=\"@color/zhihu_bottom_toolbar_apply_text\"/>\n</selector>"
  },
  {
    "path": "matisse/src/main/res/color/zhihu_bottom_toolbar_preview.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright 2017 Zhihu Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n  -->\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:color=\"@color/zhihu_bottom_toolbar_preview_text_disable\"\n          android:state_enabled=\"false\"/>\n    <item android:color=\"@color/zhihu_bottom_toolbar_preview_text\"/>\n</selector>"
  },
  {
    "path": "matisse/src/main/res/color/zhihu_preview_bottom_toolbar_apply.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright 2017 Zhihu Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n  -->\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:color=\"@color/zhihu_preview_bottom_toolbar_apply_text_disable\"\n          android:state_enabled=\"false\"/>\n    <item android:color=\"@color/zhihu_preview_bottom_toolbar_apply_text\"/>\n</selector>\n"
  },
  {
    "path": "matisse/src/main/res/layout/activity_matisse.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  Copyright 2017 Zhihu Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n  -->\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/root\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\">\n\n    <androidx.appcompat.widget.Toolbar\n        android:id=\"@+id/toolbar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"?colorPrimary\"\n        android:elevation=\"4dp\"\n        android:theme=\"?toolbar\"\n        tools:targetApi=\"lollipop\">\n\n        <TextView\n            android:id=\"@+id/selected_album\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"?actionBarSize\"\n            android:drawableRight=\"@drawable/ic_arrow_drop_down_white_24dp\"\n            android:foreground=\"?selectableItemBackground\"\n            android:gravity=\"center\"\n            android:textColor=\"?attr/album.element.color\"\n            android:textSize=\"20sp\" />\n    </androidx.appcompat.widget.Toolbar>\n\n    <FrameLayout\n        android:id=\"@+id/bottom_toolbar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentBottom=\"true\"\n        android:background=\"?attr/bottomToolbar.bg\"\n        android:elevation=\"4dp\"\n        tools:targetApi=\"lollipop\">\n\n        <TextView\n            android:id=\"@+id/button_preview\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"start\"\n            android:foreground=\"?selectableItemBackground\"\n            android:padding=\"16dp\"\n            android:text=\"@string/button_preview\"\n            android:textColor=\"?attr/bottomToolbar.preview.textColor\"\n            android:textSize=\"16sp\" />\n\n        <LinearLayout\n            android:padding=\"16dp\"\n            android:id=\"@+id/originalLayout\"\n            android:visibility=\"visible\"\n            android:orientation=\"horizontal\"\n            android:layout_gravity=\"center\"\n            android:layout_width=\"wrap_content\"\n            android:foreground=\"?selectableItemBackground\"\n            android:layout_height=\"wrap_content\"\n            tools:showIn=\"@layout/activity_matisse\">\n\n\n            <com.zhihu.matisse.internal.ui.widget.CheckRadioView\n                android:id=\"@+id/original\"\n                android:src=\"@drawable/ic_preview_radio_off\"\n                android:layout_gravity=\"center_vertical\"\n                android:layout_width=\"16dp\"\n                android:layout_height=\"16dp\" />\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_gravity=\"center_vertical\"\n                android:enabled=\"true\"\n                android:paddingStart=\"4dp\"\n                android:paddingLeft=\"4dp\"\n                android:text=\"@string/button_original\"\n                android:textColor=\"?attr/bottomToolbar.preview.textColor\"\n                android:textSize=\"14sp\" />\n\n        </LinearLayout>\n\n\n        <TextView\n            android:id=\"@+id/button_apply\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"end\"\n            android:foreground=\"?selectableItemBackground\"\n            android:padding=\"16dp\"\n            android:textColor=\"?attr/bottomToolbar.apply.textColor\"\n            android:textSize=\"16sp\" />\n    </FrameLayout>\n\n    <FrameLayout\n        android:id=\"@+id/container\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_above=\"@id/bottom_toolbar\"\n        android:layout_below=\"@id/toolbar\"\n        android:visibility=\"gone\" />\n\n    <FrameLayout\n        android:id=\"@+id/empty_view\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_above=\"@id/bottom_toolbar\"\n        android:layout_below=\"@id/toolbar\"\n        android:visibility=\"gone\">\n\n        <TextView\n            android:id=\"@+id/empty_view_content\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"center\"\n            android:drawablePadding=\"8dp\"\n            android:drawableTop=\"?attr/album.emptyView\"\n            android:gravity=\"center\"\n            android:text=\"@string/empty_text\"\n            android:textColor=\"?attr/album.emptyView.textColor\"\n            android:textSize=\"16sp\" />\n\n    </FrameLayout>\n</RelativeLayout>"
  },
  {
    "path": "matisse/src/main/res/layout/activity_media_preview.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright 2017 Zhihu Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n  -->\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\n    <com.zhihu.matisse.internal.ui.widget.PreviewViewPager\n        android:id=\"@+id/pager\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:background=\"@android:color/black\"/>\n\n    <FrameLayout\n        android:id=\"@+id/bottom_toolbar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentBottom=\"true\"\n        android:background=\"@color/preview_bottom_toolbar_bg\"\n        android:elevation=\"4dp\"\n        tools:targetApi=\"lollipop\">\n\n        <TextView\n            android:id=\"@+id/button_back\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:foreground=\"?selectableItemBackground\"\n            android:padding=\"16dp\"\n            android:layout_gravity=\"start\"\n            android:text=\"@string/button_back\"\n            android:textColor=\"?attr/preview.bottomToolbar.back.textColor\"\n            android:textSize=\"16sp\"/>\n\n\n        <LinearLayout\n            android:layout_gravity=\"center\"\n            android:orientation=\"horizontal\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\">\n\n\n            <LinearLayout\n                android:padding=\"16dp\"\n                android:id=\"@+id/originalLayout\"\n                android:visibility=\"gone\"\n                android:orientation=\"horizontal\"\n                android:layout_gravity=\"center\"\n                android:layout_width=\"wrap_content\"\n                android:foreground=\"?selectableItemBackground\"\n                android:layout_height=\"wrap_content\"\n                tools:showIn=\"@layout/activity_matisse\">\n\n\n                <com.zhihu.matisse.internal.ui.widget.CheckRadioView\n                    android:id=\"@+id/original\"\n                    android:src=\"@drawable/ic_preview_radio_off\"\n                    android:layout_gravity=\"center_vertical\"\n                    android:tint=\"#ffffff\"\n                    android:layout_width=\"16dp\"\n                    android:layout_height=\"16dp\" />\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_gravity=\"center_vertical\"\n                    android:enabled=\"true\"\n                    android:paddingStart=\"4dp\"\n                    android:paddingLeft=\"4dp\"\n                    android:text=\"@string/button_original\"\n                    android:textColor=\"?attr/preview.bottomToolbar.back.textColor\"\n                    android:textSize=\"14sp\" />\n\n            </LinearLayout>\n\n\n\n            <TextView\n                android:id=\"@+id/size\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_gravity=\"center\"\n                android:textColor=\"@color/preview_bottom_size\"\n                android:textSize=\"16sp\"\n                android:visibility=\"gone\"/>\n        </LinearLayout>\n\n\n\n        <TextView\n            android:id=\"@+id/button_apply\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"end\"\n            android:foreground=\"?selectableItemBackground\"\n            android:padding=\"16dp\"\n            android:textColor=\"?attr/preview.bottomToolbar.apply.textColor\"\n            android:textSize=\"16sp\"/>\n\n    </FrameLayout>\n\n    <FrameLayout\n        android:id=\"@+id/top_toolbar\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentRight=\"true\"\n        android:layout_margin=\"8dp\"\n        android:fitsSystemWindows=\"true\">\n\n        <com.zhihu.matisse.internal.ui.widget.CheckView\n            android:id=\"@+id/check_view\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:padding=\"10dp\"/>\n    </FrameLayout>\n\n</RelativeLayout>"
  },
  {
    "path": "matisse/src/main/res/layout/album_list_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright 2017 Zhihu Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n  -->\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"@dimen/album_item_height\">\n\n    <com.zhihu.matisse.internal.ui.widget.RoundedRectangleImageView\n        android:id=\"@+id/album_cover\"\n        android:layout_width=\"40dp\"\n        android:layout_height=\"40dp\"\n        android:layout_centerVertical=\"true\"\n        android:layout_marginStart=\"16dp\"\n        android:layout_marginLeft=\"16dp\" />\n\n    <TextView\n        android:id=\"@+id/album_name\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignTop=\"@id/album_cover\"\n        android:layout_marginStart=\"8dp\"\n        android:layout_toEndOf=\"@id/album_cover\"\n        android:ellipsize=\"end\"\n        android:maxLines=\"1\"\n        android:textColor=\"?attr/album.dropdown.title.color\"\n        android:textSize=\"16sp\"\n        android:layout_marginLeft=\"8dp\"\n        android:layout_toRightOf=\"@id/album_cover\" />\n\n    <TextView\n        android:id=\"@+id/album_media_count\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignStart=\"@id/album_name\"\n        android:layout_below=\"@id/album_name\"\n        android:layout_marginTop=\"4dp\"\n        android:layout_toEndOf=\"@id/album_cover\"\n        android:ellipsize=\"end\"\n        android:maxLines=\"1\"\n        android:textColor=\"?album.dropdown.count.color\"\n        android:textSize=\"14sp\"\n        android:layout_alignLeft=\"@id/album_name\"\n        android:layout_toRightOf=\"@id/album_cover\" />\n</RelativeLayout>"
  },
  {
    "path": "matisse/src/main/res/layout/fragment_media_selection.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright 2017 Zhihu Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n  -->\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n             android:layout_width=\"match_parent\"\n             android:layout_height=\"match_parent\"\n             android:background=\"?attr/page.bg\">\n\n    <androidx.recyclerview.widget.RecyclerView\n        android:id=\"@+id/recyclerview\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:clipChildren=\"false\"\n        android:clipToPadding=\"false\"\n        android:paddingBottom=\"@dimen/media_grid_spacing\"\n        android:paddingTop=\"@dimen/media_grid_spacing\"/>\n\n</FrameLayout>\n"
  },
  {
    "path": "matisse/src/main/res/layout/fragment_preview_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright 2017 Zhihu Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n  -->\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n             android:layout_width=\"match_parent\"\n             android:layout_height=\"match_parent\">\n\n    <it.sephiroth.android.library.imagezoom.ImageViewTouch\n        android:id=\"@+id/image_view\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"/>\n\n    <ImageView\n        android:id=\"@+id/video_play_button\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center\"\n        android:src=\"@drawable/ic_play_circle_outline_white_48dp\"/>\n</FrameLayout>\n"
  },
  {
    "path": "matisse/src/main/res/layout/media_grid_content.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright 2017 Zhihu Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n  -->\n<merge xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <ImageView\n        android:id=\"@+id/media_thumbnail\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"/>\n\n    <com.zhihu.matisse.internal.ui.widget.CheckView\n        android:id=\"@+id/check_view\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"top|right\"/>\n\n    <ImageView\n        android:id=\"@+id/gif\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"right|bottom\"\n        android:layout_marginBottom=\"8dp\"\n        android:layout_marginRight=\"8dp\"\n        android:src=\"@drawable/ic_gif\"\n        android:visibility=\"gone\"/>\n\n    <TextView\n        android:id=\"@+id/video_duration\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"right|bottom\"\n        android:layout_marginBottom=\"8dp\"\n        android:layout_marginRight=\"8dp\"\n        android:textColor=\"@android:color/white\"\n        android:textSize=\"12sp\"\n        android:visibility=\"gone\"/>\n</merge>"
  },
  {
    "path": "matisse/src/main/res/layout/media_grid_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright 2017 Zhihu Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n  -->\n<com.zhihu.matisse.internal.ui.widget.MediaGrid\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:foreground=\"?selectableItemBackground\"/>"
  },
  {
    "path": "matisse/src/main/res/layout/photo_capture_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright 2017 Zhihu Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n  -->\n<com.zhihu.matisse.internal.ui.widget.SquareFrameLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:background=\"?item.placeholder\"\n    android:foreground=\"?selectableItemBackground\">\n\n    <TextView\n        android:id=\"@+id/hint\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center\"\n        android:drawablePadding=\"8dp\"\n        android:drawableTop=\"@drawable/ic_photo_camera_white_24dp\"\n        android:gravity=\"center\"\n        android:text=\"@string/photo_grid_capture\"\n        android:textColor=\"?capture.textColor\"\n        android:textSize=\"14sp\"/>\n\n</com.zhihu.matisse.internal.ui.widget.SquareFrameLayout>"
  },
  {
    "path": "matisse/src/main/res/values/attrs.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright 2017 Zhihu Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n  -->\n<resources>\n    <attr name=\"toolbar\" format=\"reference\"/>\n    <attr name=\"album.dropdown.title.color\" format=\"reference|color\"/>\n    <attr name=\"album.dropdown.count.color\" format=\"reference|color\"/>\n    <attr name=\"album.element.color\" format=\"reference|color\"/>\n    <attr name=\"album.thumbnail.placeholder\" format=\"reference|color\"/>\n    <attr name=\"album.emptyView\" format=\"reference\"/>\n    <attr name=\"album.emptyView.textColor\" format=\"reference|color\"/>\n    <attr name=\"listPopupWindowStyle\" format=\"reference\"/>\n    <attr name=\"page.bg\" format=\"reference|color\"/>\n    <attr name=\"bottomToolbar.bg\" format=\"reference|color\"/>\n    <attr name=\"bottomToolbar.preview.textColor\" format=\"reference|color\"/>\n    <attr name=\"bottomToolbar.apply.textColor\" format=\"reference|color\"/>\n    <attr name=\"preview.bottomToolbar.back.textColor\" format=\"reference|color\"/>\n    <attr name=\"preview.bottomToolbar.apply.textColor\" format=\"reference|color\"/>\n    <attr name=\"item.placeholder\" format=\"reference|color\"/>\n    <attr name=\"item.checkCircle.backgroundColor\" format=\"reference|color\"/>\n    <attr name=\"item.checkCircle.borderColor\" format=\"reference|color\"/>\n    <attr name=\"capture.textColor\" format=\"reference|color\"/>\n</resources>"
  },
  {
    "path": "matisse/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright 2017 Zhihu Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n  -->\n<resources>\n\n    <color name=\"preview_bottom_toolbar_bg\">#CC000000</color>\n    <color name=\"preview_bottom_size\">#61FFFFFF</color>\n\n</resources>"
  },
  {
    "path": "matisse/src/main/res/values/colors_dracula.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright 2017 Zhihu Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n  -->\n<resources>\n    <color name=\"dracula_primary\">#263237</color>\n    <color name=\"dracula_primary_dark\">#1D282C</color>\n\n    <color name=\"dracula_album_popup_bg\">#34474E</color>\n    <color name=\"dracula_album_dropdown_title_text\">#DEFFFFFF</color>\n    <color name=\"dracula_album_dropdown_count_text\">#89FFFFFF</color>\n    <color name=\"dracula_album_dropdown_thumbnail_placeholder\">#455A64</color>\n    <color name=\"dracula_album_empty_view\">#4DFFFFFF</color>\n\n    <color name=\"dracula_item_placeholder\">#37474F</color>\n    <color name=\"dracula_item_checkCircle_backgroundColor\">#263237</color>\n    <color name=\"dracula_item_checkCircle_borderColor\">#FFFFFF</color>\n    <color name=\"dracula_capture\">#FFFFFF</color>\n\n    <color name=\"dracula_page_bg\">#232E32</color>\n    <color name=\"dracula_bottom_toolbar_bg\">#34474E</color>\n\n    <color name=\"dracula_bottom_toolbar_preview_text\">#DEFFFFFF</color>\n    <color name=\"dracula_bottom_toolbar_preview_text_disable\">#4DFFFFFF</color>\n    <color name=\"dracula_bottom_toolbar_apply_text\">#03A9F4</color>\n    <color name=\"dracula_bottom_toolbar_apply_text_disable\">#4D03A9F4</color>\n\n    <color name=\"dracula_preview_bottom_toolbar_back_text\">#FFFFFF</color>\n    <color name=\"dracula_preview_bottom_toolbar_apply_text\">#03A9F4</color>\n    <color name=\"dracula_preview_bottom_toolbar_apply_text_disable\">#4D03A9F4</color>\n</resources>"
  },
  {
    "path": "matisse/src/main/res/values/colors_zhihu.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright 2017 Zhihu Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n  -->\n<resources>\n    <color name=\"zhihu_primary\">#1E8AE8</color>\n    <color name=\"zhihu_primary_dark\">#176EB9</color>\n\n    <color name=\"zhihu_album_popup_bg\">#FFFFFF</color>\n    <color name=\"zhihu_album_dropdown_title_text\">#DE000000</color>\n    <color name=\"zhihu_album_dropdown_count_text\">#999999</color>\n    <color name=\"zhihu_album_dropdown_thumbnail_placeholder\">#EAEEF4</color>\n    <color name=\"zhihu_album_empty_view\">#4D000000</color>\n\n    <color name=\"zhihu_item_placeholder\">#EAEEF4</color>\n    <color name=\"zhihu_item_checkCircle_backgroundColor\">#1E8AE8</color>\n    <color name=\"zhihu_item_checkCircle_borderColor\">#FFFFFF</color>\n    <color name=\"zhihu_capture\">#424242</color>\n\n    <color name=\"zhihu_page_bg\">#FFFFFF</color>\n    <color name=\"zhihu_bottom_toolbar_bg\">#FFFFFF</color>\n\n    <color name=\"zhihu_bottom_toolbar_preview_text\">#DE000000</color>\n    <color name=\"zhihu_bottom_toolbar_preview_text_disable\">#4D000000</color>\n    <color name=\"zhihu_bottom_toolbar_apply_text\">#0077D9</color>\n    <color name=\"zhihu_bottom_toolbar_apply_text_disable\">#4D0077D9</color>\n\n    <color name=\"zhihu_preview_bottom_toolbar_back_text\">#FFFFFF</color>\n    <color name=\"zhihu_preview_bottom_toolbar_apply_text\">#0077D9</color>\n    <color name=\"zhihu_preview_bottom_toolbar_apply_text_disable\">#4D0077D9</color>\n\n    <color name=\"zhihu_check_original_radio_disable\">#808080</color>\n</resources>"
  },
  {
    "path": "matisse/src/main/res/values/dimens.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright 2017 Zhihu Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n  -->\n<resources>\n    <dimen name=\"media_grid_size\">48dp</dimen>\n    <dimen name=\"media_grid_spacing\">4dp</dimen>\n\n    <dimen name=\"album_item_height\">72dp</dimen>\n</resources>"
  },
  {
    "path": "matisse/src/main/res/values/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright 2017 Zhihu Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n  -->\n<resources\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    tools:ignore=\"MissingTranslation\">\n    <string name=\"album_name_all\">All Media</string>\n\n    <string name=\"button_preview\">Preview</string>\n    <string name=\"button_apply_default\">Apply</string>\n    <string name=\"button_apply\">Apply(%1$d)</string>\n    <string name=\"button_back\">Back</string>\n    <string name=\"photo_grid_capture\">Camera</string>\n    <string name=\"empty_text\">No media yet</string>\n    <string name=\"button_ok\">OK</string>\n\n    <string name=\"error_over_count_default\">You have reached max selectable</string>\n    <string name=\"error_over_count\">You can only select up to %1$d media files</string>\n    <string name=\"error_under_quality\">Under quality</string>\n    <string name=\"error_over_quality\">Over quality</string>\n    <string name=\"error_file_type\">Unsupported file type</string>\n    <string name=\"error_type_conflict\">Can\\'t select images and videos at the same time</string>\n    <string name=\"error_no_video_activity\">No App found supporting video preview</string>\n    <string name=\"error_over_original_size\">Can\\'t select the images larger than %1$d MB</string>\n    <string name=\"error_over_original_count\">%1$d images over %2$d MB. Original will be unchecked</string>\n    <string name=\"button_original\">Original</string>\n    <string name=\"button_sure_default\">Sure</string>\n    <string name=\"button_sure\">Sure(%1$d)</string>\n</resources>\n"
  },
  {
    "path": "matisse/src/main/res/values/styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright 2017 Zhihu Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n  -->\n<resources>\n\n    //====================================== Theme Zhihu ===========================================\n\n    <style name=\"Matisse.Zhihu\" parent=\"Theme.AppCompat.Light.NoActionBar\">\n        <item name=\"colorPrimary\">@color/zhihu_primary</item>\n        <item name=\"colorPrimaryDark\">@color/zhihu_primary_dark</item>\n        <item name=\"toolbar\">@style/Toolbar.Zhihu</item>\n        <item name=\"album.dropdown.title.color\">@color/zhihu_album_dropdown_title_text</item>\n        <item name=\"album.dropdown.count.color\">@color/zhihu_album_dropdown_count_text</item>\n        <item name=\"album.element.color\">@android:color/white</item>\n        <item name=\"album.thumbnail.placeholder\">@color/zhihu_album_dropdown_thumbnail_placeholder</item>\n        <item name=\"album.emptyView\">@drawable/ic_empty_zhihu</item>\n        <item name=\"album.emptyView.textColor\">@color/zhihu_album_empty_view</item>\n        <item name=\"item.placeholder\">@color/zhihu_item_placeholder</item>\n        <item name=\"item.checkCircle.backgroundColor\">@color/zhihu_item_checkCircle_backgroundColor</item>\n        <item name=\"item.checkCircle.borderColor\">@color/zhihu_item_checkCircle_borderColor</item>\n        <item name=\"page.bg\">@color/zhihu_page_bg</item>\n        <item name=\"bottomToolbar.bg\">@color/zhihu_bottom_toolbar_bg</item>\n        <item name=\"bottomToolbar.preview.textColor\">@color/zhihu_bottom_toolbar_preview</item>\n        <item name=\"bottomToolbar.apply.textColor\">@color/zhihu_bottom_toolbar_apply</item>\n        <item name=\"preview.bottomToolbar.back.textColor\">@color/zhihu_preview_bottom_toolbar_back_text</item>\n        <item name=\"preview.bottomToolbar.apply.textColor\">@color/zhihu_preview_bottom_toolbar_apply</item>\n        <item name=\"listPopupWindowStyle\">@style/Popup.Zhihu</item>\n        <item name=\"capture.textColor\">@color/zhihu_capture</item>\n    </style>\n\n    <style name=\"Toolbar.Zhihu\" parent=\"ThemeOverlay.AppCompat.Dark.ActionBar\">\n\n    </style>\n\n    <style name=\"Popup.Zhihu\" parent=\"Widget.AppCompat.Light.ListPopupWindow\">\n        <item name=\"android:popupBackground\">@color/zhihu_album_popup_bg</item>\n    </style>\n\n    //===================================== Theme Dracula ==========================================\n\n    <style name=\"Matisse.Dracula\" parent=\"Theme.AppCompat.NoActionBar\">\n        <item name=\"colorPrimary\">@color/dracula_primary</item>\n        <item name=\"colorPrimaryDark\">@color/dracula_primary_dark</item>\n        <item name=\"toolbar\">@style/Toolbar.Dracula</item>\n        <item name=\"album.dropdown.title.color\">@color/dracula_album_dropdown_title_text</item>\n        <item name=\"album.dropdown.count.color\">@color/dracula_album_dropdown_count_text</item>\n        <item name=\"album.element.color\">@android:color/white</item>\n        <item name=\"album.thumbnail.placeholder\">@color/dracula_album_dropdown_thumbnail_placeholder</item>\n        <item name=\"album.emptyView\">@drawable/ic_empty_dracula</item>\n        <item name=\"album.emptyView.textColor\">@color/dracula_album_empty_view</item>\n        <item name=\"item.placeholder\">@color/dracula_item_placeholder</item>\n        <item name=\"item.checkCircle.backgroundColor\">@color/dracula_item_checkCircle_backgroundColor</item>\n        <item name=\"item.checkCircle.borderColor\">@color/dracula_item_checkCircle_borderColor</item>\n        <item name=\"page.bg\">@color/dracula_page_bg</item>\n        <item name=\"bottomToolbar.bg\">@color/dracula_bottom_toolbar_bg</item>\n        <item name=\"bottomToolbar.preview.textColor\">@color/dracula_bottom_toolbar_preview</item>\n        <item name=\"bottomToolbar.apply.textColor\">@color/dracula_bottom_toolbar_apply</item>\n        <item name=\"preview.bottomToolbar.back.textColor\">@color/dracula_preview_bottom_toolbar_back_text</item>\n        <item name=\"preview.bottomToolbar.apply.textColor\">@color/dracula_preview_bottom_toolbar_apply</item>\n        <item name=\"android:listPopupWindowStyle\">@style/Popup.Dracula</item>\n        <item name=\"listPopupWindowStyle\">@style/Popup.Dracula</item>\n        <item name=\"capture.textColor\">@color/dracula_capture</item>\n    </style>\n\n    <style name=\"Toolbar.Dracula\" parent=\"ThemeOverlay.AppCompat.Dark.ActionBar\">\n\n    </style>\n\n    <style name=\"Popup.Dracula\" parent=\"Widget.AppCompat.ListPopupWindow\">\n        <item name=\"android:popupBackground\">@color/dracula_album_popup_bg</item>\n    </style>\n\n</resources>"
  },
  {
    "path": "matisse/src/main/res/values-ar/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"album_name_all\">جميع وسائل الإعلام</string>\n\n    <string name=\"button_preview\">معاينة</string>\n    <string name=\"button_apply_default\">تطبيق</string>\n    <string name=\"button_apply\">تطبيق (%1$d)</string>\n    <string name=\"button_back\">رجوع</string>\n    <string name=\"photo_grid_capture\">كاميرا</string>\n    <string name=\"empty_text\">لا وسائل الإعلام حتى الآن</string>\n    <string name=\"button_ok\">تم</string>\n\n    <string name=\"error_over_count_default\">لقد وصلت إلى الحد الأقصى للاختيار</string>\n    <string name=\"error_over_count\">يمكنك تحديد ما يصل إلى %1$d من ملفات الوسائط فقط</string>\n    <string name=\"error_under_quality\">تحت الجودة</string>\n    <string name=\"error_over_quality\">فوق الجودة</string>\n    <string name=\"error_file_type\">نوع ملف غير مدعوم</string>\n    <string name=\"error_type_conflict\">لا يمكن تحديد الصور ومقاطع الفيديو في نفس الوقت</string>\n    <string name=\"error_no_video_activity\">لم يتم العثور على التطبيق دعم معاينة الفيديو</string>\n</resources>\n"
  },
  {
    "path": "matisse/src/main/res/values-ca/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright 2017 Zhihu Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n  -->\n<resources>\n    <string name=\"album_name_all\">Tots</string>\n\n    <string name=\"button_preview\">Previsualitza</string>\n    <string name=\"button_apply_default\">Selecciona</string>\n    <string name=\"button_apply\">Selecciona(%1$d)</string>\n    <string name=\"button_back\">Enrere</string>\n    <string name=\"photo_grid_capture\">Càmera</string>\n    <string name=\"empty_text\">Cap arxiu seleccionat</string>\n    <string name=\"button_ok\">OK</string>\n\n    <string name=\"error_over_count_default\">Has sobrepassat el màxim d\\'arxius seleccionables</string>\n    <string name=\"error_over_count\">Només pots seleccionar fins a %1$d arxius multimedia</string>\n    <string name=\"error_under_quality\">Baixa qualitat</string>\n    <string name=\"error_over_quality\">Excés qualitat</string>\n    <string name=\"error_file_type\">Tipus d\\'arxiu no permés</string>\n    <string name=\"error_type_conflict\">No es poden seleccionar imatges i vídeos al mateix temps</string>\n    <string name=\"error_no_video_activity\">No s\\'ha trobat cap app que suporti la previsualització de vídeo</string>\n</resources>\n"
  },
  {
    "path": "matisse/src/main/res/values-de/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright 2017 Zhihu Inc.\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n  http://www.apache.org/licenses/LICENSE-2.0\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n  -->\n<resources>\n    <string name=\"album_name_all\">Alle Medien</string>\n\n    <string name=\"button_preview\">Vorschau</string>\n    <string name=\"button_apply_default\">Auswählen</string>\n    <string name=\"button_apply\">Auswählen(%1$d)</string>\n    <string name=\"button_back\">Zurück</string>\n    <string name=\"photo_grid_capture\">Kamera</string>\n    <string name=\"empty_text\">Keine Medien gewählt</string>\n    <string name=\"button_ok\">OK</string>\n\n    <string name=\"error_over_count_default\">Sie haben die maximale Anzahl an Medien ausgewählt.</string>\n    <string name=\"error_over_count\">Sie können nur %1$d Medien auswählen</string>\n    <string name=\"error_under_quality\">Unter Qualität</string>\n    <string name=\"error_over_quality\">Über Qualität</string>\n    <string name=\"error_file_type\">Dateityp nicht unterstützt</string>\n    <string name=\"error_type_conflict\">Sie können Bilder und Videos nicht gleichzeitig auswählen</string>\n    <string name=\"error_no_video_activity\">Keine App zur Videovorschau gefunden</string>\n</resources>\n"
  },
  {
    "path": "matisse/src/main/res/values-es/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright 2017 Zhihu Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n  -->\n<resources>\n    <string name=\"album_name_all\">Todos</string>\n\n    <string name=\"button_preview\">Previsualizar</string>\n    <string name=\"button_apply_default\">Seleccionar</string>\n    <string name=\"button_apply\">Seleccionar(%1$d)</string>\n    <string name=\"button_back\">Atrás</string>\n    <string name=\"photo_grid_capture\">Cámara</string>\n    <string name=\"empty_text\">Ningún archivo seleccionado</string>\n    <string name=\"button_ok\">OK</string>\n\n    <string name=\"error_over_count_default\">Has alcanzado el máximo de archivos seleccionables</string>\n    <string name=\"error_over_count\">Sólo puede seleccionar hasta %1$d archivos multimedia</string>\n    <string name=\"error_under_quality\">Baja calidad</string>\n    <string name=\"error_over_quality\">Exceso calidad</string>\n    <string name=\"error_file_type\">Tipo de fichero no soportado</string>\n    <string name=\"error_type_conflict\">No se puede seleccionar imágenes y vídeos al mismo tiempo</string>\n    <string name=\"error_no_video_activity\">No se ha encontrado ninguna app que soporte la previsualización de vídeo</string>\n</resources>\n"
  },
  {
    "path": "matisse/src/main/res/values-it/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright 2017 Zhihu Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n  -->\n<resources>\n    <string name=\"album_name_all\">Tutti i media</string>\n\n    <string name=\"button_preview\">Anteprima</string>\n    <string name=\"button_apply_default\">Conferma</string>\n    <string name=\"button_apply\">Conferma(%1$d)</string>\n    <string name=\"button_back\">Indietro</string>\n    <string name=\"photo_grid_capture\">Camera</string>\n    <string name=\"empty_text\">Nessun media disponibile</string>\n    <string name=\"button_ok\">OK</string>\n\n    <string name=\"error_over_count_default\">Hai selezionato il numero massimo di elementi</string>\n    <string name=\"error_over_count\">Puoi selezionare fino a %1$d elementi</string>\n    <string name=\"error_under_quality\">Sotto qualità</string>\n    <string name=\"error_over_quality\">Sopra qualità</string>\n    <string name=\"error_file_type\">Tipo di file non supportato</string>\n    <string name=\"error_type_conflict\">Non è possibile selezionare contemporaneamente immagini e video</string>\n    <string name=\"error_no_video_activity\">Non è stata trovata alcuna app che supporti l\\'anteprima video</string>\n</resources>\n"
  },
  {
    "path": "matisse/src/main/res/values-ko/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n  <string name=\"album_name_all\">전체보기</string>\n  <string name=\"button_preview\">미리보기</string>\n  <string name=\"photo_grid_capture\">카메라</string>\n  <string name=\"error_no_video_activity\">비디오 미리보기를 지원하는 앱을 찾을 수 없습니다.</string>\n  <string name=\"error_type_conflict\">이미지와 비디오는 동시에 선택 할 수 없습니다.</string>\n  <string name=\"button_apply_default\">적용</string>\n  <string name=\"button_apply\">적용(%1$d)</string>\n  <string name=\"button_back\">뒤로</string>\n  <string name=\"empty_text\">미디어 파일이 없습니다.</string>\n  <string name=\"button_ok\">확인</string>\n  <string name=\"error_over_count_default\">더이상 선택할 수 없습니다.</string>\n  <string name=\"error_over_count\">최대 %1$d까지 선택 가능합니다.</string>\n  <string name=\"error_under_quality\">화질이 너무 낮습니다.</string>\n  <string name=\"error_over_quality\">화질이 너무 높습니다.</string>\n  <string name=\"error_file_type\">지원되지 않는 파일 유형입니다.</string>\n</resources>"
  },
  {
    "path": "matisse/src/main/res/values-pl/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright 2017 Zhihu Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n  -->\n<resources>\n    <string name=\"album_name_all\">Wszystkie Media</string>\n\n    <string name=\"button_preview\">Preview</string>\n    <string name=\"button_apply_default\">Zatwierdź</string>\n    <string name=\"button_apply\">Zatwierdź(%1$d)</string>\n    <string name=\"button_back\">Wstecz</string>\n    <string name=\"photo_grid_capture\">Aparat</string>\n    <string name=\"empty_text\">Brak mediów</string>\n    <string name=\"button_ok\">OK</string>\n\n    <string name=\"error_over_count_default\">Osiągnąłeś limit wybieralnych elementów</string>\n    <string name=\"error_over_count\">Możesz wybrać do %1$d plików</string>\n    <string name=\"error_under_quality\">Poniżej jakości</string>\n    <string name=\"error_over_quality\">Powyżej jakości</string>\n    <string name=\"error_file_type\">Niewspierany typ pliku</string>\n    <string name=\"error_type_conflict\">Nie można wybierać obrazów i filmów w tym samym czasie</string>\n    <string name=\"error_no_video_activity\">Nie znaleziono aplikacji wspierającej podgląd wideo</string>\n    <string name=\"button_original\">Oryginał</string>\n    <string name=\"button_sure_default\">Zatwierdź</string>\n    <string name=\"button_sure\">Zatwierdź(%1$d)</string>\n</resources>\n"
  },
  {
    "path": "matisse/src/main/res/values-pt-rBR/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright 2017 Zhihu Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n  -->\n<resources>\n    <string name=\"album_name_all\">Todas as mídias</string>\n\n    <string name=\"button_preview\">Pré-visualizaçao</string>\n    <string name=\"button_apply_default\">Aplicar</string>\n    <string name=\"button_apply\">Aplicar(%1$d)</string>\n    <string name=\"button_back\">Voltar</string>\n    <string name=\"photo_grid_capture\">Câmera</string>\n    <string name=\"empty_text\">Nenhuma mídia disponível</string>\n    <string name=\"button_ok\">OK</string>\n\n    <string name=\"error_over_count_default\">Você atingiu o máximo de itens possíveis para seleção</string>\n    <string name=\"error_over_count\">Você só pode selecionar até %1$d arquivos de mídia</string>\n    <string name=\"error_under_quality\">Abaixo da qualidade</string>\n    <string name=\"error_over_quality\">Acima da qualidade</string>\n    <string name=\"error_file_type\">Tipo de arquivo não suportado</string>\n    <string name=\"error_type_conflict\">Não é possível selecionar arquivos de imagem e vídeo simultaneamente</string>\n    <string name=\"error_no_video_activity\">Nenhum player de vídeo disponível para reprodução</string>\n</resources>\n"
  },
  {
    "path": "matisse/src/main/res/values-ru/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright 2017 Zhihu Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n  -->\n<resources>\n    <string name=\"album_name_all\">Все</string>\n\n    <string name=\"button_preview\">Предпросмотр</string>\n    <string name=\"button_apply_default\">Применить</string>\n    <string name=\"button_apply\">Применить(%1$d)</string>\n    <string name=\"button_back\">Назад</string>\n    <string name=\"photo_grid_capture\">Камера</string>\n    <string name=\"empty_text\">Пусто</string>\n    <string name=\"button_ok\">OK</string>\n\n    <string name=\"error_over_count_default\">Вы выбрали максимальное количество файлов</string>\n    <plurals name=\"error_over_count\">\n        <item quantity=\"one\">Можно выбрать не более %1$d файла</item>\n        <item quantity=\"few\">Можно выбрать не более %1$d файлов</item>\n        <item quantity=\"many\">Можно выбрать не более %1$d файлов</item>\n        <item quantity=\"other\">Можно выбрать не более %1$d файлов</item>\n    </plurals>\n    <string name=\"error_under_quality\">Слишком низкое качество</string>\n    <string name=\"error_over_quality\">Слишком высокое качество</string>\n    <string name=\"error_file_type\">Неподдерживаемый тип файла</string>\n    <string name=\"error_type_conflict\">Невозможно выбрать изображения и видео одновременно</string>\n    <string name=\"error_no_video_activity\">Приложение для предпросмотра видео не найдено</string>\n    <string name=\"button_original\">Оригинал</string>\n    <string name=\"button_sure\">Применить(%1$d)</string>\n    <string name=\"button_sure_default\">Применить</string>\n</resources>\n"
  },
  {
    "path": "matisse/src/main/res/values-tr-rTR/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright 2017 Zhihu Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n  -->\n<resources>\n    <string name=\"album_name_all\">Tüm Medya</string>\n\n    <string name=\"button_preview\">Ön İzleme</string>\n    <string name=\"button_apply_default\">Uygula</string>\n    <string name=\"button_apply\">Uygula(%1$d)</string>\n    <string name=\"button_back\">Geri</string>\n    <string name=\"photo_grid_capture\">Kamera</string>\n    <string name=\"empty_text\">Henüz medya yok</string>\n    <string name=\"button_ok\">TAMAM</string>\n\n    <string name=\"error_over_count_default\">Maksimum seçilebilir değere ulaştınız</string>\n    <string name=\"error_over_count\">Sadece %1$d medya dosyasını seçebilirsiniz</string>\n    <string name=\"error_under_quality\">Düşük kalite</string>\n    <string name=\"error_over_quality\">Yüksek kalite</string>\n    <string name=\"error_file_type\">Desteklenmeyen dosya tipi</string>\n    <string name=\"error_type_conflict\">Görüntüleri ve videoları aynı anda seçemezsiniz</string>\n    <string name=\"error_no_video_activity\">Video önizlemesini destekleyen hiçbir uygulama bulunamadı</string>\n</resources>"
  },
  {
    "path": "matisse/src/main/res/values-uk/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright 2017 Zhihu Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n  -->\n<resources>\n    <string name=\"album_name_all\">Всі</string>\n\n    <string name=\"button_preview\">Перегляд</string>\n    <string name=\"button_apply_default\">Застосувати</string>\n    <string name=\"button_apply\">Застосувати(%1$d)</string>\n    <string name=\"button_back\">Назад</string>\n    <string name=\"photo_grid_capture\">Камера</string>\n    <string name=\"empty_text\">Пусто</string>\n    <string name=\"button_ok\">OK</string>\n\n    <string name=\"error_over_count_default\">Вы обрали максимальну кількість файлів</string>\n    <plurals name=\"error_over_count\">\n        <item quantity=\"one\">Можливо обрати не більше %1$d файла</item>\n        <item quantity=\"few\">Можливо обрати не більше %1$d файлів</item>\n        <item quantity=\"many\">Можливо обрати не більше %1$d файлів</item>\n        <item quantity=\"other\">Можливо обрати не більше %1$d файлів</item>\n    </plurals>\n    <string name=\"error_under_quality\">Занадто низька якість</string>\n    <string name=\"error_over_quality\">Занадто висока якість</string>\n    <string name=\"error_file_type\">Непідтримуваний тип файла</string>\n    <string name=\"error_type_conflict\">Неможливо обрати зображення і відео одночасно</string>\n    <string name=\"error_no_video_activity\">Застосунок для перегляду відео не знайдений</string>\n</resources>\n"
  },
  {
    "path": "matisse/src/main/res/values-vi/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"error_over_original_size\">Không thể chọn hình ảnh lớn hơn %1$d MB</string>\n    <string name=\"album_name_all\">Tất cả</string>\n    <string name=\"button_apply\">Áp dụng(%1$d)</string>\n    <string name=\"button_apply_default\">Áp dụng</string>\n    <string name=\"button_back\">Về</string>\n    <string name=\"button_ok\">Đồng ý</string>\n    <string name=\"button_original\">Bản gốc</string>\n    <string name=\"button_preview\">Xem trước</string>\n    <string name=\"button_sure\">Đồng ý(%1$d)</string>\n    <string name=\"button_sure_default\">Đồng ý</string>\n    <string name=\"empty_text\">Chưa có dữ liệu</string>\n    <string name=\"error_file_type\">Không hỗ trợ loại tệp này</string>\n    <string name=\"error_no_video_activity\">Không tìm thấy ứng dụng nào hỗ trợ xem trước video</string>\n    <string name=\"error_over_count\">Bạn chỉ có thể chọn tối đa %1$d tệp</string>\n    <string name=\"error_over_count_default\">Bạn đã đạt đến mức tối đa có thể lựa chọn</string>\n    <string name=\"error_over_original_count\">%1$d hình ảnh trên %2$d MB. Bản gốc sẽ được bỏ chọn</string>\n    <string name=\"error_over_quality\">Chất lượng quá cao</string>\n    <string name=\"error_type_conflict\">Không thể chọn hình ảnh và video cùng một lúc</string>\n    <string name=\"error_under_quality\">Chất lượng thấp</string>\n    <string name=\"photo_grid_capture\">Camera</string>\n</resources>"
  },
  {
    "path": "matisse/src/main/res/values-zh/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright 2017 Zhihu Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n  -->\n<resources>\n    <string name=\"album_name_all\">全部</string>\n\n    <string name=\"button_preview\">预览</string>\n    <string name=\"button_apply_default\">使用</string>\n    <string name=\"button_apply\">使用(%1$d)</string>\n    <string name=\"button_back\">返回</string>\n    <string name=\"photo_grid_capture\">拍一张</string>\n    <string name=\"empty_text\">还没有图片或视频</string>\n    <string name=\"button_ok\">我知道了</string>\n\n    <string name=\"error_over_count_default\">您已经达到最大选择数量</string>\n    <string name=\"error_over_count\">最多只能选择 %1$d 个文件</string>\n    <string name=\"error_under_quality\">图片质量太低</string>\n    <string name=\"error_over_quality\">图片质量太高</string>\n    <string name=\"error_file_type\">不支持的文件类型</string>\n    <string name=\"error_type_conflict\">不能同时选择图片和视频</string>\n    <string name=\"error_no_video_activity\">没有支持视频预览的应用</string>\n    <string name=\"error_over_original_size\">\"该照片大于 %1$d M，无法上传将取消勾选原图\"</string>\n    <string name=\"error_over_original_count\">\"有 %1$d 张照片大于 %2$d M\\n无法上传，将取消勾选原图\"</string>\n    <string name=\"button_original\">原图</string>\n    <string name=\"button_sure_default\">确定</string>\n    <string name=\"button_sure\">确定(%1$d)</string>\n</resources>"
  },
  {
    "path": "matisse/src/main/res/values-zh-rTW/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright 2017 Zhihu Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n  -->\n<resources>\n    <string name=\"album_name_all\">全部</string>\n\n    <string name=\"button_preview\">預覽</string>\n    <string name=\"button_apply_default\">使用</string>\n    <string name=\"button_apply\">使用(%1$d)</string>\n    <string name=\"button_back\">返回</string>\n    <string name=\"photo_grid_capture\">拍一張</string>\n    <string name=\"empty_text\">還沒有圖片或影片</string>\n    <string name=\"button_ok\">我知道了</string>\n\n    <string name=\"error_over_count_default\">您已經達到最大選擇數量</string>\n    <string name=\"error_over_count\">最多只能選擇 %1$d 個文件</string>\n    <string name=\"error_under_quality\">圖片質量太低</string>\n    <string name=\"error_over_quality\">圖片質量太高</string>\n    <string name=\"error_file_type\">不支援的文件類型</string>\n    <string name=\"error_type_conflict\">不能同時選擇圖片和影片</string>\n    <string name=\"error_no_video_activity\">沒有支持影片預覽的應用程式</string>\n    <string name=\"button_original\">原圖</string>\n    <string name=\"button_sure_default\">确定</string>\n    <string name=\"button_sure\">确定(%1$d)</string>\n</resources>"
  },
  {
    "path": "sample/build.gradle",
    "content": "/*\n * Copyright 2017 Zhihu Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\napply plugin: 'com.android.application'\n\nandroid {\n    compileSdkVersion 29\n\n    defaultConfig {\n        applicationId 'com.zhihu.matisse.sample'\n        minSdkVersion 14\n        targetSdkVersion 29\n        versionCode 1\n        versionName \"1.0\"\n    }\n    lintOptions {\n        abortOnError false\n    }\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_1_8\n        targetCompatibility JavaVersion.VERSION_1_8\n    }\n}\n\n\ndependencies {\n    implementation project(':matisse')\n\n//    implementation 'com.zhihu.android:matisse:0.5.2'\n    implementation fileTree(dir: 'libs', include: ['*.jar'])\n\n    implementation \"androidx.appcompat:appcompat:1.1.0\"\n    implementation \"androidx.recyclerview:recyclerview:1.0.0\"\n    implementation 'com.tbruyelle.rxpermissions2:rxpermissions:0.9.5@aar'\n    implementation 'io.reactivex.rxjava2:rxjava:2.2.12'\n    implementation 'com.github.bumptech.glide:glide:4.9.0'\n    implementation 'com.squareup.picasso:picasso:2.5.2'\n}\n"
  },
  {
    "path": "sample/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 /Library/android-sdk-macosx/tools/proguard/proguard-android.txt\n# You can edit the include path and order by changing the ProGuard\n# include property in project.properties.\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-dontwarn com.squareup.okhttp.**\n"
  },
  {
    "path": "sample/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright 2017 Zhihu Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n  -->\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n          package=\"com.zhihu.matisse.sample\">\n\n    <application\n        android:allowBackup=\"true\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:theme=\"@style/AppTheme\">\n        <activity\n            android:name=\"com.zhihu.matisse.sample.SampleActivity\"\n            android:configChanges=\"orientation|screenSize|keyboardHidden\">\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        <provider\n            android:name=\"androidx.core.content.FileProvider\"\n            android:authorities=\"com.zhihu.matisse.sample.fileprovider\"\n            android:exported=\"false\"\n            android:grantUriPermissions=\"true\">\n            <meta-data\n                android:name=\"android.support.FILE_PROVIDER_PATHS\"\n                android:resource=\"@xml/file_paths_public\"></meta-data>\n        </provider>\n    </application>\n\n</manifest>\n"
  },
  {
    "path": "sample/src/main/java/com/zhihu/matisse/sample/GifSizeFilter.java",
    "content": "/*\n * Copyright 2017 Zhihu Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.zhihu.matisse.sample;\n\nimport android.content.Context;\nimport android.graphics.Point;\n\nimport com.zhihu.matisse.MimeType;\nimport com.zhihu.matisse.filter.Filter;\nimport com.zhihu.matisse.internal.entity.IncapableCause;\nimport com.zhihu.matisse.internal.entity.Item;\nimport com.zhihu.matisse.internal.utils.PhotoMetadataUtils;\n\nimport java.util.HashSet;\nimport java.util.Set;\n\nclass GifSizeFilter extends Filter {\n\n    private int mMinWidth;\n    private int mMinHeight;\n    private int mMaxSize;\n\n    GifSizeFilter(int minWidth, int minHeight, int maxSizeInBytes) {\n        mMinWidth = minWidth;\n        mMinHeight = minHeight;\n        mMaxSize = maxSizeInBytes;\n    }\n\n    @Override\n    public Set<MimeType> constraintTypes() {\n        return new HashSet<MimeType>() {{\n            add(MimeType.GIF);\n        }};\n    }\n\n    @Override\n    public IncapableCause filter(Context context, Item item) {\n        if (!needFiltering(context, item))\n            return null;\n\n        Point size = PhotoMetadataUtils.getBitmapBound(context.getContentResolver(), item.getContentUri());\n        if (size.x < mMinWidth || size.y < mMinHeight || item.size > mMaxSize) {\n            return new IncapableCause(IncapableCause.DIALOG, context.getString(R.string.error_gif, mMinWidth,\n                    String.valueOf(PhotoMetadataUtils.getSizeInMB(mMaxSize))));\n        }\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "sample/src/main/java/com/zhihu/matisse/sample/SampleActivity.java",
    "content": "/*\n * Copyright 2017 Zhihu Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.zhihu.matisse.sample;\n\nimport android.Manifest;\nimport android.annotation.SuppressLint;\nimport android.content.Intent;\nimport android.content.pm.ActivityInfo;\nimport android.net.Uri;\nimport android.os.Bundle;\nimport android.util.Log;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.TextView;\nimport android.widget.Toast;\n\nimport androidx.appcompat.app.AppCompatActivity;\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport com.tbruyelle.rxpermissions2.RxPermissions;\nimport com.zhihu.matisse.Matisse;\nimport com.zhihu.matisse.MimeType;\nimport com.zhihu.matisse.engine.impl.GlideEngine;\nimport com.zhihu.matisse.engine.impl.PicassoEngine;\nimport com.zhihu.matisse.filter.Filter;\nimport com.zhihu.matisse.internal.entity.CaptureStrategy;\n\nimport java.util.List;\n\npublic class SampleActivity extends AppCompatActivity implements View.OnClickListener {\n\n    private static final int REQUEST_CODE_CHOOSE = 23;\n\n    private UriAdapter mAdapter;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_main);\n        findViewById(R.id.zhihu).setOnClickListener(this);\n        findViewById(R.id.dracula).setOnClickListener(this);\n        findViewById(R.id.only_gif).setOnClickListener(this);\n\n        RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerview);\n        recyclerView.setLayoutManager(new LinearLayoutManager(this));\n        recyclerView.setAdapter(mAdapter = new UriAdapter());\n    }\n\n    // <editor-fold defaultstate=\"collapsed\" desc=\"onClick\">\n    @SuppressLint(\"CheckResult\")\n    @Override\n    public void onClick(final View v) {\n        RxPermissions rxPermissions = new RxPermissions(this);\n        rxPermissions.request(Manifest.permission.WRITE_EXTERNAL_STORAGE)\n                .subscribe(aBoolean -> {\n                    if (aBoolean) {\n                        startAction(v);\n                    } else {\n                        Toast.makeText(SampleActivity.this, R.string.permission_request_denied, Toast.LENGTH_LONG)\n                                .show();\n                    }\n                }, Throwable::printStackTrace);\n    }\n    // </editor-fold>\n\n    private void startAction(View v) {\n        switch (v.getId()) {\n            case R.id.zhihu:\n                Matisse.from(SampleActivity.this)\n                        .choose(MimeType.ofImage(), false)\n                        .countable(true)\n                        .capture(true)\n                        .captureStrategy(\n                                new CaptureStrategy(true, \"com.zhihu.matisse.sample.fileprovider\", \"test\"))\n                        .maxSelectable(9)\n                        .addFilter(new GifSizeFilter(320, 320, 5 * Filter.K * Filter.K))\n                        .gridExpectedSize(\n                                getResources().getDimensionPixelSize(R.dimen.grid_expected_size))\n                        .restrictOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)\n                        .thumbnailScale(0.85f)\n                        .imageEngine(new GlideEngine())\n                        .setOnSelectedListener((uriList, pathList) -> {\n                            Log.e(\"onSelected\", \"onSelected: pathList=\" + pathList);\n                        })\n                        .showSingleMediaType(true)\n                        .originalEnable(true)\n                        .maxOriginalSize(10)\n                        .autoHideToolbarOnSingleTap(true)\n                        .setOnCheckedListener(isChecked -> {\n                            Log.e(\"isChecked\", \"onCheck: isChecked=\" + isChecked);\n                        })\n                        .forResult(REQUEST_CODE_CHOOSE);\n                break;\n            case R.id.dracula:\n                Matisse.from(SampleActivity.this)\n                        .choose(MimeType.ofImage())\n                        .theme(R.style.Matisse_Dracula)\n                        .countable(false)\n                        .addFilter(new GifSizeFilter(320, 320, 5 * Filter.K * Filter.K))\n                        .maxSelectable(9)\n                        .originalEnable(true)\n                        .maxOriginalSize(10)\n                        .imageEngine(new PicassoEngine())\n                        .forResult(REQUEST_CODE_CHOOSE);\n                break;\n            case R.id.only_gif:\n                Matisse.from(SampleActivity.this)\n                        .choose(MimeType.of(MimeType.GIF), false)\n                        .countable(true)\n                        .maxSelectable(9)\n                        .addFilter(new GifSizeFilter(320, 320, 5 * Filter.K * Filter.K))\n                        .gridExpectedSize(\n                                getResources().getDimensionPixelSize(R.dimen.grid_expected_size))\n                        .restrictOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)\n                        .thumbnailScale(0.85f)\n                        .imageEngine(new GlideEngine())\n                        .showSingleMediaType(true)\n                        .originalEnable(true)\n                        .maxOriginalSize(10)\n                        .autoHideToolbarOnSingleTap(true)\n                        .forResult(REQUEST_CODE_CHOOSE);\n                break;\n            default:\n                break;\n        }\n        mAdapter.setData(null, null);\n    }\n\n    @Override\n    protected void onActivityResult(int requestCode, int resultCode, Intent data) {\n        super.onActivityResult(requestCode, resultCode, data);\n        if (requestCode == REQUEST_CODE_CHOOSE && resultCode == RESULT_OK) {\n            mAdapter.setData(Matisse.obtainResult(data), Matisse.obtainPathResult(data));\n            Log.e(\"OnActivityResult \", String.valueOf(Matisse.obtainOriginalState(data)));\n        }\n    }\n\n    private static class UriAdapter extends RecyclerView.Adapter<UriAdapter.UriViewHolder> {\n\n        private List<Uri> mUris;\n        private List<String> mPaths;\n\n        void setData(List<Uri> uris, List<String> paths) {\n            mUris = uris;\n            mPaths = paths;\n            notifyDataSetChanged();\n        }\n\n        @Override\n        public UriViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {\n            return new UriViewHolder(\n                    LayoutInflater.from(parent.getContext()).inflate(R.layout.uri_item, parent, false));\n        }\n\n        @Override\n        public void onBindViewHolder(UriViewHolder holder, int position) {\n            holder.mUri.setText(mUris.get(position).toString());\n            holder.mPath.setText(mPaths.get(position));\n\n            holder.mUri.setAlpha(position % 2 == 0 ? 1.0f : 0.54f);\n            holder.mPath.setAlpha(position % 2 == 0 ? 1.0f : 0.54f);\n        }\n\n        @Override\n        public int getItemCount() {\n            return mUris == null ? 0 : mUris.size();\n        }\n\n        static class UriViewHolder extends RecyclerView.ViewHolder {\n\n            private TextView mUri;\n            private TextView mPath;\n\n            UriViewHolder(View contentView) {\n                super(contentView);\n                mUri = (TextView) contentView.findViewById(R.id.uri);\n                mPath = (TextView) contentView.findViewById(R.id.path);\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "sample/src/main/res/layout/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  Copyright 2017 Zhihu Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n  -->\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"@android:color/white\"\n    android:orientation=\"vertical\">\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:elevation=\"4dp\"\n        android:orientation=\"vertical\">\n\n        <Button\n            android:id=\"@+id/zhihu\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginLeft=\"@dimen/item_margin_horizontal\"\n            android:layout_marginTop=\"8dp\"\n            android:layout_marginRight=\"@dimen/item_margin_horizontal\"\n            android:minHeight=\"56dp\"\n            android:text=\"Zhihu\"\n            android:textAllCaps=\"false\"\n            android:textColor=\"@android:color/white\"\n            android:theme=\"@style/ZhihuOverlay\" />\n\n        <Button\n            android:id=\"@+id/dracula\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginLeft=\"@dimen/item_margin_horizontal\"\n            android:layout_marginTop=\"16dp\"\n            android:layout_marginRight=\"@dimen/item_margin_horizontal\"\n            android:minHeight=\"56dp\"\n            android:text=\"Dracula\"\n            android:textAllCaps=\"false\"\n            android:textColor=\"@android:color/white\"\n            android:theme=\"@style/DraculaOverlay\" />\n\n        <Button\n            android:id=\"@+id/only_gif\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginLeft=\"@dimen/item_margin_horizontal\"\n            android:layout_marginTop=\"16dp\"\n            android:layout_marginRight=\"@dimen/item_margin_horizontal\"\n            android:layout_marginBottom=\"32dp\"\n            android:minHeight=\"56dp\"\n            android:text=\"OnlyGif\"\n            android:textAllCaps=\"false\"\n            android:textColor=\"@android:color/white\"\n            android:theme=\"@style/ZhihuOverlay\" />\n\n        <TextView\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"56dp\"\n            android:gravity=\"center_vertical\"\n            android:paddingLeft=\"@dimen/item_margin_horizontal\"\n            android:text=\"@string/app_name\"\n            android:textColor=\"@android:color/black\"\n            android:textSize=\"22sp\" />\n    </LinearLayout>\n\n    <androidx.recyclerview.widget.RecyclerView\n        android:id=\"@+id/recyclerview\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:background=\"@color/list_bg\"\n        android:clipChildren=\"false\"\n        android:clipToPadding=\"false\"\n        android:paddingTop=\"16dp\"\n        android:paddingBottom=\"16dp\">\n\n    </androidx.recyclerview.widget.RecyclerView>\n\n</LinearLayout>\n"
  },
  {
    "path": "sample/src/main/res/layout/uri_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright 2017 Zhihu Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n  -->\n<LinearLayout 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=\"wrap_content\"\n    android:orientation=\"vertical\">\n\n    <TextView\n        android:id=\"@+id/uri\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:gravity=\"center_vertical\"\n        android:paddingBottom=\"2dp\"\n        android:paddingLeft=\"8dp\"\n        android:paddingRight=\"8dp\"\n        android:paddingTop=\"4dp\"\n        android:text=\"@+id/uri\"\n        android:textColor=\"@android:color/white\"\n        android:textSize=\"16sp\"\n        tools:text=\"content://media/external/images/media/12345\"\n        tools:textColor=\"@android:color/black\" />\n\n    <TextView\n        android:id=\"@+id/path\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:gravity=\"center_vertical\"\n        android:paddingBottom=\"4dp\"\n        android:paddingLeft=\"8dp\"\n        android:paddingRight=\"8dp\"\n        android:paddingTop=\"2dp\"\n        android:text=\"@+id/uri\"\n        android:textColor=\"@android:color/white\"\n        android:textSize=\"16sp\"\n        tools:text=\"/storage/emulated/0/Pictures/JPEG_20170516_12345.jpg\"\n        tools:textColor=\"@android:color/black\" />\n</LinearLayout>\n"
  },
  {
    "path": "sample/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright 2017 Zhihu Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n  -->\n<resources>\n    <color name=\"list_bg\">#3B3B3B</color>\n</resources>\n"
  },
  {
    "path": "sample/src/main/res/values/dimens.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright 2017 Zhihu Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n  -->\n<resources>\n    <dimen name=\"grid_expected_size\">120dp</dimen>\n    <dimen name=\"item_margin_horizontal\">24dp</dimen>\n    <dimen name=\"item_margin_vertical\">8dp</dimen>\n</resources>\n"
  },
  {
    "path": "sample/src/main/res/values/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright 2017 Zhihu Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n  -->\n<resources>\n    <string name=\"app_name\">Matisse</string>\n    <string name=\"permission_request_denied\">Permission request denied</string>\n    <string name=\"error_gif\">x or y bound size should be at least %1$dpx and file size should be no more than %2$sM</string>\n</resources>\n"
  },
  {
    "path": "sample/src/main/res/values/styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright 2017 Zhihu Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n  -->\n<resources>\n\n    <!-- Base application theme. -->\n    <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.NoActionBar\">\n        <!-- Customize your theme here. -->\n    </style>\n\n    <style name=\"ZhihuOverlay\" parent=\"ThemeOverlay.AppCompat\">\n        <item name=\"colorButtonNormal\">@color/zhihu_primary</item>\n    </style>\n\n    <style name=\"DraculaOverlay\" parent=\"ThemeOverlay.AppCompat\">\n        <item name=\"colorButtonNormal\">@color/dracula_primary</item>\n    </style>\n\n</resources>\n"
  },
  {
    "path": "sample/src/main/res/values-ca/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright 2017 Zhihu Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n  -->\n<resources>\n    <string name=\"app_name\">Matisse</string>\n    <string name=\"permission_request_denied\">Permís rebutjat</string>\n    <string name=\"error_gif\">x or y ha de ser almenys %1$dpx i el tamany de l\\'arxiu no ha de ser major que %2$sM</string>\n</resources>"
  },
  {
    "path": "sample/src/main/res/values-es/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright 2017 Zhihu Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n  -->\n<resources>\n    <string name=\"app_name\">Matisse</string>\n    <string name=\"permission_request_denied\">Permiso denegado</string>\n    <string name=\"error_gif\">x or y debe ser al menos %1$dpx y el tamaño del archivo no debe ser mayor que %2$sM</string>\n</resources>"
  },
  {
    "path": "sample/src/main/res/values-it/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright 2017 Zhihu Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n  -->\n<resources>\n    <string name=\"app_name\">Matisse</string>\n    <string name=\"permission_request_denied\">Richiesta di permessi negata</string>\n    <string name=\"error_gif\">Le dimensioni orizzontali o verticali devono essere di almeno %1$dpx e la dimensione del file non deve superare %2$sM</string>\n</resources>\n"
  },
  {
    "path": "sample/src/main/res/values-ko/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n  <string name=\"app_name\">Matisse</string>\n  <string name=\"permission_request_denied\">권한요청이 거부되었습니다.</string>\n  <string name=\"error_gif\">x 나 y의 크기는 %1$dpx 이상, 파일 크기는 %2$sM 이하여야 합니다.</string>\n</resources>"
  },
  {
    "path": "sample/src/main/res/values-pt-rBR/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright 2017 Zhihu Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n  -->\n<resources>\n    <string name=\"permission_request_denied\">Permissão negada</string>\n    <string name=\"error_gif\">o limite do tamanho de x ou y deve ser de pelo menos %1$dpx e o tamanho do arquivo não deve ser maior que %2$sM</string>\n</resources>\n"
  },
  {
    "path": "sample/src/main/res/values-ru/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright 2017 Zhihu Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n  -->\n<resources>\n    <string name=\"permission_request_denied\">Разрешение не предоставлено</string>\n    <string name=\"error_gif\">Высота и ширина изображения должна составлять минимум %1$dpx, а размер файла не должен превышать %2$sM</string>\n</resources>\n"
  },
  {
    "path": "sample/src/main/res/values-tr-rTR/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright 2017 Zhihu Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n  -->\n<resources>\n    <string name=\"app_name\">Matisse</string>\n    <string name=\"permission_request_denied\">İzin isteği  reddedildi</string>\n    <string name=\"error_gif\">x veya y\\'ye bağlı boyut en az %1$ dpx olmalıdır Dosya boyutu %2$sM \\'den daha fazla olmamalıdır</string>\n</resources>\n"
  },
  {
    "path": "sample/src/main/res/values-uk/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright 2017 Zhihu Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n  -->\n<resources>\n    <string name=\"permission_request_denied\">Дозвіл не надано</string>\n    <string name=\"error_gif\">Висота та ширина зображення повинна становити мінімум %1$dpx, а розмір файлу не повинен перевищувати %2$sM</string>\n</resources>\n"
  },
  {
    "path": "sample/src/main/res/values-zh/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright 2017 Zhihu Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n  -->\n<resources>\n    <string name=\"permission_request_denied\">需要相应的权限</string>\n    <string name=\"error_gif\">长宽不小于 %1$dpx，且大小不超过 %2$sM。</string>\n    <string name=\"error_original\">大小不超过 %1$sM。</string>\n</resources>\n"
  },
  {
    "path": "sample/src/main/res/values-zh-rTW/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright 2017 Zhihu Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n  -->\n<resources>\n    <string name=\"permission_request_denied\">需要相應的權限</string>\n    <string name=\"error_gif\">長寬不小于 %1$dpx，且大小不超過 %2$sM。</string>\n</resources>"
  },
  {
    "path": "sample/src/main/res/xml/file_paths_private.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright 2017 Zhihu Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n  -->\n<paths>\n    <external-path\n        name=\"my_images\"\n        path=\"Android/data/com.zhihu.matisse.sample/files/Pictures\"/>\n</paths>"
  },
  {
    "path": "sample/src/main/res/xml/file_paths_public.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  Copyright 2017 Zhihu Inc.\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n  -->\n<paths>\n    <external-path\n        name=\"my_images\"\n        path=\"Pictures\"/>\n</paths>"
  },
  {
    "path": "settings.gradle",
    "content": "include ':sample', ':matisse'\n"
  }
]